Compare commits

...

17 Commits

Author SHA1 Message Date
Andrew Gerrand
0584eb2e77 [release-branch.r58] reflect: disallow Interface method on Value obtained via unexported name
Also remove exp/datafmt that depends on the broken reflect behavior.

««« CL 5267049 / eeca0d4a91a3
reflect: disallow Interface method on Value obtained via unexported name

Had been allowing it for use by fmt, but it is too hard to lock down.
Fix other packages not to depend on it.

R=r, r
CC=golang-dev
https://golang.org/cl/5266054
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5297042
2011-10-18 14:06:11 +11:00
Russ Cox
adfa87c5d7 [release-branch.r58] cgo: handle new Apple LLVM-based gcc from Xcode 4.2
««« CL 4607045 / 142f0bc0d6e7
cgo: handle new Apple LLVM-based gcc from Xcode 4.2

That gcc does not include enumerator names and values
in its DWARF debug output.  Create a data block from which
we can read the values instead.

Fixes #1881.

R=iant
CC=golang-dev
https://golang.org/cl/4607045
»»»

R=adg
CC=golang-dev
https://golang.org/cl/4708042
2011-07-12 10:19:11 -04:00
Russ Cox
af97a0b94c [release-branch.r58] doc: document r58.1
««« CL 4700041 / 7c6b5a591a86
doc: document r58.1

R=adg, dsymonds
CC=golang-dev
https://golang.org/cl/4700041
»»»

R=adg
CC=golang-dev
https://golang.org/cl/4701041
2011-07-12 01:41:46 -04:00
Russ Cox
beca117ea3 [release-branch.r58] build: use awk instead of giant egrep regexp
««« CL 4603056 / 655a4be3968f
build: use awk instead of giant egrep regexp

Avoids buggy version of egrep on some Macs.

R=r, dsymonds
CC=golang-dev
https://golang.org/cl/4603056
»»»

R=adg
CC=golang-dev
https://golang.org/cl/4695042
2011-07-11 23:38:12 -04:00
Russ Cox
ecfec2c61a [release-branch.r58] runtime/cgo: fix for OS X 10.7
««« CL 4603057 / 0905a2ca94c6
runtime/cgo: fix for OS X 10.7

Correct a few error messages (libcgo -> runtime/cgo)
and delete old nacl_386.c file too.

Fixes #1657.

R=iant
CC=golang-dev
https://golang.org/cl/4603057
»»»

R=adg
CC=golang-dev
https://golang.org/cl/4698041
2011-07-11 23:38:02 -04:00
Andrew Gerrand
fb10bce0c2 [release-branch.r58] document release.r58
««« CL 4643058 / 0a5e3e664637
document release.r58

R=rsc, r, bsiegert
CC=golang-dev
https://golang.org/cl/4643058
»»»

R=golang-dev
CC=golang-dev
https://golang.org/cl/4641084
2011-06-30 09:49:11 +10:00
Andrew Gerrand
ff5182390a [release-branch.r58] gofix: fixes for os/signal changes
««« CL 4630056 / 8fe2bc5c3d53
gofix: fixes for os/signal changes

Fixes #1971.

R=adg, rsc
CC=golang-dev
https://golang.org/cl/4630056

»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/4645071
2011-06-29 16:57:41 +10:00
Andrew Gerrand
36d155b2b5 [release-branch.r58] gopprof: update list of memory allocators
««« CL 4650048 / 09d52e36dab9
gopprof: update list of memory allocators

Also import new weblist command from Google version.

R=r, bradfitz
CC=golang-dev
https://golang.org/cl/4650048
»»»

R=rsc
CC=golang-dev
https://golang.org/cl/4654072
2011-06-29 15:40:29 +10:00
Andrew Gerrand
f86856b083 [release-branch.r58] ld: dwarf emit filenames in debug_line header instead of as extended opcodes.
««« CL 4609043 / caaab1e64d49
ld: dwarf emit filenames in debug_line header instead of as extended opcodes.

Makes it possible for older tools like objdump to find the filenames,
fixes  objdump -d -l --start-address=0x400c00 --stop-address=0x400c36 6.out
fixes #1950

R=rsc
CC=golang-dev
https://golang.org/cl/4609043
»»»

R=rsc
CC=golang-dev
https://golang.org/cl/4648068
2011-06-29 15:38:55 +10:00
Andrew Gerrand
f12a1d38b2 [release-branch.r58] 6g, 8g: fix goto fix
««« CL 4632041 / cbc2b570b2ca
6g, 8g: fix goto fix

R=ken2
CC=golang-dev
https://golang.org/cl/4632041
»»»

R=rsc
CC=golang-dev
https://golang.org/cl/4667046
2011-06-29 15:33:16 +10:00
Andrew Gerrand
3b32b3eb3d [release-branch.r58] gc: work around goto bug
««« CL 4629042 / ec3b60d1fe6e
gc: work around goto bug

R=ken2
CC=golang-dev
https://golang.org/cl/4629042
»»»

R=rsc
CC=golang-dev
https://golang.org/cl/4662063
2011-06-29 15:32:06 +10:00
Andrew Gerrand
f70c7b2b63 [release-branch.r58] doc/faq: remove misleading FAQ entry
««« CL 4638046 / 9017f7cbac7d
doc/faq: remove misleading FAQ entry

R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/4638046
»»»

R=r
CC=golang-dev
https://golang.org/cl/4648066
2011-06-29 15:00:27 +10:00
Andrew Gerrand
01a1c91696 [release-branch.r58] doc/faq: add question about converting from []T to []interface{}
««« CL 4639046 / 995095e59d58
doc/faq: add question about converting from []T to []interface{}

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/4639046
»»»

R=r
CC=golang-dev
https://golang.org/cl/4630077
2011-06-29 14:58:01 +10:00
Andrew Gerrand
dbdc8698df [release-branch.r58] doc/GoCourseDay1: shrink the PDF by rewriting it using ps2pdf.
««« CL 4626056 / b83d5dcc660d
doc/GoCourseDay1: shrink the PDF by rewriting it using ps2pdf.
No difference in content or appearance.
Forgot to do this when I updated this file a few days ago.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4626056
»»»

R=r
CC=golang-dev
https://golang.org/cl/4630076
2011-06-29 14:56:46 +10:00
Andrew Gerrand
47906598d8 [release-branch.r58] docs/GoCourseDay1.pdf: fix error in operator table.
««« CL 4637041 / df607ef238c9
docs/GoCourseDay1.pdf: fix error in operator table.
Communications op was listed as a binary; it isn't any more.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4637041
»»»

R=r
CC=golang-dev
https://golang.org/cl/4625076
2011-06-29 14:55:32 +10:00
Andrew Gerrand
0ea0d7b65c [release-branch.r58] docs: Update notes for 3-day Go course.
««« CL 4605041 / 71776ebc7416
docs: Update notes for 3-day Go course.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/4605041
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/4657058
2011-06-29 14:45:53 +10:00
Andrew Gerrand
35f3007cf1 create release-branch.r58 2011-06-29 13:46:53 +10:00
41 changed files with 1272 additions and 1837 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -14,6 +14,86 @@ hg pull
hg update release.r<i>NN</i>
</pre>
<h2 id="r58">r58 (released 2011/06/29)</h2>
<p>
The r58 release corresponds to
<code><a href="weekly.html#2011-06-09">weekly.2011-06-09</a></code>
with additional bug fixes.
This section highlights the most significant changes in this release.
For a more detailed summary, see the
<a href="weekly.html#2011-06-09">weekly release notes</a>.
For complete information, see the
<a href="http://code.google.com/p/go/source/list?r=release-branch.r58">Mercurial change list</a>.
</p>
<h3 id="r58.lang">Language</h3>
<p>
This release fixes a <a href="http://code.google.com/p/go/source/detail?r=b720749486e1">use of uninitialized memory in programs that misuse <code>goto</code></a>.
</p>
<h3 id="r58.pkg">Packages</h3>
<p>
As usual, <a href="/cmd/gofix/">gofix</a> will handle the bulk of the rewrites
necessary for these changes to package APIs.
</p>
<p>
<a href="/pkg/http/">Package http</a> drops the <code>finalURL</code> return
value from the <a href="/pkg/http/#Client.Get">Client.Get</a> method. The value
is now available via the new <code>Request</code> field on <a
href="/pkg/http/#Response">http.Response</a>.
Most instances of the type map[string][]string in have been
replaced with the new <a href="/pkg/http/#Values">Values</a> type.
</p>
<p>
<a href="/pkg/exec/">Package exec</a> has been redesigned with a more
convenient and succinct API.
</p>
<p>
<a href="/pkg/strconv/">Package strconv</a>'s <a href="/pkg/strconv/#Quote">Quote</a>
function now escapes only those Unicode code points not classified as printable
by <a href="/pkg/unicode/#IsPrint">unicode.IsPrint</a>.
Previously Quote would escape all non-ASCII characters.
This also affects the <a href="/pkg/fmt/">fmt</a> package's <code>"%q"</code>
formatting directive. The previous quoting behavior is still available via
strconv's new <a href="/pkg/strconv/#QuoteToASCII">QuoteToASCII</a> function.
</p>
<p>
<a href="/pkg/os/signal/">Package os/signal</a>'s
<a href="/pkg/os/#Signal">Signal</a> and
<a href="/pkg/os/#UnixSignal">UnixSignal</a> types have been moved to the
<a href="/pkg/os/">os</a> package.
</p>
<p>
<a href="/pkg/image/draw/">Package image/draw</a> is the new name for
<code>exp/draw</code>. The GUI-related code from <code>exp/draw</code> is now
located in the <a href="/pkg/exp/gui/">exp/gui</a> package.
</p>
<h3 id="r58.cmd">Tools</h3>
<p>
<a href="/cmd/goinstall/">Goinstall</a> now observes the GOPATH environment
variable to build and install your own code and external libraries outside of
the Go tree (and avoid writing Makefiles).
</p>
<h3 id="r58.minor">Minor revisions</h3>
<p>r58.1 adds
<a href="http://code.google.com/p/go/source/detail?r=293c25943586">build</a> and
<a href="http://code.google.com/p/go/source/detail?r=bf17e96b6582">runtime</a>
changes to make Go run on OS X 10.7 Lion.
</p>
<h2 id="r57">r57 (released 2011/05/03)</h2>
<p>
@@ -162,6 +242,7 @@ For other uses, see the <a href="/pkg/runtime/pprof/">runtime/pprof</a> document
<h3 id="r57.minor">Minor revisions</h3>
<p>r57.1 fixes a <a href="http://code.google.com/p/go/source/detail?r=ff2bc62726e7145eb2ecc1e0f076998e4a8f86f0">nil pointer dereference in http.FormFile</a>.</p>
<p>r57.2 fixes a <a href="http://code.google.com/p/go/source/detail?r=063b0ff67d8277df03c956208abc068076818dae">use of uninitialized memory in programs that misuse <code>goto</code></a>.</p>
<h2 id="r56">r56 (released 2011/03/16)</h2>

View File

@@ -14,7 +14,7 @@ hg pull
hg update weekly.<i>YYYY-MM-DD</i>
</pre>
<h2 id="2011-06-09">2011-06-09</h2>
<h2 id="2011-06-09">2011-06-09 (<a href="release.html#r58">base for r58</a>)</h2>
<pre>
This release includes changes to the strconv, http, and exp/draw packages.

View File

@@ -23,6 +23,17 @@ concepts: syntax, types, allocation, constants, I/O, sorting, printing,
goroutines, and channels.
</p>
<h3 id="course_notes">Course Notes</h3>
<p>
Slides from a 3-day course about the Go programming language.
A more thorough introduction than the tutorial.
</p>
<ul>
<li><a href="GoCourseDay1.pdf">Day 1: Basics</a> <small>[270KB PDF]</small>
<li><a href="GoCourseDay2.pdf">Day 2: Types, Methods, Interfaces</a> <small>[270KB PDF]</small>
<li><a href="GoCourseDay3.pdf">Day 3: Concurrency and Communication</a> <small>[180KB PDF]</small>
</ul>
<h3 id="effective_go"><a href="effective_go.html">Effective Go</a></h3>
<p>
A document that gives tips for writing clear, idiomatic Go code.
@@ -209,7 +220,7 @@ from Hoares 1978 paper to Go provides insight into how and why Go works as it
does.
</i></p>
<h3 id="emerging_go"><a href="talks/gofrontend-gcc-summit-2010.pdf">The Go frontend for GCC</a></h3>
<h3 id="go_frontend_gcc"><a href="talks/gofrontend-gcc-summit-2010.pdf">The Go frontend for GCC</a></h3>
<p>
A description of the Go language frontend for gcc.
Ian Lance Taylor's paper delivered at the GCC Summit 2010.

View File

@@ -183,16 +183,6 @@ easier to understand what happens when things combine.
<h2 id="Usage">Usage</h2>
<h3 id="Who_should_use_the_language">
Who should use the language?</h3>
<p>
Go is an experiment. We hope adventurous users will give it a try and see
if they enjoy it. Not every programmer
will, but we hope enough will find satisfaction in the approach it
offers to justify further development.
</p>
<h3 id="Is_Google_using_go_internally"> Is Google using Go internally?</h3>
<p>
@@ -598,6 +588,24 @@ the interface idea. Sometimes, though, they're necessary to resolve ambiguities
among similar interfaces.
</p>
<h3 id="convert_slice_of_interface">
Can I convert a []T to an []interface{}?</h3>
<p>
Not directly because they do not have the same representation in memory.
It is necessary to copy the elements individually to the destination
slice. This example converts a slice of <code>int</code> to a slice of
<code>interface{}</code>:
</p>
<pre>
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
</pre>
<h2 id="values">Values</h2>
<h3 id="conversions">

View File

@@ -10,8 +10,7 @@ After you've read this tutorial, you should look at
which digs deeper into how the language is used and
talks about the style and idioms of programming in Go.
Also, slides from a 3-day course about Go are available.
Although they're badly out of date, they provide some
background and a lot of examples:
They provide some background and a lot of examples:
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.

View File

@@ -11,8 +11,7 @@ After you've read this tutorial, you should look at
which digs deeper into how the language is used and
talks about the style and idioms of programming in Go.
Also, slides from a 3-day course about Go are available.
Although they're badly out of date, they provide some
background and a lot of examples:
They provide some background and a lot of examples:
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.

View File

@@ -124,6 +124,64 @@ newplist(void)
return pl;
}
void
clearstk(void)
{
Plist *pl;
Prog *p, *p1, *p2, *p3;
Node dst, end, zero, con;
if(plast->firstpc->to.offset <= 0)
return;
// reestablish context for inserting code
// at beginning of function.
pl = plast;
p1 = pl->firstpc;
p2 = p1->link;
pc = mal(sizeof(*pc));
clearp(pc);
p1->link = pc;
// zero stack frame
// MOVW $4(SP), R1
nodreg(&dst, types[tptr], 1);
p = gins(AMOVW, N, &dst);
p->from.type = D_CONST;
p->from.reg = REGSP;
p->from.offset = 4;
// MOVW $n(R1), R2
nodreg(&end, types[tptr], 2);
p = gins(AMOVW, N, &end);
p->from.type = D_CONST;
p->from.reg = 1;
p->from.offset = p1->to.offset;
// MOVW $0, R3
nodreg(&zero, types[TUINT32], 3);
nodconst(&con, types[TUINT32], 0);
gmove(&con, &zero);
// L:
// MOVW.P R3, 0(R1) +4
// CMP R1, R2
// BNE L
p = gins(AMOVW, &zero, &dst);
p->to.type = D_OREG;
p->to.offset = 4;
p->scond |= C_PBIT;
p3 = p;
p = gins(ACMP, &dst, N);
raddr(&end, p);
patch(gbranch(ABNE, T), p3);
// continue with original code.
gins(ANOP, N, N)->link = p2;
pc = P;
}
void
gused(Node *n)
{

View File

@@ -120,6 +120,44 @@ newplist(void)
return pl;
}
void
clearstk(void)
{
Plist *pl;
Prog *p1, *p2;
Node sp, di, cx, con, ax;
if((uint32)plast->firstpc->to.offset <= 0)
return;
// reestablish context for inserting code
// at beginning of function.
pl = plast;
p1 = pl->firstpc;
p2 = p1->link;
pc = mal(sizeof(*pc));
clearp(pc);
p1->link = pc;
// zero stack frame
nodreg(&sp, types[tptr], D_SP);
nodreg(&di, types[tptr], D_DI);
nodreg(&cx, types[TUINT64], D_CX);
nodconst(&con, types[TUINT64], (uint32)p1->to.offset / widthptr);
gins(ACLD, N, N);
gins(AMOVQ, &sp, &di);
gins(AMOVQ, &con, &cx);
nodconst(&con, types[TUINT64], 0);
nodreg(&ax, types[TUINT64], D_AX);
gins(AMOVQ, &con, &ax);
gins(AREP, N, N);
gins(ASTOSQ, N, N);
// continue with original code.
gins(ANOP, N, N)->link = p2;
pc = P;
}
void
gused(Node *n)
{

View File

@@ -122,6 +122,44 @@ newplist(void)
return pl;
}
void
clearstk(void)
{
Plist *pl;
Prog *p1, *p2;
Node sp, di, cx, con, ax;
if(plast->firstpc->to.offset <= 0)
return;
// reestablish context for inserting code
// at beginning of function.
pl = plast;
p1 = pl->firstpc;
p2 = p1->link;
pc = mal(sizeof(*pc));
clearp(pc);
p1->link = pc;
// zero stack frame
nodreg(&sp, types[tptr], D_SP);
nodreg(&di, types[tptr], D_DI);
nodreg(&cx, types[TUINT32], D_CX);
nodconst(&con, types[TUINT32], p1->to.offset / widthptr);
gins(ACLD, N, N);
gins(AMOVL, &sp, &di);
gins(AMOVL, &con, &cx);
nodconst(&con, types[TUINT32], 0);
nodreg(&ax, types[TUINT32], D_AX);
gins(AMOVL, &con, &ax);
gins(AREP, N, N);
gins(ASTOSL, N, N);
// continue with original code.
gins(ANOP, N, N)->link = p2;
pc = P;
}
void
gused(Node *n)
{

View File

@@ -13,6 +13,7 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
"encoding/binary"
"flag"
"fmt"
"go/ast"
@@ -477,7 +478,27 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
}
}
d := p.gccDebug(b.Bytes())
// Apple's LLVM-based gcc does not include the enumeration
// names and values in its DWARF debug output. In case we're
// using such a gcc, create a data block initialized with the values.
// We can read them out of the object file.
fmt.Fprintf(&b, "long long __cgodebug_data[] = {\n")
for _, n := range names {
if n.Kind == "const" {
fmt.Fprintf(&b, "\t%s,\n", n.C)
} else {
fmt.Fprintf(&b, "\t0,\n")
}
}
fmt.Fprintf(&b, "\t0\n")
fmt.Fprintf(&b, "};\n")
d, bo, debugData := p.gccDebug(b.Bytes())
enumVal := make([]int64, len(debugData)/8)
for i := range enumVal {
enumVal[i] = int64(bo.Uint64(debugData[i*8:]))
}
// Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
types := make([]dwarf.Type, len(names))
@@ -569,9 +590,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// Remove injected enum to ensure the value will deep-compare
// equally in future loads of the same constant.
n.Type.EnumValues[k] = 0, false
} else if n.Kind == "const" && i < len(enumVal) {
n.Const = strconv.Itoa64(enumVal[i])
}
}
}
}
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
@@ -593,6 +617,9 @@ func (p *Package) rewriteRef(f *File) {
// are trying to do a ,err call. Also check that
// functions are only used in calls.
for _, r := range f.Ref {
if r.Name.Kind == "const" && r.Name.Const == "" {
error(r.Pos(), "unable to find value of constant C.%s", r.Name.Go)
}
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context {
case "call", "call2":
@@ -692,29 +719,57 @@ func (p *Package) gccCmd() []string {
}
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and any messages
// printed to standard error.
func (p *Package) gccDebug(stdin []byte) *dwarf.Data {
// returns the corresponding DWARF data and, if present, debug data block.
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd())
// Try to parse f as ELF and Mach-O and hope one works.
var f interface {
DWARF() (*dwarf.Data, os.Error)
}
var err os.Error
if f, err = elf.Open(gccTmp); err != nil {
if f, err = macho.Open(gccTmp); err != nil {
if f, err = pe.Open(gccTmp); err != nil {
fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp)
if f, err := macho.Open(gccTmp); err == nil {
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
}
var data []byte
if f.Symtab != nil {
for i := range f.Symtab.Syms {
s := &f.Symtab.Syms[i]
// Mach-O still uses a leading _ to denote non-assembly symbols.
if s.Name == "_"+"__cgodebug_data" {
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data = sdat[s.Value-sect.Addr:]
}
}
}
}
}
}
return d, f.ByteOrder, data
}
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err)
// Can skip debug data block in ELF and PE for now.
// The DWARF information is complete.
if f, err := elf.Open(gccTmp); err == nil {
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
}
return d, f.ByteOrder, nil
}
return d
if f, err := pe.Open(gccTmp); err == nil {
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
}
return d, binary.LittleEndian, nil
}
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp)
panic("not reached")
}
// gccDefines runs gcc -E -dM -xc - over the C program stdin

View File

@@ -1241,9 +1241,14 @@ funccompile(Node *n, int isclosure)
stksize = 0;
dclcontext = PAUTO;
funcdepth = n->funcdepth + 1;
hasgoto = 0;
compile(n);
if(hasgoto)
clearstk();
curfn = nil;
funcdepth = 0;
dclcontext = PEXTERN;
}

View File

@@ -209,6 +209,7 @@ gen(Node *n)
break;
case OGOTO:
hasgoto = 1;
newlab(OGOTO, n, N);
gjmp(P);
break;

View File

@@ -1255,3 +1255,6 @@ void zhist(Biobuf *b, int line, vlong offset);
void zname(Biobuf *b, Sym *s, int t);
void data(void);
void text(void);
EXTERN int hasgoto;
void clearstk(void);

View File

@@ -14,6 +14,7 @@ GOFILES=\
httpserver.go\
procattr.go\
reflect.go\
signal.go\
typecheck.go\
include ../../Make.cmd

View File

@@ -10,6 +10,7 @@ import (
"go/token"
"os"
"strconv"
"strings"
)
type fix struct {
@@ -258,13 +259,28 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
// imports returns true if f imports path.
func imports(f *ast.File, path string) bool {
return importSpec(f, path) != nil
}
// importSpec returns the import spec if f imports path,
// or nil otherwise.
func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
t, err := strconv.Unquote(s.Path.Value)
if err == nil && t == path {
return true
if importPath(s) == path {
return s
}
}
return false
return nil
}
// importPath returns the unquoted import path of s,
// or "" if the path is not properly quoted.
func importPath(s *ast.ImportSpec) string {
t, err := strconv.Unquote(s.Path.Value)
if err == nil {
return t
}
return ""
}
// isPkgDot returns true if t is the expression "pkg.name"
@@ -420,3 +436,138 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
},
}
}
// addImport adds the import path to the file f, if absent.
func addImport(f *ast.File, path string) {
if imports(f, path) {
return
}
newImport := &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(path),
},
}
var impdecl *ast.GenDecl
// Find an import decl to add to.
for _, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if ok && gen.Tok == token.IMPORT {
impdecl = gen
break
}
}
// No import decl found. Add one.
if impdecl == nil {
impdecl = &ast.GenDecl{
Tok: token.IMPORT,
}
f.Decls = append(f.Decls, nil)
copy(f.Decls[1:], f.Decls)
f.Decls[0] = impdecl
}
// Ensure the import decl has parentheses, if needed.
if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() {
impdecl.Lparen = impdecl.Pos()
}
// Assume the import paths are alphabetically ordered.
// If they are not, the result is ugly, but legal.
insertAt := len(impdecl.Specs) // default to end of specs
for i, spec := range impdecl.Specs {
impspec := spec.(*ast.ImportSpec)
if importPath(impspec) > path {
insertAt = i
break
}
}
impdecl.Specs = append(impdecl.Specs, nil)
copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:])
impdecl.Specs[insertAt] = newImport
f.Imports = append(f.Imports, newImport)
}
// deleteImport deletes the import path from the file f, if present.
func deleteImport(f *ast.File, path string) {
oldImport := importSpec(f, path)
// Find the import node that imports path, if any.
for i, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.IMPORT {
continue
}
for j, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
if oldImport != impspec {
continue
}
// We found an import spec that imports path.
// Delete it.
copy(gen.Specs[j:], gen.Specs[j+1:])
gen.Specs = gen.Specs[:len(gen.Specs)-1]
// If this was the last import spec in this decl,
// delete the decl, too.
if len(gen.Specs) == 0 {
copy(f.Decls[i:], f.Decls[i+1:])
f.Decls = f.Decls[:len(f.Decls)-1]
} else if len(gen.Specs) == 1 {
gen.Lparen = token.NoPos // drop parens
}
break
}
}
// Delete it from f.Imports.
for i, imp := range f.Imports {
if imp == oldImport {
copy(f.Imports[i:], f.Imports[i+1:])
f.Imports = f.Imports[:len(f.Imports)-1]
break
}
}
}
func usesImport(f *ast.File, path string) (used bool) {
spec := importSpec(f, path)
if spec == nil {
return
}
name := spec.Name.String()
switch name {
case "<nil>":
// If the package name is not explicitly specified,
// make an educated guess. This is not guaranteed to be correct.
lastSlash := strings.LastIndex(path, "/")
if lastSlash == -1 {
name = path
} else {
name = path[lastSlash+1:]
}
case "_", ".":
// Not sure if this import is used - err on the side of caution.
return true
}
walk(f, func(n interface{}) {
sel, ok := n.(*ast.SelectorExpr)
if ok && isTopName(sel.X, name) {
used = true
}
})
return
}

49
src/cmd/gofix/signal.go Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2011 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 main
import (
"go/ast"
"strings"
)
func init() {
register(fix{
"signal",
signal,
`Adapt code to types moved from os/signal to signal.
http://codereview.appspot.com/4437091
`,
})
}
func signal(f *ast.File) (fixed bool) {
if !imports(f, "os/signal") {
return
}
walk(f, func(n interface{}) {
s, ok := n.(*ast.SelectorExpr)
if !ok || !isTopName(s.X, "signal") {
return
}
sel := s.Sel.String()
if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") {
s.X = &ast.Ident{Name: "os"}
fixed = true
}
})
if fixed {
addImport(f, "os")
if !usesImport(f, "os/signal") {
deleteImport(f, "os/signal")
}
}
return
}

View File

@@ -0,0 +1,96 @@
// Copyright 2011 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 main
func init() {
addTestCases(signalTests)
}
var signalTests = []testCase{
{
Name: "signal.0",
In: `package main
import (
_ "a"
"os/signal"
_ "z"
)
type T1 signal.UnixSignal
type T2 signal.Signal
func f() {
_ = signal.SIGHUP
_ = signal.Incoming
}
`,
Out: `package main
import (
_ "a"
"os"
"os/signal"
_ "z"
)
type T1 os.UnixSignal
type T2 os.Signal
func f() {
_ = os.SIGHUP
_ = signal.Incoming
}
`,
},
{
Name: "signal.1",
In: `package main
import (
"os"
"os/signal"
)
func f() {
var _ os.Error
_ = signal.SIGHUP
}
`,
Out: `package main
import "os"
func f() {
var _ os.Error
_ = os.SIGHUP
}
`,
},
{
Name: "signal.2",
In: `package main
import "os"
import "os/signal"
func f() {
var _ os.Error
_ = signal.SIGHUP
}
`,
Out: `package main
import "os"
func f() {
var _ os.Error
_ = os.SIGHUP
}
`,
},
}

View File

@@ -1804,7 +1804,7 @@ mkvarname(char* name, int da)
// flush previous compilation unit.
static void
flushunit(DWDie *dwinfo, vlong pc, vlong unitstart)
flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length)
{
vlong here;
@@ -1820,7 +1820,9 @@ flushunit(DWDie *dwinfo, vlong pc, vlong unitstart)
here = cpos();
seek(cout, unitstart, 0);
LPUT(here - unitstart - sizeof(int32));
LPUT(here - unitstart - sizeof(int32)); // unit_length
WPUT(3); // dwarf version
LPUT(header_length); // header lenght starting here
cflush();
seek(cout, here, 0);
}
@@ -1832,7 +1834,7 @@ writelines(void)
Prog *q;
Sym *s;
Auto *a;
vlong unitstart, offs;
vlong unitstart, headerend, offs;
vlong pc, epc, lc, llc, lline;
int currfile;
int i, lang, da, dt;
@@ -1842,6 +1844,7 @@ writelines(void)
char *n, *nn;
unitstart = -1;
headerend = -1;
pc = 0;
epc = 0;
lc = 1;
@@ -1859,7 +1862,7 @@ writelines(void)
// we're entering a new compilation unit
if (inithist(s->autom)) {
flushunit(dwinfo, epc, unitstart);
flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10);
unitstart = cpos();
if(debug['v'] > 1) {
@@ -1880,10 +1883,10 @@ writelines(void)
// Write .debug_line Line Number Program Header (sec 6.2.4)
// Fields marked with (*) must be changed for 64-bit dwarf
LPUT(0); // unit_length (*), will be filled in later.
LPUT(0); // unit_length (*), will be filled in by flushunit.
WPUT(3); // dwarf version (appendix F)
LPUT(11); // header_length (*), starting here.
LPUT(0); // header_length (*), filled in by flushunit.
// cpos == unitstart + 4 + 2 + 4
cput(1); // minimum_instruction_length
cput(1); // default_is_stmt
cput(LINE_BASE); // line_base
@@ -1894,17 +1897,15 @@ writelines(void)
cput(1); // standard_opcode_lengths[3]
cput(1); // standard_opcode_lengths[4]
cput(0); // include_directories (empty)
cput(0); // file_names (empty) (emitted by DW_LNE's below)
// header_length ends here.
for (i=1; i < histfilesize; i++) {
cput(0); // start extended opcode
uleb128put(1 + strlen(histfile[i]) + 4);
cput(DW_LNE_define_file);
strnput(histfile[i], strlen(histfile[i]) + 4);
// 4 zeros: the string termination + 3 fields.
}
cput(0); // terminate file_names.
headerend = cpos();
pc = s->text->pc;
epc = pc;
currfile = 1;
@@ -2009,7 +2010,7 @@ writelines(void)
dwfunc->hash = nil;
}
flushunit(dwinfo, epc, unitstart);
flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10);
linesize = cpos() - lineo;
}

View File

@@ -150,7 +150,8 @@ pprof [options] <profile>
The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
$GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
or /pprof/filteredprofile.
For instance: "pprof http://myserver.com:80$HEAP_PAGE".
For instance:
pprof http://myserver.com:80$HEAP_PAGE
If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
pprof --symbols <program>
Maps addresses to symbol names. In this mode, stdin should be a
@@ -532,7 +533,7 @@ sub Init() {
ConfigureObjTools($main::prog)
}
# Break the opt_list_prefix into the prefix_list array
# Break the opt_lib_prefix into the prefix_list array
@prefix_list = split (',', $main::opt_lib_prefix);
# Remove trailing / from the prefixes, in the list to prevent
@@ -626,7 +627,7 @@ sub Main() {
if ($main::opt_disasm) {
PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm, $total);
} elsif ($main::opt_list) {
PrintListing($libs, $flat, $cumulative, $main::opt_list);
PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
} elsif ($main::opt_text) {
# Make sure the output is empty when have nothing to report
# (only matters when --heapcheck is given but we must be
@@ -814,7 +815,7 @@ sub InteractiveCommand {
my $ignore;
($routine, $ignore) = ParseInteractiveArgs($3);
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
my $reduced = ReduceProfile($symbols, $profile);
# Get derived profiles
@@ -841,21 +842,22 @@ sub InteractiveCommand {
return 1;
}
if (m/^\s*list\s*(.+)/) {
if (m/^\s*(web)?list\s*(.+)/) {
my $html = (defined($1) && ($1 eq "web"));
$main::opt_list = 1;
my $routine;
my $ignore;
($routine, $ignore) = ParseInteractiveArgs($1);
($routine, $ignore) = ParseInteractiveArgs($2);
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
my $reduced = ReduceProfile($symbols, $profile);
# Get derived profiles
my $flat = FlatProfile($reduced);
my $cumulative = CumulativeProfile($reduced);
PrintListing($libs, $flat, $cumulative, $routine);
PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
return 1;
}
if (m/^\s*disasm\s*(.+)/) {
@@ -866,7 +868,7 @@ sub InteractiveCommand {
($routine, $ignore) = ParseInteractiveArgs($1);
# Process current profile to account for various settings
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
my $reduced = ReduceProfile($symbols, $profile);
# Get derived profiles
@@ -890,7 +892,7 @@ sub InteractiveCommand {
($focus, $ignore) = ParseInteractiveArgs($2);
# Process current profile to account for various settings
my $profile = ProcessProfile($orig_profile, $symbols, $focus, $ignore);
my $profile = ProcessProfile($total, $orig_profile, $symbols, $focus, $ignore);
my $reduced = ReduceProfile($symbols, $profile);
# Get derived profiles
@@ -916,6 +918,7 @@ sub InteractiveCommand {
sub ProcessProfile {
my $total_count = shift;
my $orig_profile = shift;
my $symbols = shift;
my $focus = shift;
@@ -923,7 +926,6 @@ sub ProcessProfile {
# Process current profile to account for various settings
my $profile = $orig_profile;
my $total_count = TotalProfile($profile);
printf("Total: %s %s\n", Unparse($total_count), Units());
if ($focus ne '') {
$profile = FocusProfile($symbols, $profile, $focus);
@@ -970,6 +972,11 @@ Commands:
list [routine_regexp] [-ignore1] [-ignore2]
Show source listing of routines whose names match "routine_regexp"
weblist [routine_regexp] [-ignore1] [-ignore2]
Displays a source listing of routines whose names match "routine_regexp"
in a web browser. You can click on source lines to view the
corresponding disassembly.
top [--cum] [-ignore1] [-ignore2]
top20 [--cum] [-ignore1] [-ignore2]
top37 [--cum] [-ignore1] [-ignore2]
@@ -1144,7 +1151,7 @@ sub PrintText {
$sym);
}
$lines++;
last if ($line_limit >= 0 && $lines > $line_limit);
last if ($line_limit >= 0 && $lines >= $line_limit);
}
}
@@ -1291,11 +1298,32 @@ sub ByName {
# Print source-listing for all all routines that match $main::opt_list
sub PrintListing {
my $total = shift;
my $libs = shift;
my $flat = shift;
my $cumulative = shift;
my $list_opts = shift;
my $html = shift;
my $output = \*STDOUT;
my $fname = "";
if ($html) {
# Arrange to write the output to a temporary file
$fname = TempName($main::next_tmpfile, "html");
$main::next_tmpfile++;
if (!open(TEMP, ">$fname")) {
print STDERR "$fname: $!\n";
return;
}
$output = \*TEMP;
print $output HtmlListingHeader();
printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
$main::prog, Unparse($total), Units());
}
my $listed = 0;
foreach my $lib (@{$libs}) {
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
my $offset = AddressSub($lib->[1], $lib->[3]);
@@ -1307,15 +1335,98 @@ sub PrintListing {
my $addr = AddressAdd($start_addr, $offset);
for (my $i = 0; $i < $length; $i++) {
if (defined($cumulative->{$addr})) {
PrintSource($lib->[0], $offset,
$routine, $flat, $cumulative,
$start_addr, $end_addr);
$listed += PrintSource(
$lib->[0], $offset,
$routine, $flat, $cumulative,
$start_addr, $end_addr,
$html,
$output);
last;
}
$addr = AddressInc($addr);
}
}
}
if ($html) {
if ($listed > 0) {
print $output HtmlListingFooter();
close($output);
RunWeb($fname);
} else {
close($output);
unlink($fname);
}
}
}
sub HtmlListingHeader {
return <<'EOF';
<DOCTYPE html>
<html>
<head>
<title>Pprof listing</title>
<style type="text/css">
body {
font-family: sans-serif;
}
h1 {
font-size: 1.5em;
margin-bottom: 4px;
}
.legend {
font-size: 1.25em;
}
.line {
color: #aaaaaa;
}
.livesrc {
color: #0000ff;
cursor: pointer;
}
.livesrc:hover {
background-color: #cccccc;
}
.asm {
color: #888888;
display: none;
}
</style>
<script type="text/javascript">
function pprof_toggle_asm(e) {
var target;
if (!e) e = window.event;
if (e.target) target = e.target;
else if (e.srcElement) target = e.srcElement;
if (target && target.className == "livesrc") {
var asm = target.nextSibling;
if (asm && asm.className == "asm") {
asm.style.display = (asm.style.display == "block" ? "none" : "block");
e.preventDefault();
return false;
}
}
}
</script>
</head>
<body>
EOF
}
sub HtmlListingFooter {
return <<'EOF';
</body>
</html>
EOF
}
sub HtmlEscape {
my $text = shift;
$text =~ s/&/&amp;/g;
$text =~ s/</&lt;/g;
$text =~ s/>/&gt;/g;
return $text;
}
# Returns the indentation of the line, if it has any non-whitespace
@@ -1338,6 +1449,8 @@ sub PrintSource {
my $cumulative = shift;
my $start_addr = shift;
my $end_addr = shift;
my $html = shift;
my $output = shift;
# Disassemble all instructions (just to get line numbers)
my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
@@ -1353,7 +1466,7 @@ sub PrintSource {
}
if (!defined($filename)) {
print STDERR "no filename found in $routine\n";
return;
return 0;
}
# Hack 2: assume that the largest line number from $filename is the
@@ -1386,7 +1499,7 @@ sub PrintSource {
{
if (!open(FILE, "<$filename")) {
print STDERR "$filename: $!\n";
return;
return 0;
}
my $l = 0;
my $first_indentation = -1;
@@ -1414,12 +1527,21 @@ sub PrintSource {
# Assign all samples to the range $firstline,$lastline,
# Hack 4: If an instruction does not occur in the range, its samples
# are moved to the next instruction that occurs in the range.
my $samples1 = {};
my $samples2 = {};
my $running1 = 0; # Unassigned flat counts
my $running2 = 0; # Unassigned cumulative counts
my $total1 = 0; # Total flat counts
my $total2 = 0; # Total cumulative counts
my $samples1 = {}; # Map from line number to flat count
my $samples2 = {}; # Map from line number to cumulative count
my $running1 = 0; # Unassigned flat counts
my $running2 = 0; # Unassigned cumulative counts
my $total1 = 0; # Total flat counts
my $total2 = 0; # Total cumulative counts
my %disasm = (); # Map from line number to disassembly
my $running_disasm = ""; # Unassigned disassembly
my $skip_marker = "---\n";
if ($html) {
$skip_marker = "";
for (my $l = $firstline; $l <= $lastline; $l++) {
$disasm{$l} = "";
}
}
foreach my $e (@instructions) {
# Add up counts for all address that fall inside this instruction
my $c1 = 0;
@@ -1428,6 +1550,15 @@ sub PrintSource {
$c1 += GetEntry($flat, $a);
$c2 += GetEntry($cumulative, $a);
}
if ($html) {
$running_disasm .= sprintf(" %6s %6s \t\t%8s: %s\n",
HtmlPrintNumber($c1),
HtmlPrintNumber($c2),
$e->[0],
CleanDisassembly($e->[3]));
}
$running1 += $c1;
$running2 += $c2;
$total1 += $c1;
@@ -1442,6 +1573,10 @@ sub PrintSource {
AddEntry($samples2, $line, $running2);
$running1 = 0;
$running2 = 0;
if ($html) {
$disasm{$line} .= $running_disasm;
$running_disasm = '';
}
}
}
@@ -1449,16 +1584,28 @@ sub PrintSource {
AddEntry($samples1, $lastline, $running1);
AddEntry($samples2, $lastline, $running2);
printf("ROUTINE ====================== %s in %s\n" .
"%6s %6s Total %s (flat / cumulative)\n",
ShortFunctionName($routine),
$filename,
Units(),
Unparse($total1),
Unparse($total2));
if ($html) {
printf $output (
"<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
"Total:%6s %6s (flat / cumulative %s)\n",
HtmlEscape(ShortFunctionName($routine)),
HtmlEscape($filename),
Unparse($total1),
Unparse($total2),
Units());
} else {
printf $output (
"ROUTINE ====================== %s in %s\n" .
"%6s %6s Total %s (flat / cumulative)\n",
ShortFunctionName($routine),
$filename,
Unparse($total1),
Unparse($total2),
Units());
}
if (!open(FILE, "<$filename")) {
print STDERR "$filename: $!\n";
return;
return 0;
}
my $l = 0;
while (<FILE>) {
@@ -1468,16 +1615,47 @@ sub PrintSource {
(($l <= $oldlastline + 5) || ($l <= $lastline))) {
chop;
my $text = $_;
if ($l == $firstline) { printf("---\n"); }
printf("%6s %6s %4d: %s\n",
UnparseAlt(GetEntry($samples1, $l)),
UnparseAlt(GetEntry($samples2, $l)),
$l,
$text);
if ($l == $lastline) { printf("---\n"); }
if ($l == $firstline) { print $output $skip_marker; }
my $n1 = GetEntry($samples1, $l);
my $n2 = GetEntry($samples2, $l);
if ($html) {
my $dis = $disasm{$l};
if (!defined($dis) || $n1 + $n2 == 0) {
# No samples/disassembly for this source line
printf $output (
"<span class=\"line\">%5d</span> " .
"<span class=\"deadsrc\">%6s %6s %s</span>\n",
$l,
HtmlPrintNumber($n1),
HtmlPrintNumber($n2),
HtmlEscape($text));
} else {
printf $output (
"<span class=\"line\">%5d</span> " .
"<span class=\"livesrc\">%6s %6s %s</span>" .
"<span class=\"asm\">%s</span>\n",
$l,
HtmlPrintNumber($n1),
HtmlPrintNumber($n2),
HtmlEscape($text),
HtmlEscape($dis));
}
} else {
printf $output(
"%6s %6s %4d: %s\n",
UnparseAlt($n1),
UnparseAlt($n2),
$l,
$text);
}
if ($l == $lastline) { print $output $skip_marker; }
};
}
close(FILE);
if ($html) {
print $output "</pre>\n";
}
return 1;
}
# Return the source line for the specified file/linenumber.
@@ -1625,16 +1803,11 @@ sub PrintDisassembledFunction {
$address =~ s/^0x//;
$address =~ s/^0*//;
# Trim symbols
my $d = $e->[3];
while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
printf("%6s %6s %8s: %6s\n",
UnparseAlt($flat_count[$x]),
UnparseAlt($cum_count[$x]),
$address,
$d);
CleanDisassembly($e->[3]));
}
}
}
@@ -2254,6 +2427,16 @@ sub UnparseAlt {
}
}
# Alternate pretty-printed form: 0 maps to ""
sub HtmlPrintNumber {
my $num = shift;
if ($num == 0) {
return "";
} else {
return Unparse($num);
}
}
# Return output units
sub Units {
if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
@@ -2415,6 +2598,8 @@ sub RemoveUninterestingFrames {
'copyin',
'gostring',
'gostringsize',
'growslice1',
'appendslice1',
'hash_init',
'hash_subtable_new',
'hash_conv',
@@ -2422,6 +2607,8 @@ sub RemoveUninterestingFrames {
'hash_insert_internal',
'hash_insert',
'mapassign',
'runtime.mapassign',
'runtime.appendslice',
'runtime.mapassign1',
'makechan',
'makemap',
@@ -2433,11 +2620,13 @@ sub RemoveUninterestingFrames {
'unsafe.New',
'runtime.mallocgc',
'runtime.catstring',
'runtime.growslice',
'runtime.ifaceT2E',
'runtime.ifaceT2I',
'runtime.makechan',
'runtime.makechan_c',
'runtime.makemap',
'runtime.makemap_c',
'runtime.makeslice',
'runtime.mal',
'runtime.slicebytetostring',
@@ -4302,6 +4491,14 @@ sub ShortFunctionName {
return $function;
}
# Trim overly long symbols found in disassembler output
sub CleanDisassembly {
my $d = shift;
while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
return $d;
}
##### Miscellaneous #####
# Find the right versions of the above object tools to use. The

View File

@@ -76,7 +76,6 @@ DIRS=\
encoding/hex\
encoding/pem\
exec\
exp/datafmt\
exp/eval\
exp/gui\
exp/gui/x11\

View File

@@ -352,8 +352,8 @@ func (d *Data) Type(off Offset) (Type, os.Error) {
}
}
if ndim == 0 {
err = DecodeError{"info", e.Offset, "missing dimension for array"}
goto Error
// LLVM generates this for x[].
t.Count = -1
}
case TagBaseType:

View File

@@ -546,6 +546,12 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}
// Symbols returns the symbol table for f.
func (f *File) Symbols() ([]Symbol, os.Error) {
sym, _, err := f.getSymbols(SHT_SYMTAB)
return sym, err
}
type ImportedSymbol struct {
Name string
Version string

View File

@@ -15,7 +15,13 @@ fi
# Get list of directories from Makefile
dirs=$(gomake --no-print-directory echo-dirs)
dirpat=$(echo $dirs C | sed 's/ /|/g; s/.*/^(&)$/')
dirpat=$(echo $dirs C | awk '{
for(i=1;i<=NF;i++){
x=$i
gsub("/", "\\/", x)
printf("/^(%s)$/\n", x)
}
}')
for dir in $dirs; do (
cd $dir || exit 1
@@ -30,7 +36,7 @@ for dir in $dirs; do (
deps=$(
sed -n '/^import.*"/p; /^import[ \t]*(/,/^)/p' $sources /dev/null |
cut -d '"' -f2 |
egrep "$dirpat" |
awk "$dirpat" |
grep -v "^$dir\$" |
sed 's/$/.install/' |
sed 's;^C\.install;runtime/cgo.install;' |

View File

@@ -1,12 +0,0 @@
# Copyright 2009 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.
include ../../../Make.inc
TARG=exp/datafmt
GOFILES=\
datafmt.go\
parser.go\
include ../../../Make.pkg

View File

@@ -1,731 +0,0 @@
// Copyright 2009 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 datafmt implements syntax-directed, type-driven formatting
of arbitrary data structures. Formatting a data structure consists of
two phases: first, a parser reads a format specification and builds a
"compiled" format. Then, the format can be applied repeatedly to
arbitrary values. Applying a format to a value evaluates to a []byte
containing the formatted value bytes, or nil.
A format specification is a set of package declarations and format rules:
Format = [ Entry { ";" Entry } [ ";" ] ] .
Entry = PackageDecl | FormatRule .
(The syntax of a format specification is presented in the same EBNF
notation as used in the Go language specification. The syntax of white
space, comments, identifiers, and string literals is the same as in Go.)
A package declaration binds a package name (such as 'ast') to a
package import path (such as '"go/ast"'). Each package used (in
a type name, see below) must be declared once before use.
PackageDecl = PackageName ImportPath .
PackageName = identifier .
ImportPath = string .
A format rule binds a rule name to a format expression. A rule name
may be a type name or one of the special names 'default' or '/'.
A type name may be the name of a predeclared type (for example, 'int',
'float32', etc.), the package-qualified name of a user-defined type
(for example, 'ast.MapType'), or an identifier indicating the structure
of unnamed composite types ('array', 'chan', 'func', 'interface', 'map',
or 'ptr'). Each rule must have a unique name; rules can be declared in
any order.
FormatRule = RuleName "=" Expression .
RuleName = TypeName | "default" | "/" .
TypeName = [ PackageName "." ] identifier .
To format a value, the value's type name is used to select the format rule
(there is an override mechanism, see below). The format expression of the
selected rule specifies how the value is formatted. Each format expression,
when applied to a value, evaluates to a byte sequence or nil.
In its most general form, a format expression is a list of alternatives,
each of which is a sequence of operands:
Expression = [ Sequence ] { "|" [ Sequence ] } .
Sequence = Operand { Operand } .
The formatted result produced by an expression is the result of the first
alternative sequence that evaluates to a non-nil result; if there is no
such alternative, the expression evaluates to nil. The result produced by
an operand sequence is the concatenation of the results of its operands.
If any operand in the sequence evaluates to nil, the entire sequence
evaluates to nil.
There are five kinds of operands:
Operand = Literal | Field | Group | Option | Repetition .
Literals evaluate to themselves, with two substitutions. First,
%-formats expand in the manner of fmt.Printf, with the current value
passed as the parameter. Second, the current indentation (see below)
is inserted after every newline or form feed character.
Literal = string .
This table shows string literals applied to the value 42 and the
corresponding formatted result:
"foo" foo
"%x" 2a
"x = %d" x = 42
"%#x = %d" 0x2a = 42
A field operand is a field name optionally followed by an alternate
rule name. The field name may be an identifier or one of the special
names @ or *.
Field = FieldName [ ":" RuleName ] .
FieldName = identifier | "@" | "*" .
If the field name is an identifier, the current value must be a struct,
and there must be a field with that name in the struct. The same lookup
rules apply as in the Go language (for instance, the name of an anonymous
field is the unqualified type name). The field name denotes the field
value in the struct. If the field is not found, formatting is aborted
and an error message is returned. (TODO consider changing the semantics
such that if a field is not found, it evaluates to nil).
The special name '@' denotes the current value.
The meaning of the special name '*' depends on the type of the current
value:
array, slice types array, slice element (inside {} only, see below)
interfaces value stored in interface
pointers value pointed to by pointer
(Implementation restriction: channel, function and map types are not
supported due to missing reflection support).
Fields are evaluated as follows: If the field value is nil, or an array
or slice element does not exist, the result is nil (see below for details
on array/slice elements). If the value is not nil the field value is
formatted (recursively) using the rule corresponding to its type name,
or the alternate rule name, if given.
The following example shows a complete format specification for a
struct 'myPackage.Point'. Assume the package
package myPackage // in directory myDir/myPackage
type Point struct {
name string;
x, y int;
}
Applying the format specification
myPackage "myDir/myPackage";
int = "%d";
hexInt = "0x%x";
string = "---%s---";
myPackage.Point = name "{" x ", " y:hexInt "}";
to the value myPackage.Point{"foo", 3, 15} results in
---foo---{3, 0xf}
Finally, an operand may be a grouped, optional, or repeated expression.
A grouped expression ("group") groups a more complex expression (body)
so that it can be used in place of a single operand:
Group = "(" [ Indentation ">>" ] Body ")" .
Indentation = Expression .
Body = Expression .
A group body may be prefixed by an indentation expression followed by '>>'.
The indentation expression is applied to the current value like any other
expression and the result, if not nil, is appended to the current indentation
during the evaluation of the body (see also formatting state, below).
An optional expression ("option") is enclosed in '[]' brackets.
Option = "[" Body "]" .
An option evaluates to its body, except that if the body evaluates to nil,
the option expression evaluates to an empty []byte. Thus an option's purpose
is to protect the expression containing the option from a nil operand.
A repeated expression ("repetition") is enclosed in '{}' braces.
Repetition = "{" Body [ "/" Separator ] "}" .
Separator = Expression .
A repeated expression is evaluated as follows: The body is evaluated
repeatedly and its results are concatenated until the body evaluates
to nil. The result of the repetition is the (possibly empty) concatenation,
but it is never nil. An implicit index is supplied for the evaluation of
the body: that index is used to address elements of arrays or slices. If
the corresponding elements do not exist, the field denoting the element
evaluates to nil (which in turn may terminate the repetition).
The body of a repetition may be followed by a '/' and a "separator"
expression. If the separator is present, it is invoked between repetitions
of the body.
The following example shows a complete format specification for formatting
a slice of unnamed type. Applying the specification
int = "%b";
array = { * / ", " }; // array is the type name for an unnamed slice
to the value '[]int{2, 3, 5, 7}' results in
10, 11, 101, 111
Default rule: If a format rule named 'default' is present, it is used for
formatting a value if no other rule was found. A common default rule is
default = "%v"
to provide default formatting for basic types without having to specify
a specific rule for each basic type.
Global separator rule: If a format rule named '/' is present, it is
invoked with the current value between literals. If the separator
expression evaluates to nil, it is ignored.
For instance, a global separator rule may be used to punctuate a sequence
of values with commas. The rules:
default = "%v";
/ = ", ";
will format an argument list by printing each one in its default format,
separated by a comma and a space.
*/
package datafmt
import (
"bytes"
"fmt"
"go/token"
"io"
"os"
"reflect"
"runtime"
)
// ----------------------------------------------------------------------------
// Format representation
// Custom formatters implement the Formatter function type.
// A formatter is invoked with the current formatting state, the
// value to format, and the rule name under which the formatter
// was installed (the same formatter function may be installed
// under different names). The formatter may access the current state
// to guide formatting and use State.Write to append to the state's
// output.
//
// A formatter must return a boolean value indicating if it evaluated
// to a non-nil value (true), or a nil value (false).
//
type Formatter func(state *State, value interface{}, ruleName string) bool
// A FormatterMap is a set of custom formatters.
// It maps a rule name to a formatter function.
//
type FormatterMap map[string]Formatter
// A parsed format expression is built from the following nodes.
//
type (
expr interface{}
alternatives []expr // x | y | z
sequence []expr // x y z
literal [][]byte // a list of string segments, possibly starting with '%'
field struct {
fieldName string // including "@", "*"
ruleName string // "" if no rule name specified
}
group struct {
indent, body expr // (indent >> body)
}
option struct {
body expr // [body]
}
repetition struct {
body, separator expr // {body / separator}
}
custom struct {
ruleName string
fun Formatter
}
)
// A Format is the result of parsing a format specification.
// The format may be applied repeatedly to format values.
//
type Format map[string]expr
// ----------------------------------------------------------------------------
// Formatting
// An application-specific environment may be provided to Format.Apply;
// the environment is available inside custom formatters via State.Env().
// Environments must implement copying; the Copy method must return an
// complete copy of the receiver. This is necessary so that the formatter
// can save and restore an environment (in case of an absent expression).
//
// If the Environment doesn't change during formatting (this is under
// control of the custom formatters), the Copy function can simply return
// the receiver, and thus can be very light-weight.
//
type Environment interface {
Copy() Environment
}
// State represents the current formatting state.
// It is provided as argument to custom formatters.
//
type State struct {
fmt Format // format in use
env Environment // user-supplied environment
errors chan os.Error // not chan *Error (errors <- nil would be wrong!)
hasOutput bool // true after the first literal has been written
indent bytes.Buffer // current indentation
output bytes.Buffer // format output
linePos token.Position // position of line beginning (Column == 0)
default_ expr // possibly nil
separator expr // possibly nil
}
func newState(fmt Format, env Environment, errors chan os.Error) *State {
s := new(State)
s.fmt = fmt
s.env = env
s.errors = errors
s.linePos = token.Position{Line: 1}
// if we have a default rule, cache it's expression for fast access
if x, found := fmt["default"]; found {
s.default_ = x
}
// if we have a global separator rule, cache it's expression for fast access
if x, found := fmt["/"]; found {
s.separator = x
}
return s
}
// Env returns the environment passed to Format.Apply.
func (s *State) Env() interface{} { return s.env }
// LinePos returns the position of the current line beginning
// in the state's output buffer. Line numbers start at 1.
//
func (s *State) LinePos() token.Position { return s.linePos }
// Pos returns the position of the next byte to be written to the
// output buffer. Line numbers start at 1.
//
func (s *State) Pos() token.Position {
offs := s.output.Len()
return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs}
}
// Write writes data to the output buffer, inserting the indentation
// string after each newline or form feed character. It cannot return an error.
//
func (s *State) Write(data []byte) (int, os.Error) {
n := 0
i0 := 0
for i, ch := range data {
if ch == '\n' || ch == '\f' {
// write text segment and indentation
n1, _ := s.output.Write(data[i0 : i+1])
n2, _ := s.output.Write(s.indent.Bytes())
n += n1 + n2
i0 = i + 1
s.linePos.Offset = s.output.Len()
s.linePos.Line++
}
}
n3, _ := s.output.Write(data[i0:])
return n + n3, nil
}
type checkpoint struct {
env Environment
hasOutput bool
outputLen int
linePos token.Position
}
func (s *State) save() checkpoint {
saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos}
if s.env != nil {
saved.env = s.env.Copy()
}
return saved
}
func (s *State) restore(m checkpoint) {
s.env = m.env
s.output.Truncate(m.outputLen)
}
func (s *State) error(msg string) {
s.errors <- os.NewError(msg)
runtime.Goexit()
}
// TODO At the moment, unnamed types are simply mapped to the default
// names below. For instance, all unnamed arrays are mapped to
// 'array' which is not really sufficient. Eventually one may want
// to be able to specify rules for say an unnamed slice of T.
//
func typename(typ reflect.Type) string {
switch typ.Kind() {
case reflect.Array:
return "array"
case reflect.Slice:
return "array"
case reflect.Chan:
return "chan"
case reflect.Func:
return "func"
case reflect.Interface:
return "interface"
case reflect.Map:
return "map"
case reflect.Ptr:
return "ptr"
}
return typ.String()
}
func (s *State) getFormat(name string) expr {
if fexpr, found := s.fmt[name]; found {
return fexpr
}
if s.default_ != nil {
return s.default_
}
s.error(fmt.Sprintf("no format rule for type: '%s'", name))
return nil
}
// eval applies a format expression fexpr to a value. If the expression
// evaluates internally to a non-nil []byte, that slice is appended to
// the state's output buffer and eval returns true. Otherwise, eval
// returns false and the state remains unchanged.
//
func (s *State) eval(fexpr expr, value reflect.Value, index int) bool {
// an empty format expression always evaluates
// to a non-nil (but empty) []byte
if fexpr == nil {
return true
}
switch t := fexpr.(type) {
case alternatives:
// append the result of the first alternative that evaluates to
// a non-nil []byte to the state's output
mark := s.save()
for _, x := range t {
if s.eval(x, value, index) {
return true
}
s.restore(mark)
}
return false
case sequence:
// append the result of all operands to the state's output
// unless a nil result is encountered
mark := s.save()
for _, x := range t {
if !s.eval(x, value, index) {
s.restore(mark)
return false
}
}
return true
case literal:
// write separator, if any
if s.hasOutput {
// not the first literal
if s.separator != nil {
sep := s.separator // save current separator
s.separator = nil // and disable it (avoid recursion)
mark := s.save()
if !s.eval(sep, value, index) {
s.restore(mark)
}
s.separator = sep // enable it again
}
}
s.hasOutput = true
// write literal segments
for _, lit := range t {
if len(lit) > 1 && lit[0] == '%' {
// segment contains a %-format at the beginning
if lit[1] == '%' {
// "%%" is printed as a single "%"
s.Write(lit[1:])
} else {
// use s instead of s.output to get indentation right
fmt.Fprintf(s, string(lit), value.Interface())
}
} else {
// segment contains no %-formats
s.Write(lit)
}
}
return true // a literal never evaluates to nil
case *field:
// determine field value
switch t.fieldName {
case "@":
// field value is current value
case "*":
// indirection: operation is type-specific
switch v := value; v.Kind() {
case reflect.Array:
if v.Len() <= index {
return false
}
value = v.Index(index)
case reflect.Slice:
if v.IsNil() || v.Len() <= index {
return false
}
value = v.Index(index)
case reflect.Map:
s.error("reflection support for maps incomplete")
case reflect.Ptr:
if v.IsNil() {
return false
}
value = v.Elem()
case reflect.Interface:
if v.IsNil() {
return false
}
value = v.Elem()
case reflect.Chan:
s.error("reflection support for chans incomplete")
case reflect.Func:
s.error("reflection support for funcs incomplete")
default:
s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type()))
}
default:
// value is value of named field
var field reflect.Value
if sval := value; sval.Kind() == reflect.Struct {
field = sval.FieldByName(t.fieldName)
if !field.IsValid() {
// TODO consider just returning false in this case
s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type()))
}
}
value = field
}
// determine rule
ruleName := t.ruleName
if ruleName == "" {
// no alternate rule name, value type determines rule
ruleName = typename(value.Type())
}
fexpr = s.getFormat(ruleName)
mark := s.save()
if !s.eval(fexpr, value, index) {
s.restore(mark)
return false
}
return true
case *group:
// remember current indentation
indentLen := s.indent.Len()
// update current indentation
mark := s.save()
s.eval(t.indent, value, index)
// if the indentation evaluates to nil, the state's output buffer
// didn't change - either way it's ok to append the difference to
// the current indentation
s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()])
s.restore(mark)
// format group body
mark = s.save()
b := true
if !s.eval(t.body, value, index) {
s.restore(mark)
b = false
}
// reset indentation
s.indent.Truncate(indentLen)
return b
case *option:
// evaluate the body and append the result to the state's output
// buffer unless the result is nil
mark := s.save()
if !s.eval(t.body, value, 0) { // TODO is 0 index correct?
s.restore(mark)
}
return true // an option never evaluates to nil
case *repetition:
// evaluate the body and append the result to the state's output
// buffer until a result is nil
for i := 0; ; i++ {
mark := s.save()
// write separator, if any
if i > 0 && t.separator != nil {
// nil result from separator is ignored
mark := s.save()
if !s.eval(t.separator, value, i) {
s.restore(mark)
}
}
if !s.eval(t.body, value, i) {
s.restore(mark)
break
}
}
return true // a repetition never evaluates to nil
case *custom:
// invoke the custom formatter to obtain the result
mark := s.save()
if !t.fun(s, value.Interface(), t.ruleName) {
s.restore(mark)
return false
}
return true
}
panic("unreachable")
return false
}
// Eval formats each argument according to the format
// f and returns the resulting []byte and os.Error. If
// an error occurred, the []byte contains the partially
// formatted result. An environment env may be passed
// in which is available in custom formatters through
// the state parameter.
//
func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) {
if f == nil {
return nil, os.NewError("format is nil")
}
errors := make(chan os.Error)
s := newState(f, env, errors)
go func() {
for _, v := range args {
fld := reflect.ValueOf(v)
if !fld.IsValid() {
errors <- os.NewError("nil argument")
return
}
mark := s.save()
if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct?
s.restore(mark)
}
}
errors <- nil // no errors
}()
err := <-errors
return s.output.Bytes(), err
}
// ----------------------------------------------------------------------------
// Convenience functions
// Fprint formats each argument according to the format f
// and writes to w. The result is the total number of bytes
// written and an os.Error, if any.
//
func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) {
data, err := f.Eval(env, args...)
if err != nil {
// TODO should we print partial result in case of error?
return 0, err
}
return w.Write(data)
}
// Print formats each argument according to the format f
// and writes to standard output. The result is the total
// number of bytes written and an os.Error, if any.
//
func (f Format) Print(args ...interface{}) (int, os.Error) {
return f.Fprint(os.Stdout, nil, args...)
}
// Sprint formats each argument according to the format f
// and returns the resulting string. If an error occurs
// during formatting, the result string contains the
// partially formatted result followed by an error message.
//
func (f Format) Sprint(args ...interface{}) string {
var buf bytes.Buffer
_, err := f.Fprint(&buf, nil, args...)
if err != nil {
var i interface{} = args
fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err)
}
return buf.String()
}

View File

@@ -1,351 +0,0 @@
// Copyright 2009 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 datafmt
import (
"fmt"
"testing"
"go/token"
)
var fset = token.NewFileSet()
func parse(t *testing.T, form string, fmap FormatterMap) Format {
f, err := Parse(fset, "", []byte(form), fmap)
if err != nil {
t.Errorf("Parse(%s): %v", form, err)
return nil
}
return f
}
func verify(t *testing.T, f Format, expected string, args ...interface{}) {
if f == nil {
return // allow other tests to run
}
result := f.Sprint(args...)
if result != expected {
t.Errorf(
"result : `%s`\nexpected: `%s`\n\n",
result, expected)
}
}
func formatter(s *State, value interface{}, rule_name string) bool {
switch rule_name {
case "/":
fmt.Fprintf(s, "%d %d %d", s.Pos().Line, s.LinePos().Column, s.Pos().Column)
return true
case "blank":
s.Write([]byte{' '})
return true
case "int":
if value.(int)&1 == 0 {
fmt.Fprint(s, "even ")
} else {
fmt.Fprint(s, "odd ")
}
return true
case "nil":
return false
case "testing.T":
s.Write([]byte("testing.T"))
return true
}
panic("unreachable")
return false
}
func TestCustomFormatters(t *testing.T) {
fmap0 := FormatterMap{"/": formatter}
fmap1 := FormatterMap{"int": formatter, "blank": formatter, "nil": formatter}
fmap2 := FormatterMap{"testing.T": formatter}
f := parse(t, `int=`, fmap0)
verify(t, f, ``, 1, 2, 3)
f = parse(t, `int="#"`, nil)
verify(t, f, `###`, 1, 2, 3)
f = parse(t, `int="#";string="%s"`, fmap0)
verify(t, f, "#1 0 1#1 0 7#1 0 13\n2 0 0foo2 0 8\n", 1, 2, 3, "\n", "foo", "\n")
f = parse(t, ``, fmap1)
verify(t, f, `even odd even odd `, 0, 1, 2, 3)
f = parse(t, `/ =@:blank; float64="#"`, fmap1)
verify(t, f, `# # #`, 0.0, 1.0, 2.0)
f = parse(t, `float64=@:nil`, fmap1)
verify(t, f, ``, 0.0, 1.0, 2.0)
f = parse(t, `testing "testing"; ptr=*`, fmap2)
verify(t, f, `testing.T`, t)
// TODO needs more tests
}
// ----------------------------------------------------------------------------
// Formatting of basic and simple composite types
func check(t *testing.T, form, expected string, args ...interface{}) {
f := parse(t, form, nil)
if f == nil {
return // allow other tests to run
}
result := f.Sprint(args...)
if result != expected {
t.Errorf(
"format : %s\nresult : `%s`\nexpected: `%s`\n\n",
form, result, expected)
}
}
func TestBasicTypes(t *testing.T) {
check(t, ``, ``)
check(t, `bool=":%v"`, `:true:false`, true, false)
check(t, `int="%b %d %o 0x%x"`, `101010 42 52 0x2a`, 42)
check(t, `int="%"`, `%`, 42)
check(t, `int="%%"`, `%`, 42)
check(t, `int="**%%**"`, `**%**`, 42)
check(t, `int="%%%%%%"`, `%%%`, 42)
check(t, `int="%%%d%%"`, `%42%`, 42)
const i = -42
const is = `-42`
check(t, `int ="%d"`, is, i)
check(t, `int8 ="%d"`, is, int8(i))
check(t, `int16="%d"`, is, int16(i))
check(t, `int32="%d"`, is, int32(i))
check(t, `int64="%d"`, is, int64(i))
const u = 42
const us = `42`
check(t, `uint ="%d"`, us, uint(u))
check(t, `uint8 ="%d"`, us, uint8(u))
check(t, `uint16="%d"`, us, uint16(u))
check(t, `uint32="%d"`, us, uint32(u))
check(t, `uint64="%d"`, us, uint64(u))
const f = 3.141592
const fs = `3.141592`
check(t, `float64="%g"`, fs, f)
check(t, `float32="%g"`, fs, float32(f))
check(t, `float64="%g"`, fs, float64(f))
}
func TestArrayTypes(t *testing.T) {
var a0 [10]int
check(t, `array="array";`, `array`, a0)
a1 := [...]int{1, 2, 3}
check(t, `array="array";`, `array`, a1)
check(t, `array={*}; int="%d";`, `123`, a1)
check(t, `array={* / ", "}; int="%d";`, `1, 2, 3`, a1)
check(t, `array={* / *}; int="%d";`, `12233`, a1)
a2 := []interface{}{42, "foo", 3.14}
check(t, `array={* / ", "}; interface=*; string="bar"; default="%v";`, `42, bar, 3.14`, a2)
}
func TestChanTypes(t *testing.T) {
var c0 chan int
check(t, `chan="chan"`, `chan`, c0)
c1 := make(chan int)
go func() { c1 <- 42 }()
check(t, `chan="chan"`, `chan`, c1)
// check(t, `chan=*`, `42`, c1); // reflection support for chans incomplete
}
func TestFuncTypes(t *testing.T) {
var f0 func() int
check(t, `func="func"`, `func`, f0)
f1 := func() int { return 42 }
check(t, `func="func"`, `func`, f1)
// check(t, `func=*`, `42`, f1); // reflection support for funcs incomplete
}
func TestMapTypes(t *testing.T) {
var m0 map[string]int
check(t, `map="map"`, `map`, m0)
m1 := map[string]int{}
check(t, `map="map"`, `map`, m1)
// check(t, `map=*`, ``, m1); // reflection support for maps incomplete
}
func TestPointerTypes(t *testing.T) {
var p0 *int
check(t, `ptr="ptr"`, `ptr`, p0)
check(t, `ptr=*`, ``, p0)
check(t, `ptr=*|"nil"`, `nil`, p0)
x := 99991
p1 := &x
check(t, `ptr="ptr"`, `ptr`, p1)
check(t, `ptr=*; int="%d"`, `99991`, p1)
}
func TestDefaultRule(t *testing.T) {
check(t, `default="%v"`, `42foo3.14`, 42, "foo", 3.14)
check(t, `default="%v"; int="%x"`, `abcdef`, 10, 11, 12, 13, 14, 15)
check(t, `default="%v"; int="%x"`, `ab**ef`, 10, 11, "**", 14, 15)
check(t, `default="%x"; int=@:default`, `abcdef`, 10, 11, 12, 13, 14, 15)
}
func TestGlobalSeparatorRule(t *testing.T) {
check(t, `int="%d"; / ="-"`, `1-2-3-4`, 1, 2, 3, 4)
check(t, `int="%x%x"; / ="*"`, `aa*aa`, 10, 10)
}
// ----------------------------------------------------------------------------
// Formatting of a struct
type T1 struct {
a int
}
const F1 = `datafmt "datafmt";` +
`int = "%d";` +
`datafmt.T1 = "<" a ">";`
func TestStruct1(t *testing.T) { check(t, F1, "<42>", T1{42}) }
// ----------------------------------------------------------------------------
// Formatting of a struct with an optional field (ptr)
type T2 struct {
s string
p *T1
}
const F2a = F1 +
`string = "%s";` +
`ptr = *;` +
`datafmt.T2 = s ["-" p "-"];`
const F2b = F1 +
`string = "%s";` +
`ptr = *;` +
`datafmt.T2 = s ("-" p "-" | "empty");`
func TestStruct2(t *testing.T) {
check(t, F2a, "foo", T2{"foo", nil})
check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}})
check(t, F2b, "fooempty", T2{"foo", nil})
}
// ----------------------------------------------------------------------------
// Formatting of a struct with a repetitive field (slice)
type T3 struct {
s string
a []int
}
const F3a = `datafmt "datafmt";` +
`default = "%v";` +
`array = *;` +
`datafmt.T3 = s {" " a a / ","};`
const F3b = `datafmt "datafmt";` +
`int = "%d";` +
`string = "%s";` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T3 = s [a:empty ": " {a / "-"}]`
func TestStruct3(t *testing.T) {
check(t, F3a, "foo", T3{"foo", nil})
check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}})
check(t, F3b, "bar", T3{"bar", nil})
check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}})
}
// ----------------------------------------------------------------------------
// Formatting of a struct with alternative field
type T4 struct {
x *int
a []int
}
const F4a = `datafmt "datafmt";` +
`int = "%d";` +
`ptr = *;` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T4 = "<" (x:empty x | "-") ">" `
const F4b = `datafmt "datafmt";` +
`int = "%d";` +
`ptr = *;` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T4 = "<" (a:empty {a / ", "} | "-") ">" `
func TestStruct4(t *testing.T) {
x := 7
check(t, F4a, "<->", T4{nil, nil})
check(t, F4a, "<7>", T4{&x, nil})
check(t, F4b, "<->", T4{nil, nil})
check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}})
}
// ----------------------------------------------------------------------------
// Formatting a struct (documentation example)
type Point struct {
name string
x, y int
}
const FPoint = `datafmt "datafmt";` +
`int = "%d";` +
`hexInt = "0x%x";` +
`string = "---%s---";` +
`datafmt.Point = name "{" x ", " y:hexInt "}";`
func TestStructPoint(t *testing.T) {
p := Point{"foo", 3, 15}
check(t, FPoint, "---foo---{3, 0xf}", p)
}
// ----------------------------------------------------------------------------
// Formatting a slice (documentation example)
const FSlice = `int = "%b";` +
`array = { * / ", " }`
func TestSlice(t *testing.T) { check(t, FSlice, "10, 11, 101, 111", []int{2, 3, 5, 7}) }
// TODO add more tests

View File

@@ -1,386 +0,0 @@
// Copyright 2009 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 datafmt
import (
"container/vector"
"go/scanner"
"go/token"
"os"
"strconv"
"strings"
)
// ----------------------------------------------------------------------------
// Parsing
type parser struct {
scanner.ErrorVector
scanner scanner.Scanner
file *token.File
pos token.Pos // token position
tok token.Token // one token look-ahead
lit string // token literal
packs map[string]string // PackageName -> ImportPath
rules map[string]expr // RuleName -> Expression
}
func (p *parser) next() {
p.pos, p.tok, p.lit = p.scanner.Scan()
switch p.tok {
case token.CHAN, token.FUNC, token.INTERFACE, token.MAP, token.STRUCT:
// Go keywords for composite types are type names
// returned by reflect. Accept them as identifiers.
p.tok = token.IDENT // p.lit is already set correctly
}
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte) {
p.ErrorVector.Reset()
p.file = fset.AddFile(filename, fset.Base(), len(src))
p.scanner.Init(p.file, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message
p.next() // initializes pos, tok, lit
p.packs = make(map[string]string)
p.rules = make(map[string]expr)
}
func (p *parser) error(pos token.Pos, msg string) {
p.Error(p.file.Position(pos), msg)
}
func (p *parser) errorExpected(pos token.Pos, msg string) {
msg = "expected " + msg
if pos == p.pos {
// the error happened at the current position;
// make the error message more specific
msg += ", found '" + p.tok.String() + "'"
if p.tok.IsLiteral() {
msg += " " + p.lit
}
}
p.error(pos, msg)
}
func (p *parser) expect(tok token.Token) token.Pos {
pos := p.pos
if p.tok != tok {
p.errorExpected(pos, "'"+tok.String()+"'")
}
p.next() // make progress in any case
return pos
}
func (p *parser) parseIdentifier() string {
name := p.lit
p.expect(token.IDENT)
return name
}
func (p *parser) parseTypeName() (string, bool) {
pos := p.pos
name, isIdent := p.parseIdentifier(), true
if p.tok == token.PERIOD {
// got a package name, lookup package
if importPath, found := p.packs[name]; found {
name = importPath
} else {
p.error(pos, "package not declared: "+name)
}
p.next()
name, isIdent = name+"."+p.parseIdentifier(), false
}
return name, isIdent
}
// Parses a rule name and returns it. If the rule name is
// a package-qualified type name, the package name is resolved.
// The 2nd result value is true iff the rule name consists of a
// single identifier only (and thus could be a package name).
//
func (p *parser) parseRuleName() (string, bool) {
name, isIdent := "", false
switch p.tok {
case token.IDENT:
name, isIdent = p.parseTypeName()
case token.DEFAULT:
name = "default"
p.next()
case token.QUO:
name = "/"
p.next()
default:
p.errorExpected(p.pos, "rule name")
p.next() // make progress in any case
}
return name, isIdent
}
func (p *parser) parseString() string {
s := ""
if p.tok == token.STRING {
s, _ = strconv.Unquote(p.lit)
// Unquote may fail with an error, but only if the scanner found
// an illegal string in the first place. In this case the error
// has already been reported.
p.next()
return s
} else {
p.expect(token.STRING)
}
return s
}
func (p *parser) parseLiteral() literal {
s := []byte(p.parseString())
// A string literal may contain %-format specifiers. To simplify
// and speed up printing of the literal, split it into segments
// that start with "%" possibly followed by a last segment that
// starts with some other character.
var list vector.Vector
i0 := 0
for i := 0; i < len(s); i++ {
if s[i] == '%' && i+1 < len(s) {
// the next segment starts with a % format
if i0 < i {
// the current segment is not empty, split it off
list.Push(s[i0:i])
i0 = i
}
i++ // skip %; let loop skip over char after %
}
}
// the final segment may start with any character
// (it is empty iff the string is empty)
list.Push(s[i0:])
// convert list into a literal
lit := make(literal, list.Len())
for i := 0; i < list.Len(); i++ {
lit[i] = list.At(i).([]byte)
}
return lit
}
func (p *parser) parseField() expr {
var fname string
switch p.tok {
case token.ILLEGAL:
if p.lit != "@" {
return nil
}
fname = "@"
p.next()
case token.MUL:
fname = "*"
p.next()
case token.IDENT:
fname = p.parseIdentifier()
default:
return nil
}
var ruleName string
if p.tok == token.COLON {
p.next()
ruleName, _ = p.parseRuleName()
}
return &field{fname, ruleName}
}
func (p *parser) parseOperand() (x expr) {
switch p.tok {
case token.STRING:
x = p.parseLiteral()
case token.LPAREN:
p.next()
x = p.parseExpression()
if p.tok == token.SHR {
p.next()
x = &group{x, p.parseExpression()}
}
p.expect(token.RPAREN)
case token.LBRACK:
p.next()
x = &option{p.parseExpression()}
p.expect(token.RBRACK)
case token.LBRACE:
p.next()
x = p.parseExpression()
var div expr
if p.tok == token.QUO {
p.next()
div = p.parseExpression()
}
x = &repetition{x, div}
p.expect(token.RBRACE)
default:
x = p.parseField() // may be nil
}
return x
}
func (p *parser) parseSequence() expr {
var list vector.Vector
for x := p.parseOperand(); x != nil; x = p.parseOperand() {
list.Push(x)
}
// no need for a sequence if list.Len() < 2
switch list.Len() {
case 0:
return nil
case 1:
return list.At(0).(expr)
}
// convert list into a sequence
seq := make(sequence, list.Len())
for i := 0; i < list.Len(); i++ {
seq[i] = list.At(i).(expr)
}
return seq
}
func (p *parser) parseExpression() expr {
var list vector.Vector
for {
x := p.parseSequence()
if x != nil {
list.Push(x)
}
if p.tok != token.OR {
break
}
p.next()
}
// no need for an alternatives if list.Len() < 2
switch list.Len() {
case 0:
return nil
case 1:
return list.At(0).(expr)
}
// convert list into a alternatives
alt := make(alternatives, list.Len())
for i := 0; i < list.Len(); i++ {
alt[i] = list.At(i).(expr)
}
return alt
}
func (p *parser) parseFormat() {
for p.tok != token.EOF {
pos := p.pos
name, isIdent := p.parseRuleName()
switch p.tok {
case token.STRING:
// package declaration
importPath := p.parseString()
// add package declaration
if !isIdent {
p.error(pos, "illegal package name: "+name)
} else if _, found := p.packs[name]; !found {
p.packs[name] = importPath
} else {
p.error(pos, "package already declared: "+name)
}
case token.ASSIGN:
// format rule
p.next()
x := p.parseExpression()
// add rule
if _, found := p.rules[name]; !found {
p.rules[name] = x
} else {
p.error(pos, "format rule already declared: "+name)
}
default:
p.errorExpected(p.pos, "package declaration or format rule")
p.next() // make progress in any case
}
if p.tok == token.SEMICOLON {
p.next()
} else {
break
}
}
p.expect(token.EOF)
}
func remap(p *parser, name string) string {
i := strings.Index(name, ".")
if i >= 0 {
packageName, suffix := name[0:i], name[i:]
// lookup package
if importPath, found := p.packs[packageName]; found {
name = importPath + suffix
} else {
var invalidPos token.Position
p.Error(invalidPos, "package not declared: "+packageName)
}
}
return name
}
// Parse parses a set of format productions from source src. Custom
// formatters may be provided via a map of formatter functions. If
// there are no errors, the result is a Format and the error is nil.
// Otherwise the format is nil and a non-empty ErrorList is returned.
//
func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error) {
// parse source
var p parser
p.init(fset, filename, src)
p.parseFormat()
// add custom formatters, if any
for name, form := range fmap {
name = remap(&p, name)
if _, found := p.rules[name]; !found {
p.rules[name] = &custom{name, form}
} else {
var invalidPos token.Position
p.Error(invalidPos, "formatter already declared: "+name)
}
}
return p.rules, p.GetError(scanner.NoMultiples)
}

View File

@@ -62,7 +62,7 @@ type I int
func (i I) String() string { return Sprintf("<%d>", int(i)) }
type B struct {
i I
I I
j int
}
@@ -84,8 +84,8 @@ func (g G) GoString() string {
}
type S struct {
f F // a struct field that Formats
g G // a struct field that GoStrings
F F // a struct field that Formats
G G // a struct field that GoStrings
}
// A type with a String method with pointer receiver for testing %p
@@ -322,8 +322,8 @@ var fmttests = []struct {
{"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`},
// +v on structs with Stringable items
{"%+v", B{1, 2}, `{i:<1> j:2}`},
{"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`},
{"%+v", B{1, 2}, `{I:<1> j:2}`},
{"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`},
// q on Stringable items
{"%s", I(23), `<23>`},
@@ -339,7 +339,7 @@ var fmttests = []struct {
{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
{"%#v", 1000000000, "1000000000"},
{"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`},
{"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`},
{"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}, "b":fmt_test.B{I:3, j:4}}`},
{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`},
// slices with other formats
@@ -374,11 +374,11 @@ var fmttests = []struct {
// Formatter
{"%x", F(1), "<x=F(1)>"},
{"%x", G(2), "2"},
{"%+v", S{F(4), G(5)}, "{f:<v=F(4)> g:5}"},
{"%+v", S{F(4), G(5)}, "{F:<v=F(4)> G:5}"},
// GoStringer
{"%#v", G(6), "GoString(6)"},
{"%#v", S{F(7), G(8)}, "fmt_test.S{f:<v=F(7)>, g:GoString(8)}"},
{"%#v", S{F(7), G(8)}, "fmt_test.S{F:<v=F(7)>, G:GoString(8)}"},
// %T
{"%T", (4 - 3i), "complex128"},

View File

@@ -259,10 +259,8 @@ func Sprintln(a ...interface{}) string {
// the thing inside the interface, not the interface itself.
func getField(v reflect.Value, i int) reflect.Value {
val := v.Field(i)
if i := val; i.Kind() == reflect.Interface {
if inter := i.Interface(); inter != nil {
return reflect.ValueOf(inter)
}
if val.Kind() == reflect.Interface && !val.IsNil() {
val = val.Elem()
}
return val
}
@@ -289,27 +287,32 @@ func (p *pp) unknownType(v interface{}) {
p.buf.WriteByte('?')
}
func (p *pp) badVerb(verb int, val interface{}) {
func (p *pp) badVerb(verb int, val interface{}, val1 reflect.Value) {
p.add('%')
p.add('!')
p.add(verb)
p.add('(')
if val == nil {
p.buf.Write(nilAngleBytes)
} else {
switch {
case val != nil:
p.buf.WriteString(reflect.TypeOf(val).String())
p.add('=')
p.printField(val, 'v', false, false, 0)
case val1.IsValid():
p.buf.WriteString(val1.Type().String())
p.add('=')
p.printValue(val1, 'v', false, false, 0)
default:
p.buf.Write(nilAngleBytes)
}
p.add(')')
}
func (p *pp) fmtBool(v bool, verb int, value interface{}) {
func (p *pp) fmtBool(v bool, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 't', 'v':
p.fmt.fmt_boolean(v)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
@@ -323,7 +326,7 @@ func (p *pp) fmtC(c int64) {
p.fmt.pad(p.runeBuf[0:w])
}
func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
func (p *pp) fmtInt64(v int64, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 'b':
p.fmt.integer(v, 2, signed, ldigits)
@@ -337,7 +340,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
if 0 <= v && v <= unicode.MaxRune {
p.fmt.fmt_qc(v)
} else {
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
case 'x':
p.fmt.integer(v, 16, signed, ldigits)
@@ -346,7 +349,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
case 'X':
p.fmt.integer(v, 16, signed, udigits)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
@@ -376,7 +379,7 @@ func (p *pp) fmtUnicode(v int64) {
p.fmt.precPresent = precPresent
}
func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}, value1 reflect.Value) {
switch verb {
case 'b':
p.fmt.integer(int64(v), 2, unsigned, ldigits)
@@ -396,7 +399,7 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
if 0 <= v && v <= unicode.MaxRune {
p.fmt.fmt_qc(int64(v))
} else {
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
case 'x':
p.fmt.integer(int64(v), 16, unsigned, ldigits)
@@ -405,11 +408,11 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
case 'U':
p.fmtUnicode(int64(v))
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtFloat32(v float32, verb int, value interface{}) {
func (p *pp) fmtFloat32(v float32, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 'b':
p.fmt.fmt_fb32(v)
@@ -424,11 +427,11 @@ func (p *pp) fmtFloat32(v float32, verb int, value interface{}) {
case 'G':
p.fmt.fmt_G32(v)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtFloat64(v float64, verb int, value interface{}) {
func (p *pp) fmtFloat64(v float64, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 'b':
p.fmt.fmt_fb64(v)
@@ -443,33 +446,33 @@ func (p *pp) fmtFloat64(v float64, verb int, value interface{}) {
case 'G':
p.fmt.fmt_G64(v)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) {
func (p *pp) fmtComplex64(v complex64, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 'e', 'E', 'f', 'F', 'g', 'G':
p.fmt.fmt_c64(v, verb)
case 'v':
p.fmt.fmt_c64(v, 'g')
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) {
func (p *pp) fmtComplex128(v complex128, verb int, value interface{}, value1 reflect.Value) {
switch verb {
case 'e', 'E', 'f', 'F', 'g', 'G':
p.fmt.fmt_c128(v, verb)
case 'v':
p.fmt.fmt_c128(v, 'g')
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) {
func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}, value1 reflect.Value) {
switch verb {
case 'v':
if goSyntax {
@@ -486,11 +489,11 @@ func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) {
case 'q':
p.fmt.fmt_q(v)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) {
func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}, value1 reflect.Value) {
if verb == 'v' || verb == 'd' {
if goSyntax {
p.buf.Write(bytesBytes)
@@ -525,7 +528,7 @@ func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interf
case 'q':
p.fmt.fmt_q(s)
default:
p.badVerb(verb, value)
p.badVerb(verb, value, value1)
}
}
@@ -535,12 +538,12 @@ func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSynt
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
u = value.Pointer()
default:
p.badVerb(verb, field)
p.badVerb(verb, field, value)
return
}
if goSyntax {
p.add('(')
p.buf.WriteString(reflect.TypeOf(field).String())
p.buf.WriteString(value.Type().String())
p.add(')')
p.add('(')
if u == 0 {
@@ -561,12 +564,48 @@ var (
uintptrBits = reflect.TypeOf(uintptr(0)).Bits()
)
func (p *pp) handleMethods(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString, handled bool) {
// Is it a Formatter?
if formatter, ok := field.(Formatter); ok {
handled = true
wasString = false
formatter.Format(p, verb)
return
}
// Must not touch flags before Formatter looks at them.
if plus {
p.fmt.plus = false
}
// If we're doing Go syntax and the field knows how to supply it, take care of it now.
if goSyntax {
p.fmt.sharp = false
if stringer, ok := field.(GoStringer); ok {
wasString = false
handled = true
// Print the result of GoString unadorned.
p.fmtString(stringer.GoString(), 's', false, field, reflect.Value{})
return
}
} else {
// Is it a Stringer?
if stringer, ok := field.(Stringer); ok {
wasString = false
handled = true
p.printField(stringer.String(), verb, plus, false, depth)
return
}
}
handled = false
return
}
func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) {
if field == nil {
if verb == 'T' || verb == 'v' {
p.buf.Write(nilAngleBytes)
} else {
p.badVerb(verb, field)
p.badVerb(verb, field, reflect.Value{})
}
return false
}
@@ -581,115 +620,133 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth
p.fmtPointer(field, reflect.ValueOf(field), verb, goSyntax)
return false
}
// Is it a Formatter?
if formatter, ok := field.(Formatter); ok {
formatter.Format(p, verb)
return false // this value is not a string
}
// Must not touch flags before Formatter looks at them.
if plus {
p.fmt.plus = false
}
// If we're doing Go syntax and the field knows how to supply it, take care of it now.
if goSyntax {
p.fmt.sharp = false
if stringer, ok := field.(GoStringer); ok {
// Print the result of GoString unadorned.
p.fmtString(stringer.GoString(), 's', false, field)
return false // this value is not a string
}
} else {
// Is it a Stringer?
if stringer, ok := field.(Stringer); ok {
p.printField(stringer.String(), verb, plus, false, depth)
return false // this value is not a string
}
if wasString, handled := p.handleMethods(field, verb, plus, goSyntax, depth); handled {
return wasString
}
// Some types can be done without reflection.
switch f := field.(type) {
case bool:
p.fmtBool(f, verb, field)
p.fmtBool(f, verb, field, reflect.Value{})
return false
case float32:
p.fmtFloat32(f, verb, field)
p.fmtFloat32(f, verb, field, reflect.Value{})
return false
case float64:
p.fmtFloat64(f, verb, field)
p.fmtFloat64(f, verb, field, reflect.Value{})
return false
case complex64:
p.fmtComplex64(complex64(f), verb, field)
p.fmtComplex64(complex64(f), verb, field, reflect.Value{})
return false
case complex128:
p.fmtComplex128(f, verb, field)
p.fmtComplex128(f, verb, field, reflect.Value{})
return false
case int:
p.fmtInt64(int64(f), verb, field)
p.fmtInt64(int64(f), verb, field, reflect.Value{})
return false
case int8:
p.fmtInt64(int64(f), verb, field)
p.fmtInt64(int64(f), verb, field, reflect.Value{})
return false
case int16:
p.fmtInt64(int64(f), verb, field)
p.fmtInt64(int64(f), verb, field, reflect.Value{})
return false
case int32:
p.fmtInt64(int64(f), verb, field)
p.fmtInt64(int64(f), verb, field, reflect.Value{})
return false
case int64:
p.fmtInt64(f, verb, field)
p.fmtInt64(f, verb, field, reflect.Value{})
return false
case uint:
p.fmtUint64(uint64(f), verb, goSyntax, field)
p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{})
return false
case uint8:
p.fmtUint64(uint64(f), verb, goSyntax, field)
p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{})
return false
case uint16:
p.fmtUint64(uint64(f), verb, goSyntax, field)
p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{})
return false
case uint32:
p.fmtUint64(uint64(f), verb, goSyntax, field)
p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{})
return false
case uint64:
p.fmtUint64(f, verb, goSyntax, field)
p.fmtUint64(f, verb, goSyntax, field, reflect.Value{})
return false
case uintptr:
p.fmtUint64(uint64(f), verb, goSyntax, field)
p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{})
return false
case string:
p.fmtString(f, verb, goSyntax, field)
p.fmtString(f, verb, goSyntax, field, reflect.Value{})
return verb == 's' || verb == 'v'
case []byte:
p.fmtBytes(f, verb, goSyntax, depth, field)
p.fmtBytes(f, verb, goSyntax, depth, field, reflect.Value{})
return verb == 's'
}
// Need to use reflection
value := reflect.ValueOf(field)
return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth)
}
// printValue is like printField but starts with a reflect value, not an interface{} value.
func (p *pp) printValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) {
if !value.IsValid() {
if verb == 'T' || verb == 'v' {
p.buf.Write(nilAngleBytes)
} else {
p.badVerb(verb, nil, value)
}
return false
}
// Special processing considerations.
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
case 'T':
p.printField(value.Type().String(), 's', false, false, 0)
return false
case 'p':
p.fmtPointer(nil, value, verb, goSyntax)
return false
}
// Handle values with special methods.
// Call always, even when field == nil, because handleMethods clears p.fmt.plus for us.
var field interface{}
if value.CanInterface() {
field = value.Interface()
}
if wasString, handled := p.handleMethods(field, verb, plus, goSyntax, depth); handled {
return wasString
}
return p.printReflectValue(value, verb, plus, goSyntax, depth)
}
// printReflectValue is the fallback for both printField and printValue.
// It uses reflect to print the value.
func (p *pp) printReflectValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) {
BigSwitch:
switch f := value; f.Kind() {
case reflect.Bool:
p.fmtBool(f.Bool(), verb, field)
p.fmtBool(f.Bool(), verb, nil, value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p.fmtInt64(f.Int(), verb, field)
p.fmtInt64(f.Int(), verb, nil, value)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p.fmtUint64(uint64(f.Uint()), verb, goSyntax, field)
p.fmtUint64(uint64(f.Uint()), verb, goSyntax, nil, value)
case reflect.Float32, reflect.Float64:
if f.Type().Size() == 4 {
p.fmtFloat32(float32(f.Float()), verb, field)
p.fmtFloat32(float32(f.Float()), verb, nil, value)
} else {
p.fmtFloat64(float64(f.Float()), verb, field)
p.fmtFloat64(float64(f.Float()), verb, nil, value)
}
case reflect.Complex64, reflect.Complex128:
if f.Type().Size() == 8 {
p.fmtComplex64(complex64(f.Complex()), verb, field)
p.fmtComplex64(complex64(f.Complex()), verb, nil, value)
} else {
p.fmtComplex128(complex128(f.Complex()), verb, field)
p.fmtComplex128(complex128(f.Complex()), verb, nil, value)
}
case reflect.String:
p.fmtString(f.String(), verb, goSyntax, field)
p.fmtString(f.String(), verb, goSyntax, nil, value)
case reflect.Map:
if goSyntax {
p.buf.WriteString(f.Type().String())
@@ -706,9 +763,9 @@ BigSwitch:
p.buf.WriteByte(' ')
}
}
p.printField(key.Interface(), verb, plus, goSyntax, depth+1)
p.printValue(key, verb, plus, goSyntax, depth+1)
p.buf.WriteByte(':')
p.printField(f.MapIndex(key).Interface(), verb, plus, goSyntax, depth+1)
p.printValue(f.MapIndex(key), verb, plus, goSyntax, depth+1)
}
if goSyntax {
p.buf.WriteByte('}')
@@ -717,7 +774,7 @@ BigSwitch:
}
case reflect.Struct:
if goSyntax {
p.buf.WriteString(reflect.TypeOf(field).String())
p.buf.WriteString(value.Type().String())
}
p.add('{')
v := f
@@ -736,20 +793,20 @@ BigSwitch:
p.buf.WriteByte(':')
}
}
p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1)
p.printValue(getField(v, i), verb, plus, goSyntax, depth+1)
}
p.buf.WriteByte('}')
case reflect.Interface:
value := f.Elem()
if !value.IsValid() {
if goSyntax {
p.buf.WriteString(reflect.TypeOf(field).String())
p.buf.WriteString(value.Type().String())
p.buf.Write(nilParenBytes)
} else {
p.buf.Write(nilAngleBytes)
}
} else {
return p.printField(value.Interface(), verb, plus, goSyntax, depth+1)
return p.printValue(value, verb, plus, goSyntax, depth+1)
}
case reflect.Array, reflect.Slice:
// Byte slices are special.
@@ -765,11 +822,11 @@ BigSwitch:
for i := range bytes {
bytes[i] = byte(f.Index(i).Uint())
}
p.fmtBytes(bytes, verb, goSyntax, depth, field)
p.fmtBytes(bytes, verb, goSyntax, depth, nil, value)
return verb == 's'
}
if goSyntax {
p.buf.WriteString(reflect.TypeOf(field).String())
p.buf.WriteString(value.Type().String())
p.buf.WriteByte('{')
} else {
p.buf.WriteByte('[')
@@ -782,7 +839,7 @@ BigSwitch:
p.buf.WriteByte(' ')
}
}
p.printField(f.Index(i).Interface(), verb, plus, goSyntax, depth+1)
p.printValue(f.Index(i), verb, plus, goSyntax, depth+1)
}
if goSyntax {
p.buf.WriteByte('}')
@@ -797,17 +854,17 @@ BigSwitch:
switch a := f.Elem(); a.Kind() {
case reflect.Array, reflect.Slice:
p.buf.WriteByte('&')
p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
p.printValue(a, verb, plus, goSyntax, depth+1)
break BigSwitch
case reflect.Struct:
p.buf.WriteByte('&')
p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
p.printValue(a, verb, plus, goSyntax, depth+1)
break BigSwitch
}
}
if goSyntax {
p.buf.WriteByte('(')
p.buf.WriteString(reflect.TypeOf(field).String())
p.buf.WriteString(value.Type().String())
p.buf.WriteByte(')')
p.buf.WriteByte('(')
if v == 0 {
@@ -824,7 +881,7 @@ BigSwitch:
}
p.fmt0x64(uint64(v), true)
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
p.fmtPointer(field, value, verb, goSyntax)
p.fmtPointer(nil, value, verb, goSyntax)
default:
p.unknownType(f)
}

View File

@@ -855,13 +855,13 @@ func TestIsNil(t *testing.T) {
func TestInterfaceExtraction(t *testing.T) {
var s struct {
w io.Writer
W io.Writer
}
s.w = os.Stdout
s.W = os.Stdout
v := Indirect(ValueOf(&s)).Field(0).Interface()
if v != s.w.(interface{}) {
t.Error("Interface() on interface: ", v, s.w)
if v != s.W.(interface{}) {
t.Error("Interface() on interface: ", v, s.W)
}
}
@@ -1156,18 +1156,18 @@ type D2 struct {
}
type S0 struct {
a, b, c int
A, B, C int
D1
D2
}
type S1 struct {
b int
B int
S0
}
type S2 struct {
a int
A int
*S1
}
@@ -1182,36 +1182,36 @@ type S1y struct {
type S3 struct {
S1x
S2
d, e int
D, E int
*S1y
}
type S4 struct {
*S4
a int
A int
}
var fieldTests = []FTest{
{struct{}{}, "", nil, 0},
{struct{}{}, "foo", nil, 0},
{S0{a: 'a'}, "a", []int{0}, 'a'},
{S0{}, "d", nil, 0},
{S1{S0: S0{a: 'a'}}, "a", []int{1, 0}, 'a'},
{S1{b: 'b'}, "b", []int{0}, 'b'},
{struct{}{}, "Foo", nil, 0},
{S0{A: 'a'}, "A", []int{0}, 'a'},
{S0{}, "D", nil, 0},
{S1{S0: S0{A: 'a'}}, "A", []int{1, 0}, 'a'},
{S1{B: 'b'}, "B", []int{0}, 'b'},
{S1{}, "S0", []int{1}, 0},
{S1{S0: S0{c: 'c'}}, "c", []int{1, 2}, 'c'},
{S2{a: 'a'}, "a", []int{0}, 'a'},
{S1{S0: S0{C: 'c'}}, "C", []int{1, 2}, 'c'},
{S2{A: 'a'}, "A", []int{0}, 'a'},
{S2{}, "S1", []int{1}, 0},
{S2{S1: &S1{b: 'b'}}, "b", []int{1, 0}, 'b'},
{S2{S1: &S1{S0: S0{c: 'c'}}}, "c", []int{1, 1, 2}, 'c'},
{S2{}, "d", nil, 0},
{S2{S1: &S1{B: 'b'}}, "B", []int{1, 0}, 'b'},
{S2{S1: &S1{S0: S0{C: 'c'}}}, "C", []int{1, 1, 2}, 'c'},
{S2{}, "D", nil, 0},
{S3{}, "S1", nil, 0},
{S3{S2: S2{a: 'a'}}, "a", []int{1, 0}, 'a'},
{S3{}, "b", nil, 0},
{S3{d: 'd'}, "d", []int{2}, 0},
{S3{e: 'e'}, "e", []int{3}, 'e'},
{S4{a: 'a'}, "a", []int{1}, 'a'},
{S4{}, "b", nil, 0},
{S3{S2: S2{A: 'a'}}, "A", []int{1, 0}, 'a'},
{S3{}, "B", nil, 0},
{S3{D: 'd'}, "D", []int{2}, 0},
{S3{E: 'e'}, "E", []int{3}, 'e'},
{S4{A: 'a'}, "A", []int{1}, 'a'},
{S4{}, "B", nil, 0},
}
func TestFieldByIndex(t *testing.T) {
@@ -1508,3 +1508,68 @@ func TestVariadic(t *testing.T) {
t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world")
}
}
type Private struct {
x int
y **int
}
func (p *Private) m() {
}
type Public struct {
X int
Y **int
}
func (p *Public) M() {
}
func TestUnexported(t *testing.T) {
var pub Public
v := ValueOf(&pub)
isValid(v.Elem().Field(0))
isValid(v.Elem().Field(1))
isValid(v.Elem().FieldByName("X"))
isValid(v.Elem().FieldByName("Y"))
isValid(v.Type().Method(0).Func)
isNonNil(v.Elem().Field(0).Interface())
isNonNil(v.Elem().Field(1).Interface())
isNonNil(v.Elem().FieldByName("X").Interface())
isNonNil(v.Elem().FieldByName("Y").Interface())
isNonNil(v.Type().Method(0).Func.Interface())
var priv Private
v = ValueOf(&priv)
isValid(v.Elem().Field(0))
isValid(v.Elem().Field(1))
isValid(v.Elem().FieldByName("x"))
isValid(v.Elem().FieldByName("y"))
isValid(v.Type().Method(0).Func)
shouldPanic(func() { v.Elem().Field(0).Interface() })
shouldPanic(func() { v.Elem().Field(1).Interface() })
shouldPanic(func() { v.Elem().FieldByName("x").Interface() })
shouldPanic(func() { v.Elem().FieldByName("y").Interface() })
shouldPanic(func() { v.Type().Method(0).Func.Interface() })
}
func shouldPanic(f func()) {
defer func() {
if recover() == nil {
panic("did not panic")
}
}()
f()
}
func isNonNil(x interface{}) {
if x == nil {
panic("nil interface")
}
}
func isValid(v Value) {
if !v.IsValid() {
panic("zero Value")
}
}

View File

@@ -104,7 +104,7 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
return true
default:
// Normal equality suffices
return v1.Interface() == v2.Interface()
return valueInterface(v1, false) == valueInterface(v2, false)
}
panic("Not reached")

View File

@@ -841,14 +841,7 @@ func (v Value) CanInterface() bool {
if iv.kind == Invalid {
panic(&ValueError{"reflect.Value.CanInterface", iv.kind})
}
// TODO(rsc): Check flagRO too. Decide what to do about asking for
// interface for a value obtained via an unexported field.
// If the field were of a known type, say chan int or *sync.Mutex,
// the caller could interfere with the data after getting the
// interface. But fmt.Print depends on being able to look.
// Now that reflect is more efficient the special cases in fmt
// might be less important.
return v.InternalMethod == 0
return v.InternalMethod == 0 && iv.flag&flagRO == 0
}
// Interface returns v's value as an interface{}.
@@ -856,19 +849,25 @@ func (v Value) CanInterface() bool {
// (as opposed to Type.Method), Interface cannot return an
// interface value, so it panics.
func (v Value) Interface() interface{} {
return v.internal().Interface()
return valueInterface(v, true)
}
func (iv internalValue) Interface() interface{} {
func valueInterface(v Value, safe bool) interface{} {
iv := v.internal()
return iv.valueInterface(safe)
}
func (iv internalValue) valueInterface(safe bool) interface{} {
if iv.method {
panic("reflect.Value.Interface: cannot create interface value for method with bound receiver")
}
/*
if v.flag()&noExport != 0 {
panic("reflect.Value.Interface: cannot return value obtained from unexported struct field")
}
*/
if safe && iv.flag&flagRO != 0 {
// Do not allow access to unexported values via Interface,
// because they might be pointers that should not be
// writable or methods or function that should not be callable.
panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
}
if iv.kind == Interface {
// Special case: return the element inside the interface.
// Won't recurse further because an interface cannot contain an interface.
@@ -1669,7 +1668,7 @@ func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv interna
if addr == nil {
addr = unsafe.Pointer(new(interface{}))
}
x := iv.Interface()
x := iv.valueInterface(false)
if dst.NumMethod() == 0 {
*(*interface{})(addr) = x
} else {

View File

@@ -8,11 +8,13 @@
static void* threadentry(void*);
static pthread_key_t k1, k2;
#define magic1 (0x23581321U)
static void
inittls(void)
{
uint32 x, y;
pthread_key_t tofree[16], k;
pthread_key_t tofree[128], k;
int i, ntofree;
int havek1, havek2;
@@ -35,9 +37,8 @@ inittls(void)
* 0x48+4*0x108 = 0x468 and 0x48+4*0x109 = 0x46c.
*
* The linker and runtime hard-code these constant offsets
* from %gs where we expect to find m and g. The code
* below verifies that the constants are correct once it has
* obtained the keys. Known to ../cmd/8l/obj.c:/468
* from %gs where we expect to find m and g.
* Known to ../cmd/8l/obj.c:/468
* and to ../pkg/runtime/darwin/386/sys.s:/468
*
* This is truly disgusting and a bit fragile, but taking care
@@ -48,55 +49,54 @@ inittls(void)
* require an extra instruction and memory reference in
* every stack growth prolog and would also require
* rewriting the code that 8c generates for extern registers.
*
* Things get more disgusting on OS X 10.7 Lion.
* The 0x48 base mentioned above is the offset of the tsd
* array within the per-thread structure on Leopard and Snow Leopard.
* On Lion, the base moved a little, so while the math above
* still applies, the base is different. Thus, we cannot
* look for specific key values if we want to build binaries
* that run on both systems. Instead, forget about the
* specific key values and just allocate and initialize per-thread
* storage until we find a key that writes to the memory location
* we want. Then keep that key.
*/
havek1 = 0;
havek2 = 0;
ntofree = 0;
while(!havek1 || !havek2) {
if(pthread_key_create(&k, nil) < 0) {
fprintf(stderr, "libcgo: pthread_key_create failed\n");
fprintf(stderr, "runtime/cgo: pthread_key_create failed\n");
abort();
}
if(k == 0x108) {
pthread_setspecific(k, (void*)magic1);
asm volatile("movl %%gs:0x468, %0" : "=r"(x));
asm volatile("movl %%gs:0x46c, %0" : "=r"(y));
if(x == magic1) {
havek1 = 1;
k1 = k;
continue;
}
if(k == 0x109) {
} else if(y == magic1) {
havek2 = 1;
k2 = k;
continue;
} else {
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "runtime/cgo: could not obtain pthread_keys\n");
fprintf(stderr, "\ttried");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#x", (unsigned)tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
}
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "libcgo: could not obtain pthread_keys\n");
fprintf(stderr, "\twanted 0x108 and 0x109\n");
fprintf(stderr, "\tgot");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#lx", tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
pthread_setspecific(k, 0);
}
for(i=0; i<ntofree; i++)
pthread_key_delete(tofree[i]);
/*
* We got the keys we wanted. Make sure that we observe
* updates to k1 at 0x468, to verify that the TLS array
* offset from %gs hasn't changed.
* We got the keys we wanted. Free the others.
*/
pthread_setspecific(k1, (void*)0x12345678);
asm volatile("movl %%gs:0x468, %0" : "=r"(x));
pthread_setspecific(k1, (void*)0x87654321);
asm volatile("movl %%gs:0x468, %0" : "=r"(y));
if(x != 0x12345678 || y != 0x87654321) {
printf("libcgo: thread-local storage %#lx not at %%gs:0x468 - x=%#x y=%#x\n", k1, x, y);
abort();
}
for(i=0; i<ntofree; i++)
pthread_key_delete(tofree[i]);
}
static void

View File

@@ -8,24 +8,25 @@
static void* threadentry(void*);
static pthread_key_t k1, k2;
#define magic1 (0x23581321345589ULL)
static void
inittls(void)
{
uint64 x, y;
pthread_key_t tofree[16], k;
pthread_key_t tofree[128], k;
int i, ntofree;
int havek1, havek2;
/*
* Same logic, code as darwin_386.c:/inittls, except that words
* are 8 bytes long now, and the thread-local storage starts at 0x60.
* So the offsets are
* are 8 bytes long now, and the thread-local storage starts
* at 0x60 on Leopard / Snow Leopard. So the offsets are
* 0x60+8*0x108 = 0x8a0 and 0x60+8*0x109 = 0x8a8.
*
* The linker and runtime hard-code these constant offsets
* from %gs where we expect to find m and g. The code
* below verifies that the constants are correct once it has
* obtained the keys. Known to ../cmd/6l/obj.c:/8a0
* from %gs where we expect to find m and g.
* Known to ../cmd/6l/obj.c:/8a0
* and to ../pkg/runtime/darwin/amd64/sys.s:/8a0
*
* As disgusting as on the 386; same justification.
@@ -35,49 +36,37 @@ inittls(void)
ntofree = 0;
while(!havek1 || !havek2) {
if(pthread_key_create(&k, nil) < 0) {
fprintf(stderr, "libcgo: pthread_key_create failed\n");
fprintf(stderr, "runtime/cgo: pthread_key_create failed\n");
abort();
}
if(k == 0x108) {
pthread_setspecific(k, (void*)magic1);
asm volatile("movq %%gs:0x8a0, %0" : "=r"(x));
asm volatile("movq %%gs:0x8a8, %0" : "=r"(y));
if(x == magic1) {
havek1 = 1;
k1 = k;
continue;
}
if(k == 0x109) {
} else if(y == magic1) {
havek2 = 1;
k2 = k;
continue;
} else {
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "runtime/cgo: could not obtain pthread_keys\n");
fprintf(stderr, "\ttried");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#x", (unsigned)tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
}
if(ntofree >= nelem(tofree)) {
fprintf(stderr, "libcgo: could not obtain pthread_keys\n");
fprintf(stderr, "\twanted 0x108 and 0x109\n");
fprintf(stderr, "\tgot");
for(i=0; i<ntofree; i++)
fprintf(stderr, " %#x", (unsigned)tofree[i]);
fprintf(stderr, "\n");
abort();
}
tofree[ntofree++] = k;
pthread_setspecific(k, 0);
}
for(i=0; i<ntofree; i++)
pthread_key_delete(tofree[i]);
/*
* We got the keys we wanted. Make sure that we observe
* updates to k1 at 0x8a0, to verify that the TLS array
* offset from %gs hasn't changed.
* We got the keys we wanted. Free the others.
*/
pthread_setspecific(k1, (void*)0x123456789abcdef0ULL);
asm volatile("movq %%gs:0x8a0, %0" : "=r"(x));
pthread_setspecific(k2, (void*)0x0fedcba987654321);
asm volatile("movq %%gs:0x8a8, %0" : "=r"(y));
if(x != 0x123456789abcdef0ULL || y != 0x0fedcba987654321) {
printf("libcgo: thread-local storage %#x not at %%gs:0x8a0 - x=%#llx y=%#llx\n", (unsigned)k1, x, y);
abort();
}
for(i=0; i<ntofree; i++)
pthread_key_delete(tofree[i]);
}
void

View File

@@ -1,19 +0,0 @@
// Copyright 2010 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.
#include "libcgo.h"
static void
xinitcgo(void)
{
}
void (*initcgo)(void) = xinitcgo;
void
libcgo_sys_thread_start(ThreadStart *ts)
{
// unimplemented
*(int*)0 = 0;
}

View File

@@ -40,7 +40,7 @@ xlibcgo_thread_start(ThreadStart *arg)
/* Make our own copy that can persist after we return. */
ts = malloc(sizeof *ts);
if(ts == nil) {
fprintf(stderr, "libcgo: out of memory in thread_start\n");
fprintf(stderr, "runtime/cgo: out of memory in thread_start\n");
abort();
}
*ts = *arg;

22
test/fixedbugs/bug344.go Normal file
View File

@@ -0,0 +1,22 @@
// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: bug344
// Copyright 2011 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 main
import "fmt"
func main() {
// invalid use of goto.
// do whatever you like, just don't crash.
i := 42
a := []*int{&i, &i, &i, &i}
x := a[0]
goto start
for _, x = range a {
start:
fmt.Sprint(*x)
}
}

View File

@@ -12,20 +12,20 @@ package main
import "reflect"
type T struct {
f float32
g float32
F float32
G float32
s string
t string
S string
T string
u uint32
v uint32
U uint32
V uint32
w uint32
x uint32
W uint32
X uint32
y uint32
z uint32
Y uint32
Z uint32
}
func add(s, t string) string {
@@ -40,16 +40,16 @@ func assert(b bool) {
func main() {
var x T
x.f = 1.0
x.g = x.f
x.s = add("abc", "def")
x.t = add("abc", "def")
x.u = 1
x.v = 2
x.w = 1 << 28
x.x = 2 << 28
x.y = 0x12345678
x.z = x.y
x.F = 1.0
x.G = x.F
x.S = add("abc", "def")
x.T = add("abc", "def")
x.U = 1
x.V = 2
x.W = 1 << 28
x.X = 2 << 28
x.Y = 0x12345678
x.Z = x.Y
// check mem and string
v := reflect.ValueOf(x)