diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-02-18 09:50:58 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-02-18 09:50:58 +0100 |
commit | c072558b90f1bbedc2022b0f30c8b1ac4712538e (patch) | |
tree | 67767591619e4bd8111fb05fac185cde94fb7378 | |
parent | 5859517b767c99749a45651c15d4bae5520ebae8 (diff) | |
download | golang-c072558b90f1bbedc2022b0f30c8b1ac4712538e.tar.gz |
Imported Upstream version 2011.02.15upstream/2011.02.15
264 files changed, 9009 insertions, 2705 deletions
@@ -30,6 +30,7 @@ Charles L. Dorian <cldorian@gmail.com> Chris Jones <chris@cjones.org> Chris Lennert <calennert@gmail.com> Christian Himpel <chressie@googlemail.com> +Christopher Nielsen <m4dh4tt3r@gmail.com> Christopher Wedgwood <cw@f00f.org> Clement Skau <clementskau@gmail.com> Conrad Meyer <cemeyer@cs.washington.edu> @@ -37,6 +38,7 @@ Corey Thomasson <cthom.lists@gmail.com> Dan Sinclair <dan.sinclair@gmail.com> Daniel Fleischman <danielfleischman@gmail.com> Daniel Theophanes <kardianos@gmail.com> +Dave Cheney <dave@cheney.net> David G. Andersen <dave.andersen@gmail.com> David Titarenco <david.titarenco@gmail.com> Dean Prichard <dean.prichard@gmail.com> diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 769a5b10e..1f19a0d3f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -63,6 +63,7 @@ Charles L. Dorian <cldorian@gmail.com> Chris Jones <chris@cjones.org> <chris.jones.yar@gmail.com> Chris Lennert <calennert@gmail.com> Christian Himpel <chressie@googlemail.com> <chressie@gmail.com> +Christopher Nielsen <m4dh4tt3r@gmail.com> Christopher Wedgwood <cw@f00f.org> Clement Skau <clementskau@gmail.com> Conrad Meyer <cemeyer@cs.washington.edu> @@ -72,6 +73,7 @@ Daniel Fleischman <danielfleischman@gmail.com> Daniel Nadasi <dnadasi@google.com> Berengar Lehr <Berengar.Lehr@gmx.de> Daniel Theophanes <kardianos@gmail.com> +Dave Cheney <dave@cheney.net> David Anderson <danderson@google.com> David G. Andersen <dave.andersen@gmail.com> David Symonds <dsymonds@golang.org> diff --git a/doc/code.html b/doc/code.html index 55afe09af..9236cf263 100644 --- a/doc/code.html +++ b/doc/code.html @@ -160,9 +160,9 @@ is the package's default name for imports. Go's convention is that the package name is the last element of the import path: the package imported as <code>"crypto/rot13"</code> should be named <code>rot13</code>. -At the moment, the Go tools impose a restriction that package names are unique -across all packages linked into a single binary, but that restriction -will be lifted soon. +There is no requirement that package names be unique +across all packages linked into a single binary, +only that the import paths (their full file names) be unique. </p> <p> diff --git a/doc/codelab/wiki/final.go b/doc/codelab/wiki/final.go index 8ecd97d74..c97a699d4 100644 --- a/doc/codelab/wiki/final.go +++ b/doc/codelab/wiki/final.go @@ -64,7 +64,7 @@ func init() { } func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { - err := templates[tmpl].Execute(p, w) + err := templates[tmpl].Execute(w, p) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) } diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html index fe99c32d1..fc8c27bfa 100644 --- a/doc/codelab/wiki/index.html +++ b/doc/codelab/wiki/index.html @@ -573,7 +573,11 @@ redirect the client to the edit Page so the content may be created: </p> <pre> -func viewHandler(w http.ResponseWriter, r *http.Request, title string) { +func viewHandler(w http.ResponseWriter, r *http.Request) { + title, err := getTitle(w, r) + if err != nil { + return + } p, err := loadPage(title) if err != nil { http.Redirect(w, r, "/edit/"+title, http.StatusFound) @@ -658,10 +662,14 @@ Now let's fix up <code>saveHandler</code>: </p> <pre> -func saveHandler(w http.ResponseWriter, r *http.Request, title string) { +func saveHandler(w http.ResponseWriter, r *http.Request) { + title, err := getTitle(w, r) + if err != nil { + return + } body := r.FormValue("body") p := &Page{Title: title, Body: []byte(body)} - err := p.save() + err = p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) return @@ -702,7 +710,7 @@ Then we create an <code>init</code> function, which will be called before <code>ParseFile</code> that does not return an error code; instead, it panics if an error is encountered. A panic is appropriate here; if the templates can't be loaded the only sensible thing to do is exit the program. -</p +</p> <pre> func init() { @@ -726,7 +734,7 @@ the <code>Execute</code> method on the appropriate <code>Template</code> from <pre> func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { - err := templates[tmpl].Execute(p, w) + err := templates[tmpl].Execute(w, p) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) } @@ -747,6 +755,7 @@ Then we can create a global variable to store our validation regexp: </p> <pre> +var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") </pre> <p> diff --git a/doc/codelab/wiki/srcextract.go b/doc/codelab/wiki/srcextract.go index cab092f58..67294784e 100644 --- a/doc/codelab/wiki/srcextract.go +++ b/doc/codelab/wiki/srcextract.go @@ -9,6 +9,7 @@ import ( "go/token" "log" "os" + "template" ) var ( @@ -31,15 +32,6 @@ func main() { if err != nil { log.Fatal(err) } - // create printer - p := &printer.Config{ - Mode: 0, - Tabwidth: 8, - Styler: nil, - } - if *html { - p.Mode = printer.GenHTML - } // create filter filter := func(name string) bool { return name == *getName @@ -48,8 +40,9 @@ func main() { if !ast.FilterFile(file, filter) { os.Exit(1) } - b := new(bytes.Buffer) - p.Fprint(b, fs, file) + // print the AST + var b bytes.Buffer + printer.Fprint(&b, fs, file) // drop package declaration if !*showPkg { for { @@ -71,5 +64,9 @@ func main() { } } // output - b.WriteTo(os.Stdout) + if *html { + template.HTMLEscape(os.Stdout, b.Bytes()) + } else { + b.WriteTo(os.Stdout) + } } diff --git a/doc/codelab/wiki/wiki.html b/doc/codelab/wiki/wiki.html index ff2c3088b..7ef97b45b 100644 --- a/doc/codelab/wiki/wiki.html +++ b/doc/codelab/wiki/wiki.html @@ -477,7 +477,7 @@ redirect the client to the edit Page so the content may be created: </p> <pre> -!./srcextract.bin -src=final.go -name=viewHandler +!./srcextract.bin -src=final-noclosure.go -name=viewHandler </pre> <p> @@ -539,7 +539,7 @@ Now let's fix up <code>saveHandler</code>: </p> <pre> -!./srcextract.bin -src=final.go -name=saveHandler +!./srcextract.bin -src=final-noclosure.go -name=saveHandler </pre> <p> @@ -574,7 +574,7 @@ Then we create an <code>init</code> function, which will be called before <code>ParseFile</code> that does not return an error code; instead, it panics if an error is encountered. A panic is appropriate here; if the templates can't be loaded the only sensible thing to do is exit the program. -</p +</p> <pre> !./srcextract.bin -src=final.go -name=init @@ -610,7 +610,7 @@ Then we can create a global variable to store our validation regexp: </p> <pre> -!./srcextract.bin -src=final-noclosure.go -name=TitleValidator +!./srcextract.bin -src=final-noclosure.go -name=titleValidator </pre> <p> diff --git a/doc/devel/release.html b/doc/devel/release.html index f965b5cad..57da6ca60 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -5,6 +5,112 @@ <p>This page summarizes the changes between tagged releases of Go. For full details, see the <a href="http://code.google.com/p/go/source/list">Mercurial change log</a>.</p> +<h3 id="2011-02-01">2011-02-15</h3> + +<pre> +This release includes changes to the io and template packages. +You may need to update your code. + +The io.ReadByter and io.ReadRuner interface types have been renamed to +io.ByteReader and io.RuneReader respectively. + +The order of arguments to template.Execute has been reversed to be consistent +the notion of "destination first", as with io.Copy, fmt.Fprint, and others. + +Gotest now works for package main in directories using Make.cmd-based makefiles. + +The memory allocation runtime problems from the last release are not completely +fixed. The virtual memory exhaustion problems encountered by people using +ulimit -v have been fixed, but there remain known garbage collector problems +when using GOMAXPROCS > 1. + +Other changes: +* 5l: stopped generating 64-bit eor. +* 8l: more work on plan9 support (thanks Yuval Pavel Zholkover). +* archive/zip: handle files with data descriptors. +* arm: working peep-hole optimizer. +* asn1: marshal true as 255, not 1. +* buffer.go: minor optimization, expanded comment. +* build: drop syslog on DISABLE_NET_TESTS=1 (thanks Gustavo Niemeyer), + allow clean.bash to work on fresh checkout, + change "all tests pass" message to be more obvious, + fix spaces in GOROOT (thanks Christopher Nielsen). +* bytes: fix bug in buffer.ReadBytes (thanks Evan Shaw). +* 5g: better int64 code, + don’t use MVN instruction. +* cgo: don't run cgo when not compiling (thanks Gustavo Niemeyer), + fix _cgo_run timestamp file order (thanks Gustavo Niemeyer), + fix handling of signed enumerations (thanks Gustavo Niemeyer), + os/arch dependent #cgo directives (thanks Gustavo Niemeyer), + rename internal f to avoid conflict with possible C global named f. +* codereview: fix hgpatch on windows (thanks Yasuhiro Matsumoto), + record repository, base revision, + use cmd.communicate (thanks Yasuhiro Matsumoto). +* container/ring: replace Iter() with Do(). +* crypto/cipher: add resync open to OCFB mode. +* crypto/openpgp/armor: bug fixes. +* crypto/openpgp/packet: new subpackage. +* crypto/tls: load a chain of certificates from a file, + select best cipher suite, not worst. +* crypto/x509: add support for name constraints. +* debug/pe: ImportedSymbols fixes (thanks Wei Guangjing). +* doc/code: update to reflect that package names need not be unique. +* doc/codelab/wiki: a bunch of fixes (thanks Andrey Mirtchovski). +* doc/install: update for new versions of Mercurial. +* encoding/line: fix line returned after EOF. +* flag: allow hexadecimal (0xFF) and octal (0377) input for integer flags. +* fmt.Scan: scan binary-exponent floating format, 2.4p-3, + hexadecimal (0xFF) and octal (0377) integers. +* fmt: document %%; also %b for floating point. +* gc, ld: detect stale or incompatible object files, + package name main no longer reserved. +* gc: correct receiver in method missing error (thanks Lorenzo Stoakes), + correct rounding of denormal constants (thanks Eoghan Sherry), + select receive bug fix. +* go/printer, gofmt: smarter handling of multi-line raw strings. +* go/printer: line comments must always end in a newline, + remove notion of "Styler", remove HTML mode. +* gob: allow Decode(nil) and have it just discard the next value. +* godoc: use IsAbs to test for absolute paths (fix for win32) (thanks Yasuhiro Matsumoto), + don't hide package lookup error if there's no command with the same name. +* gotest: enable unit tests for main programs. +* http: add Server type supporting timeouts, + add pipelining to ClientConn, ServerConn (thanks Petar Maymounkov), + handle unchunked, un-lengthed HTTP/1.1 responses. +* io: add RuneReader. +* json: correct Marshal documentation. +* netchan: graceful handling of closed connection (thanks Graham Miller). +* os: implement new Process API (thanks Alex Brainman). +* regexp tests: make some benchmarks more meaningful. +* regexp: add support for matching against text read from RuneReader interface. +* rpc: make more tolerant of errors, properly discard values (thanks Roger Peppe). +* runtime: detect failed thread creation on Windows, + faster allocator, garbage collector, + fix virtual memory exhaustion, + implemented windows console ctrl handler (SIGINT) (thanks Hector Chu), + more detailed panic traces, line number work, + improved Windows callback handling (thanks Hector Chu). +* spec: adjust notion of Assignability, + allow import of packages named main, + clarification re: method sets of newly declared pointer types, + fix a few typos (thanks Anthony Martin), + fix Typeof() return type (thanks Gustavo Niemeyer), + move to Unicode 6.0. +* sync: diagnose Unlock of unlocked Mutex, + new Waitgroup type (thanks Gustavo Niemeyer). +* syscall: add SetsockoptIpMreq (thanks Dave Cheney), + add sockaddr_dl, sysctl with routing message support for darwin, freebsd (thanks Mikio Hara), + do not use NULL for zero-length read, write, + implement windows version of Fsync (thanks Alex Brainman), + make ForkExec acquire the ForkLock under windows (thanks Hector Chu), + make windows API return errno instead of bool (thanks Alex Brainman), + remove obsolete socket IO control (thanks Mikio Hara). +* template: add simple formatter chaining (thanks Kyle Consalus), + allow a leading '*' to indirect through a pointer. +* testing: include elapsed time in test output +* windows: replace remaining __MINGW32__ instances with _WIN32 (thanks Joe Poirier). +</pre> + <h3 id="2011-02-01">2011-02-01</h3> <pre> diff --git a/doc/effective_go.html b/doc/effective_go.html index 3f6f89b8b..8f94f467b 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -824,7 +824,7 @@ executions. Here's a silly example. </p> <pre> -for i := 0; i < 5; i++ { +for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) } </pre> @@ -1486,7 +1486,7 @@ for a min function that chooses the least of a list of integers: func Min(a ...int) int { min := int(^uint(0) >> 1) // largest int for _, i := range a { - if i < min { + if i < min { min = i } } @@ -2670,7 +2670,7 @@ suppresses the usual check for a <code>return</code> statement. // A toy implementation of cube root using Newton's method. func CubeRoot(x float64) float64 { z := x/3 // Arbitrary intitial value - for i := 0; i < 1e6; i++ { + for i := 0; i < 1e6; i++ { prevz := z z -= (z*z*z-x) / (3*z*z) if veryClose(z, prevz) { @@ -2705,7 +2705,7 @@ func init() { <p> When <code>panic</code> is called, including implicitly for run-time -errors such indexing an array out of bounds or failing a type +errors such as indexing an array out of bounds or failing a type assertion, it immediately stops execution of the current function and begins unwinding the stack of the goroutine, running any deferred functions along the way. If that unwinding reaches the top of the @@ -2727,7 +2727,7 @@ inside a server without killing the other executing goroutines. </p> <pre> -func server(workChan <-chan *Work) { +func server(workChan <-chan *Work) { for work := range workChan { go safelyDo(work) } @@ -2751,7 +2751,16 @@ calling <code>recover</code> handles the condition completely. </p> <p> -Note that with this recovery pattern in place, the <code>do</code> +Because <code>recover</code> always returns <code>nil</code> unless called directly +from a deferred function, deferred code can call library routines that themselves +use <code>panic</code> and <code>recover</code> without failing. As an example, +the deferred function in <code>safelyDo</code> might call a logging function before +calling <code>recover</code>, and that logging code would run unaffected +by the panicking state. +</p> + +<p> +With our recovery pattern in place, the <code>do</code> function (and anything it calls) can get out of any bad situation cleanly by calling <code>panic</code>. We can use that idea to simplify error handling in complex software. Let's look at an @@ -2876,7 +2885,7 @@ func main() { } func QR(w http.ResponseWriter, req *http.Request) { - templ.Execute(req.FormValue("s"), w) + templ.Execute(w, req.FormValue("s")) } func UrlHtmlFormatter(w io.Writer, fmt string, v ...interface{}) { diff --git a/doc/go_spec.html b/doc/go_spec.html index 4e5d9c639..a95ed704a 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,5 +1,5 @@ <!-- title The Go Programming Language Specification --> -<!-- subtitle Version of February 1, 2011 --> +<!-- subtitle Version of February 8, 2011 --> <!-- TODO @@ -104,12 +104,12 @@ The following terms are used to denote specific Unicode character classes: <pre class="ebnf"> unicode_char = /* an arbitrary Unicode code point */ . unicode_letter = /* a Unicode code point classified as "Letter" */ . -unicode_digit = /* a Unicode code point classified as "Digit" */ . +unicode_digit = /* a Unicode code point classified as "Decimal Digit" */ . </pre> <p> -In <a href="http://www.unicode.org/versions/Unicode5.2.0/">The Unicode Standard 5.2</a>, -Section 4.5 General Category-Normative +In <a href="http://www.unicode.org/versions/Unicode6.0.0/">The Unicode Standard 6.0</a>, +Section 4.5 "General Category" defines a set of character categories. Go treats those characters in category Lu, Ll, Lt, Lm, or Lo as Unicode letters, and those in category Nd as Unicode digits. @@ -610,6 +610,17 @@ type literals. </p> <p> +The <i>static type</i> (or just <i>type</i>) of a variable is the +type defined by its declaration. Variables of interface type +also have a distinct <i>dynamic type</i>, which +is the actual type of the value stored in the variable at run-time. +The dynamic type may vary during execution but is always +<a href="#Assignability">assignable</a> +to the static type of the interface variable. For non-interface +types, the dynamic type is always the static type. +</p> + +<p> Each type <code>T</code> has an <i>underlying type</i>: If <code>T</code> is a predeclared type or a type literal, the corresponding underlying type is <code>T</code> itself. Otherwise, <code>T</code>'s underlying type @@ -630,6 +641,7 @@ is <code>string</code>. The underlying type of <code>[]T1</code>, <code>T3</code and <code>T4</code> is <code>[]T1</code>. </p> +<h3 id="Method_sets">Method sets</h3> <p> A type may have a <i>method set</i> associated with it (§<a href="#Interface_types">Interface types</a>, §<a href="#Method_declarations">Method declarations</a>). @@ -642,16 +654,6 @@ is the set of all methods with receiver <code>*T</code> or <code>T</code> Any other type has an empty method set. In a method set, each method must have a unique name. </p> -<p> -The <i>static type</i> (or just <i>type</i>) of a variable is the -type defined by its declaration. Variables of interface type -also have a distinct <i>dynamic type</i>, which -is the actual type of the value stored in the variable at run-time. -The dynamic type may vary during execution but is always -<a href="#Assignability">assignable</a> -to the static type of the interface variable. For non-interface -types, the dynamic type is always the static type. -</p> <h3 id="Boolean_types">Boolean types</h3> @@ -917,7 +919,8 @@ a type named <code>T</code>: </p> <ul> <li>If <code>S</code> contains an anonymous field <code>T</code>, the - method set of <code>S</code> includes the method set of <code>T</code>. + <a href="#Method_sets">method set</a> of <code>S</code> includes the + method set of <code>T</code>. </li> <li>If <code>S</code> contains an anonymous field <code>*T</code>, the @@ -1016,7 +1019,7 @@ func(n int) func(p *T) <h3 id="Interface_types">Interface types</h3> <p> -An interface type specifies a <a href="#Types">method set</a> called its <i>interface</i>. +An interface type specifies a <a href="#Method_sets">method set</a> called its <i>interface</i>. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to <i>implement the interface</i>. @@ -1349,11 +1352,12 @@ by a value of type <code>T</code>. </ul> <p> -If <code>T</code> is a struct type, either all fields of <code>T</code> -must be <a href="#Exported_identifiers">exported</a>, or the assignment must be in -the same package in which <code>T</code> is declared. +If <code>T</code> is a struct type with non-<a href="#Exported_identifiers">exported</a> +fields, the assignment must be in the same package in which <code>T</code> is declared, +or <code>x</code> must be the receiver of a method call. In other words, a struct value can be assigned to a struct variable only if -every field of the struct may be legally assigned individually by the program. +every field of the struct may be legally assigned individually by the program, +or if the assignment is initializing the receiver of a method of the struct type. </p> <p> @@ -1677,7 +1681,7 @@ type Cipher interface { <p> The declared type does not inherit any <a href="#Method_declarations">methods</a> -bound to the existing type, but the <a href="#Types">method set</a> +bound to the existing type, but the <a href="#Method_sets">method set</a> of an interface type or of elements of a composite type remains unchanged: </p> @@ -1690,6 +1694,10 @@ func (m *Mutex) Unlock() { /* Unlock implementation */ } // NewMutex has the same composition as Mutex but its method set is empty. type NewMutex Mutex +// The method set of the <a href="#Pointer_types">base type</a> of PtrMutex remains unchanged, +// but the method set of PtrMutex is empty. +type PtrMutex *Mutex + // The method set of *PrintableMutex contains the methods // Lock and Unlock bound to its anonymous field Mutex. type PrintableMutex struct { @@ -2593,8 +2601,8 @@ if Join(Split(value, len(value)/2)) != value { </pre> <p> -A method call <code>x.m()</code> is valid if the method set of -(the type of) <code>x</code> contains <code>m</code> and the +A method call <code>x.m()</code> is valid if the <a href="#Method_sets">method set</a> +of (the type of) <code>x</code> contains <code>m</code> and the argument list can be assigned to the parameter list of <code>m</code>. If <code>x</code> is <a href="#Address_operators">addressable</a> and <code>&x</code>'s method set contains <code>m</code>, <code>x.m()</code> is shorthand @@ -2889,8 +2897,8 @@ Comparison operators compare two operands and yield a value of type <code>bool</ <pre class="grammar"> == equal != not equal -< less -<= less or equal +< less +<= less or equal > greater >= greater or equal </pre> @@ -3057,7 +3065,7 @@ need to be presented regarding send, receive, select, and goroutines.</span> <h3 id="Method_expressions">Method expressions</h3> <p> -If <code>M</code> is in the method set of type <code>T</code>, +If <code>M</code> is in the <a href="#Method_sets">method set</a> of type <code>T</code>, <code>T.M</code> is a function that is callable as a regular function with the same arguments as <code>M</code> prefixed by an additional argument that is the receiver of the method. @@ -4004,7 +4012,7 @@ the channel until the channel is closed; it does not produce the zero value sent before the channel is closed (§<a href="#Close_and_closed"><code>close</code> and <code>closed</code></a>). </li> -</ol +</ol> <p> The iteration values are assigned to the respective @@ -4436,7 +4444,7 @@ At any time the following relationship holds: </p> <pre> -0 <= len(s) <= cap(s) +0 <= len(s) <= cap(s) </pre> <p> @@ -4646,13 +4654,6 @@ func recover() interface{} </pre> <p> -<span class="alert">TODO: Most of this text could move to the respective -comments in <code>runtime.go</code> once the functions are implemented. -They are here, at least for now, for reference and discussion. -</span> -</p> - -<p> When a function <code>F</code> calls <code>panic</code>, normal execution of <code>F</code> stops immediately. Any functions whose execution was <a href="#Defer_statements">deferred</a> by the @@ -4667,117 +4668,43 @@ the argument to <code>panic</code>. This termination sequence is called <i>panicking</i>. </p> +<pre> +panic(42) +panic("unreachable") +panic(Error("cannot parse")) +</pre> + <p> The <code>recover</code> function allows a program to manage behavior of a panicking goroutine. Executing a <code>recover</code> call -inside a deferred function (but not any function called by it) stops +<i>inside</i> a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution, and retrieves the error value passed to the call of <code>panic</code>. If <code>recover</code> is called outside the deferred function it will -not stop a panicking sequence. In this case, and when the goroutine -is not panicking, <code>recover</code> returns <code>nil</code>. +not stop a panicking sequence. In this case, or when the goroutine +is not panicking, or if the argument supplied to <code>panic</code> +was <code>nil</code>, <code>recover</code> returns <code>nil</code>. </p> <p> -If the function defined here, +The <code>protect</code> function in the example below invokes +the function argument <code>g</code> and protects callers from +run-time panics raised by <code>g</code>. </p> <pre> -func f(hideErrors bool) { +func protect(g func()) { defer func() { + log.Println("done") // Println executes normally even in there is a panic if x := recover(); x != nil { - println("panicking with value", x) - if !hideErrors { - panic(x) // go back to panicking - } - } - println("function returns normally") // executes only when hideErrors==true - }() - println("before") - p() - println("after") // never executes -} - -func p() { - panic(3) -} -</pre> - -<p> -is called with <code>hideErrors=true</code>, it prints -</p> - -<pre> -before -panicking with value 3 -function returns normally -</pre> - -<p> -and resumes normal execution in the function that called <code>f</code>. Otherwise, it prints -</p> - -<pre> -before -panicking with value 3 -</pre> - -<p> -and, absent further <code>recover</code> calls, terminates the program. -</p> - -<p> -Since deferred functions run before assigning the return values to the caller -of the deferring function, a deferred invocation of a function literal may modify the -invoking function's return values in the event of a panic. This permits a function to protect its -caller from panics that occur in functions it calls. -</p> - -<pre> -func IsPrintable(s string) (ok bool) { - ok = true - defer func() { - if recover() != nil { - println("input is not printable") - ok = false + log.Printf("runtime panic: %v", x) } - // Panicking has stopped; execution will resume normally in caller. - // The return value will be true normally, false if a panic occurred. - }() - panicIfNotPrintable(s) // will panic if validations fails. - return -} -</pre> - -<!--- -<p> -A deferred function that calls <code>recover</code> will see the -argument passed to <code>panic</code>. However, functions called -<i>from</i> the deferred function run normally, without behaving as -though they are panicking. This allows deferred code to run normally -in case recovery is necessary and guarantees that functions that manage -their own panics will not fail incorrectly. The function -</p> - -<pre> -func g() { - s := ReadString() - defer func() { - if IsPrintable(s) { - println("finished processing", s) - } else { - println("finished processing unprintable string") - } - }() - Analyze(s) + } + log.Println("start") + g() } </pre> -<p> -will not cause <code>IsPrintable</code> to print <code>"input is not printable"</code> -due to a <code>panic</code> triggered by the call to <code>Analyze</code>. -</p> ---> <h3 id="Bootstrapping">Bootstrapping</h3> @@ -5064,8 +4991,12 @@ The importing of packages, by construction, guarantees that there can be no cyclic dependencies in initialization. </p> <p> -A complete program, possibly created by linking multiple packages, -must have one package called <code>main</code>, with a function +A complete program is created by linking a single, unimported package +called the <i>main package</i> with all the packages it imports, transitively. +The main package must +have package name <code>main</code> and +declare a function <code>main</code> that takes no +arguments and returns no value. </p> <pre> @@ -5073,20 +5004,12 @@ func main() { ... } </pre> <p> -defined. -The function <code>main.main()</code> takes no arguments and returns no value. -</p> -<p> -Program execution begins by initializing the <code>main</code> package and then -invoking <code>main.main()</code>. -</p> -<p> -When <code>main.main()</code> returns, the program exits. It does not wait for -other (non-<code>main</code>) goroutines to complete. +Program execution begins by initializing the main package and then +invoking the function <code>main</code>. </p> <p> -Implementation restriction: The compiler assumes package <code>main</code> -is not imported by any other package. +When the function <code>main</code> returns, the program exits. +It does not wait for other (non-<code>main</code>) goroutines to complete. </p> <h2 id="Run_time_panics">Run-time panics</h2> @@ -5133,8 +5056,8 @@ func Alignof(variable ArbitraryType) int func Offsetof(selector ArbitraryType) int func Sizeof(variable ArbitraryType) int -func Reflect(val interface {}) (typ runtime.Type, addr uintptr) -func Typeof(val interface {}) reflect.Type +func Reflect(val interface{}) (typ runtime.Type, addr uintptr) +func Typeof(val interface{}) (typ interface{}) func Unreflect(typ runtime.Type, addr uintptr) interface{} </pre> diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html index ece22036a..e3d946f8d 100644 --- a/doc/go_tutorial.html +++ b/doc/go_tutorial.html @@ -5,10 +5,13 @@ This document is a tutorial introduction to the basics of the Go programming language, intended for programmers familiar with C or C++. It is not a comprehensive guide to the language; at the moment the document closest to that is the <a href='/doc/go_spec.html'>language specification</a>. -After you've read this tutorial, you might want to look at +After you've read this tutorial, you should look at <a href='/doc/effective_go.html'>Effective Go</a>, -which digs deeper into how the language is used. -Also, slides from a 3-day course about Go are available: +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: <a href='/doc/GoCourseDay1.pdf'>Day 1</a>, <a href='/doc/GoCourseDay2.pdf'>Day 2</a>, <a href='/doc/GoCourseDay3.pdf'>Day 3</a>. @@ -258,11 +261,11 @@ of course you can change a string <i>variable</i> simply by reassigning it. This snippet from <code>strings.go</code> is legal code: <p> <pre> <!-- progs/strings.go /hello/ /ciao/ --> -11 s := "hello" -12 if s[1] != 'e' { os.Exit(1) } -13 s = "good bye" -14 var p *string = &s -15 *p = "ciao" +10 s := "hello" +11 if s[1] != 'e' { os.Exit(1) } +12 s = "good bye" +13 var p *string = &s +14 *p = "ciao" </pre> <p> However the following statements are illegal because they would modify diff --git a/doc/go_tutorial.txt b/doc/go_tutorial.txt index 5eea3c980..2b2a0cda1 100644 --- a/doc/go_tutorial.txt +++ b/doc/go_tutorial.txt @@ -6,10 +6,13 @@ This document is a tutorial introduction to the basics of the Go programming language, intended for programmers familiar with C or C++. It is not a comprehensive guide to the language; at the moment the document closest to that is the <a href='/doc/go_spec.html'>language specification</a>. -After you've read this tutorial, you might want to look at +After you've read this tutorial, you should look at <a href='/doc/effective_go.html'>Effective Go</a>, -which digs deeper into how the language is used. -Also, slides from a 3-day course about Go are available: +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: <a href='/doc/GoCourseDay1.pdf'>Day 1</a>, <a href='/doc/GoCourseDay2.pdf'>Day 2</a>, <a href='/doc/GoCourseDay3.pdf'>Day 3</a>. diff --git a/doc/htmlgen.go b/doc/htmlgen.go index 5d0bad8b5..4d68767c3 100644 --- a/doc/htmlgen.go +++ b/doc/htmlgen.go @@ -18,13 +18,13 @@ import ( ) var ( - lines = make([][]byte, 0, 10000) // assume big enough - linebuf = make([]byte, 10000) // assume big enough + lines = make([][]byte, 0, 2000) // probably big enough; grows if not empty = []byte("") newline = []byte("\n") tab = []byte("\t") quote = []byte(`"`) + indent = []byte{' ', ' ', ' ', ' '} sectionMarker = []byte("----\n") preStart = []byte("<pre>") @@ -52,9 +52,7 @@ func read() { if err != nil { log.Fatal(err) } - n := len(lines) - lines = lines[0 : n+1] - lines[n] = line + lines = append(lines, line) } } @@ -173,19 +171,7 @@ func trim(l []byte) []byte { return l } -// expand tabs to 4 spaces. don't worry about columns. +// expand tabs to spaces. don't worry about columns. func expandTabs(l []byte) []byte { - j := 0 // position in linebuf. - for _, c := range l { - if c == '\t' { - for k := 0; k < 4; k++ { - linebuf[j] = ' ' - j++ - } - } else { - linebuf[j] = c - j++ - } - } - return linebuf[0:j] + return bytes.Replace(l, tab, indent, -1) } diff --git a/doc/install.html b/doc/install.html index 5917da964..d8fa8b468 100644 --- a/doc/install.html +++ b/doc/install.html @@ -110,6 +110,21 @@ package repository will most likely be old and broken.) If that fails, try installing manually from the <a href="http://mercurial.selenic.com/wiki/Download">Mercurial Download</a> page.</p> </p> +<p> +Mercurial versions 1.7.x and up require the configuration of +<a href="http://mercurial.selenic.com/wiki/CACertificates">Certification Authorities</a> +(CAs). Error messages of the form: +</p> +<pre> +warning: go.googlecode.com certificate with fingerprint b1:af: ... bc not verified (check hostfingerprints or web.cacerts config setting) +</pre> +<p> +when using Mercurial indicate that the CAs are missing. +Check your Mercurial version (<code>hg --version</code>) and +<a href="http://mercurial.selenic.com/wiki/CACertificates#Configuration_of_HTTPS_certificate_authorities">configure the CAs</a> +if necessary. +</p> + <h2 id="fetch">Fetch the repository</h2> <p> @@ -138,8 +153,7 @@ If all goes well, it will finish by printing output like: </p> <pre> ---- cd ../test -N known bugs; 0 unexpected bugs +ALL TESTS PASSED --- Installed Go for linux/amd64 in /home/you/go. diff --git a/include/libc.h b/include/libc.h index 1103bcf81..a91039dbf 100644 --- a/include/libc.h +++ b/include/libc.h @@ -291,7 +291,7 @@ extern char* getgoarch(void); extern char* getgoroot(void); extern char* getgoversion(void); -#ifdef __MINGW32__ +#ifdef _WIN32 struct timespec { int tv_sec; long tv_nsec; diff --git a/include/u.h b/include/u.h index 3cc1f335c..690b2f616 100644 --- a/include/u.h +++ b/include/u.h @@ -78,7 +78,7 @@ extern "C" { #define _NEEDUINT 1 #define _NEEDULONG 1 -#ifdef __MINGW32__ +#ifdef _WIN32 typedef jmp_buf sigjmp_buf; #endif typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long)]; @@ -139,7 +139,7 @@ typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long)]; # undef _NEEDUSHORT # undef _NEEDUINT # undef _NEEDULONG -#elif defined(__MINGW32__) +#elif defined(_WIN32) #else /* No idea what system this is -- try some defaults */ # include <pthread.h> @@ -208,7 +208,7 @@ typedef u64int uint64; */ #if defined(__GNUC__) # undef strcmp /* causes way too many warnings */ -# if __GNUC__ >= 4 || (__GNUC__==3 && !defined(__APPLE_CC__) && !defined(__MINGW32__)) +# if __GNUC__ >= 4 || (__GNUC__==3 && !defined(__APPLE_CC__) && !defined(_WIN32)) # undef AUTOLIB # define AUTOLIB(x) int __p9l_autolib_ ## x __attribute__ ((weak)); # undef AUTOFRAMEWORK diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py index 095270577..96efc855b 100644 --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@ -253,7 +253,7 @@ class CL(object): def Flush(self, ui, repo): if self.name == "new": - self.Upload(ui, repo, gofmt_just_warn=True) + self.Upload(ui, repo, gofmt_just_warn=True, creating=True) dir = CodeReviewDir(ui, repo) path = dir + '/cl.' + self.name f = open(path+'!', "w") @@ -279,8 +279,8 @@ class CL(object): typecheck(s, str) return s - def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False): - if not self.files: + def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False, creating=False, quiet=False): + if not self.files and not creating: ui.warn("no files in change list\n") if ui.configbool("codereview", "force_gofmt", True) and gofmt: CheckFormat(ui, repo, self.files, just_warn=gofmt_just_warn) @@ -292,15 +292,20 @@ class CL(object): ("cc", JoinComma(self.cc)), ("description", self.desc), ("base_hashes", ""), - # Would prefer not to change the subject - # on reupload, but /upload requires it. - ("subject", self.Subject()), ] if self.name != "new": form_fields.append(("issue", self.name)) vcs = None - if self.files: + # We do not include files when creating the issue, + # because we want the patch sets to record the repository + # and base revision they are diffs against. We use the patch + # set message for that purpose, but there is no message with + # the first patch set. Instead the message gets used as the + # new CL's overall subject. So omit the diffs when creating + # and then we'll run an immediate upload. + # This has the effect that every CL begins with an empty "Patch set 1". + if self.files and not creating: vcs = MercurialVCS(upload_options, ui, repo) data = vcs.GenerateDiff(self.files) files = vcs.GetBaseFiles(data) @@ -311,6 +316,12 @@ class CL(object): uploaded_diff_file = [("data", "data.diff", data)] else: uploaded_diff_file = [("data", "data.diff", emptydiff)] + + if vcs and self.name != "new": + form_fields.append(("subject", "diff -r " + vcs.base_rev + " " + getremote(ui, repo, {}).path)) + else: + # First upload sets the subject for the CL itself. + form_fields.append(("subject", self.Subject())) ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) response_body = MySend("/upload", body, content_type=ctype) patchset = None @@ -320,7 +331,10 @@ class CL(object): msg = lines[0] patchset = lines[1].strip() patches = [x.split(" ", 1) for x in lines[2:]] - ui.status(msg + "\n") + if response_body.startswith("Issue updated.") and quiet: + pass + else: + ui.status(msg + "\n") set_status("uploaded CL metadata + diffs") if not response_body.startswith("Issue created.") and not response_body.startswith("Issue updated."): raise util.Abort("failed to update issue: " + response_body) @@ -342,14 +356,15 @@ class CL(object): self.Flush(ui, repo) return - def Mail(self, ui,repo): + def Mail(self, ui, repo): pmsg = "Hello " + JoinComma(self.reviewer) if self.cc: pmsg += " (cc: %s)" % (', '.join(self.cc),) pmsg += ",\n" pmsg += "\n" + repourl = getremote(ui, repo, {}).path if not self.mailed: - pmsg += "I'd like you to review this change.\n" + pmsg += "I'd like you to review this change to\n" + repourl + "\n" else: pmsg += "Please take another look.\n" typecheck(pmsg, str) @@ -1082,7 +1097,10 @@ def change(ui, repo, *pats, **opts): dirty[cl] = True for d, _ in dirty.items(): + name = d.name d.Flush(ui, repo) + if name == "new": + d.Upload(ui, repo, quiet=True) if opts["stdout"]: ui.write(cl.EditorText()) @@ -1124,15 +1142,12 @@ def clpatch(ui, repo, clname, **opts): if err != "": return err try: - cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=True) + cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32") except: return "hgpatch: " + ExceptionDetail() - if os.fork() == 0: - cmd.stdin.write(patch) - os._exit(0) - cmd.stdin.close() - out = cmd.stdout.read() - if cmd.wait() != 0 and not opts["ignore_hgpatch_failure"]: + + out, err = cmd.communicate(patch) + if cmd.returncode != 0 and not opts["ignore_hgpatch_failure"]: return "hgpatch failed" cl.local = True cl.files = out.strip().split() diff --git a/lib/godoc/package.html b/lib/godoc/package.html index 570ae4016..4205f9315 100644 --- a/lib/godoc/package.html +++ b/lib/godoc/package.html @@ -5,7 +5,7 @@ --> {.section PAst} - <pre>{@ FSet|html}</pre> + <pre>{@ FSet|html-esc}</pre> {.end} {.section PDoc} <!-- PackageName is printed as title by the top-level template --> @@ -31,46 +31,45 @@ <h2 id="Constants">Constants</h2> {.repeated section @} {Doc|html-comment} - <pre>{Decl FSet|html}</pre> + <pre>{Decl FSet|html-esc}</pre> {.end} {.end} {.section Vars} <h2 id="Variables">Variables</h2> {.repeated section @} {Doc|html-comment} - <pre>{Decl FSet|html}</pre> + <pre>{Decl FSet|html-esc}</pre> {.end} {.end} {.section Funcs} {.repeated section @} {# Name is a string - no need for FSet} <h2 id="{Name|html-esc}">func <a href="/{Decl FSet|url-pos}">{Name|html-esc}</a></h2> - <p><code>{Decl FSet|html}</code></p> + <p><code>{Decl FSet|html-esc}</code></p> {Doc|html-comment} {.end} {.end} {.section Types} {.repeated section @} - {# Type.Name is a string - no need for FSet} <h2 id="{Type.Name FSet|html-esc}">type <a href="/{Decl FSet|url-pos}">{Type.Name FSet|html-esc}</a></h2> {Doc|html-comment} - <p><pre>{Decl FSet|html}</pre></p> + <p><pre>{Decl FSet|html-esc}</pre></p> {.repeated section Consts} {Doc|html-comment} - <pre>{Decl FSet|html}</pre> + <pre>{Decl FSet|html-esc}</pre> {.end} {.repeated section Vars} {Doc|html-comment} - <pre>{Decl FSet|html}</pre> + <pre>{Decl FSet|html-esc}</pre> {.end} {.repeated section Factories} <h3 id="{Type.Name FSet|html-esc}.{Name|html-esc}">func <a href="/{Decl FSet|url-pos}">{Name|html-esc}</a></h3> - <p><code>{Decl FSet|html}</code></p> + <p><code>{Decl FSet|html-esc}</code></p> {Doc|html-comment} {.end} {.repeated section Methods} - <h3 id="{Type.Name FSet|html-esc}.{Name|html-esc}">func ({Recv FSet|html}) <a href="/{Decl FSet|url-pos}">{Name|html-esc}</a></h3> - <p><code>{Decl FSet|html}</code></p> + <h3 id="{Type.Name FSet|html-esc}.{Name|html-esc}">func ({Recv FSet|html-esc}) <a href="/{Decl FSet|url-pos}">{Name|html-esc}</a></h3> + <p><code>{Decl FSet|html-esc}</code></p> {Doc|html-comment} {.end} {.end} diff --git a/misc/dashboard/godashboard/main.html b/misc/dashboard/godashboard/main.html index 8eb27869e..9572f181e 100644 --- a/misc/dashboard/godashboard/main.html +++ b/misc/dashboard/godashboard/main.html @@ -12,7 +12,7 @@ <li>Build Status</li> <li><a href="/package">Packages</a></li> <li><a href="/project">Projects</a></li> - <li><a href="/benchmarks">Benchmarks</a></li> +<!-- <li><a href="/benchmarks">Benchmarks</a></li> --> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/dashboard/godashboard/package.html b/misc/dashboard/godashboard/package.html index 08dd6a31d..13640c8e7 100644 --- a/misc/dashboard/godashboard/package.html +++ b/misc/dashboard/godashboard/package.html @@ -10,7 +10,7 @@ <li><a href="/">Build Status</a></li> <li>Packages</li> <li><a href="/project">Projects</a></li> - <li><a href="/benchmarks">Benchmarks</a></li> +<!-- <li><a href="/benchmarks">Benchmarks</a></li> --> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/dashboard/godashboard/project.html b/misc/dashboard/godashboard/project.html index a9363806f..f1cf7c023 100644 --- a/misc/dashboard/godashboard/project.html +++ b/misc/dashboard/godashboard/project.html @@ -14,7 +14,7 @@ <li><a href="/">Build Status</a></li> <li><a href="/package">Packages</a></li> <li>Projects</li> - <li><a href="/benchmarks">Benchmarks</a></li> +<!-- <li><a href="/benchmarks">Benchmarks</a></li> --> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go index bc11bb759..3ca5ed80c 100644 --- a/misc/goplay/goplay.go +++ b/misc/goplay/goplay.go @@ -66,7 +66,7 @@ func FrontPage(w http.ResponseWriter, req *http.Request) { if err != nil { data = helloWorld } - frontPage.Execute(data, w) + frontPage.Execute(w, data) } // Compile is an HTTP handler that reads Go source code from the request, @@ -123,7 +123,7 @@ func Compile(w http.ResponseWriter, req *http.Request) { if *htmlOutput { w.Write(out) } else { - output.Execute(out, w) + output.Execute(w, out) } } @@ -132,9 +132,9 @@ func Compile(w http.ResponseWriter, req *http.Request) { func error(w http.ResponseWriter, out []byte, err os.Error) { w.WriteHeader(404) if out != nil { - output.Execute(out, w) + output.Execute(w, out) } else { - output.Execute(err.String(), w) + output.Execute(w, err.String()) } } diff --git a/src/Make.cmd b/src/Make.cmd index 34f5663bc..2b9aba4a5 100644 --- a/src/Make.cmd +++ b/src/Make.cmd @@ -27,3 +27,22 @@ CLEANFILES+=$(TARG) nuke: clean rm -f $(QUOTED_GOBIN)/$(TARG) + +# for gotest +testpackage: _test/main.a + +testpackage-clean: + rm -f _test/main.a _gotest_.$O + +testpackage: _test/main.a + +_test/main.a: _gotest_.$O + @mkdir -p _test + rm -f $@ + gopack grc $@ _gotest_.$O + +_gotest_.$O: $(GOFILES) $(GOTESTFILES) + $(GC) -o $@ $(GOFILES) $(GOTESTFILES) + +importpath: + echo main diff --git a/src/Make.pkg b/src/Make.pkg index 0ffab7294..ca0fa9ee2 100644 --- a/src/Make.pkg +++ b/src/Make.pkg @@ -113,20 +113,20 @@ dir: ifdef CGOFILES _cgo_run: $(CGOFILES) - @touch _cgo_run CGOPKGPATH=$(dir) cgo -- $(CGO_CFLAGS) $(CGOFILES) + touch _cgo_run # _CGO_CFLAGS and _CGO_LDFLAGS are defined via the evaluation of _cgo_flags. # The include happens before the commands in the recipe run, # so it cannot be done in the same recipe that runs cgo. -_cgo_flags: _cgo_run +_load_cgo_flags: _cgo_run $(eval include _cgo_flags) # Include any previous flags in case cgo files are up to date. -include _cgo_flags # Ugly but necessary - cgo writes these files too. -_cgo_gotypes.go _cgo_export.c _cgo_export.h _cgo_main.c _cgo_defun.c: _cgo_flags +_cgo_gotypes.go _cgo_export.c _cgo_export.h _cgo_main.c _cgo_defun.c: _load_cgo_flags @true %.cgo1.go %.cgo2.c: _cgo_defun.c @@ -167,7 +167,7 @@ _CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup _CGO_LDFLAGS_windows=-shared -lm -mthreads # Have to compile the runtime header. -RUNTIME_CFLAGS=-I"$(pkgdir)" +RUNTIME_CFLAGS=-I$(pkgdir) # Compile _cgo_defun.c with 6c; needs access to the runtime headers. _cgo_defun.$O: _cgo_defun.c diff --git a/src/clean.bash b/src/clean.bash index d96eb52df..5c1dded56 100755 --- a/src/clean.bash +++ b/src/clean.bash @@ -25,8 +25,8 @@ for i in lib9 libbio libmach cmd pkg \ do( cd "$GOROOT"/src/$i || exit 1 if test -f clean.bash; then - bash clean.bash + bash clean.bash --gomake $MAKE else - gomake clean + $MAKE clean fi )done diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index b36094a78..e762f5646 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -187,7 +187,7 @@ assemble(char *file) pass = 1; pinit(file); - Bprint(&obuf, "%s\n", thestring); + Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); for(i=0; i<nDlist; i++) dodefine(Dlist[i]); diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index 43eb73c94..d45aabc5e 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -373,7 +373,7 @@ outcode(void) } } - Bprint(&outbuf, "%s\n", thestring); + Bprint(&outbuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); if(ndynimp > 0 || ndynexp > 0) { int i; diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index 78f2f4aeb..4da8db2ae 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -64,17 +64,21 @@ cgen64(Node *n, Node *res) return; case OCOM: + regalloc(&t1, lo1.type, N); + gmove(ncon(-1), &t1); + split64(res, &lo2, &hi2); regalloc(&n1, lo1.type, N); gins(AMOVW, &lo1, &n1); - gins(AMVN, &n1, &n1); + gins(AEOR, &t1, &n1); gins(AMOVW, &n1, &lo2); gins(AMOVW, &hi1, &n1); - gins(AMVN, &n1, &n1); + gins(AEOR, &t1, &n1); gins(AMOVW, &n1, &hi2); + regfree(&t1); regfree(&n1); splitclean(); splitclean(); @@ -204,14 +208,17 @@ cgen64(Node *n, Node *res) // here and below (verify it optimizes to EOR) gins(AEOR, &al, &al); gins(AEOR, &ah, &ah); - } else if(v > 32) { + } else + if(v > 32) { gins(AEOR, &al, &al); // MOVW bl<<(v-32), ah gshift(AMOVW, &bl, SHIFT_LL, (v-32), &ah); - } else if(v == 32) { + } else + if(v == 32) { gins(AEOR, &al, &al); gins(AMOVW, &bl, &ah); - } else if(v > 0) { + } else + if(v > 0) { // MOVW bl<<v, al gshift(AMOVW, &bl, SHIFT_LL, v, &al); @@ -341,7 +348,8 @@ olsh_break: gins(AEOR, &al, &al); gins(AEOR, &ah, &ah); } - } else if(v > 32) { + } else + if(v > 32) { if(bh.type->etype == TINT32) { // MOVW bh->(v-32), al gshift(AMOVW, &bh, SHIFT_AR, v-32, &al); @@ -353,7 +361,8 @@ olsh_break: gshift(AMOVW, &bh, SHIFT_LR, v-32, &al); gins(AEOR, &ah, &ah); } - } else if(v == 32) { + } else + if(v == 32) { gins(AMOVW, &bh, &al); if(bh.type->etype == TINT32) { // MOVW bh->31, ah @@ -361,7 +370,8 @@ olsh_break: } else { gins(AEOR, &ah, &ah); } - } else if( v > 0) { + } else + if( v > 0) { // MOVW bl>>v, al gshift(AMOVW, &bl, SHIFT_LR, v, &al); @@ -384,11 +394,16 @@ olsh_break: regalloc(&s, types[TUINT32], N); regalloc(&creg, types[TUINT32], N); - if (is64(r->type)) { + if(is64(r->type)) { // shift is >= 1<<32 split64(r, &cl, &ch); gmove(&ch, &s); - p1 = gins(ATST, &s, N); + gins(ATST, &s, N); + if(bh.type->etype == TINT32) + p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + else + p1 = gins(AEOR, &ah, &ah); + p1->scond = C_SCOND_NE; p6 = gbranch(ABNE, T); gmove(&cl, &s); splitclean(); @@ -441,7 +456,6 @@ olsh_break: p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else p1 = gins(AEOR, &ah, &ah); - p1->scond = C_SCOND_EQ; p4 = gbranch(ABEQ, T); // check if shift is < 64 @@ -461,33 +475,23 @@ olsh_break: // MOVW bh->(s-32), al p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &al); p1->scond = C_SCOND_LO; - - // MOVW bh->31, ah - p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); - p1->scond = C_SCOND_LO; } else { // MOVW bh>>(v-32), al p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &al); p1->scond = C_SCOND_LO; - - p1 = gins(AEOR, &ah, &ah); - p1->scond = C_SCOND_LO; } // BLO end p5 = gbranch(ABLO, T); // s >= 64 - if (p6 != P) patch(p6, pc); + if(p6 != P) + patch(p6, pc); if(bh.type->etype == TINT32) { // MOVW bh->31, al gshift(AMOVW, &bh, SHIFT_AR, 31, &al); - - // MOVW bh->31, ah - gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); } else { gins(AEOR, &al, &al); - gins(AEOR, &ah, &ah); } patch(p2, pc); diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index ce74d6478..0c6dbbf71 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -87,6 +87,10 @@ Dconv(Fmt *fp) int32 v; a = va_arg(fp->args, Addr*); + if(a == A) { + sprint(str, "<nil>"); + goto conv; + } i = a->type; switch(i) { @@ -183,7 +187,7 @@ Dconv(Fmt *fp) // a->type = D_ADDR; // goto conv; } -//conv: +conv: return fmtstrcpy(fp, str); } diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index f619a6206..ca12d70f2 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -48,7 +48,6 @@ peep(void) /* * complete R structure */ - t = 0; for(r=firstr; r!=R; r=r1) { r1 = r->link; if(r1 == R) @@ -68,7 +67,6 @@ peep(void) r1->p1 = r2; r = r2; - t++; case ADATA: case AGLOBL: @@ -77,8 +75,10 @@ peep(void) p = p->link; } } +//dumpit("begin", firstr); loop1: + t = 0; for(r=firstr; r!=R; r=r->link) { p = r->prog; @@ -99,71 +99,75 @@ loop1: case AMOVW: case AMOVF: case AMOVD: - if(!regtyp(&p->to)) - break; -// if(isdconst(&p->from)) { -// constprop(&p->from, &p->to, r->s1); -// break; -// } - if(!regtyp(&p->from)) - break; - if(p->from.type != p->to.type) - break; - if(copyprop(r)) { - excise(r); - t++; - break; + if(regtyp(&p->from)) + if(p->from.type == p->to.type) + if(p->scond == C_SCOND_NONE) { + if(copyprop(r)) { + excise(r); + t++; + break; + } + if(subprop(r) && copyprop(r)) { + excise(r); + t++; + break; + } } - if(subprop(r) && copyprop(r)) { - excise(r); - t++; - break; + break; + + if(p->scond == C_SCOND_NONE) + if(regtyp(&p->to)) + if(isdconst(&p->from)) { + constprop(&p->from, &p->to, r->s1); } + break; } } if(t) goto loop1; - /* - * look for MOVB x,R; MOVB R,R - */ + +return; + for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { - default: - continue; - case AEOR: - /* - * EOR -1,x,y => MVN x,y - */ - if(isdconst(&p->from) && p->from.offset == -1) { - p->as = AMVN; - p->from.type = D_REG; - if(p->reg != NREG) - p->from.reg = p->reg; - else - p->from.reg = p->to.reg; - p->reg = NREG; - } - continue; +// case AEOR: +// /* +// * EOR -1,x,y => MVN x,y +// */ +// if(isdconst(&p->from) && p->from.offset == -1) { +// p->as = AMVN; +// p->from.type = D_REG; +// if(p->reg != NREG) +// p->from.reg = p->reg; +// else +// p->from.reg = p->to.reg; +// p->reg = NREG; +// } +// break; + case AMOVH: case AMOVHU: case AMOVB: case AMOVBU: + /* + * look for MOVB x,R; MOVB R,R + */ if(p->to.type != D_REG) - continue; + break; + if(r1 == R) + break; + p1 = r1->prog; + if(p1->as != p->as) + break; + if(p1->from.type != D_REG || p1->from.reg != p->to.reg) + break; + if(p1->to.type != D_REG || p1->to.reg != p->to.reg) + break; + excise(r1); break; } r1 = r->link; - if(r1 == R) - continue; - p1 = r1->prog; - if(p1->as != p->as) - continue; - if(p1->from.type != D_REG || p1->from.reg != p->to.reg) - continue; - if(p1->to.type != D_REG || p1->to.reg != p->to.reg) - continue; - excise(r1); } // for(r=firstr; r!=R; r=r->link) { @@ -335,6 +339,8 @@ subprop(Reg *r0) case AMULLU: case AMULA: + case AMVN: + return 0; case ACMN: case AADD: @@ -347,7 +353,6 @@ subprop(Reg *r0) case AORR: case AAND: case AEOR: - case AMVN: case AMUL: case AMULU: case ADIV: @@ -364,7 +369,8 @@ subprop(Reg *r0) case ADIVD: case ADIVF: if(p->to.type == v1->type) - if(p->to.reg == v1->reg) { + if(p->to.reg == v1->reg) + if(p->scond == C_SCOND_NONE) { if(p->reg == NREG) p->reg = p->to.reg; goto gotit; @@ -376,6 +382,7 @@ subprop(Reg *r0) case AMOVW: if(p->to.type == v1->type) if(p->to.reg == v1->reg) + if(p->scond == C_SCOND_NONE) goto gotit; break; @@ -662,7 +669,7 @@ shiftprop(Reg *r) FAIL("can't swap"); if(p1->reg == NREG && p1->to.reg == n) FAIL("shift result used twice"); - case AMVN: +// case AMVN: if(p1->from.type == D_SHIFT) FAIL("shift result used in shift"); if(p1->from.type != D_REG || p1->from.reg != n) @@ -971,7 +978,7 @@ copyu(Prog *p, Adr *v, Adr *s) } return 0; - case ANOP: /* read, write */ + case ANOP: /* read,, write */ case AMOVW: case AMOVF: case AMOVD: @@ -979,6 +986,8 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVHU: case AMOVB: case AMOVBU: + case AMOVFW: + case AMOVWF: case AMOVDW: case AMOVWD: case AMOVFD: @@ -1014,6 +1023,7 @@ copyu(Prog *p, Adr *v, Adr *s) case AMULLU: /* read, read, write, write */ case AMULA: + case AMVN: return 2; case AADD: /* read, read, write */ @@ -1027,7 +1037,6 @@ copyu(Prog *p, Adr *v, Adr *s) case AORR: case AAND: case AEOR: - case AMVN: case AMUL: case AMULU: case ADIV: @@ -1043,12 +1052,12 @@ copyu(Prog *p, Adr *v, Adr *s) case ADIVF: case ADIVD: - case ACMPF: + case ACMPF: /* read, read, */ case ACMPD: - case ATST: case ACMP: case ACMN: case ACASE: + case ATST: /* read,, */ if(s != A) { if(copysub(&p->from, v, s, 1)) return 1; @@ -1150,53 +1159,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; } -int -a2type(Prog *p) -{ - - switch(p->as) { - - case ATST: - case ACMP: - case ACMN: - - case AMULLU: - case AMULA: - - case AADD: - case ASUB: - case ARSB: - case ASLL: - case ASRL: - case ASRA: - case AORR: - case AAND: - case AEOR: - case AMVN: - case AMUL: - case AMULU: - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - return D_REG; - - case ACMPF: - case ACMPD: - - case AADDF: - case AADDD: - case ASUBF: - case ASUBD: - case AMULF: - case AMULD: - case ADIVF: - case ADIVD: - return D_FREG; - } - return D_NONE; -} - /* * direct reference, * could be set/use depending on @@ -1233,15 +1195,15 @@ copyau(Adr *a, Adr *v) return 1; if(v->type == D_REG) { if(a->type == D_CONST && a->reg != NREG) { - if(v->reg == a->reg) + if(a->reg == v->reg) return 1; } else if(a->type == D_OREG) { - if(v->reg == a->reg) + if(a->reg == v->reg) return 1; } else if(a->type == D_REGREG) { - if(v->reg == a->reg) + if(a->reg == v->reg) return 1; if(a->offset == v->reg) return 1; @@ -1256,17 +1218,33 @@ copyau(Adr *a, Adr *v) return 0; } +/* + * compare v to the center + * register in p (p->reg) + * the trick is that this + * register might be D_REG + * D_FREG. there are basically + * two cases, + * ADD r,r,r + * CMP r,r, + */ int copyau1(Prog *p, Adr *v) { - if(regtyp(v)) { - if(a2type(p) == v->type) - if(p->reg == v->reg) { - if(a2type(p) != v->type) - print("botch a2type %P\n", p); - return 1; + if(regtyp(v)) + if(p->reg == v->reg) { + if(p->to.type != D_NONE) { + if(v->type == p->to.type) + return 1; + return 0; + } + if(p->from.type != D_NONE) { + if(v->type == p->from.type) + return 1; + return 0; } + print("copyau1: cant tell %P\n", p); } return 0; } @@ -1479,24 +1457,24 @@ applypred(Reg *rstart, Joininfo *j, int cond, int branch) pred = predinfo[rstart->prog->as - ABEQ].notscond; for(r = j->start;; r = successor(r)) { - if (r->prog->as == AB) { - if (r != j->last || branch == Delbranch) + if(r->prog->as == AB) { + if(r != j->last || branch == Delbranch) excise(r); else { - if (cond == Truecond) + if(cond == Truecond) r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode; else r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode; } } else - if (predicable(r->prog)) + if(predicable(r->prog)) r->prog->scond = (r->prog->scond&~C_SCOND)|pred; - if (r->s1 != r->link) { + if(r->s1 != r->link) { r->s1 = r->link; r->link->p1 = r; } - if (r == j->last) + if(r == j->last) break; } } diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index eaf02b237..f31f70535 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -140,8 +140,8 @@ regopt(Prog *firstp) if(first == 0) { fmtinstall('Q', Qconv); } - first++; + first++; if(debug['K']) { if(first != 13) return; @@ -491,7 +491,7 @@ brk: * peep-hole on basic block */ if(!debug['R'] || debug['P']) { -// peep(); + peep(); } /* @@ -1375,3 +1375,71 @@ noreturn(Prog *p) return 1; return 0; } + +void +dumpone(Reg *r) +{ + int z; + Bits bit; + + print("%d:%P", r->loop, r->prog); + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; +// if(bany(&bit)) { +// print("\t"); +// if(bany(&r->set)) +// print(" s:%Q", r->set); +// if(bany(&r->use1)) +// print(" u1:%Q", r->use1); +// if(bany(&r->use2)) +// print(" u2:%Q", r->use2); +// if(bany(&r->refbehind)) +// print(" rb:%Q ", r->refbehind); +// if(bany(&r->refahead)) +// print(" ra:%Q ", r->refahead); +// if(bany(&r->calbehind)) +// print("cb:%Q ", r->calbehind); +// if(bany(&r->calahead)) +// print(" ca:%Q ", r->calahead); +// if(bany(&r->regdiff)) +// print(" d:%Q ", r->regdiff); +// if(bany(&r->act)) +// print(" a:%Q ", r->act); +// } + print("\n"); +} + +void +dumpit(char *str, Reg *r0) +{ + Reg *r, *r1; + + print("\n%s\n", str); + for(r = r0; r != R; r = r->link) { + dumpone(r); + r1 = r->p2; + if(r1 != R) { + print(" pred:"); + for(; r1 != R; r1 = r1->p2link) + print(" %.4ud", r1->prog->loc); + print("\n"); + } +// r1 = r->s1; +// if(r1 != R) { +// print(" succ:"); +// for(; r1 != R; r1 = r1->s1) +// print(" %.4ud", r1->prog->loc); +// print("\n"); +// } + } +} diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 34565629f..7163997c0 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -637,13 +637,6 @@ wput(int32 l) cflush(); } -void -wputl(ushort w) -{ - cput(w); - cput(w>>8); -} - void hput(int32 l) @@ -672,20 +665,6 @@ lput(int32 l) } void -lputl(int32 l) -{ - - cbp[3] = l>>24; - cbp[2] = l>>16; - cbp[1] = l>>8; - cbp[0] = l; - cbp += 4; - cbc -= 4; - if(cbc <= 0) - cflush(); -} - -void cflush(void) { int n; @@ -1491,15 +1470,24 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o1 |= (p->scond & C_SCOND) << 28; break; case 80: /* fmov zfcon,freg */ - if((p->scond & C_SCOND) != C_SCOND_NONE) - diag("floating point cannot be conditional"); // cant happen - o1 = 0xf3000110; // EOR 64 - - // always clears the double float register + if(p->as == AMOVD) { + o1 = 0xeeb00b00; // VMOV imm 64 + o2 = oprrr(ASUBD, p->scond); + } else { + o1 = 0x0eb00a00; // VMOV imm 32 + o2 = oprrr(ASUBF, p->scond); + } + v = 0x70; // 1.0 r = p->to.reg; - o1 |= r << 0; + + // movf $1.0, r + o1 |= (p->scond & C_SCOND) << 28; o1 |= r << 12; - o1 |= r << 16; + o1 |= (v&0xf) << 0; + o1 |= (v&0xf0) << 12; + + // subf r,r,r + o2 |= r | (r<<16) | (r<<12); break; case 81: /* fmov sfcon,freg */ o1 = 0x0eb00a00; // VMOV imm 32 diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index c31028416..e42be4e98 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -410,6 +410,7 @@ Sym* lookup(char*, int); void cput(int); void hput(int32); void lput(int32); +void lputb(int32); void lputl(int32); void* mysbrk(uint32); void names(void); diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 9ad0193ac..8b3135e06 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -236,7 +236,7 @@ Optab optab[] = { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, - { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 4, 0 }, + { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 8, 0 }, { AMOVF, C_SFCON,C_NONE, C_FREG, 81, 4, 0 }, { ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0 }, diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index 1b8bb6344..37144c888 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -189,7 +189,7 @@ assemble(char *file) pass = 1; pinit(file); - Bprint(&obuf, "%s\n", thestring); + Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); for(i=0; i<nDlist; i++) dodefine(Dlist[i]); diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index 47975a0c8..6d886f459 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -231,7 +231,7 @@ outcode(void) } Binit(&b, f, OWRITE); - Bprint(&b, "%s\n", thestring); + Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); if(ndynimp > 0 || ndynexp > 0) { int i; diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index d6ffa4ff9..d179e77b1 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -63,52 +63,6 @@ entryvalue(void) return s->value; } -void -wputl(uint16 w) -{ - cput(w); - cput(w>>8); -} - -void -wputb(uint16 w) -{ - cput(w>>8); - cput(w); -} - -void -lputb(int32 l) -{ - cput(l>>24); - cput(l>>16); - cput(l>>8); - cput(l); -} - -void -vputb(uint64 v) -{ - lputb(v>>32); - lputb(v); -} - -void -lputl(int32 l) -{ - cput(l); - cput(l>>8); - cput(l>>16); - cput(l>>24); -} - -void -vputl(uint64 v) -{ - lputl(v); - lputl(v>>32); -} - vlong datoff(vlong addr) { diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 7f22493e0..70473ecd2 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -429,6 +429,7 @@ vlong rnd(vlong, vlong); void span(void); void undef(void); vlong symaddr(Sym*); +void vputb(uint64); void vputl(uint64); void wputb(uint16); void wputl(uint16); diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index bf298b266..d5fa959aa 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -189,7 +189,7 @@ assemble(char *file) pass = 1; pinit(file); - Bprint(&obuf, "%s\n", thestring); + Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); for(i=0; i<nDlist; i++) dodefine(Dlist[i]); diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index be48885f8..d07a5439c 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -230,7 +230,7 @@ outcode(void) } Binit(&b, f, OWRITE); - Bprint(&b, "%s\n", thestring); + Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); if(ndynimp > 0 || ndynexp > 0) { int i; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 6e83d8dea..d90eab7e7 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -59,45 +59,6 @@ entryvalue(void) return s->value; } -void -wputl(ushort w) -{ - cput(w); - cput(w>>8); -} - -void -wput(ushort w) -{ - cput(w>>8); - cput(w); -} - -void -lput(int32 l) -{ - cput(l>>24); - cput(l>>16); - cput(l>>8); - cput(l); -} - -void -lputl(int32 l) -{ - cput(l); - cput(l>>8); - cput(l>>16); - cput(l>>24); -} - -void -vputl(uvlong l) -{ - lputl(l >> 32); - lputl(l); -} - vlong datoff(vlong addr) { @@ -688,6 +649,8 @@ asmb(void) ElfPhdr *ph, *pph; ElfShdr *sh; Section *sect; + Sym *sym; + int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); @@ -741,7 +704,7 @@ asmb(void) seek(cout, rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen, 0); break; case 2: - seek(cout, HEADR+segtext.filelen+segdata.filelen, 0); + symo = HEADR+segtext.filelen+segdata.filelen; break; case 3: case 4: @@ -761,11 +724,27 @@ asmb(void) symo = rnd(symo, PEFILEALIGN); break; } - if(HEADTYPE != 10 && !debug['s']) { + if(!debug['s']) { seek(cout, symo, 0); - if(debug['v']) - Bprint(&bso, "%5.2f dwarf\n", cputime()); - dwarfemitdebugsections(); + + if(HEADTYPE == 2) { + asmplan9sym(); + cflush(); + + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + + } else if(HEADTYPE != 10) { + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + } } } if(debug['v']) @@ -777,25 +756,25 @@ asmb(void) if(iself) goto Elfput; case 0: /* garbage */ - lput(0x160L<<16); /* magic and sections */ - lput(0L); /* time and date */ - lput(rnd(HEADR+segtext.filelen, 4096)+segdata.filelen); - lput(symsize); /* nsyms */ - lput((0x38L<<16)|7L); /* size of optional hdr and flags */ - lput((0413<<16)|0437L); /* magic and version */ - lput(rnd(HEADR+segtext.filelen, 4096)); /* sizes */ - lput(segdata.filelen); - lput(segdata.len - segdata.filelen); - lput(entryvalue()); /* va of entry */ - lput(INITTEXT-HEADR); /* va of base of text */ - lput(segdata.vaddr); /* va of base of data */ - lput(segdata.vaddr+segdata.filelen); /* va of base of bss */ - lput(~0L); /* gp reg mask */ - lput(0L); - lput(0L); - lput(0L); - lput(0L); - lput(~0L); /* gp value ?? */ + lputb(0x160L<<16); /* magic and sections */ + lputb(0L); /* time and date */ + lputb(rnd(HEADR+segtext.filelen, 4096)+segdata.filelen); + lputb(symsize); /* nsyms */ + lputb((0x38L<<16)|7L); /* size of optional hdr and flags */ + lputb((0413<<16)|0437L); /* magic and version */ + lputb(rnd(HEADR+segtext.filelen, 4096)); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); + lputb(entryvalue()); /* va of entry */ + lputb(INITTEXT-HEADR); /* va of base of text */ + lputb(segdata.vaddr); /* va of base of data */ + lputb(segdata.vaddr+segdata.filelen); /* va of base of bss */ + lputb(~0L); /* gp reg mask */ + lputb(0L); + lputb(0L); + lputb(0L); + lputb(0L); + lputb(~0L); /* gp value ?? */ break; lputl(0); /* x */ case 1: /* unix coff */ @@ -814,7 +793,7 @@ asmb(void) lputl(rnd(segtext.filelen, INITRND)); /* text sizes */ lputl(segdata.filelen); /* data sizes */ lputl(segdata.len - segdata.filelen); /* bss sizes */ - lput(entryvalue()); /* va of entry */ + lputb(entryvalue()); /* va of entry */ lputl(INITTEXT); /* text start */ lputl(segdata.vaddr); /* data start */ /* @@ -868,14 +847,14 @@ asmb(void) break; case 2: /* plan9 */ magic = 4*11*11+7; - lput(magic); /* magic */ - lput(segtext.filelen); /* sizes */ - lput(segdata.filelen); - lput(segdata.len - segdata.filelen); - lput(symsize); /* nsyms */ - lput(entryvalue()); /* va of entry */ - lput(spsize); /* sp offsets */ - lput(lcsize); /* line offsets */ + lputb(magic); /* magic */ + lputb(segtext.filelen); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); + lputb(symsize); /* nsyms */ + lputb(entryvalue()); /* va of entry */ + lputb(spsize); /* sp offsets */ + lputb(lcsize); /* line offsets */ break; case 3: /* MS-DOS .COM */ diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index e0746fc75..f2546cf20 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -363,9 +363,9 @@ void follow(void); void instinit(void); void listinit(void); Sym* lookup(char*, int); -void lput(int32); +void lputb(int32); void lputl(int32); -void vputl(uvlong); +void vputl(uint64); void strnput(char*, int); void main(int, char*[]); void* mal(uint32); diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index fefb6d8b0..9c687f2fc 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -188,6 +188,7 @@ main(int argc, char *argv[]) INITRND = 0; break; case 2: /* plan 9 */ + tlsoffset = -8; HEADR = 32L; if(INITTEXT == -1) INITTEXT = 4096+32; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 878a73dac..67acfa167 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -250,6 +250,7 @@ patch(void) Prog *p, *q; Sym *s; int32 vexit; + Sym *plan9_tos; if(debug['v']) Bprint(&bso, "%5.2f mkfwd\n", cputime()); @@ -260,6 +261,10 @@ patch(void) Bflush(&bso); s = lookup("exit", 0); vexit = s->value; + + if(HEADTYPE == 2) + plan9_tos = lookup("_tos", 0); + for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { if(HEADTYPE == 10) { // Windows @@ -303,9 +308,15 @@ patch(void) if(HEADTYPE == 2) { // Plan 9 if(p->from.type == D_INDIR+D_GS && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; p->as = AMOVL; - p->from.type = D_ADDR+D_STATIC; - p->from.offset += 0xdfffefc0; + p->from.type = D_EXTERN; + p->from.sym = plan9_tos; + p->from.offset = 0; } } if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { @@ -389,6 +400,7 @@ dostkoff(void) int a; Prog *pmorestack; Sym *symmorestack; + Sym *plan9_tos; pmorestack = P; symmorestack = lookup("runtime.morestack", 0); @@ -399,6 +411,9 @@ dostkoff(void) pmorestack = symmorestack->text; symmorestack->text->from.scale |= NOSPLIT; } + + if(HEADTYPE == 2) + plan9_tos = lookup("_tos", 0); for(cursym = textp; cursym != nil; cursym = cursym->next) { if(cursym->text == nil || cursym->text->link == nil) @@ -443,9 +458,15 @@ dostkoff(void) case 2: // Plan 9 p->as = AMOVL; - p->from.type = D_ADDR+D_STATIC; - p->from.offset = 0xdfffefc0; + p->from.type = D_EXTERN; + p->from.sym = plan9_tos; p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; break; default: diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index c4868345c..b3aa9aded 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -25,9 +25,12 @@ the package. For example: CFLAGS and LDFLAGS may be defined with pseudo #cgo directives within these comments to tweak the behavior of gcc. Values defined -in multiple directives are concatenated together. For example: +in multiple directives are concatenated together. Options prefixed +by $GOOS, $GOARCH, or $GOOS/$GOARCH are only defined in matching +systems. For example: // #cgo CFLAGS: -DPNG_DEBUG=1 + // #cgo linux CFLAGS: -DLINUX=1 // #cgo LDFLAGS: -lpng // #include <png.h> import "C" diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index cadc6fae9..e6ce21ed3 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -19,6 +19,7 @@ import ( "go/parser" "go/token" "os" + "runtime" "strconv" "strings" "unicode" @@ -66,6 +67,8 @@ func cname(s string) string { func (p *Package) ParseFlags(f *File, srcfile string) { linesIn := strings.Split(f.Preamble, "\n", -1) linesOut := make([]string, 0, len(linesIn)) + +NextLine: for _, line := range linesIn { l := strings.TrimSpace(line) if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { @@ -79,11 +82,29 @@ func (p *Package) ParseFlags(f *File, srcfile string) { fatal("%s: bad #cgo line: %s", srcfile, line) } - k := fields[0] - v := strings.TrimSpace(fields[1]) + var k string + kf := strings.Fields(fields[0]) + switch len(kf) { + case 1: + k = kf[0] + case 2: + k = kf[1] + switch kf[0] { + case runtime.GOOS: + case runtime.GOARCH: + case runtime.GOOS + "/" + runtime.GOARCH: + default: + continue NextLine + } + default: + fatal("%s: bad #cgo option: %s", srcfile, fields[0]) + } + if k != "CFLAGS" && k != "LDFLAGS" { fatal("%s: unsupported #cgo option %s", srcfile, k) } + + v := strings.TrimSpace(fields[1]) args, err := splitQuoted(v) if err != nil { fatal("%s: bad #cgo option %s: %s", srcfile, k, err.String()) @@ -288,7 +309,7 @@ func (p *Package) guessKinds(f *File) []*Name { var b bytes.Buffer b.WriteString(builtinProlog) b.WriteString(f.Preamble) - b.WriteString("void f(void) {\n") + b.WriteString("void __cgo__f__(void) {\n") b.WriteString("#line 0 \"cgo-test\"\n") for i, n := range toSniff { fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) @@ -753,6 +774,8 @@ var dwarfToName = map[string]string{ "double complex": "complexdouble", } +const signedDelta = 64 + // Type returns a *Type with the same memory layout as // dtype when used as the type of a variable or a struct field. func (c *typeConv) Type(dtype dwarf.Type) *Type { @@ -818,7 +841,19 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Align = 1 case *dwarf.EnumType: - switch t.Size { + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + t.C = "enum " + dt.EnumName + signed := 0 + t.EnumValues = make(map[string]int64) + for _, ev := range dt.Val { + t.EnumValues[ev.Name] = ev.Val + if ev.Val < 0 { + signed = signedDelta + } + } + switch t.Size + int64(signed) { default: fatal("unexpected: %d-byte enum type - %s", t.Size, dtype) case 1: @@ -829,14 +864,14 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Go = c.uint32 case 8: t.Go = c.uint64 - } - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - t.C = "enum " + dt.EnumName - t.EnumValues = make(map[string]int64) - for _, ev := range dt.Val { - t.EnumValues[ev.Name] = ev.Val + case 1 + signedDelta: + t.Go = c.int8 + case 2 + signedDelta: + t.Go = c.int16 + case 4 + signedDelta: + t.Go = c.int32 + case 8 + signedDelta: + t.Go = c.int64 } case *dwarf.FloatType: diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index a6f509dc4..59529a6d2 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -32,10 +32,11 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { if err != nil { fatal("%s", err) } - pid, err := os.ForkExec(cmd, argv, os.Environ(), "", []*os.File{r0, w1, w2}) + p, err := os.StartProcess(cmd, argv, os.Environ(), "", []*os.File{r0, w1, w2}) if err != nil { fatal("%s", err) } + defer p.Release() r0.Close() w1.Close() w2.Close() @@ -55,7 +56,7 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { <-c <-c - w, err := os.Wait(pid, 0) + w, err := p.Wait(0) if err != nil { fatal("%s", err) } diff --git a/src/cmd/clean.bash b/src/cmd/clean.bash index 6349919a8..92d8cc5c9 100644 --- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -3,9 +3,14 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. +gomake=gomake +if [ "$1" == "--gomake" -a "$2" != "" ]; then + gomake=$2 +fi + for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g gopack nm cgo cov ebnflint godefs godoc gofmt goinstall gotest goyacc hgpatch prof do cd $i - gomake clean + $gomake clean cd .. done diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index ed20e7e8b..833eba19a 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -54,7 +54,8 @@ widstruct(Type *t, uint32 o, int flag) if(f->type->width < 0) fatal("invalid width %lld", f->type->width); w = f->type->width; - o = rnd(o, f->type->align); + if(f->type->align > 0) + o = rnd(o, f->type->align); f->width = o; // really offset for TFIELD if(f->nname != N) { // this same stackparam logic is in addrescapes diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 994840ee8..86e3cae33 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -242,14 +242,6 @@ import_package: if(safemode && !curio.importsafe) yyerror("cannot import unsafe package %Z", importpkg->path); - - // NOTE(rsc): This is no longer a technical restriction: - // the 6g tool chain would work just fine without giving - // special meaning to a package being named main. - // Other implementations might need the restriction - // (gccgo does), so it stays in the language and the compiler. - if(strcmp($2->name, "main") == 0) - yyerror("cannot import package main"); } import_safety: diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 45b1257fa..e79d3b0f8 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -405,7 +405,7 @@ void importfile(Val *f, int line) { Biobuf *imp; - char *file; + char *file, *p, *q; int32 c; int len; Strlit *path; @@ -423,6 +423,15 @@ importfile(Val *f, int line) errorexit(); } + // The package name main is no longer reserved, + // but we reserve the import path "main" to identify + // the main package, just as we reserve the import + // path "math" to identify the standard math package. + if(strcmp(f->u.sval->s, "main") == 0) { + yyerror("cannot import \"main\""); + errorexit(); + } + if(strcmp(f->u.sval->s, "unsafe") == 0) { if(safemode) { yyerror("cannot import package unsafe"); @@ -432,7 +441,7 @@ importfile(Val *f, int line) cannedimports("unsafe.6", unsafeimport); return; } - + path = f->u.sval; if(islocalname(path)) { cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); @@ -459,9 +468,24 @@ importfile(Val *f, int line) len = strlen(namebuf); if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') { if(!skiptopkgdef(imp)) { - yyerror("import not package file: %s", namebuf); + yyerror("import %s: not a package file", file); + errorexit(); + } + } + + // check object header + p = Brdstr(imp, '\n', 1); + if(strcmp(p, "empty archive") != 0) { + if(strncmp(p, "go object ", 10) != 0) { + yyerror("import %s: not a go object file", file); errorexit(); } + q = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + if(strcmp(p+10, q) != 0) { + yyerror("import %s: object is [%s] expected [%s]", file, p+10, q); + errorexit(); + } + free(q); } // assume files move (get installed) @@ -479,6 +503,7 @@ importfile(Val *f, int line) curio.infile = file; curio.nlsemi = 0; typecheckok = 1; + for(;;) { c = getc(); if(c == EOF) diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c index 7b7e66668..b11a4f5f1 100644 --- a/src/cmd/gc/mparith3.c +++ b/src/cmd/gc/mparith3.c @@ -179,7 +179,7 @@ mpdivfltflt(Mpflt *a, Mpflt *b) double mpgetflt(Mpflt *a) { - int s, i; + int s, i, e; uvlong v, vm; double f; @@ -200,12 +200,12 @@ mpgetflt(Mpflt *a) a->exp -= 1; } - // the magic numbers (64, 63, 53, 10) are + // the magic numbers (64, 63, 53, 10, -1074) are // IEEE specific. this should be done machine // independently or in the 6g half of the compiler - // pick up the mantissa in a uvlong - s = 53; + // pick up the mantissa and a rounding bit in a uvlong + s = 53+1; v = 0; for(i=Mpnorm-1; s>=Mpscale; i--) { v = (v<<Mpscale) | a->val.a[i]; @@ -224,13 +224,26 @@ mpgetflt(Mpflt *a) if(s > 0) v = (v<<s) | (a->val.a[i]>>(Mpscale-s)); + // gradual underflow + e = Mpnorm*Mpscale + a->exp - 53; + if(e < -1074) { + s = -e - 1074; + if(s > 54) + s = 54; + v |= vm & ((1ULL<<s) - 1); + vm >>= s; + e = -1074; + } + //print("vm=%.16llux v=%.16llux\n", vm, v); // round toward even - if(v != (1ULL<<63) || (vm&1ULL) != 0) - vm += v>>63; + if(v != 0 || (vm&2ULL) != 0) + vm = (vm>>1) + (vm&1ULL); + else + vm >>= 1; f = (double)(vm); - f = ldexp(f, Mpnorm*Mpscale + a->exp - 53); + f = ldexp(f, e); if(a->val.neg) f = -f; diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index 0d0d70ac9..fbabe0d43 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -21,7 +21,7 @@ dumpobj(void) errorexit(); } - Bprint(bout, "%s\n", thestring); + Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); Bprint(bout, " exports automatically generated from\n"); Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name); dumpexport(); diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 5686e9599..58a147745 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -157,7 +157,7 @@ walkselect(Node *sel) if(n->left == N || isblank(n->left)) n->left = nodnil(); else if(n->left->op == ONAME && - (!n->colas || (n->class&PHEAP) == 0) && + (!n->colas || (n->left->class&PHEAP) == 0) && convertop(ch->type->type, n->left->type, nil) == OCONVNOP) { n->left = nod(OADDR, n->left, N); n->left->etype = 1; // pointer does not escape @@ -170,9 +170,9 @@ walkselect(Node *sel) typecheck(&a, Erv); r = nod(OAS, n->left, tmp); typecheck(&r, Etop); + cas->nbody = concat(list1(r), cas->nbody); cas->nbody = concat(n->ninit, cas->nbody); n->ninit = nil; - cas->nbody = concat(list1(r), cas->nbody); n->left = a; } } diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index cb5e2a831..0755ca3cd 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -203,6 +203,7 @@ fatal(char *fmt, ...) flusherrors(); +*(int*)0=0; print("%L: internal compiler error: ", lineno); va_start(arg, fmt); vfprint(1, fmt, arg); @@ -213,7 +214,7 @@ fatal(char *fmt, ...) if(strncmp(getgoversion(), "release", 7) == 0) { print("\n"); print("Please file a bug report including a short program that triggers the error.\n"); - print("http://code.google.com/p/go/issues/entry?template=compilerbug"); + print("http://code.google.com/p/go/issues/entry?template=compilerbug\n"); } hcrash(); errorexit(); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 931d0327a..5edca964a 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -96,7 +96,7 @@ typecheck(Node **np, int top) Node *n, *l, *r; NodeList *args; int lno, ok, ntop; - Type *t, *missing, *have; + Type *t, *tp, *missing, *have; Sym *sym; Val v; char *why; @@ -552,6 +552,7 @@ reswitch: ok = Erv; goto ret; } + tp = t; if(isptr[t->etype] && t->type->etype != TINTER) { t = t->type; if(t == T) @@ -563,7 +564,7 @@ reswitch: if(lookdot(n, t, 1)) yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym); else - yyerror("%#N undefined (type %T has no field or method %S)", n, t, n->right->sym); + yyerror("%#N undefined (type %T has no field or method %S)", n, tp, n->right->sym); goto error; } switch(n->op) { diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index 66b01aa64..da1466b21 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -11,7 +11,6 @@ package main import ( - "bytes" "fmt" "go/scanner" "go/token" @@ -335,12 +334,12 @@ func selectionTag(w io.Writer, text []byte, selections int) { } -// FormatText HTML-escapes text and returns it wrapped in <pre> tags. -// Conscutive text segments are wrapped in HTML spans (with tags as +// FormatText HTML-escapes text and writes it to w. +// Consecutive text segments are wrapped in HTML spans (with tags as // defined by startTags and endTag) as follows: // -// - if line >= 0, line numbers are printed before each line, starting -// with the value of line +// - if line >= 0, line number (ln) spans are inserted before each line, +// starting with the value of line // - if the text is Go source, comments get the "comment" span class // - each occurrence of the regular expression pattern gets the "highlight" // span class @@ -349,10 +348,7 @@ func selectionTag(w io.Writer, text []byte, selections int) { // Comments, highlights, and selections may overlap arbitrarily; the respective // HTML span classes are specified in the startTags variable. // -func FormatText(text []byte, line int, goSource bool, pattern string, selection Selection) []byte { - var buf bytes.Buffer - buf.WriteString("<pre>\n") - +func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) { var comments, highlights Selection if goSource { comments = commentSelection(text) @@ -370,11 +366,8 @@ func FormatText(text []byte, line int, goSource bool, pattern string, selection } } } - FormatSelections(&buf, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection) + FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection) } else { - template.HTMLEscape(&buf, text) + template.HTMLEscape(w, text) } - - buf.WriteString("</pre>\n") - return buf.Bytes() } diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 6a00a3e70..c91dc33db 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -274,7 +274,7 @@ func relativePath(path string) string { // ---------------------------------------------------------------------------- // Tab conversion -var spaces = []byte(" ") // 16 spaces seems like a good number +var spaces = []byte(" ") // 32 spaces seems like a good number const ( indenting = iota @@ -291,25 +291,31 @@ type tconv struct { func (p *tconv) writeIndent() (err os.Error) { i := p.indent - for i > len(spaces) { + for i >= len(spaces) { i -= len(spaces) if _, err = p.output.Write(spaces); err != nil { return } } - _, err = p.output.Write(spaces[0:i]) + // i < len(spaces) + if i > 0 { + _, err = p.output.Write(spaces[0:i]) + } return } func (p *tconv) Write(data []byte) (n int, err os.Error) { + if len(data) == 0 { + return + } pos := 0 // valid if p.state == collecting var b byte for n, b = range data { switch p.state { case indenting: switch b { - case '\t', '\v': + case '\t': p.indent += *tabwidth case '\n': p.indent = 0 @@ -336,7 +342,7 @@ func (p *tconv) Write(data []byte) (n int, err os.Error) { } } n = len(data) - if p.state == collecting { + if pos < n && p.state == collecting { _, err = p.output.Write(data[pos:]) } return @@ -346,47 +352,51 @@ func (p *tconv) Write(data []byte) (n int, err os.Error) { // ---------------------------------------------------------------------------- // Templates -// Write an AST-node to w; optionally html-escaped. -func writeNode(w io.Writer, fset *token.FileSet, node interface{}, html bool) { - mode := printer.TabIndent | printer.UseSpaces - if html { - mode |= printer.GenHTML - } +// Write an AST node to w. +func writeNode(w io.Writer, fset *token.FileSet, x interface{}) { // convert trailing tabs into spaces using a tconv filter // to ensure a good outcome in most browsers (there may still // be tabs in comments and strings, but converting those into // the right number of spaces is much harder) - (&printer.Config{mode, *tabwidth, nil}).Fprint(&tconv{output: w}, fset, node) + // + // TODO(gri) rethink printer flags - perhaps tconv can be eliminated + // with an another printer mode (which is more efficiently + // implemented in the printer than here with another layer) + mode := printer.TabIndent | printer.UseSpaces + (&printer.Config{mode, *tabwidth}).Fprint(&tconv{output: w}, fset, x) } -// Write text to w; optionally html-escaped. -func writeText(w io.Writer, text []byte, html bool) { - if html { - template.HTMLEscape(w, text) - return +// Write anything to w. +func writeAny(w io.Writer, fset *token.FileSet, x interface{}) { + switch v := x.(type) { + case []byte: + w.Write(v) + case string: + w.Write([]byte(v)) + case ast.Decl, ast.Expr, ast.Stmt, *ast.File: + writeNode(w, fset, x) + default: + fmt.Fprint(w, x) } - w.Write(text) } -// Write anything to w; optionally html-escaped. -func writeAny(w io.Writer, fset *token.FileSet, html bool, x interface{}) { +// Write anything html-escaped to w. +func writeAnyHTML(w io.Writer, fset *token.FileSet, x interface{}) { switch v := x.(type) { case []byte: - writeText(w, v, html) + template.HTMLEscape(w, v) case string: - writeText(w, []byte(v), html) + template.HTMLEscape(w, []byte(v)) case ast.Decl, ast.Expr, ast.Stmt, *ast.File: - writeNode(w, fset, x, html) + var buf bytes.Buffer + writeNode(&buf, fset, x) + FormatText(w, buf.Bytes(), -1, true, "", nil) default: - if html { - var buf bytes.Buffer - fmt.Fprint(&buf, x) - writeText(w, buf.Bytes(), true) - } else { - fmt.Fprint(w, x) - } + var buf bytes.Buffer + fmt.Fprint(&buf, x) + template.HTMLEscape(w, buf.Bytes()) } } @@ -401,24 +411,16 @@ func fileset(x []interface{}) *token.FileSet { } -// Template formatter for "html" format. -func htmlFmt(w io.Writer, format string, x ...interface{}) { - writeAny(w, fileset(x), true, x[0]) -} - - // Template formatter for "html-esc" format. func htmlEscFmt(w io.Writer, format string, x ...interface{}) { - var buf bytes.Buffer - writeAny(&buf, fileset(x), false, x[0]) - template.HTMLEscape(w, buf.Bytes()) + writeAnyHTML(w, fileset(x), x[0]) } // Template formatter for "html-comment" format. func htmlCommentFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, fileset(x), false, x[0]) + writeAny(&buf, fileset(x), x[0]) // TODO(gri) Provide list of words (e.g. function parameters) // to be emphasized by ToHTML. doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping @@ -427,14 +429,14 @@ func htmlCommentFmt(w io.Writer, format string, x ...interface{}) { // Template formatter for "" (default) format. func textFmt(w io.Writer, format string, x ...interface{}) { - writeAny(w, fileset(x), false, x[0]) + writeAny(w, fileset(x), x[0]) } // Template formatter for "urlquery-esc" format. func urlQueryEscFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, fileset(x), false, x[0]) + writeAny(&buf, fileset(x), x[0]) template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes())))) } @@ -603,7 +605,6 @@ func numlinesFmt(w io.Writer, format string, x ...interface{}) { var fmap = template.FormatterMap{ "": textFmt, - "html": htmlFmt, "html-esc": htmlEscFmt, "html-comment": htmlCommentFmt, "urlquery-esc": urlQueryEscFmt, @@ -683,7 +684,7 @@ func servePage(w http.ResponseWriter, title, subtitle, query string, content []b content, } - if err := godocHTML.Execute(&d, w); err != nil { + if err := godocHTML.Execute(w, &d); err != nil { log.Printf("godocHTML.Execute: %s", err) } } @@ -751,7 +752,7 @@ func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin func applyTemplate(t *template.Template, name string, data interface{}) []byte { var buf bytes.Buffer - if err := t.Execute(data, &buf); err != nil { + if err := t.Execute(&buf, data); err != nil { log.Printf("%s.Execute: %s", name, err) } return buf.Bytes() @@ -775,8 +776,12 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit return } - contents := FormatText(src, 1, pathutil.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) - servePage(w, title+" "+relpath, "", "", contents) + var buf bytes.Buffer + buf.WriteString("<pre>") + FormatText(&buf, src, 1, pathutil.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) + buf.WriteString("</pre>") + + servePage(w, title+" "+relpath, "", "", buf.Bytes()) } @@ -891,6 +896,11 @@ type PageInfo struct { } +func (info *PageInfo) IsEmpty() bool { + return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil +} + + type httpHandler struct { pattern string // url pattern; e.g. "/pkg/" fsRoot string // file system root to which the pattern is mapped diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 581409cde..56f31f5cf 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -430,7 +430,6 @@ func (a *AltWords) filter(s string) *AltWords { // Indexer // Adjust these flags as seems best. -const includeNonGoFiles = true const includeMainPackages = true const includeTestFiles = true @@ -728,7 +727,7 @@ func isWhitelisted(filename string) bool { } -func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { +func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool) { if !f.IsRegular() { return } @@ -746,7 +745,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { } goFile = true - case !includeNonGoFiles || !isWhitelisted(f.Name): + case !fulltextIndex || !isWhitelisted(f.Name): return } @@ -811,7 +810,7 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index { } for _, f := range list { if !f.IsDirectory() { - x.visitFile(dirname, f) + x.visitFile(dirname, f, fulltextIndex) } } } diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index f1b11a760..ea1e3c42e 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -83,20 +83,21 @@ func exec(rw http.ResponseWriter, args []string) (status int) { if *verbose { log.Printf("executing %v", args) } - pid, err := os.ForkExec(bin, args, os.Environ(), *goroot, fds) + p, err := os.StartProcess(bin, args, os.Environ(), *goroot, fds) defer r.Close() w.Close() if err != nil { - log.Printf("os.ForkExec(%q): %v", bin, err) + log.Printf("os.StartProcess(%q): %v", bin, err) return 2 } + defer p.Release() var buf bytes.Buffer io.Copy(&buf, r) - wait, err := os.Wait(pid, 0) + wait, err := p.Wait(0) if err != nil { os.Stderr.Write(buf.Bytes()) - log.Printf("os.Wait(%d, 0): %v", pid, err) + log.Printf("os.Wait(%d, 0): %v", p.Pid, err) return 2 } status = wait.ExitStatus() @@ -317,7 +318,7 @@ func main() { } relpath := path abspath := path - if len(path) > 0 && path[0] != '/' { + if !pathutil.IsAbs(path) { abspath = absolutePath(path, pkgHandler.fsRoot) } else { relpath = relativePath(path) @@ -336,12 +337,17 @@ func main() { // if there are multiple packages in a directory. info := pkgHandler.getPageInfo(abspath, relpath, "", mode) - if info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil { + if info.IsEmpty() { // try again, this time assume it's a command - if len(path) > 0 && path[0] != '/' { + if !pathutil.IsAbs(path) { abspath = absolutePath(path, cmdHandler.fsRoot) } - info = cmdHandler.getPageInfo(abspath, relpath, "", mode) + cmdInfo := cmdHandler.getPageInfo(abspath, relpath, "", mode) + // only use the cmdInfo if it actually contains a result + // (don't hide errors reported from looking up a package) + if !cmdInfo.IsEmpty() { + info = cmdInfo + } } if info.Err != nil { log.Fatalf("%v", info.Err) @@ -366,7 +372,11 @@ func main() { if i > 0 { fmt.Println() } - writeAny(os.Stdout, info.FSet, *html, d) + if *html { + writeAnyHTML(os.Stdout, info.FSet, d) + } else { + writeAny(os.Stdout, info.FSet, d) + } fmt.Println() } return @@ -376,7 +386,7 @@ func main() { } } - if err := packageText.Execute(info, os.Stdout); err != nil { + if err := packageText.Execute(os.Stdout, info); err != nil { log.Printf("packageText.Execute: %s", err) } } diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go index c2838ed5a..c5f4c1edf 100755 --- a/src/cmd/godoc/snippet.go +++ b/src/cmd/godoc/snippet.go @@ -25,9 +25,14 @@ type Snippet struct { func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { // TODO instead of pretty-printing the node, should use the original source instead - var buf bytes.Buffer - writeNode(&buf, fset, decl, false) - return &Snippet{fset.Position(id.Pos()).Line, FormatText(buf.Bytes(), -1, true, id.Name, nil)} + var buf1 bytes.Buffer + writeNode(&buf1, fset, decl) + // wrap text with <pre> tag + var buf2 bytes.Buffer + buf2.WriteString("<pre>") + FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil) + buf2.WriteString("</pre>") + return &Snippet{fset.Position(id.Pos()).Line, buf2.Bytes()} } diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go index a032bd331..cc028cc4d 100644 --- a/src/cmd/godoc/utils.go +++ b/src/cmd/godoc/utils.go @@ -60,10 +60,10 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string { continue // ignore empty paths (don't assume ".") } // len(path) > 0: normalize path - if path[0] != '/' { - path = pathutil.Join(cwd, path) - } else { + if pathutil.IsAbs(path) { path = pathutil.Clean(path) + } else { + path = pathutil.Join(cwd, path) } // we have a non-empty absolute path if filter != nil && !filter(path) { diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index d7b70c461..41c12b88d 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -113,19 +113,20 @@ func processFile(f *os.File) os.Error { simplify(file) } - var res bytes.Buffer - _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, fset, file) + var buf bytes.Buffer + _, err = (&printer.Config{printerMode, *tabWidth}).Fprint(&buf, fset, file) if err != nil { return err } + res := buf.Bytes() - if bytes.Compare(src, res.Bytes()) != 0 { + if !bytes.Equal(src, res) { // formatting has changed if *list { fmt.Fprintln(os.Stdout, f.Name()) } if *write { - err = ioutil.WriteFile(f.Name(), res.Bytes(), 0) + err = ioutil.WriteFile(f.Name(), res, 0) if err != nil { return err } @@ -133,7 +134,7 @@ func processFile(f *os.File) os.Error { } if !*list && !*write { - _, err = os.Stdout.Write(res.Bytes()) + _, err = os.Stdout.Write(res) } return err diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index b5f4de1e2..2f60a3e7b 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -42,7 +42,7 @@ apply1() { bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \ bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \ bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \ - bug302.go | bug306.go ) return ;; + bug302.go | bug306.go | bug322.go ) return ;; esac # the following directories are skipped because they contain test # cases for syntax errors and thus won't parse in the first place: diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index 93a648b2b..8d4d6c5d2 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -75,7 +75,7 @@ func makeMakefile(dir, pkg string) ([]byte, os.Error) { var buf bytes.Buffer md := makedata{pkg, goFiles, cgoFiles, oFiles} - if err := makefileTemplate.Execute(&md, &buf); err != nil { + if err := makefileTemplate.Execute(&buf, &md); err != nil { return nil, err } return buf.Bytes(), nil diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index a16e98cfe..702f104a6 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -131,6 +131,7 @@ Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */ int allobj = 1; /* set when all members are object files of the same type */ int symdefsize; /* size of symdef file */ char *pkgstmt; /* string "package foo" */ +char *objhdr; /* string "go object darwin 386 release.2010-01-01 2345+" */ int dupfound; /* flag for duplicate symbol */ Hashchain *hash[NHASH]; /* hash table of text symbols */ @@ -246,6 +247,8 @@ main(int argc, char *argv[]) argc -= 3; argv += 3; (*comfun)(cp, argc, argv); /* do the command */ + if(errors && cflag) + remove(cp); cp = 0; while (argc--) { if (*argv) { @@ -590,10 +593,11 @@ void scanobj(Biobuf *b, Arfile *ap, long size) { int obj; - vlong offset; + vlong offset, offset1; Dir *d; static int lastobj = -1; uchar buf[4]; + char *p; if (!allobj) /* non-object file encountered */ return; @@ -628,14 +632,32 @@ scanobj(Biobuf *b, Arfile *ap, long size) Bseek(b, offset, 0); return; } - if (lastobj >= 0 && obj != lastobj) { + + offset1 = Boffset(b); + Bseek(b, offset, 0); + p = Brdstr(b, '\n', 1); + Bseek(b, offset1, 0); + if(p == nil || strncmp(p, "go object ", 10) != 0) { + fprint(2, "gopack: malformed object file %s\n", file); + errors++; + Bseek(b, offset, 0); + free(p); + return; + } + + if ((lastobj >= 0 && obj != lastobj) || (objhdr != nil && strcmp(p, objhdr) != 0)) { fprint(2, "gopack: inconsistent object file %s\n", file); errors++; allobj = 0; - Bseek(b, offset, 0); + free(p); return; } lastobj = obj; + if(objhdr == nil) + objhdr = p; + else + free(p); + if (!readar(b, obj, offset+size, 0)) { fprint(2, "gopack: invalid symbol reference in file %s\n", file); errors++; @@ -677,7 +699,7 @@ char* importblock; void getpkgdef(char **datap, int *lenp) { - char *tag; + char *tag, *hdr; if(pkgname == nil) { pkgname = "__emptyarchive__"; @@ -688,7 +710,11 @@ getpkgdef(char **datap, int *lenp) if(safe || Sflag) tag = "safe"; - *datap = smprint("import\n$$\npackage %s %s\n%s\n$$\n", pkgname, tag, importblock); + hdr = "empty archive"; + if(objhdr != nil) + hdr = objhdr; + + *datap = smprint("%s\nimport\n$$\npackage %s %s\n%s\n$$\n", hdr, pkgname, tag, importblock); *lenp = strlen(*datap); } diff --git a/src/cmd/gotest/gotest b/src/cmd/gotest/gotest index 87c680089..69eaae730 100755 --- a/src/cmd/gotest/gotest +++ b/src/cmd/gotest/gotest @@ -119,6 +119,12 @@ nmgrep() { done } +localname() { + # The package main has been renamed to __main__ when imported. + # Adjust its uses. + echo $1 | sed 's/^main\./__main__./' +} + importpath=$(gomake -s importpath) { # test functions are named TestFoo @@ -139,9 +145,20 @@ importpath=$(gomake -s importpath) echo # imports if echo "$tests" | egrep -v '_test\.' >/dev/null; then - if [ "$importpath" != "testing" ]; then + case "$importpath" in + testing) + ;; + main) + # Import path main is reserved, so import with + # explicit reference to ./_test/main instead. + # Also, the file we are writing defines a function named main, + # so rename this import to __main__ to avoid name conflict. + echo 'import __main__ "./_test/main"' + ;; + *) echo 'import "'$importpath'"' - fi + ;; + esac fi if $havex; then echo 'import "./_xtest_"' @@ -153,23 +170,20 @@ importpath=$(gomake -s importpath) echo 'var tests = []testing.InternalTest{' for i in $tests do - echo ' {"'$i'", '$i'},' + j=$(localname $i) + echo ' {"'$i'", '$j'},' done echo '}' # benchmark array - if [ "$benchmarks" = "" ] - then - # keep the empty array gofmt-safe. - # (not an issue for the test array, which is never empty.) - echo 'var benchmarks = []testing.InternalBenchmark{}' - else - echo 'var benchmarks = []testing.InternalBenchmark{' - for i in $benchmarks - do - echo ' {"'$i'", '$i'},' - done - echo '}' - fi + # The comment makes the multiline declaration + # gofmt-safe even when there are no benchmarks. + echo 'var benchmarks = []testing.InternalBenchmark{ //' + for i in $benchmarks + do + j=$(localname $i) + echo ' {"'$i'", '$j'},' + done + echo '}' # body echo echo 'func main() {' diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 8966b2a1f..2c6a6d084 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -148,8 +148,6 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) } if(strcmp(pkg, "main") == 0 && strcmp(name, "main") != 0) fprint(2, "%s: %s: not package main (package %s)\n", argv0, filename, name); - else if(strcmp(pkg, "main") != 0 && strcmp(name, "main") == 0) - fprint(2, "%s: %s: importing %s, found package main", argv0, filename, pkg); loadpkgdata(filename, pkg, p0, p1 - p0); } diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index b1a62f25e..c144d4295 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -378,10 +378,9 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) int n, c1, c2, c3, c4; uint32 magic; vlong import0, import1, eof; - char src[1024]; + char *t; eof = Boffset(f) + len; - src[0] = '\0'; pn = strdup(pn); @@ -415,22 +414,34 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) line = Brdline(f, '\n'); if(line == nil) { if(Blinelen(f) > 0) { - diag("%s: malformed object file", pn); + diag("%s: not an object file", pn); return; } goto eof; } n = Blinelen(f) - 1; - if(n != strlen(thestring) || strncmp(line, thestring, n) != 0) { - if(line) - line[n] = '\0'; + line[n] = '\0'; + if(strncmp(line, "go object ", 10) != 0) { if(strlen(pn) > 3 && strcmp(pn+strlen(pn)-3, ".go") == 0) { print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thechar, pn, thechar, thechar); errorexit(); } - diag("file not %s [%s]\n", thestring, line); + if(strcmp(line, thestring) == 0) { + // old header format: just $GOOS + diag("%s: stale object file", pn); + return; + } + diag("%s: not an object file", pn); + return; + } + t = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + if(strcmp(line+10, t) != 0) { + diag("%s: object is [%s] expected [%s]", pn, line+10, t); + free(t); return; } + free(t); + line[n] = '\n'; /* skip over exports and other info -- ends with \n!\n */ import0 = Boffset(f); diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 4ac5d37f9..16dfb0dc3 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -156,6 +156,7 @@ vlong adduint8(Sym*, uint8); vlong adduint16(Sym*, uint16); void asmsym(void); void asmelfsym64(void); +void asmplan9sym(void); void strnput(char*, int); void dodata(void); void address(void); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 26e4def64..22777b6b5 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -136,6 +136,62 @@ asmelfsym32(void) genasmsym(putelfsym32); } +void +putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int i; + + switch(t) { + case 'T': + case 't': + case 'L': + case 'l': + case 'D': + case 'd': + case 'B': + case 'b': + case 'a': + case 'p': + + case 'f': + case 'z': + case 'Z': + + case 'm': + lputb(addr); + cput(t+0x80); /* 0x80 is variable length */ + + if(t == 'z' || t == 'Z') { + cput(0); + for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { + cput(s[i]); + cput(s[i+1]); + } + cput(0); + cput(0); + i++; + } else { + /* skip the '<' in filenames */ + if(t=='f') + s++; + + for(i=0; s[i]; i++) + cput(s[i]); + cput(0); + } + + symsize += 4 + 1 + i + 1; + break; + default: + return; + }; +} + +void +asmplan9sym(void) +{ + genasmsym(putplan9sym); +} static Sym *symt; @@ -165,6 +221,52 @@ slputb(int32 v) } void +wputl(ushort w) +{ + cput(w); + cput(w>>8); +} + +void +wputb(ushort w) +{ + cput(w>>8); + cput(w); +} + +void +lputb(int32 l) +{ + cput(l>>24); + cput(l>>16); + cput(l>>8); + cput(l); +} + +void +lputl(int32 l) +{ + cput(l); + cput(l>>8); + cput(l>>16); + cput(l>>24); +} + +void +vputb(uint64 v) +{ + lputb(v>>32); + lputb(v); +} + +void +vputl(uint64 v) +{ + lputl(v); + lputl(v >> 32); +} + +void putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) { int i, f, l; diff --git a/src/env.bash b/src/env.bash index 2518c4233..4fc762821 100644 --- a/src/env.bash +++ b/src/env.bash @@ -38,7 +38,7 @@ fi # Tried to use . <($MAKE ...) here, but it cannot set environment # variables in the version of bash that ships with OS X. Amazing. -eval $($MAKE --no-print-directory -f Make.inc go-env | egrep 'GOARCH|GOOS|GO_ENV') +eval $($MAKE --no-print-directory -f Make.inc go-env | egrep 'GOARCH|GOOS|GOHOSTARCH|GOHOSTOS|GO_ENV') # Shell doesn't tell us whether make succeeded, # so Make.inc generates a fake variable name. diff --git a/src/libmach/obj.c b/src/libmach/obj.c index dacb2ae1f..1ffe7a0ee 100644 --- a/src/libmach/obj.c +++ b/src/libmach/obj.c @@ -116,34 +116,24 @@ objtype(Biobuf *bp, char **name) int i; char buf[MAXIS]; int c; - -Retry: - if(Bread(bp, buf, MAXIS) < MAXIS) - return -1; - Bseek(bp, -MAXIS, 1); - for (i = 0; i < Maxobjtype; i++) { - if (obj[i].is && (*obj[i].is)(buf)) { - if (name) - *name = obj[i].name; - return i; - } - } + char *p; /* - * Maybe there's an import block we need to skip + * Look for import block. */ - for(i = 0; i < MAXIS; i++) { - if(isalpha(buf[i]) || isdigit(buf[i])) - continue; - if(i == 0 || buf[i] != '\n') - return -1; - break; - } + p = Brdline(bp, '\n'); + if(p == nil) + return -1; + if(Blinelen(bp) < 10 || strncmp(p, "go object ", 10) != 0) + return -1; + Bseek(bp, -1, 1); /* * Found one. Skip until "\n!\n" */ - while((c = Bgetc(bp)) != Beof) { + for(;;) { + if((c = Bgetc(bp)) == Beof) + return -1; if(c != '\n') continue; c = Bgetc(bp); @@ -156,8 +146,20 @@ Retry: Bungetc(bp); continue; } - goto Retry; + break; } + + if(Bread(bp, buf, MAXIS) < MAXIS) + return -1; + Bseek(bp, -MAXIS, 1); + for (i = 0; i < Maxobjtype; i++) { + if (obj[i].is && (*obj[i].is)(buf)) { + if (name) + *name = obj[i].name; + return i; + } + } + return -1; } diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 6ba6951af..619167ca4 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -187,7 +187,7 @@ NOBENCH=\ # Disable tests that depend on an external network. ifeq ($(DISABLE_NET_TESTS),1) -NOTEST+=http net +NOTEST+=http net syslog endif # Disable tests that windows cannot run yet. @@ -211,19 +211,19 @@ test.dirs: $(addsuffix .test, $(TEST)) bench.dirs: $(addsuffix .bench, $(BENCH)) %.clean: - +cd $* && gomake clean + +cd $* && $(MAKE) clean %.install: - +cd $* && gomake install + +cd $* && $(MAKE) install %.nuke: - +cd $* && gomake nuke + +cd $* && $(MAKE) nuke %.test: - +cd $* && gomake test + +cd $* && $(MAKE) test %.bench: - +cd $* && gomake bench + +cd $* && $(MAKE) bench clean: clean.dirs diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go index 579ba1602..d8d9bba60 100644 --- a/src/pkg/archive/zip/reader.go +++ b/src/pkg/archive/zip/reader.go @@ -42,6 +42,10 @@ type File struct { bodyOffset int64 } +func (f *File) hasDataDescriptor() bool { + return f.Flags&0x8 != 0 +} + // OpenReader will open the Zip file specified by name and return a Reader. func OpenReader(name string) (*Reader, os.Error) { f, err := os.Open(name, os.O_RDONLY, 0644) @@ -93,7 +97,16 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) { return } } - r := io.NewSectionReader(f.zipr, off+f.bodyOffset, int64(f.CompressedSize)) + size := int64(f.CompressedSize) + if f.hasDataDescriptor() { + if size == 0 { + // permit SectionReader to see the rest of the file + size = f.zipsize - (off + f.bodyOffset) + } else { + size += dataDescriptorLen + } + } + r := io.NewSectionReader(f.zipr, off+f.bodyOffset, size) switch f.Method { case 0: // store (no compression) rc = nopCloser{r} @@ -103,7 +116,7 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) { err = UnsupportedMethod } if rc != nil { - rc = &checksumReader{rc, crc32.NewIEEE(), f.CRC32} + rc = &checksumReader{rc, crc32.NewIEEE(), f, r} } return } @@ -111,7 +124,8 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) { type checksumReader struct { rc io.ReadCloser hash hash.Hash32 - sum uint32 + f *File + zipr io.Reader // for reading the data descriptor } func (r *checksumReader) Read(b []byte) (n int, err os.Error) { @@ -120,7 +134,12 @@ func (r *checksumReader) Read(b []byte) (n int, err os.Error) { if err != os.EOF { return } - if r.hash.Sum32() != r.sum { + if r.f.hasDataDescriptor() { + if err = readDataDescriptor(r.zipr, r.f); err != nil { + return + } + } + if r.hash.Sum32() != r.f.CRC32 { err = ChecksumError } return @@ -205,6 +224,18 @@ func readDirectoryHeader(f *File, r io.Reader) (err os.Error) { return } +func readDataDescriptor(r io.Reader, f *File) (err os.Error) { + defer func() { + if rerr, ok := recover().(os.Error); ok { + err = rerr + } + }() + read(r, &f.CRC32) + read(r, &f.CompressedSize) + read(r, &f.UncompressedSize) + return +} + func readDirectoryEnd(r io.ReaderAt, size int64) (d *directoryEnd, err os.Error) { // look for directoryEndSignature in the last 1k, then in the last 65k var b []byte diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go index 3c24f1467..72e8cccfd 100644 --- a/src/pkg/archive/zip/reader_test.go +++ b/src/pkg/archive/zip/reader_test.go @@ -52,6 +52,15 @@ var tests = []ZipTest{ }, {Name: "readme.zip"}, {Name: "readme.notzip", Error: FormatError}, + { + Name: "dd.zip", + File: []ZipTestFile{ + { + Name: "filename", + Content: []byte("This is a test textfile.\n"), + }, + }, + }, } func TestReader(t *testing.T) { @@ -102,16 +111,18 @@ func readTestZip(t *testing.T, zt ZipTest) { } // test invalid checksum - z.File[0].CRC32++ // invalidate - r, err := z.File[0].Open() - if err != nil { - t.Error(err) - return - } - var b bytes.Buffer - _, err = io.Copy(&b, r) - if err != ChecksumError { - t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ChecksumError) + if !z.File[0].hasDataDescriptor() { // skip test when crc32 in dd + z.File[0].CRC32++ // invalidate + r, err := z.File[0].Open() + if err != nil { + t.Error(err) + return + } + var b bytes.Buffer + _, err = io.Copy(&b, r) + if err != ChecksumError { + t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ChecksumError) + } } } diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go index 8a8c727d4..bfe0aae2e 100644 --- a/src/pkg/archive/zip/struct.go +++ b/src/pkg/archive/zip/struct.go @@ -4,6 +4,7 @@ const ( fileHeaderSignature = 0x04034b50 directoryHeaderSignature = 0x02014b50 directoryEndSignature = 0x06054b50 + dataDescriptorLen = 12 ) type FileHeader struct { diff --git a/src/pkg/archive/zip/testdata/dd.zip b/src/pkg/archive/zip/testdata/dd.zip Binary files differnew file mode 100644 index 000000000..e53378b0b --- /dev/null +++ b/src/pkg/archive/zip/testdata/dd.zip diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go index 24548714b..57b8f20ba 100644 --- a/src/pkg/asn1/marshal.go +++ b/src/pkg/asn1/marshal.go @@ -317,7 +317,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter switch v := value.(type) { case *reflect.BoolValue: if v.Get() { - return out.WriteByte(1) + return out.WriteByte(255) } else { return out.WriteByte(0) } diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index 4aa74371f..1acd4e05c 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -154,17 +154,20 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { } // WriteTo writes data to w until the buffer is drained or an error -// occurs. The return value n is the number of bytes written. +// occurs. The return value n is the number of bytes written; it always +// fits into an int, but it is int64 to match the io.WriterTo interface. // Any error encountered during the write is also returned. func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { b.lastRead = opInvalid - for b.off < len(b.buf) { + if b.off < len(b.buf) { m, e := w.Write(b.buf[b.off:]) - n += int64(m) b.off += m + n = int64(m) if e != nil { return n, e } + // otherwise all bytes were written, by definition of + // Write method in io.Writer } // Buffer is now empty; reset. b.Truncate(0) @@ -309,13 +312,14 @@ func (b *Buffer) UnreadByte() os.Error { // delim. func (b *Buffer) ReadBytes(delim byte) (line []byte, err os.Error) { i := IndexByte(b.buf[b.off:], delim) - size := i + 1 - b.off + size := i + 1 if i < 0 { size = len(b.buf) - b.off err = os.EOF } line = make([]byte, size) copy(line, b.buf[b.off:]) + b.off += size return } diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go index 2af9ffdef..56a2d9275 100644 --- a/src/pkg/bytes/buffer_test.go +++ b/src/pkg/bytes/buffer_test.go @@ -350,25 +350,36 @@ func TestNext(t *testing.T) { } var readBytesTests = []struct { - buffer []byte + buffer string delim byte - expected []byte + expected []string err os.Error }{ - {err: os.EOF}, - {[]byte{}, 0, []byte{}, os.EOF}, - {[]byte("a\x00"), 0, []byte("a\x00"), nil}, - {[]byte("hello\x01world"), 1, []byte("hello\x01"), nil}, - {[]byte("foo\nbar"), 0, []byte("foo\nbar"), os.EOF}, - {[]byte("alpha beta gamma"), ' ', []byte("alpha "), nil}, + {"", 0, []string{""}, os.EOF}, + {"a\x00", 0, []string{"a\x00"}, nil}, + {"abbbaaaba", 'b', []string{"ab", "b", "b", "aaab"}, nil}, + {"hello\x01world", 1, []string{"hello\x01"}, nil}, + {"foo\nbar", 0, []string{"foo\nbar"}, os.EOF}, + {"alpha\nbeta\ngamma\n", '\n', []string{"alpha\n", "beta\n", "gamma\n"}, nil}, + {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, os.EOF}, } func TestReadBytes(t *testing.T) { for _, test := range readBytesTests { - buf := NewBuffer(test.buffer) - bytes, err := buf.ReadBytes(test.delim) - if !Equal(bytes, test.expected) || err != test.err { - t.Errorf("expected %q, %v got %q, %v", test.expected, test.err, bytes, err) + buf := NewBufferString(test.buffer) + var err os.Error + for _, expected := range test.expected { + var bytes []byte + bytes, err = buf.ReadBytes(test.delim) + if string(bytes) != expected { + t.Errorf("expected %q, got %q", expected, bytes) + } + if err != nil { + break + } + } + if err != test.err { + t.Errorf("expected error %v, got %v", test.err, err) } } } diff --git a/src/pkg/container/ring/ring.go b/src/pkg/container/ring/ring.go index 335afbc3c..5925164e9 100644 --- a/src/pkg/container/ring/ring.go +++ b/src/pkg/container/ring/ring.go @@ -138,16 +138,13 @@ func (r *Ring) Len() int { } -func (r *Ring) Iter() <-chan interface{} { - c := make(chan interface{}) - go func() { - if r != nil { - c <- r.Value - for p := r.Next(); p != r; p = p.next { - c <- p.Value - } +// Do calls function f on each element of the ring, in forward order. +// The behavior of Do is undefined if f changes *r. +func (r *Ring) Do(f func(interface{})) { + if r != nil { + f(r.Value) + for p := r.Next(); p != r; p = p.next { + f(p.Value) } - close(c) - }() - return c + } } diff --git a/src/pkg/container/ring/ring_test.go b/src/pkg/container/ring/ring_test.go index ee3c41128..778c083d0 100644 --- a/src/pkg/container/ring/ring_test.go +++ b/src/pkg/container/ring/ring_test.go @@ -35,12 +35,12 @@ func verify(t *testing.T, r *Ring, N int, sum int) { // iteration n = 0 s := 0 - for p := range r.Iter() { + r.Do(func(p interface{}) { n++ if p != nil { s += p.(int) } - } + }) if n != N { t.Errorf("number of forward iterations == %d; expected %d", n, N) } @@ -128,16 +128,6 @@ func makeN(n int) *Ring { return r } - -func sum(r *Ring) int { - s := 0 - for p := range r.Iter() { - s += p.(int) - } - return s -} - - func sumN(n int) int { return (n*n + n) / 2 } diff --git a/src/pkg/crypto/cipher/ocfb.go b/src/pkg/crypto/cipher/ocfb.go index 43cb5a531..b2d877591 100644 --- a/src/pkg/crypto/cipher/ocfb.go +++ b/src/pkg/crypto/cipher/ocfb.go @@ -12,11 +12,21 @@ type ocfbEncrypter struct { outUsed int } +// An OCFBResyncOption determines if the "resynchronization step" of OCFB is +// performed. +type OCFBResyncOption bool + +const ( + OCFBResync OCFBResyncOption = true + OCFBNoResync OCFBResyncOption = false +) + // NewOCFBEncrypter returns a Stream which encrypts data with OpenPGP's cipher // feedback mode using the given Block, and an initial amount of ciphertext. // randData must be random bytes and be the same length as the Block's block -// size. -func NewOCFBEncrypter(block Block, randData []byte) (Stream, []byte) { +// size. Resync determines if the "resynchronization step" from RFC 4880, 13.9 +// step 7 is performed. Different parts of OpenPGP vary on this point. +func NewOCFBEncrypter(block Block, randData []byte, resync OCFBResyncOption) (Stream, []byte) { blockSize := block.BlockSize() if len(randData) != blockSize { return nil, nil @@ -38,7 +48,13 @@ func NewOCFBEncrypter(block Block, randData []byte) (Stream, []byte) { prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] - block.Encrypt(x.fre, prefix[2:]) + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } return x, prefix } @@ -64,8 +80,10 @@ type ocfbDecrypter struct { // NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher // feedback mode using the given Block. Prefix must be the first blockSize + 2 // bytes of the ciphertext, where blockSize is the Block's block size. If an -// incorrect key is detected then nil is returned. -func NewOCFBDecrypter(block Block, prefix []byte) Stream { +// incorrect key is detected then nil is returned. Resync determines if the +// "resynchronization step" from RFC 4880, 13.9 step 7 is performed. Different +// parts of OpenPGP vary on this point. +func NewOCFBDecrypter(block Block, prefix []byte, resync OCFBResyncOption) Stream { blockSize := block.BlockSize() if len(prefix) != blockSize+2 { return nil @@ -93,7 +111,13 @@ func NewOCFBDecrypter(block Block, prefix []byte) Stream { return nil } - block.Encrypt(x.fre, prefix[2:]) + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } return x } diff --git a/src/pkg/crypto/cipher/ocfb_test.go b/src/pkg/crypto/cipher/ocfb_test.go index 289bb7c91..40938b589 100644 --- a/src/pkg/crypto/cipher/ocfb_test.go +++ b/src/pkg/crypto/cipher/ocfb_test.go @@ -11,29 +11,34 @@ import ( "testing" ) -func TestOCFB(t *testing.T) { +func testOCFB(t *testing.T, resync OCFBResyncOption) { block, err := aes.NewCipher(commonKey128) if err != nil { t.Error(err) return } - plaintext := []byte("this is the plaintext") + plaintext := []byte("this is the plaintext, which is long enough to span several blocks.") randData := make([]byte, block.BlockSize()) rand.Reader.Read(randData) - ocfb, prefix := NewOCFBEncrypter(block, randData) + ocfb, prefix := NewOCFBEncrypter(block, randData, resync) ciphertext := make([]byte, len(plaintext)) ocfb.XORKeyStream(ciphertext, plaintext) - ocfbdec := NewOCFBDecrypter(block, prefix) + ocfbdec := NewOCFBDecrypter(block, prefix, resync) if ocfbdec == nil { - t.Error("NewOCFBDecrypter failed") + t.Errorf("NewOCFBDecrypter failed (resync: %t)", resync) return } plaintextCopy := make([]byte, len(plaintext)) ocfbdec.XORKeyStream(plaintextCopy, ciphertext) if !bytes.Equal(plaintextCopy, plaintext) { - t.Errorf("got: %x, want: %x", plaintextCopy, plaintext) + t.Errorf("got: %x, want: %x (resync: %t)", plaintextCopy, plaintext, resync) } } + +func TestOCFB(t *testing.T) { + testOCFB(t, OCFBNoResync) + testOCFB(t, OCFBResync) +} diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go index 97080f6c6..0c5ae9d71 100644 --- a/src/pkg/crypto/openpgp/armor/armor.go +++ b/src/pkg/crypto/openpgp/armor/armor.go @@ -112,7 +112,7 @@ func (l *lineReader) Read(p []byte) (n int, err os.Error) { return 0, os.EOF } - if len(line) != 64 { + if len(line) > 64 { return 0, ArmorCorrupt } diff --git a/src/pkg/crypto/openpgp/armor/armor_test.go b/src/pkg/crypto/openpgp/armor/armor_test.go index e4ffd414b..9334e94e9 100644 --- a/src/pkg/crypto/openpgp/armor/armor_test.go +++ b/src/pkg/crypto/openpgp/armor/armor_test.go @@ -34,7 +34,7 @@ func TestDecodeEncode(t *testing.T) { t.Error(err) } - if adler32.Checksum(contents) != 0x789d7f00 { + if adler32.Checksum(contents) != 0x27b144be { t.Errorf("contents: got: %x", contents) } @@ -73,13 +73,11 @@ func TestLongHeader(t *testing.T) { const armorExample1 = `-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) -iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 -kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp -cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA -byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 -WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv -okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= -=wfQG +iJwEAAECAAYFAk1Fv/0ACgkQo01+GMIMMbsYTwQAiAw+QAaNfY6WBdplZ/uMAccm +4g+81QPmTSGHnetSb6WBiY13kVzK4HQiZH8JSkmmroMLuGeJwsRTEL4wbjRyUKEt +p1xwUZDECs234F1xiG5enc5SGlRtP7foLBz9lOsjx+LEcA4sTl5/2eZR9zyFZqWW +TxRjs+fJCIFuo71xb1g= +=/teI -----END PGP SIGNATURE-----` const armorLongLine = `-----BEGIN PGP SIGNATURE----- diff --git a/src/pkg/crypto/openpgp/armor/encode.go b/src/pkg/crypto/openpgp/armor/encode.go index 410e73460..0f7de0241 100644 --- a/src/pkg/crypto/openpgp/armor/encode.go +++ b/src/pkg/crypto/openpgp/armor/encode.go @@ -116,6 +116,7 @@ func (e *encoding) Close() (err os.Error) { if err != nil { return } + e.breaker.Close() var checksumBytes [3]byte checksumBytes[0] = byte(e.crc >> 16) @@ -144,11 +145,9 @@ func Encode(out io.Writer, blockType string, headers map[string]string) (w io.Wr } } - if len(headers) > 0 { - _, err := out.Write(newline) - if err != nil { - return - } + _, err = out.Write(newline) + if err != nil { + return } e := &encoding{ diff --git a/src/pkg/crypto/openpgp/error/error.go b/src/pkg/crypto/openpgp/error/error.go index 2d80ce373..053d15967 100644 --- a/src/pkg/crypto/openpgp/error/error.go +++ b/src/pkg/crypto/openpgp/error/error.go @@ -5,6 +5,10 @@ // This package contains common error types for the OpenPGP packages. package error +import ( + "strconv" +) + // A StructuralError is returned when OpenPGP data is found to be syntactically // invalid. type StructuralError string @@ -44,3 +48,17 @@ func (ki keyIncorrect) String() string { } var KeyIncorrectError = keyIncorrect(0) + +type unknownIssuer int + +func (unknownIssuer) String() string { + return "signature make by unknown entity" +} + +var UnknownIssuerError = unknownIssuer(0) + +type UnknownPacketTypeError uint8 + +func (upte UnknownPacketTypeError) String() string { + return "unknown OpenPGP packet type: " + strconv.Itoa(int(upte)) +} diff --git a/src/pkg/crypto/openpgp/packet/Makefile b/src/pkg/crypto/openpgp/packet/Makefile new file mode 100644 index 000000000..0f0d94eb1 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/Makefile @@ -0,0 +1,22 @@ +# 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. + +include ../../../../Make.inc + +TARG=crypto/openpgp/packet +GOFILES=\ + compressed.go\ + encrypted_key.go\ + literal.go\ + one_pass_signature.go\ + packet.go\ + private_key.go\ + public_key.go\ + reader.go\ + signature.go\ + symmetrically_encrypted.go\ + symmetric_key_encrypted.go\ + userid.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/packet/compressed.go b/src/pkg/crypto/openpgp/packet/compressed.go new file mode 100644 index 000000000..1c15c24c4 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/compressed.go @@ -0,0 +1,39 @@ +// 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 packet + +import ( + "compress/flate" + "compress/zlib" + "crypto/openpgp/error" + "io" + "os" + "strconv" +) + +// Compressed represents a compressed OpenPGP packet. The decompressed contents +// will contain more OpenPGP packets. See RFC 4880, section 5.6. +type Compressed struct { + Body io.Reader +} + +func (c *Compressed) parse(r io.Reader) os.Error { + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + + switch buf[0] { + case 1: + c.Body = flate.NewReader(r) + case 2: + c.Body, err = zlib.NewReader(r) + default: + err = error.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) + } + + return err +} diff --git a/src/pkg/crypto/openpgp/packet/compressed_test.go b/src/pkg/crypto/openpgp/packet/compressed_test.go new file mode 100644 index 000000000..24fe501ed --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/compressed_test.go @@ -0,0 +1,41 @@ +// 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 packet + +import ( + "bytes" + "encoding/hex" + "os" + "io/ioutil" + "testing" +) + +func TestCompressed(t *testing.T) { + packet, err := Read(readerFromHex(compressedHex)) + if err != nil { + t.Errorf("failed to read Compressed: %s", err) + return + } + + c, ok := packet.(*Compressed) + if !ok { + t.Error("didn't find Compressed packet") + return + } + + contents, err := ioutil.ReadAll(c.Body) + if err != nil && err != os.EOF { + t.Error(err) + return + } + + expected, _ := hex.DecodeString(compressedExpectedHex) + if !bytes.Equal(expected, contents) { + t.Errorf("got:%x want:%x", contents, expected) + } +} + +const compressedHex = "a3013b2d90c4e02b72e25f727e5e496a5e49b11e1700" +const compressedExpectedHex = "cb1062004d14c8fe636f6e74656e74732e0a" diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key.go b/src/pkg/crypto/openpgp/packet/encrypted_key.go new file mode 100644 index 000000000..4a926cdb1 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/encrypted_key.go @@ -0,0 +1,66 @@ +// 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 packet + +import ( + "crypto/openpgp/error" + "crypto/rand" + "crypto/rsa" + "encoding/binary" + "io" + "os" + "strconv" +) + +// EncryptedKey represents a public-key encrypted session key. See RFC 4880, +// section 5.1. +type EncryptedKey struct { + KeyId uint64 + Algo PublicKeyAlgorithm + Encrypted []byte + CipherFunc CipherFunction // only valid after a sucessful Decrypt + Key []byte // only valid after a sucessful Decrypt +} + +func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { + var buf [10]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 3 { + return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) + } + e.KeyId = binary.BigEndian.Uint64(buf[1:9]) + e.Algo = PublicKeyAlgorithm(buf[9]) + if e.Algo == PubKeyAlgoRSA || e.Algo == PubKeyAlgoRSAEncryptOnly { + e.Encrypted, _, err = readMPI(r) + } + _, err = consumeAll(r) + return +} + +// DecryptRSA decrypts an RSA encrypted session key with the given private key. +func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) { + if e.Algo != PubKeyAlgoRSA && e.Algo != PubKeyAlgoRSAEncryptOnly { + return error.InvalidArgumentError("EncryptedKey not RSA encrypted") + } + b, err := rsa.DecryptPKCS1v15(rand.Reader, priv, e.Encrypted) + if err != nil { + return + } + e.CipherFunc = CipherFunction(b[0]) + e.Key = b[1 : len(b)-2] + expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) + var checksum uint16 + for _, v := range e.Key { + checksum += uint16(v) + } + if checksum != expectedChecksum { + return error.StructuralError("EncryptedKey checksum incorrect") + } + + return +} diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go new file mode 100644 index 000000000..755ae7a30 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go @@ -0,0 +1,67 @@ +// 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 packet + +import ( + "big" + "crypto/rsa" + "fmt" + "testing" +) + +func bigFromBase10(s string) *big.Int { + b, ok := new(big.Int).SetString(s, 10) + if !ok { + panic("bigFromBase10 failed") + } + return b +} + +func TestEncryptedKey(t *testing.T) { + p, err := Read(readerFromHex(encryptedKeyHex)) + if err != nil { + t.Errorf("error from Read: %s", err) + return + } + ek, ok := p.(*EncryptedKey) + if !ok { + t.Errorf("didn't parse an EncryptedKey, got %#v", p) + return + } + + if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + pub := rsa.PublicKey{ + E: 65537, + N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), + } + + priv := &rsa.PrivateKey{ + PublicKey: pub, + D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), + } + + err = ek.DecryptRSA(priv) + if err != nil { + t.Errorf("error from DecryptRSA: %s", err) + return + } + + if ek.CipherFunc != CipherAES256 { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + keyHex := fmt.Sprintf("%x", ek.Key) + if keyHex != expectedKeyHex { + t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) + } +} + +const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" +const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" diff --git a/src/pkg/crypto/openpgp/packet/literal.go b/src/pkg/crypto/openpgp/packet/literal.go new file mode 100644 index 000000000..5f72d6a2c --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/literal.go @@ -0,0 +1,53 @@ +// 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 packet + +import ( + "encoding/binary" + "io" + "os" +) + +// LiteralData represents an encrypted file. See RFC 4880, section 5.9. +type LiteralData struct { + IsBinary bool + FileName string + Time uint32 // Unix epoc time. Either creation time or modification time. 0 means undefined. + Body io.Reader +} + +// ForEyesOnly return whether the contents of the LiteralData have been marked +// as especially sensitive. +func (l *LiteralData) ForEyesOnly() bool { + return l.FileName == "_CONSOLE" +} + +func (l *LiteralData) parse(r io.Reader) (err os.Error) { + var buf [256]byte + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + + l.IsBinary = buf[0] == 'b' + fileNameLen := int(buf[1]) + + _, err = readFull(r, buf[:fileNameLen]) + if err != nil { + return + } + + l.FileName = string(buf[:fileNameLen]) + + _, err = readFull(r, buf[:4]) + if err != nil { + return + } + + l.Time = binary.BigEndian.Uint32(buf[:4]) + l.Body = r + return +} diff --git a/src/pkg/crypto/openpgp/packet/one_pass_signature.go b/src/pkg/crypto/openpgp/packet/one_pass_signature.go new file mode 100644 index 000000000..acbf58bbe --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/one_pass_signature.go @@ -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 packet + +import ( + "crypto" + "crypto/openpgp/error" + "crypto/openpgp/s2k" + "encoding/binary" + "io" + "os" + "strconv" +) + +// OnePassSignature represents a one-pass signature packet. See RFC 4880, +// section 5.4. +type OnePassSignature struct { + SigType SignatureType + Hash crypto.Hash + PubKeyAlgo PublicKeyAlgorithm + KeyId uint64 + IsLast bool +} + +func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { + var buf [13]byte + + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 3 { + err = error.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) + } + + var ok bool + ops.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return error.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) + } + + ops.SigType = SignatureType(buf[1]) + ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) + ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) + ops.IsLast = buf[12] != 0 + return +} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go new file mode 100644 index 000000000..80e25e250 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/packet.go @@ -0,0 +1,395 @@ +// 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. + +// This package implements parsing and serialisation of OpenPGP packets, as +// specified in RFC 4880. +package packet + +import ( + "crypto/aes" + "crypto/cast5" + "crypto/cipher" + "crypto/openpgp/error" + "io" + "os" +) + +// readFull is the same as io.ReadFull except that reading zero bytes returns +// ErrUnexpectedEOF rather than EOF. +func readFull(r io.Reader, buf []byte) (n int, err os.Error) { + n, err = io.ReadFull(r, buf) + if err == os.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. +func readLength(r io.Reader) (length int64, isPartial bool, err os.Error) { + var buf [4]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + switch { + case buf[0] < 192: + length = int64(buf[0]) + case buf[0] < 224: + length = int64(buf[0]-192) << 8 + _, err = readFull(r, buf[0:1]) + if err != nil { + return + } + length += int64(buf[0]) + 192 + case buf[0] < 255: + length = int64(1) << (buf[0] & 0x1f) + isPartial = true + default: + _, err = readFull(r, buf[0:4]) + if err != nil { + return + } + length = int64(buf[0])<<24 | + int64(buf[1])<<16 | + int64(buf[2])<<8 | + int64(buf[3]) + } + return +} + +// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. +// The continuation lengths are parsed and removed from the stream and EOF is +// returned at the end of the packet. See RFC 4880, section 4.2.2.4. +type partialLengthReader struct { + r io.Reader + remaining int64 + isPartial bool +} + +func (r *partialLengthReader) Read(p []byte) (n int, err os.Error) { + for r.remaining == 0 { + if !r.isPartial { + return 0, os.EOF + } + r.remaining, r.isPartial, err = readLength(r.r) + if err != nil { + return 0, err + } + } + + toRead := int64(len(p)) + if toRead > r.remaining { + toRead = r.remaining + } + + n, err = r.r.Read(p[:int(toRead)]) + r.remaining -= int64(n) + if n < int(toRead) && err == os.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the +// underlying Reader returns EOF before the limit has been reached. +type spanReader struct { + r io.Reader + n int64 +} + +func (l *spanReader) Read(p []byte) (n int, err os.Error) { + if l.n <= 0 { + return 0, os.EOF + } + if int64(len(p)) > l.n { + p = p[0:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + if l.n > 0 && err == os.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readHeader parses a packet header and returns an io.Reader which will return +// the contents of the packet. See RFC 4880, section 4.2. +func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err os.Error) { + var buf [4]byte + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + if buf[0]&0x80 == 0 { + err = error.StructuralError("tag byte does not have MSB set") + return + } + if buf[0]&0x40 == 0 { + // Old format packet + tag = packetType((buf[0] & 0x3f) >> 2) + lengthType := buf[0] & 3 + if lengthType == 3 { + length = -1 + contents = r + return + } + lengthBytes := 1 << lengthType + _, err = readFull(r, buf[0:lengthBytes]) + if err != nil { + return + } + for i := 0; i < lengthBytes; i++ { + length <<= 8 + length |= int64(buf[i]) + } + contents = &spanReader{r, length} + return + } + + // New format packet + tag = packetType(buf[0] & 0x3f) + length, isPartial, err := readLength(r) + if err != nil { + return + } + if isPartial { + contents = &partialLengthReader{ + remaining: length, + isPartial: true, + r: r, + } + length = -1 + } else { + contents = &spanReader{r, length} + } + return +} + +// serialiseHeader writes an OpenPGP packet header to w. See RFC 4880, section +// 4.2. +func serialiseHeader(w io.Writer, ptype packetType, length int) (err os.Error) { + var buf [5]byte + var n int + + buf[0] = 0x80 | 0x40 | byte(ptype) + if length < 192 { + buf[1] = byte(length) + n = 2 + } else if length < 8384 { + length -= 192 + buf[1] = byte(length >> 8) + buf[2] = byte(length) + n = 3 + } else { + buf[0] = 255 + buf[1] = byte(length >> 24) + buf[2] = byte(length >> 16) + buf[3] = byte(length >> 8) + buf[4] = byte(length) + n = 5 + } + + _, err = w.Write(buf[:n]) + return +} + +// Packet represents an OpenPGP packet. Users are expected to try casting +// instances of this interface to specific packet types. +type Packet interface { + parse(io.Reader) os.Error +} + +// consumeAll reads from the given Reader until error, returning the number of +// bytes read. +func consumeAll(r io.Reader) (n int64, err os.Error) { + var m int + var buf [1024]byte + + for { + m, err = r.Read(buf[:]) + n += int64(m) + if err == os.EOF { + err = nil + return + } + if err != nil { + return + } + } + + panic("unreachable") +} + +// packetType represents the numeric ids of the different OpenPGP packet types. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 +type packetType uint8 + +const ( + packetTypeEncryptedKey packetType = 1 + packetTypeSignature packetType = 2 + packetTypeSymmetricKeyEncrypted packetType = 3 + packetTypeOnePassSignature packetType = 4 + packetTypePrivateKey packetType = 5 + packetTypePublicKey packetType = 6 + packetTypePrivateSubkey packetType = 7 + packetTypeCompressed packetType = 8 + packetTypeSymmetricallyEncrypted packetType = 9 + packetTypeLiteralData packetType = 11 + packetTypeUserId packetType = 13 + packetTypePublicSubkey packetType = 14 + packetTypeSymmetricallyEncryptedMDC packetType = 18 +) + +// Read reads a single OpenPGP packet from the given io.Reader. If there is an +// error parsing a packet, the whole packet is consumed from the input. +func Read(r io.Reader) (p Packet, err os.Error) { + tag, _, contents, err := readHeader(r) + if err != nil { + return + } + + switch tag { + case packetTypeEncryptedKey: + p = new(EncryptedKey) + case packetTypeSignature: + p = new(Signature) + case packetTypeSymmetricKeyEncrypted: + p = new(SymmetricKeyEncrypted) + case packetTypeOnePassSignature: + p = new(OnePassSignature) + case packetTypePrivateKey, packetTypePrivateSubkey: + pk := new(PrivateKey) + if tag == packetTypePrivateSubkey { + pk.IsSubKey = true + } + p = pk + case packetTypePublicKey, packetTypePublicSubkey: + pk := new(PublicKey) + if tag == packetTypePublicSubkey { + pk.IsSubKey = true + } + p = pk + case packetTypeCompressed: + p = new(Compressed) + case packetTypeSymmetricallyEncrypted: + p = new(SymmetricallyEncrypted) + case packetTypeLiteralData: + p = new(LiteralData) + case packetTypeUserId: + p = new(UserId) + case packetTypeSymmetricallyEncryptedMDC: + se := new(SymmetricallyEncrypted) + se.MDC = true + p = se + default: + err = error.UnknownPacketTypeError(tag) + } + if p != nil { + err = p.parse(contents) + } + if err != nil { + consumeAll(contents) + } + return +} + +// SignatureType represents the different semantic meanings of an OpenPGP +// signature. See RFC 4880, section 5.2.1. +type SignatureType uint8 + +const ( + SigTypeBinary SignatureType = 0 + SigTypeText SignatureType = 1 + SigTypeGenericCert = 0x10 + SigTypePersonaCert = 0x11 + SigTypeCasualCert = 0x12 + SigTypePositiveCert = 0x13 + SigTypeSubkeyBinding = 0x18 +) + +// PublicKeyAlgorithm represents the different public key system specified for +// OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 +type PublicKeyAlgorithm uint8 + +const ( + PubKeyAlgoRSA PublicKeyAlgorithm = 1 + PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 + PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 + PubKeyAlgoElgamal PublicKeyAlgorithm = 16 + PubKeyAlgoDSA PublicKeyAlgorithm = 17 +) + +// CipherFunction represents the different block ciphers specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 +type CipherFunction uint8 + +const ( + CipherCAST5 = 3 + CipherAES128 = 7 + CipherAES192 = 8 + CipherAES256 = 9 +) + +// keySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) keySize() int { + switch cipher { + case CipherCAST5: + return cast5.KeySize + case CipherAES128: + return 16 + case CipherAES192: + return 24 + case CipherAES256: + return 32 + } + return 0 +} + +// blockSize returns the block size, in bytes, of cipher. +func (cipher CipherFunction) blockSize() int { + switch cipher { + case CipherCAST5: + return 8 + case CipherAES128, CipherAES192, CipherAES256: + return 16 + } + return 0 +} + +// new returns a fresh instance of the given cipher. +func (cipher CipherFunction) new(key []byte) (block cipher.Block) { + switch cipher { + case CipherCAST5: + block, _ = cast5.NewCipher(key) + case CipherAES128, CipherAES192, CipherAES256: + block, _ = aes.NewCipher(key) + } + return +} + +// readMPI reads a big integer from r. The bit length returned is the bit +// length that was specified in r. This is preserved so that the integer can be +// reserialised exactly. +func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) { + var buf [2]byte + _, err = readFull(r, buf[0:]) + if err != nil { + return + } + bitLength = uint16(buf[0])<<8 | uint16(buf[1]) + numBytes := (int(bitLength) + 7) / 8 + mpi = make([]byte, numBytes) + _, err = readFull(r, mpi) + return +} + +// writeMPI serialises a big integer to r. +func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) { + _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) + if err == nil { + _, err = w.Write(mpiBytes) + } + return +} diff --git a/src/pkg/crypto/openpgp/packet/packet_test.go b/src/pkg/crypto/openpgp/packet/packet_test.go new file mode 100644 index 000000000..6789d2abc --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/packet_test.go @@ -0,0 +1,192 @@ +// 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 packet + +import ( + "bytes" + "crypto/openpgp/error" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "testing" +) + +func TestReadFull(t *testing.T) { + var out [4]byte + + b := bytes.NewBufferString("foo") + n, err := readFull(b, out[:3]) + if n != 3 || err != nil { + t.Errorf("full read failed n:%d err:%s", n, err) + } + + b = bytes.NewBufferString("foo") + n, err = readFull(b, out[:4]) + if n != 3 || err != io.ErrUnexpectedEOF { + t.Errorf("partial read failed n:%d err:%s", n, err) + } + + b = bytes.NewBuffer(nil) + n, err = readFull(b, out[:3]) + if n != 0 || err != io.ErrUnexpectedEOF { + t.Errorf("empty read failed n:%d err:%s", n, err) + } +} + +func readerFromHex(s string) io.Reader { + data, err := hex.DecodeString(s) + if err != nil { + panic("readerFromHex: bad input") + } + return bytes.NewBuffer(data) +} + +var readLengthTests = []struct { + hexInput string + length int64 + isPartial bool + err os.Error +}{ + {"", 0, false, io.ErrUnexpectedEOF}, + {"1f", 31, false, nil}, + {"c0", 0, false, io.ErrUnexpectedEOF}, + {"c101", 256 + 1 + 192, false, nil}, + {"e0", 1, true, nil}, + {"e1", 2, true, nil}, + {"e2", 4, true, nil}, + {"ff", 0, false, io.ErrUnexpectedEOF}, + {"ff00", 0, false, io.ErrUnexpectedEOF}, + {"ff0000", 0, false, io.ErrUnexpectedEOF}, + {"ff000000", 0, false, io.ErrUnexpectedEOF}, + {"ff00000000", 0, false, nil}, + {"ff01020304", 16909060, false, nil}, +} + +func TestReadLength(t *testing.T) { + for i, test := range readLengthTests { + length, isPartial, err := readLength(readerFromHex(test.hexInput)) + if test.err != nil { + if err != test.err { + t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) + } + continue + } + if err != nil { + t.Errorf("%d: unexpected error: %s", i, err) + continue + } + if length != test.length || isPartial != test.isPartial { + t.Errorf("%d: bad result got:(%d,%t) want:(%d,%t)", i, length, isPartial, test.length, test.isPartial) + } + } +} + +var partialLengthReaderTests = []struct { + hexInput string + err os.Error + hexOutput string +}{ + {"e0", io.ErrUnexpectedEOF, ""}, + {"e001", io.ErrUnexpectedEOF, ""}, + {"e0010102", nil, "0102"}, + {"ff00000000", nil, ""}, + {"e10102e1030400", nil, "01020304"}, + {"e101", io.ErrUnexpectedEOF, ""}, +} + +func TestPartialLengthReader(t *testing.T) { + for i, test := range partialLengthReaderTests { + r := &partialLengthReader{readerFromHex(test.hexInput), 0, true} + out, err := ioutil.ReadAll(r) + if test.err != nil { + if err != test.err { + t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) + } + continue + } + if err != nil { + t.Errorf("%d: unexpected error: %s", i, err) + continue + } + + got := fmt.Sprintf("%x", out) + if got != test.hexOutput { + t.Errorf("%d: got:%s want:%s", i, test.hexOutput, got) + } + } +} + +var readHeaderTests = []struct { + hexInput string + structuralError bool + unexpectedEOF bool + tag int + length int64 + hexOutput string +}{ + {"", false, false, 0, 0, ""}, + {"7f", true, false, 0, 0, ""}, + + // Old format headers + {"80", false, true, 0, 0, ""}, + {"8001", false, true, 0, 1, ""}, + {"800102", false, false, 0, 1, "02"}, + {"81000102", false, false, 0, 1, "02"}, + {"820000000102", false, false, 0, 1, "02"}, + {"860000000102", false, false, 1, 1, "02"}, + {"83010203", false, false, 0, -1, "010203"}, + + // New format headers + {"c0", false, true, 0, 0, ""}, + {"c000", false, false, 0, 0, ""}, + {"c00102", false, false, 0, 1, "02"}, + {"c0020203", false, false, 0, 2, "0203"}, + {"c00202", false, true, 0, 2, ""}, + {"c3020203", false, false, 3, 2, "0203"}, +} + +func TestReadHeader(t *testing.T) { + for i, test := range readHeaderTests { + tag, length, contents, err := readHeader(readerFromHex(test.hexInput)) + if test.structuralError { + if _, ok := err.(error.StructuralError); ok { + continue + } + t.Errorf("%d: expected StructuralError, got:%s", i, err) + continue + } + if err != nil { + if len(test.hexInput) == 0 && err == os.EOF { + continue + } + if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { + t.Errorf("%d: unexpected error from readHeader: %s", i, err) + } + continue + } + if int(tag) != test.tag || length != test.length { + t.Errorf("%d: got:(%d,%d) want:(%d,%d)", i, int(tag), length, test.tag, test.length) + continue + } + + body, err := ioutil.ReadAll(contents) + if err != nil { + if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { + t.Errorf("%d: unexpected error from contents: %s", i, err) + } + continue + } + if test.unexpectedEOF { + t.Errorf("%d: expected ErrUnexpectedEOF from contents but got no error", i) + continue + } + got := fmt.Sprintf("%x", body) + if got != test.hexOutput { + t.Errorf("%d: got:%s want:%s", i, got, test.hexOutput) + } + } +} diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go new file mode 100644 index 000000000..b22891755 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/private_key.go @@ -0,0 +1,164 @@ +// 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 packet + +import ( + "big" + "bytes" + "crypto/cipher" + "crypto/openpgp/error" + "crypto/openpgp/s2k" + "crypto/rsa" + "crypto/sha1" + "io" + "io/ioutil" + "os" + "strconv" +) + +// PrivateKey represents a possibly encrypted private key. See RFC 4880, +// section 5.5.3. +type PrivateKey struct { + PublicKey + Encrypted bool // if true then the private key is unavailable until Decrypt has been called. + encryptedData []byte + cipher CipherFunction + s2k func(out, in []byte) + PrivateKey interface{} // An *rsa.PrivateKey. + sha1Checksum bool + iv []byte +} + +func (pk *PrivateKey) parse(r io.Reader) (err os.Error) { + err = (&pk.PublicKey).parse(r) + if err != nil { + return + } + var buf [1]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + + s2kType := buf[0] + + switch s2kType { + case 0: + pk.s2k = nil + pk.Encrypted = false + case 254, 255: + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.cipher = CipherFunction(buf[0]) + pk.Encrypted = true + pk.s2k, err = s2k.Parse(r) + if err != nil { + return + } + if s2kType == 254 { + pk.sha1Checksum = true + } + default: + return error.UnsupportedError("deprecated s2k function in private key") + } + + if pk.Encrypted { + blockSize := pk.cipher.blockSize() + if blockSize == 0 { + return error.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) + } + pk.iv = make([]byte, blockSize) + _, err = readFull(r, pk.iv) + if err != nil { + return + } + } + + pk.encryptedData, err = ioutil.ReadAll(r) + if err != nil { + return + } + + if !pk.Encrypted { + return pk.parsePrivateKey(pk.encryptedData) + } + + return +} + +// Decrypt decrypts an encrypted private key using a passphrase. +func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { + if !pk.Encrypted { + return nil + } + + key := make([]byte, pk.cipher.keySize()) + pk.s2k(key, passphrase) + block := pk.cipher.new(key) + cfb := cipher.NewCFBDecrypter(block, pk.iv) + + data := pk.encryptedData + cfb.XORKeyStream(data, data) + + if pk.sha1Checksum { + if len(data) < sha1.Size { + return error.StructuralError("truncated private key data") + } + h := sha1.New() + h.Write(data[:len(data)-sha1.Size]) + sum := h.Sum() + if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { + return error.StructuralError("private key checksum failure") + } + data = data[:len(data)-sha1.Size] + } else { + if len(data) < 2 { + return error.StructuralError("truncated private key data") + } + var sum uint16 + for i := 0; i < len(data)-2; i++ { + sum += uint16(data[i]) + } + if data[len(data)-2] != uint8(sum>>8) || + data[len(data)-1] != uint8(sum) { + return error.StructuralError("private key checksum failure") + } + data = data[:len(data)-2] + } + + return pk.parsePrivateKey(data) +} + +func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { + // TODO(agl): support DSA and ECDSA private keys. + rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) + rsaPriv := new(rsa.PrivateKey) + rsaPriv.PublicKey = *rsaPub + + buf := bytes.NewBuffer(data) + d, _, err := readMPI(buf) + if err != nil { + return + } + p, _, err := readMPI(buf) + if err != nil { + return + } + q, _, err := readMPI(buf) + if err != nil { + return + } + + rsaPriv.D = new(big.Int).SetBytes(d) + rsaPriv.P = new(big.Int).SetBytes(p) + rsaPriv.Q = new(big.Int).SetBytes(q) + pk.PrivateKey = rsaPriv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/private_key_test.go b/src/pkg/crypto/openpgp/packet/private_key_test.go new file mode 100644 index 000000000..e941cc735 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/private_key_test.go @@ -0,0 +1,37 @@ +// 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 packet + +import ( + "testing" +) + +func TestPrivateKeyRead(t *testing.T) { + packet, err := Read(readerFromHex(privKeyHex)) + if err != nil { + t.Error(err) + return + } + + privKey := packet.(*PrivateKey) + + if !privKey.Encrypted { + t.Error("private key isn't encrypted") + return + } + + err = privKey.Decrypt([]byte("testing")) + if err != nil { + t.Error(err) + return + } + + if privKey.CreationTime != 0x4cc349a8 || privKey.Encrypted { + t.Errorf("failed to parse, got: %#v", privKey) + } +} + +// Generated with `gpg --export-secret-keys "Test Key 2"` +const privKeyHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go new file mode 100644 index 000000000..4a2ed0aca --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/public_key.go @@ -0,0 +1,260 @@ +// 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 packet + +import ( + "big" + "crypto/dsa" + "crypto/openpgp/error" + "crypto/rsa" + "crypto/sha1" + "encoding/binary" + "hash" + "io" + "os" +) + +// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. +type PublicKey struct { + CreationTime uint32 // seconds since the epoch + PubKeyAlgo PublicKeyAlgorithm + PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey + Fingerprint [20]byte + KeyId uint64 + IsSubKey bool + + n, e, p, q, g, y parsedMPI +} + +func (pk *PublicKey) parse(r io.Reader) (err os.Error) { + // RFC 4880, section 5.5.2 + var buf [6]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 4 { + return error.UnsupportedError("public key version") + } + pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4]) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + case PubKeyAlgoDSA: + err = pk.parseDSA(r) + default: + err = error.UnsupportedError("public key type") + } + if err != nil { + return + } + + // RFC 4880, section 12.2 + fingerPrint := sha1.New() + pk.SerializeSignaturePrefix(fingerPrint) + pk.Serialize(fingerPrint) + copy(pk.Fingerprint[:], fingerPrint.Sum()) + pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) + + return +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { + pk.n.bytes, pk.n.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.e.bytes, pk.e.bitLength, err = readMPI(r) + if err != nil { + return + } + + if len(pk.e.bytes) > 3 { + err = error.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{ + N: new(big.Int).SetBytes(pk.n.bytes), + E: 0, + } + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// parseRSA parses DSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.q.bytes, pk.q.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + dsa := new(dsa.PublicKey) + dsa.P = new(big.Int).SetBytes(pk.p.bytes) + dsa.Q = new(big.Int).SetBytes(pk.q.bytes) + dsa.G = new(big.Int).SetBytes(pk.g.bytes) + dsa.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = dsa + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + case PubKeyAlgoDSA: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.q.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) + default: + panic("unknown public key algorithm") + } + pLength += 6 + h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +// Serialize marshals the PublicKey to w in the form of an OpenPGP public key +// packet, not including the packet header. +func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { + var buf [6]byte + buf[0] = 4 + buf[1] = byte(pk.CreationTime >> 24) + buf[2] = byte(pk.CreationTime >> 16) + buf[3] = byte(pk.CreationTime >> 8) + buf[4] = byte(pk.CreationTime) + buf[5] = byte(pk.PubKeyAlgo) + + _, err = w.Write(buf[:]) + if err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + case PubKeyAlgoDSA: + return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) + } + return error.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKey) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElgamal +} + +// VerifySignature returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.Error) { + if !pk.CanSign() { + return error.InvalidArgumentError("public key cannot generate signatures") + } + + rsaPublicKey, ok := pk.PublicKey.(*rsa.PublicKey) + if !ok { + // TODO(agl): support DSA and ECDSA keys. + return error.UnsupportedError("non-RSA public key") + } + + signed.Write(sig.HashSuffix) + hashBytes := signed.Sum() + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return error.SignatureError("hash tag doesn't match") + } + + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.Signature) + if err != nil { + return error.SignatureError("RSA verification failure") + } + return nil +} + +// VerifyKeySignature returns nil iff sig is a valid signature, make by this +// public key, of the public key in signed. +func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err os.Error) { + h := sig.Hash.New() + if h == nil { + return error.UnsupportedError("hash function") + } + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.Serialize(h) + signed.SerializeSignaturePrefix(h) + signed.Serialize(h) + + return pk.VerifySignature(h, sig) +} + +// VerifyUserIdSignature returns nil iff sig is a valid signature, make by this +// public key, of the given user id. +func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Error) { + h := sig.Hash.New() + if h == nil { + return error.UnsupportedError("hash function") + } + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.Serialize(h) + + var buf [5]byte + buf[0] = 0xb4 + buf[1] = byte(len(id) >> 24) + buf[2] = byte(len(id) >> 16) + buf[3] = byte(len(id) >> 8) + buf[4] = byte(len(id)) + h.Write(buf[:]) + h.Write([]byte(id)) + + return pk.VerifySignature(h, sig) +} + +// A parsedMPI is used to store the contents of a big integer, along with the +// bit length that was specified in the original input. This allows the MPI to +// be reserialised exactly. +type parsedMPI struct { + bytes []byte + bitLength uint16 +} + +// writeMPIs is a utility function for serialising several big integers to the +// given Writer. +func writeMPIs(w io.Writer, mpis ...parsedMPI) (err os.Error) { + for _, mpi := range mpis { + err = writeMPI(w, mpi.bitLength, mpi.bytes) + if err != nil { + return + } + } + return +} diff --git a/src/pkg/crypto/openpgp/packet/public_key_test.go b/src/pkg/crypto/openpgp/packet/public_key_test.go new file mode 100644 index 000000000..c015f64ae --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/public_key_test.go @@ -0,0 +1,58 @@ +// 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 packet + +import ( + "bytes" + "encoding/hex" + "testing" +) + +var pubKeyTests = []struct { + hexData string + hexFingerprint string + creationTime uint32 + pubKeyAlgo PublicKeyAlgorithm + keyId uint64 +}{ + {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb}, + {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed}, +} + +func TestPublicKeyRead(t *testing.T) { + for i, test := range pubKeyTests { + packet, err := Read(readerFromHex(test.hexData)) + if err != nil { + t.Errorf("#%d: Read error: %s", i, err) + return + } + pk, ok := packet.(*PublicKey) + if !ok { + t.Errorf("#%d: failed to parse, got: %#v", i, packet) + return + } + if pk.PubKeyAlgo != test.pubKeyAlgo { + t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) + } + if pk.CreationTime != test.creationTime { + t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime) + } + expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) + if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { + t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) + } + if pk.KeyId != test.keyId { + t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) + } + } +} + +const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" + +const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" + +const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" + +const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" diff --git a/src/pkg/crypto/openpgp/packet/reader.go b/src/pkg/crypto/openpgp/packet/reader.go new file mode 100644 index 000000000..5febc3bc8 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/reader.go @@ -0,0 +1,63 @@ +// 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 packet + +import ( + "crypto/openpgp/error" + "io" + "os" +) + +// Reader reads packets from an io.Reader and allows packets to be 'unread' so +// that they result from the next call to Next. +type Reader struct { + q []Packet + readers []io.Reader +} + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown packet types are skipped. +func (r *Reader) Next() (p Packet, err os.Error) { + if len(r.q) > 0 { + p = r.q[len(r.q)-1] + r.q = r.q[:len(r.q)-1] + return + } + + for len(r.readers) > 0 { + p, err = Read(r.readers[len(r.readers)-1]) + if err == nil { + return + } + if err == os.EOF { + r.readers = r.readers[:len(r.readers)-1] + continue + } + if _, ok := err.(error.UnknownPacketTypeError); !ok { + return nil, err + } + } + + return nil, os.EOF +} + +// Push causes the Reader to start reading from a new io.Reader. When an EOF +// error is seen from the new io.Reader, it is popped and the Reader continues +// to read from the next most recent io.Reader. +func (r *Reader) Push(reader io.Reader) { + r.readers = append(r.readers, reader) +} + +// Unread causes the given Packet to be returned from the next call to Next. +func (r *Reader) Unread(p Packet) { + r.q = append(r.q, p) +} + +func NewReader(r io.Reader) *Reader { + return &Reader{ + q: nil, + readers: []io.Reader{r}, + } +} diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go new file mode 100644 index 000000000..fd2518ab4 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/signature.go @@ -0,0 +1,468 @@ +// 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 packet + +import ( + "crypto" + "crypto/openpgp/error" + "crypto/openpgp/s2k" + "crypto/rand" + "crypto/rsa" + "encoding/binary" + "hash" + "io" + "os" + "strconv" +) + +// Signature represents a signature. See RFC 4880, section 5.2. +type Signature struct { + SigType SignatureType + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + + // HashSuffix is extra data that is hashed in after the signed data. + HashSuffix []byte + // HashTag contains the first two bytes of the hash for fast rejection + // of bad signed data. + HashTag [2]byte + CreationTime uint32 // Unix epoch time + Signature []byte + + // The following are optional so are nil when not included in the + // signature. + + SigLifetimeSecs, KeyLifetimeSecs *uint32 + PreferredSymmetric, PreferredHash, PreferredCompression []uint8 + IssuerKeyId *uint64 + IsPrimaryId *bool + + // FlagsValid is set if any flags were given. See RFC 4880, section + // 5.2.3.21 for details. + FlagsValid bool + FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool + + outSubpackets []outputSubpacket +} + +func (sig *Signature) parse(r io.Reader) (err os.Error) { + // RFC 4880, section 5.2.3 + var buf [5]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + if buf[0] != 4 { + err = error.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + + _, err = readFull(r, buf[:5]) + if err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + default: + err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + + var ok bool + sig.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return error.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) + l := 6 + hashedSubpacketsLength + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + copy(sig.HashSuffix[1:], buf[:5]) + hashedSubpackets := sig.HashSuffix[6:l] + _, err = readFull(r, hashedSubpackets) + if err != nil { + return + } + // See RFC 4880, section 5.2.4 + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = uint8(l >> 24) + trailer[3] = uint8(l >> 16) + trailer[4] = uint8(l >> 8) + trailer[5] = uint8(l) + + err = parseSignatureSubpackets(sig, hashedSubpackets, true) + if err != nil { + return + } + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) + unhashedSubpackets := make([]byte, unhashedSubpacketsLength) + _, err = readFull(r, unhashedSubpackets) + if err != nil { + return + } + err = parseSignatureSubpackets(sig, unhashedSubpackets, false) + if err != nil { + return + } + + _, err = readFull(r, sig.HashTag[:2]) + if err != nil { + return + } + + // We have already checked that the public key algorithm is RSA. + sig.Signature, _, err = readMPI(r) + return +} + +// parseSignatureSubpackets parses subpackets of the main signature packet. See +// RFC 4880, section 5.2.3.1. +func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err os.Error) { + for len(subpackets) > 0 { + subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) + if err != nil { + return + } + } + + if sig.CreationTime == 0 { + err = error.StructuralError("no creation time in signature") + } + + return +} + +type signatureSubpacketType uint8 + +const ( + creationTimeSubpacket signatureSubpacketType = 2 + signatureExpirationSubpacket signatureSubpacketType = 3 + keyExpirySubpacket signatureSubpacketType = 9 + prefSymmetricAlgosSubpacket signatureSubpacketType = 11 + issuerSubpacket signatureSubpacketType = 16 + prefHashAlgosSubpacket signatureSubpacketType = 21 + prefCompressionSubpacket signatureSubpacketType = 22 + primaryUserIdSubpacket signatureSubpacketType = 25 + keyFlagsSubpacket signatureSubpacketType = 27 +) + +// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. +func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err os.Error) { + // RFC 4880, section 5.2.3.1 + var length uint32 + switch { + case subpacket[0] < 192: + length = uint32(subpacket[0]) + subpacket = subpacket[1:] + case subpacket[0] < 255: + if len(subpacket) < 2 { + goto Truncated + } + length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 + subpacket = subpacket[2:] + default: + if len(subpacket) < 5 { + goto Truncated + } + length = uint32(subpacket[1])<<24 | + uint32(subpacket[2])<<16 | + uint32(subpacket[3])<<8 | + uint32(subpacket[4]) + subpacket = subpacket[5:] + } + if length > uint32(len(subpacket)) { + goto Truncated + } + rest = subpacket[length:] + subpacket = subpacket[:length] + if len(subpacket) == 0 { + err = error.StructuralError("zero length signature subpacket") + return + } + packetType := subpacket[0] & 0x7f + isCritial := subpacket[0]&0x80 == 0x80 + subpacket = subpacket[1:] + switch signatureSubpacketType(packetType) { + case creationTimeSubpacket: + if !isHashed { + err = error.StructuralError("signature creation time in non-hashed area") + return + } + if len(subpacket) != 4 { + err = error.StructuralError("signature creation time not four bytes") + return + } + sig.CreationTime = binary.BigEndian.Uint32(subpacket) + case signatureExpirationSubpacket: + // Signature expiration time, section 5.2.3.10 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = error.StructuralError("expiration subpacket with bad length") + return + } + sig.SigLifetimeSecs = new(uint32) + *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case keyExpirySubpacket: + // Key expiration time, section 5.2.3.6 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = error.StructuralError("key expiration subpacket with bad length") + return + } + sig.KeyLifetimeSecs = new(uint32) + *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case prefSymmetricAlgosSubpacket: + // Preferred symmetric algorithms, section 5.2.3.7 + if !isHashed { + return + } + sig.PreferredSymmetric = make([]byte, len(subpacket)) + copy(sig.PreferredSymmetric, subpacket) + case issuerSubpacket: + // Issuer, section 5.2.3.5 + if len(subpacket) != 8 { + err = error.StructuralError("issuer subpacket with bad length") + return + } + sig.IssuerKeyId = new(uint64) + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + case prefHashAlgosSubpacket: + // Preferred hash algorithms, section 5.2.3.8 + if !isHashed { + return + } + sig.PreferredHash = make([]byte, len(subpacket)) + copy(sig.PreferredHash, subpacket) + case prefCompressionSubpacket: + // Preferred compression algorithms, section 5.2.3.9 + if !isHashed { + return + } + sig.PreferredCompression = make([]byte, len(subpacket)) + copy(sig.PreferredCompression, subpacket) + case primaryUserIdSubpacket: + // Primary User ID, section 5.2.3.19 + if !isHashed { + return + } + if len(subpacket) != 1 { + err = error.StructuralError("primary user id subpacket with bad length") + return + } + sig.IsPrimaryId = new(bool) + if subpacket[0] > 0 { + *sig.IsPrimaryId = true + } + case keyFlagsSubpacket: + // Key flags, section 5.2.3.21 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = error.StructuralError("empty key flags subpacket") + return + } + sig.FlagsValid = true + if subpacket[0]&1 != 0 { + sig.FlagCertify = true + } + if subpacket[0]&2 != 0 { + sig.FlagSign = true + } + if subpacket[0]&4 != 0 { + sig.FlagEncryptCommunications = true + } + if subpacket[0]&8 != 0 { + sig.FlagEncryptStorage = true + } + + default: + if isCritial { + err = error.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) + return + } + } + return + +Truncated: + err = error.StructuralError("signature subpacket truncated") + return +} + +// subpacketLengthLength returns the length, in bytes, of an encoded length value. +func subpacketLengthLength(length int) int { + if length < 192 { + return 1 + } + if length < 16320 { + return 2 + } + return 5 +} + +// serialiseSubpacketLength marshals the given length into to. +func serialiseSubpacketLength(to []byte, length int) int { + if length < 192 { + to[0] = byte(length) + return 1 + } + if length < 16320 { + length -= 192 + to[0] = byte(length >> 8) + to[1] = byte(length) + return 2 + } + to[0] = 255 + to[1] = byte(length >> 24) + to[2] = byte(length >> 16) + to[3] = byte(length >> 8) + to[4] = byte(length) + return 5 +} + +// subpacketsLength returns the serialised length, in bytes, of the given +// subpackets. +func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + length += subpacketLengthLength(len(subpacket.contents) + 1) + length += 1 // type byte + length += len(subpacket.contents) + } + } + return +} + +// serialiseSubpackets marshals the given subpackets into to. +func serialiseSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + n := serialiseSubpacketLength(to, len(subpacket.contents)+1) + to[n] = byte(subpacket.subpacketType) + to = to[1+n:] + n = copy(to, subpacket.contents) + to = to[n:] + } + } + return +} + +// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. +func (sig *Signature) buildHashSuffix() (err os.Error) { + sig.outSubpackets = sig.buildSubpackets() + hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) + + var ok bool + l := 6 + hashedSubpacketsLen + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + sig.HashSuffix[1] = uint8(sig.SigType) + sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) + sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) + if !ok { + sig.HashSuffix = nil + return error.InvalidArgumentError("hash cannot be repesented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) + } + sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) + sig.HashSuffix[5] = byte(hashedSubpacketsLen) + serialiseSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = byte(l >> 24) + trailer[3] = byte(l >> 16) + trailer[4] = byte(l >> 8) + trailer[5] = byte(l) + return +} + +// SignRSA signs a message with an RSA private key. The hash, h, must contain +// the hash of message to be signed and will be mutated by this function. +func (sig *Signature) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) { + err = sig.buildHashSuffix() + if err != nil { + return + } + + h.Write(sig.HashSuffix) + digest := h.Sum() + copy(sig.HashTag[:], digest) + sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest) + return +} + +// Serialize marshals sig to w. SignRSA must have been called first. +func (sig *Signature) Serialize(w io.Writer) (err os.Error) { + if sig.Signature == nil { + return error.InvalidArgumentError("Signature: need to call SignRSA before Serialize") + } + + unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) + length := len(sig.HashSuffix) - 6 /* trailer not included */ + + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + + 2 /* hash tag */ + 2 /* length of signature MPI */ + len(sig.Signature) + err = serialiseHeader(w, packetTypeSignature, length) + if err != nil { + return + } + + _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) + if err != nil { + return + } + + unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen) + serialiseSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + + _, err = w.Write(unhashedSubpackets) + if err != nil { + return + } + _, err = w.Write(sig.HashTag[:]) + if err != nil { + return + } + return writeMPI(w, 8*uint16(len(sig.Signature)), sig.Signature) +} + +// outputSubpacket represents a subpacket to be marshaled. +type outputSubpacket struct { + hashed bool // true if this subpacket is in the hashed area. + subpacketType signatureSubpacketType + contents []byte +} + +func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { + creationTime := make([]byte, 4) + creationTime[0] = byte(sig.CreationTime >> 24) + creationTime[1] = byte(sig.CreationTime >> 16) + creationTime[2] = byte(sig.CreationTime >> 8) + creationTime[3] = byte(sig.CreationTime) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, creationTime}) + + if sig.IssuerKeyId != nil { + keyId := make([]byte, 8) + binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, keyId}) + } + + return +} diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go new file mode 100644 index 000000000..1305548b2 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/signature_test.go @@ -0,0 +1,28 @@ +// 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 packet + +import ( + "bytes" + "crypto" + "encoding/hex" + "testing" +) + +func TestSignatureRead(t *testing.T) { + signatureData, _ := hex.DecodeString(signatureDataHex) + buf := bytes.NewBuffer(signatureData) + packet, err := Read(buf) + if err != nil { + t.Error(err) + return + } + sig, ok := packet.(*Signature) + if !ok || sig.SigType != SigTypeBinary || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.SHA1 { + t.Errorf("failed to parse, got: %#v", packet) + } +} + +const signatureDataHex = "89011c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go new file mode 100644 index 000000000..d9010f88a --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -0,0 +1,102 @@ +// 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 packet + +import ( + "crypto/cipher" + "crypto/openpgp/error" + "crypto/openpgp/s2k" + "io" + "os" + "strconv" +) + +// This is the largest session key that we'll support. Since no 512-bit cipher +// has even been seriously used, this is comfortably large. +const maxSessionKeySizeInBytes = 64 + +// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC +// 4880, section 5.3. +type SymmetricKeyEncrypted struct { + CipherFunc CipherFunction + Encrypted bool + Key []byte // Empty unless Encrypted is false. + s2k func(out, in []byte) + encryptedKey []byte +} + +func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) { + // RFC 4880, section 5.3. + var buf [2]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 4 { + return error.UnsupportedError("SymmetricKeyEncrypted version") + } + ske.CipherFunc = CipherFunction(buf[1]) + + if ske.CipherFunc.keySize() == 0 { + return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) + } + + ske.s2k, err = s2k.Parse(r) + if err != nil { + return + } + + encryptedKey := make([]byte, maxSessionKeySizeInBytes) + // The session key may follow. We just have to try and read to find + // out. If it exists then we limit it to maxSessionKeySizeInBytes. + n, err := readFull(r, encryptedKey) + if err != nil && err != io.ErrUnexpectedEOF { + return + } + err = nil + if n != 0 { + if n == maxSessionKeySizeInBytes { + return error.UnsupportedError("oversized encrypted session key") + } + ske.encryptedKey = encryptedKey[:n] + } + + ske.Encrypted = true + + return +} + +// Decrypt attempts to decrypt an encrypted session key. If it returns nil, +// ske.Key will contain the session key. +func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { + if !ske.Encrypted { + return nil + } + + key := make([]byte, ske.CipherFunc.keySize()) + ske.s2k(key, passphrase) + + if len(ske.encryptedKey) == 0 { + ske.Key = key + } else { + // the IV is all zeros + iv := make([]byte, ske.CipherFunc.blockSize()) + c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) + c.XORKeyStream(ske.encryptedKey, ske.encryptedKey) + ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) + if ske.CipherFunc.blockSize() == 0 { + return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(ske.CipherFunc))) + } + ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) + ske.Key = ske.encryptedKey[1:] + if len(ske.Key)%ske.CipherFunc.blockSize() != 0 { + ske.Key = nil + return error.StructuralError("length of decrypted key not a multiple of block size") + } + } + + ske.Encrypted = false + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go new file mode 100644 index 000000000..717c8ffa6 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go @@ -0,0 +1,62 @@ +// 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 packet + +import ( + "bytes" + "encoding/hex" + "io/ioutil" + "os" + "testing" +) + +func TestSymmetricKeyEncrypted(t *testing.T) { + buf := readerFromHex(symmetricallyEncryptedHex) + packet, err := Read(buf) + if err != nil { + t.Errorf("failed to read SymmetricKeyEncrypted: %s", err) + return + } + ske, ok := packet.(*SymmetricKeyEncrypted) + if !ok { + t.Error("didn't find SymmetricKeyEncrypted packet") + return + } + err = ske.Decrypt([]byte("password")) + if err != nil { + t.Error(err) + return + } + + packet, err = Read(buf) + if err != nil { + t.Errorf("failed to read SymmetricallyEncrypted: %s", err) + return + } + se, ok := packet.(*SymmetricallyEncrypted) + if !ok { + t.Error("didn't find SymmetricallyEncrypted packet") + return + } + r, err := se.Decrypt(ske.CipherFunc, ske.Key) + if err != nil { + t.Error(err) + return + } + + contents, err := ioutil.ReadAll(r) + if err != nil && err != os.EOF { + t.Error(err) + return + } + + expectedContents, _ := hex.DecodeString(symmetricallyEncryptedContentsHex) + if !bytes.Equal(expectedContents, contents) { + t.Errorf("bad contents got:%x want:%x", contents, expectedContents) + } +} + +const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf" +const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go new file mode 100644 index 000000000..fc19ffe80 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go @@ -0,0 +1,206 @@ +// 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 packet + +import ( + "crypto/cipher" + "crypto/openpgp/error" + "crypto/sha1" + "crypto/subtle" + "hash" + "io" + "os" + "strconv" +) + +// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The +// encrypted contents will consist of more OpenPGP packets. See RFC 4880, +// sections 5.7 and 5.13. +type SymmetricallyEncrypted struct { + MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. + contents io.Reader + prefix []byte +} + +func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error { + if se.MDC { + // See RFC 4880, section 5.13. + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + if buf[0] != 1 { + return error.UnsupportedError("unknown SymmetricallyEncrypted version") + } + } + se.contents = r + return nil +} + +// Decrypt returns a ReadCloser, from which the decrypted contents of the +// packet can be read. An incorrect key can, with high probability, be detected +// immediately and this will result in a KeyIncorrect error being returned. +func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) { + keySize := c.keySize() + if keySize == 0 { + return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) + } + if len(key) != keySize { + return nil, error.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") + } + + if se.prefix == nil { + se.prefix = make([]byte, c.blockSize()+2) + _, err := readFull(se.contents, se.prefix) + if err != nil { + return nil, err + } + } else if len(se.prefix) != c.blockSize()+2 { + return nil, error.InvalidArgumentError("can't try ciphers with different block lengths") + } + + ocfbResync := cipher.OCFBResync + if se.MDC { + // MDC packets use a different form of OCFB mode. + ocfbResync = cipher.OCFBNoResync + } + + s := cipher.NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) + if s == nil { + return nil, error.KeyIncorrectError + } + + plaintext := cipher.StreamReader{S: s, R: se.contents} + + if se.MDC { + // MDC packets have an embedded hash that we need to check. + h := sha1.New() + h.Write(se.prefix) + return &seMDCReader{in: plaintext, h: h}, nil + } + + // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. + return seReader{plaintext}, nil +} + +// seReader wraps an io.Reader with a no-op Close method. +type seReader struct { + in io.Reader +} + +func (ser seReader) Read(buf []byte) (int, os.Error) { + return ser.in.Read(buf) +} + +func (ser seReader) Close() os.Error { + return nil +} + +const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size + +// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold +// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an +// MDC packet containing a hash of the previous contents which is checked +// against the running hash. See RFC 4880, section 5.13. +type seMDCReader struct { + in io.Reader + h hash.Hash + trailer [mdcTrailerSize]byte + scratch [mdcTrailerSize]byte + trailerUsed int + error bool + eof bool +} + +func (ser *seMDCReader) Read(buf []byte) (n int, err os.Error) { + if ser.error { + err = io.ErrUnexpectedEOF + return + } + if ser.eof { + err = os.EOF + return + } + + // If we haven't yet filled the trailer buffer then we must do that + // first. + for ser.trailerUsed < mdcTrailerSize { + n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) + ser.trailerUsed += n + if err == os.EOF { + if ser.trailerUsed != mdcTrailerSize { + n = 0 + err = io.ErrUnexpectedEOF + ser.error = true + return + } + ser.eof = true + n = 0 + return + } + + if err != nil { + n = 0 + return + } + } + + // If it's a short read then we read into a temporary buffer and shift + // the data into the caller's buffer. + if len(buf) <= mdcTrailerSize { + n, err = readFull(ser.in, ser.scratch[:len(buf)]) + copy(buf, ser.trailer[:n]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], ser.trailer[n:]) + copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) + if n < len(buf) { + ser.eof = true + err = os.EOF + } + return + } + + n, err = ser.in.Read(buf[mdcTrailerSize:]) + copy(buf, ser.trailer[:]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], buf[n:]) + + if err == os.EOF { + ser.eof = true + } + return +} + +func (ser *seMDCReader) Close() os.Error { + if ser.error { + return error.SignatureError("error during reading") + } + + for !ser.eof { + // We haven't seen EOF so we need to read to the end + var buf [1024]byte + _, err := ser.Read(buf[:]) + if err == os.EOF { + break + } + if err != nil { + return error.SignatureError("error during reading") + } + } + + // This is a new-format packet tag byte for a type 19 (MDC) packet. + const mdcPacketTagByte = byte(0x80) | 0x40 | 19 + if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { + return error.SignatureError("MDC packet not found") + } + ser.h.Write(ser.trailer[:2]) + + final := ser.h.Sum() + if subtle.ConstantTimeCompare(final, ser.trailer[2:]) == 1 { + return error.SignatureError("hash mismatch") + } + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go new file mode 100644 index 000000000..ee5a30d32 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go @@ -0,0 +1,78 @@ +// 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 packet + +import ( + "bytes" + "crypto/openpgp/error" + "crypto/sha1" + "encoding/hex" + "io/ioutil" + "os" + "testing" +) + +// TestReader wraps a []byte and returns reads of a specific length. +type testReader struct { + data []byte + stride int +} + +func (t *testReader) Read(buf []byte) (n int, err os.Error) { + n = t.stride + if n > len(t.data) { + n = len(t.data) + } + if n > len(buf) { + n = len(buf) + } + copy(buf, t.data) + t.data = t.data[n:] + if len(t.data) == 0 { + err = os.EOF + } + return +} + +func testMDCReader(t *testing.T) { + mdcPlaintext, _ := hex.DecodeString(mdcPlaintextHex) + + for stride := 1; stride < len(mdcPlaintext)/2; stride++ { + r := &testReader{data: mdcPlaintext, stride: stride} + mdcReader := &seMDCReader{in: r, h: sha1.New()} + body, err := ioutil.ReadAll(mdcReader) + if err != nil { + t.Errorf("stride: %d, error: %s", stride, err) + continue + } + if !bytes.Equal(body, mdcPlaintext[:len(mdcPlaintext)-22]) { + t.Errorf("stride: %d: bad contents %x", stride, body) + continue + } + + err = mdcReader.Close() + if err != nil { + t.Errorf("stride: %d, error on Close: %s", err) + } + } + + mdcPlaintext[15] ^= 80 + + r := &testReader{data: mdcPlaintext, stride: 2} + mdcReader := &seMDCReader{in: r, h: sha1.New()} + _, err := ioutil.ReadAll(mdcReader) + if err != nil { + t.Errorf("corruption test, error: %s", err) + return + } + err = mdcReader.Close() + if err == nil { + t.Error("corruption: no error") + } else if _, ok := err.(*error.SignatureError); !ok { + t.Errorf("corruption: expected SignatureError, got: %s", err) + } +} + +const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c849cb4c4de10200aff40b45d31432c80cb384299a0655966d6939dfdeed1dddf980" diff --git a/src/pkg/crypto/openpgp/packet/userid.go b/src/pkg/crypto/openpgp/packet/userid.go new file mode 100644 index 000000000..ed2ad7774 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/userid.go @@ -0,0 +1,105 @@ +// 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 packet + +import ( + "io" + "io/ioutil" + "os" + "strings" +) + +// UserId contains text that is intended to represent the name and email +// address of the key holder. See RFC 4880, section 5.11. By convention, this +// takes the form "Full Name (Comment) <email@example.com>" +type UserId struct { + Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below. + + Name, Comment, Email string +} + +func (uid *UserId) parse(r io.Reader) (err os.Error) { + // RFC 4880, section 5.11 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uid.Id = string(b) + uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) + return +} + +// parseUserId extracts the name, comment and email from a user id string that +// is formatted as "Full Name (Comment) <email@example.com>". +func parseUserId(id string) (name, comment, email string) { + var n, c, e struct { + start, end int + } + var state int + + for offset, rune := range id { + switch state { + case 0: + // Entering name + n.start = offset + state = 1 + fallthrough + case 1: + // In name + if rune == '(' { + state = 2 + n.end = offset + } else if rune == '<' { + state = 5 + n.end = offset + } + case 2: + // Entering comment + c.start = offset + state = 3 + fallthrough + case 3: + // In comment + if rune == ')' { + state = 4 + c.end = offset + } + case 4: + // Between comment and email + if rune == '<' { + state = 5 + } + case 5: + // Entering email + e.start = offset + state = 6 + fallthrough + case 6: + // In email + if rune == '>' { + state = 7 + e.end = offset + } + default: + // After email + } + } + switch state { + case 1: + // ended in the name + n.end = len(id) + case 3: + // ended in comment + c.end = len(id) + case 6: + // ended in email + e.end = len(id) + } + + name = strings.TrimSpace(id[n.start:n.end]) + comment = strings.TrimSpace(id[c.start:c.end]) + email = strings.TrimSpace(id[e.start:e.end]) + return +} diff --git a/src/pkg/crypto/openpgp/packet/userid_test.go b/src/pkg/crypto/openpgp/packet/userid_test.go new file mode 100644 index 000000000..394873dc3 --- /dev/null +++ b/src/pkg/crypto/openpgp/packet/userid_test.go @@ -0,0 +1,42 @@ +// 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 packet + +import ( + "testing" +) + +var userIdTests = []struct { + id string + name, comment, email string +}{ + {"", "", "", ""}, + {"John Smith", "John Smith", "", ""}, + {"John Smith ()", "John Smith", "", ""}, + {"John Smith () <>", "John Smith", "", ""}, + {"(comment", "", "comment", ""}, + {"(comment)", "", "comment", ""}, + {"<email", "", "", "email"}, + {"<email> sdfk", "", "", "email"}, + {" John Smith ( Comment ) asdkflj < email > lksdfj", "John Smith", "Comment", "email"}, + {" John Smith < email > lksdfj", "John Smith", "", "email"}, + {"(<foo", "", "<foo", ""}, + {"René Descartes (العربي)", "René Descartes", "العربي", ""}, +} + +func TestParseUserId(t *testing.T) { + for i, test := range userIdTests { + name, comment, email := parseUserId(test.id) + if name != test.name { + t.Errorf("%d: name mismatch got:%s want:%s", i, name, test.name) + } + if comment != test.comment { + t.Errorf("%d: comment mismatch got:%s want:%s", i, comment, test.comment) + } + if email != test.email { + t.Errorf("%d: email mismatch got:%s want:%s", i, email, test.email) + } + } +} diff --git a/src/pkg/crypto/openpgp/s2k/s2k.go b/src/pkg/crypto/openpgp/s2k/s2k.go index f369d7ed4..873b33dc0 100644 --- a/src/pkg/crypto/openpgp/s2k/s2k.go +++ b/src/pkg/crypto/openpgp/s2k/s2k.go @@ -7,15 +7,12 @@ package s2k import ( - "crypto/md5" + "crypto" "crypto/openpgp/error" - "crypto/ripemd160" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" "hash" "io" "os" + "strconv" ) // Simple writes to out the result of computing the Simple S2K function (RFC @@ -87,9 +84,13 @@ func Parse(r io.Reader) (f func(out, in []byte), err os.Error) { return } - h := hashFuncFromType(buf[1]) + hash, ok := HashIdToHash(buf[1]) + if !ok { + return nil, error.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) + } + h := hash.New() if h == nil { - return nil, error.UnsupportedError("hash for S2K function") + return nil, error.UnsupportedError("hash not availible: " + strconv.Itoa(int(hash))) } switch buf[0] { @@ -122,25 +123,38 @@ func Parse(r io.Reader) (f func(out, in []byte), err os.Error) { return nil, error.UnsupportedError("S2K function") } -// hashFuncFromType returns a hash.Hash which corresponds to the given hash -// type byte. See RFC 4880, section 9.4. -func hashFuncFromType(hashType byte) hash.Hash { - switch hashType { - case 1: - return md5.New() - case 2: - return sha1.New() - case 3: - return ripemd160.New() - case 8: - return sha256.New() - case 9: - return sha512.New384() - case 10: - return sha512.New() - case 11: - return sha256.New224() +// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with +// Go's crypto.Hash type. See RFC 4880, section 9.4. +var hashToHashIdMapping = []struct { + id byte + hash crypto.Hash +}{ + {1, crypto.MD5}, + {2, crypto.SHA1}, + {3, crypto.RIPEMD160}, + {8, crypto.SHA256}, + {9, crypto.SHA384}, + {10, crypto.SHA512}, + {11, crypto.SHA224}, +} + +// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP +// hash id. +func HashIdToHash(id byte) (h crypto.Hash, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.hash, true + } } + return 0, false +} - return nil +// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. +func HashToHashId(h crypto.Hash) (id byte, ok bool) { + for _, m := range hashToHashIdMapping { + if m.hash == h { + return m.id, true + } + } + return 0, false } diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go index 4b2b7a26f..281d6dc6a 100755 --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -28,15 +28,15 @@ func (r *rngReader) Read(b []byte) (n int, err os.Error) { if r.prov == 0 { const provType = syscall.PROV_RSA_FULL const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - ok, errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if !ok { + errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if errno != 0 { r.mu.Unlock() return 0, os.NewSyscallError("CryptAcquireContext", errno) } } r.mu.Unlock() - ok, errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if !ok { + errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if errno != 0 { return 0, os.NewSyscallError("CryptGenRandom", errno) } return len(b), nil diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index af46ea511..809c8c15e 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -57,6 +57,7 @@ Curves: var suite *cipherSuite var suiteId uint16 +FindCipherSuite: for _, id := range clientHello.cipherSuites { for _, supported := range config.cipherSuites() { if id == supported { @@ -67,7 +68,7 @@ Curves: continue } suiteId = id - break + break FindCipherSuite } } } diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index b11d3225d..e8290d728 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -124,14 +124,22 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os. return } - certDERBlock, _ := pem.Decode(certPEMBlock) - if certDERBlock == nil { + var certDERBlock *pem.Block + for { + certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) + if certDERBlock == nil { + break + } + if certDERBlock.Type == "CERTIFICATE" { + cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) + } + } + + if len(cert.Certificate) == 0 { err = os.ErrorString("crypto/tls: failed to parse certificate PEM data") return } - cert.Certificate = [][]byte{certDERBlock.Bytes} - keyPEMBlock, err := ioutil.ReadFile(keyFile) if err != nil { return @@ -153,7 +161,7 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os. // We don't need to parse the public key for TLS, but we so do anyway // to check that it looks sane and matches the private key. - x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes) + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return } diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 599263432..3af8ba8ca 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -331,6 +331,10 @@ type Certificate struct { DNSNames []string EmailAddresses []string + // Name constraints + PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. + PermittedDNSDomains []string + PolicyIdentifiers []asn1.ObjectIdentifier } @@ -475,6 +479,18 @@ type policyInformation struct { // policyQualifiers omitted } +// RFC 5280, 4.2.1.10 +type nameConstraints struct { + Permitted []generalSubtree "optional,tag:0" + Excluded []generalSubtree "optional,tag:1" +} + +type generalSubtree struct { + Name string "tag:2,optional,ia5" + Min int "optional,tag:0" + Max int "optional,tag:1" +} + func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.Error) { switch algo { case RSA: @@ -603,6 +619,43 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { // If we didn't parse any of the names then we // fall through to the critical check below. + case 30: + // RFC 5280, 4.2.1.10 + + // NameConstraints ::= SEQUENCE { + // permittedSubtrees [0] GeneralSubtrees OPTIONAL, + // excludedSubtrees [1] GeneralSubtrees OPTIONAL } + // + // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + // + // GeneralSubtree ::= SEQUENCE { + // base GeneralName, + // minimum [0] BaseDistance DEFAULT 0, + // maximum [1] BaseDistance OPTIONAL } + // + // BaseDistance ::= INTEGER (0..MAX) + + var constraints nameConstraints + _, err := asn1.Unmarshal(e.Value, &constraints) + if err != nil { + return nil, err + } + + if len(constraints.Excluded) > 0 && e.Critical { + return out, UnhandledCriticalExtension{} + } + + for _, subtree := range constraints.Permitted { + if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 { + if e.Critical { + return out, UnhandledCriticalExtension{} + } + continue + } + out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name) + } + continue + case 35: // RFC 5280, 4.2.1.1 var a authKeyId @@ -699,10 +752,11 @@ var ( oidExtensionBasicConstraints = []int{2, 5, 29, 19} oidExtensionSubjectAltName = []int{2, 5, 29, 17} oidExtensionCertificatePolicies = []int{2, 5, 29, 32} + oidExtensionNameConstraints = []int{2, 5, 29, 30} ) func buildExtensions(template *Certificate) (ret []extension, err os.Error) { - ret = make([]extension, 6 /* maximum number of elements. */ ) + ret = make([]extension, 7 /* maximum number of elements. */ ) n := 0 if template.KeyUsage != 0 { @@ -779,6 +833,22 @@ func buildExtensions(template *Certificate) (ret []extension, err os.Error) { n++ } + if len(template.PermittedDNSDomains) > 0 { + ret[n].Id = oidExtensionNameConstraints + ret[n].Critical = template.PermittedDNSDomainsCritical + + var out nameConstraints + out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains)) + for i, permitted := range template.PermittedDNSDomains { + out.Permitted[i] = generalSubtree{Name: permitted} + } + ret[n].Value, err = asn1.Marshal(out) + if err != nil { + return + } + n++ + } + // Adding another extension here? Remember to update the maximum number // of elements in the make() at the top of the function. @@ -793,7 +863,8 @@ var ( // CreateSelfSignedCertificate creates a new certificate based on // a template. The following members of template are used: SerialNumber, // Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA, -// MaxPathLen, SubjectKeyId, DNSNames. +// MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, +// PermittedDNSDomains. // // The certificate is signed by parent. If parent is equal to template then the // certificate is self-signed. The parameter pub is the public key of the diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index 2fe47fdbe..57889e7e1 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -171,7 +171,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) { IsCA: true, DNSNames: []string{"test.example.com"}, - PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, } derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv) @@ -190,6 +191,10 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers) } + if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" { + t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) + } + err = cert.CheckSignatureFrom(cert) if err != nil { t.Errorf("Signature verification failed: %s", err) diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go index 82c02407b..1bcbdc5e9 100644 --- a/src/pkg/debug/pe/file.go +++ b/src/pkg/debug/pe/file.go @@ -57,7 +57,6 @@ type ImportDirectory struct { FirstThunk uint32 dll string - rva []uint32 } // Data reads and returns the contents of the PE section. @@ -267,34 +266,28 @@ func (f *File) ImportedSymbols() ([]string, os.Error) { } ida = append(ida, dt) } - for i, _ := range ida { + names, _ := ds.Data() + var all []string + for _, dt := range ida { + dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) + d, _ = ds.Data() + // seek to OriginalFirstThunk + d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] for len(d) > 0 { va := binary.LittleEndian.Uint32(d[0:4]) d = d[4:] if va == 0 { break } - ida[i].rva = append(ida[i].rva, va) - } - } - for _, _ = range ida { - for len(d) > 0 { - va := binary.LittleEndian.Uint32(d[0:4]) - d = d[4:] - if va == 0 { - break + if va&0x80000000 > 0 { // is Ordinal + // TODO add dynimport ordinal support. + //ord := va&0x0000FFFF + } else { + fn, _ := getString(names, int(va-ds.VirtualAddress+2)) + all = append(all, fn+":"+dt.dll) } } } - names, _ := ds.Data() - var all []string - for _, dt := range ida { - dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) - for _, va := range dt.rva { - fn, _ := getString(names, int(va-ds.VirtualAddress+2)) - all = append(all, fn+":"+dt.dll) - } - } return all, nil } diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go index bbe530c27..69ad5fed1 100644 --- a/src/pkg/ebnf/ebnf_test.go +++ b/src/pkg/ebnf/ebnf_test.go @@ -15,31 +15,31 @@ var fset = token.NewFileSet() var grammars = []string{ - `Program = . - `, - - `Program = foo . - foo = "foo" . - `, - - `Program = "a" | "b" "c" . - `, - - `Program = "a" ... "z" . - `, - - `Program = Song . - Song = { Note } . - Note = Do | (Re | Mi | Fa | So | La) | Ti . - Do = "c" . - Re = "d" . - Mi = "e" . - Fa = "f" . - So = "g" . - La = "a" . - Ti = ti . - ti = "b" . - `, +`Program = . +`, + +`Program = foo . +foo = "foo" . +`, + +`Program = "a" | "b" "c" . +`, + +`Program = "a" ... "z" . +`, + +`Program = Song . + Song = { Note } . + Note = Do | (Re | Mi | Fa | So | La) | Ti . + Do = "c" . + Re = "d" . + Mi = "e" . + Fa = "f" . + So = "g" . + La = "a" . + Ti = ti . + ti = "b" . +`, } diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 77ff3a9f3..ee2f23dbb 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This package implements translation between -// unsigned integer values and byte sequences. +// Package binary implements translation between +// unsigned integer values and byte sequences +// and the reading and writing of fixed-size values. package binary import ( diff --git a/src/pkg/encoding/line/line.go b/src/pkg/encoding/line/line.go index 779b5758a..f46ce1c83 100644 --- a/src/pkg/encoding/line/line.go +++ b/src/pkg/encoding/line/line.go @@ -105,6 +105,9 @@ func (l *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { l.buf = l.buf[:oldLen+n] if readErr != nil { l.err = readErr + if len(l.buf) == 0 { + return nil, false, readErr + } } } panic("unreachable") diff --git a/src/pkg/encoding/line/line_test.go b/src/pkg/encoding/line/line_test.go index ff16d10c7..ff3d51669 100644 --- a/src/pkg/encoding/line/line_test.go +++ b/src/pkg/encoding/line/line_test.go @@ -7,6 +7,7 @@ package line import ( "bytes" "io" + "io/ioutil" "os" "testing" ) @@ -108,3 +109,25 @@ func TestReadAfterLines(t *testing.T) { t.Errorf("bad result for Read: got %q; expected %q", outbuf.String(), restData) } } + +func TestReadEmptyBuffer(t *testing.T) { + l := NewReader(bytes.NewBuffer(nil), 10) + line, isPrefix, err := l.ReadLine() + if err != os.EOF { + t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) + } +} + +func TestLinesAfterRead(t *testing.T) { + l := NewReader(bytes.NewBuffer([]byte("foo")), 10) + _, err := ioutil.ReadAll(l) + if err != nil { + t.Error(err) + return + } + + line, isPrefix, err := l.ReadLine() + if err != os.EOF { + t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) + } +} diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index 4f4c8c777..80f6f3c7d 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -22,12 +22,12 @@ const ( // Stdin, Stdout, and Stderr are Files representing pipes // connected to the running command's standard input, output, and error, // or else nil, depending on the arguments to Run. -// Pid is the running command's operating system process ID. +// Process represents the underlying operating system process. type Cmd struct { - Stdin *os.File - Stdout *os.File - Stderr *os.File - Pid int + Stdin *os.File + Stdout *os.File + Stderr *os.File + Process *os.Process } // PathError records the name of a binary that was not @@ -88,24 +88,24 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { // If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr) // of the returned Cmd is the other end of the pipe. // Otherwise the field in Cmd is nil. -func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (p *Cmd, err os.Error) { - p = new(Cmd) +func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) { + c = new(Cmd) var fd [3]*os.File - if fd[0], p.Stdin, err = modeToFiles(stdin, 0); err != nil { + if fd[0], c.Stdin, err = modeToFiles(stdin, 0); err != nil { goto Error } - if fd[1], p.Stdout, err = modeToFiles(stdout, 1); err != nil { + if fd[1], c.Stdout, err = modeToFiles(stdout, 1); err != nil { goto Error } if stderr == MergeWithStdout { fd[2] = fd[1] - } else if fd[2], p.Stderr, err = modeToFiles(stderr, 2); err != nil { + } else if fd[2], c.Stderr, err = modeToFiles(stderr, 2); err != nil { goto Error } // Run command. - p.Pid, err = os.ForkExec(name, argv, envv, dir, fd[0:]) + c.Process, err = os.StartProcess(name, argv, envv, dir, fd[0:]) if err != nil { goto Error } @@ -118,7 +118,7 @@ func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int if fd[2] != os.Stderr && fd[2] != fd[1] { fd[2].Close() } - return p, nil + return c, nil Error: if fd[0] != os.Stdin && fd[0] != nil { @@ -130,63 +130,67 @@ Error: if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] { fd[2].Close() } - if p.Stdin != nil { - p.Stdin.Close() + if c.Stdin != nil { + c.Stdin.Close() } - if p.Stdout != nil { - p.Stdout.Close() + if c.Stdout != nil { + c.Stdout.Close() } - if p.Stderr != nil { - p.Stderr.Close() + if c.Stderr != nil { + c.Stderr.Close() + } + if c.Process != nil { + c.Process.Release() } return nil, err } -// Wait waits for the running command p, -// returning the Waitmsg returned by os.Wait and an error. -// The options are passed through to os.Wait. -// Setting options to 0 waits for p to exit; +// Wait waits for the running command c, +// returning the Waitmsg returned when the process exits. +// The options are passed to the process's Wait method. +// Setting options to 0 waits for c to exit; // other options cause Wait to return for other // process events; see package os for details. -func (p *Cmd) Wait(options int) (*os.Waitmsg, os.Error) { - if p.Pid <= 0 { +func (c *Cmd) Wait(options int) (*os.Waitmsg, os.Error) { + if c.Process == nil { return nil, os.ErrorString("exec: invalid use of Cmd.Wait") } - w, err := os.Wait(p.Pid, options) + w, err := c.Process.Wait(options) if w != nil && (w.Exited() || w.Signaled()) { - p.Pid = -1 + c.Process.Release() + c.Process = nil } return w, err } -// Close waits for the running command p to exit, +// Close waits for the running command c to exit, // if it hasn't already, and then closes the non-nil file descriptors -// p.Stdin, p.Stdout, and p.Stderr. -func (p *Cmd) Close() os.Error { - if p.Pid > 0 { +// c.Stdin, c.Stdout, and c.Stderr. +func (c *Cmd) Close() os.Error { + if c.Process != nil { // Loop on interrupt, but // ignore other errors -- maybe // caller has already waited for pid. - _, err := p.Wait(0) + _, err := c.Wait(0) for err == os.EINTR { - _, err = p.Wait(0) + _, err = c.Wait(0) } } // Close the FDs that are still open. var err os.Error - if p.Stdin != nil && p.Stdin.Fd() >= 0 { - if err1 := p.Stdin.Close(); err1 != nil { + if c.Stdin != nil && c.Stdin.Fd() >= 0 { + if err1 := c.Stdin.Close(); err1 != nil { err = err1 } } - if p.Stdout != nil && p.Stdout.Fd() >= 0 { - if err1 := p.Stdout.Close(); err1 != nil && err != nil { + if c.Stdout != nil && c.Stdout.Fd() >= 0 { + if err1 := c.Stdout.Close(); err1 != nil && err != nil { err = err1 } } - if p.Stderr != nil && p.Stderr != p.Stdout && p.Stderr.Fd() >= 0 { - if err1 := p.Stderr.Close(); err1 != nil && err != nil { + if c.Stderr != nil && c.Stderr != c.Stdout && c.Stderr.Fd() >= 0 { + if err1 := c.Stderr.Close(); err1 != nil && err != nil { err = err1 } } diff --git a/src/pkg/exp/eval/gen.go b/src/pkg/exp/eval/gen.go index a2b119846..de98a5d15 100644 --- a/src/pkg/exp/eval/gen.go +++ b/src/pkg/exp/eval/gen.go @@ -368,7 +368,7 @@ func main() { if err != nil { log.Exit(err) } - err = t.Execute(data, os.Stdout) + err = t.Execute(os.Stdout, data) if err != nil { log.Exit(err) } diff --git a/src/pkg/exp/eval/stmt.go b/src/pkg/exp/eval/stmt.go index 77ff066d0..5c5d4338a 100644 --- a/src/pkg/exp/eval/stmt.go +++ b/src/pkg/exp/eval/stmt.go @@ -908,7 +908,7 @@ func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) { return default: - log.Panic("Unexpected branch token %v", s.Tok) + log.Panicf("Unexpected branch token %v", s.Tok) } a.flow.put1(false, pc) diff --git a/src/pkg/exp/wingui/gui.go b/src/pkg/exp/wingui/gui.go index 41ee5b789..cf392934c 100644 --- a/src/pkg/exp/wingui/gui.go +++ b/src/pkg/exp/wingui/gui.go @@ -51,7 +51,8 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { case WM_COMMAND: switch uint32(lparam) { case bh: - if ok, e := PostMessage(hwnd, WM_CLOSE, 0, 0); !ok { + e := PostMessage(hwnd, WM_CLOSE, 0, 0) + if e != 0 { abortErrNo("PostMessage", e) } default: @@ -125,7 +126,7 @@ func rungui() int { ShowWindow(wh, SW_SHOWDEFAULT) // UpdateWindow - if _, e := UpdateWindow(wh); e != 0 { + if e := UpdateWindow(wh); e != 0 { abortErrNo("UpdateWindow", e) } diff --git a/src/pkg/exp/wingui/winapi.go b/src/pkg/exp/wingui/winapi.go index 2f480ec9e..c96f45299 100644 --- a/src/pkg/exp/wingui/winapi.go +++ b/src/pkg/exp/wingui/winapi.go @@ -130,18 +130,18 @@ var ( //sys RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) = user32.RegisterClassExW //sys CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) = user32.CreateWindowExW //sys DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.DefWindowProcW -//sys DestroyWindow(hwnd uint32) (ok bool, errno int) = user32.DestroyWindow +//sys DestroyWindow(hwnd uint32) (errno int) = user32.DestroyWindow //sys PostQuitMessage(exitcode int32) = user32.PostQuitMessage -//sys ShowWindow(hwnd uint32, cmdshow int32) (ok bool) = user32.ShowWindow -//sys UpdateWindow(hwnd uint32) (ok bool, errno int) = user32.UpdateWindow +//sys ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) = user32.ShowWindow +//sys UpdateWindow(hwnd uint32) (errno int) = user32.UpdateWindow //sys GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW -//sys TranslateMessage(msg *Msg) (ok bool) = user32.TranslateMessage +//sys TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage //sys DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW //sys LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) = user32.LoadIconW //sys LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) = user32.LoadCursorW //sys SetCursor(cursor uint32) (precursor uint32, errno int) = user32.SetCursor //sys SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.SendMessageW -//sys PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (ok bool, errno int) = user32.PostMessageW +//sys PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) = user32.PostMessageW func MakeIntResource(id uint16) *uint16 { return (*uint16)(unsafe.Pointer(uintptr(id))) diff --git a/src/pkg/exp/wingui/zwinapi.go b/src/pkg/exp/wingui/zwinapi.go index 324bf1773..60aaac6cf 100644 --- a/src/pkg/exp/wingui/zwinapi.go +++ b/src/pkg/exp/wingui/zwinapi.go @@ -79,10 +79,9 @@ func DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult return } -func DestroyWindow(hwnd uint32) (ok bool, errno int) { - r0, _, e1 := syscall.Syscall(procDestroyWindow, 1, uintptr(hwnd), 0, 0) - ok = bool(r0 != 0) - if !ok { +func DestroyWindow(hwnd uint32) (errno int) { + r1, _, e1 := syscall.Syscall(procDestroyWindow, 1, uintptr(hwnd), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -99,16 +98,15 @@ func PostQuitMessage(exitcode int32) { return } -func ShowWindow(hwnd uint32, cmdshow int32) (ok bool) { +func ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) { r0, _, _ := syscall.Syscall(procShowWindow, 2, uintptr(hwnd), uintptr(cmdshow), 0) - ok = bool(r0 != 0) + wasvisible = bool(r0 != 0) return } -func UpdateWindow(hwnd uint32) (ok bool, errno int) { - r0, _, e1 := syscall.Syscall(procUpdateWindow, 1, uintptr(hwnd), 0, 0) - ok = bool(r0 != 0) - if !ok { +func UpdateWindow(hwnd uint32) (errno int) { + r1, _, e1 := syscall.Syscall(procUpdateWindow, 1, uintptr(hwnd), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -135,9 +133,9 @@ func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) return } -func TranslateMessage(msg *Msg) (ok bool) { +func TranslateMessage(msg *Msg) (done bool) { r0, _, _ := syscall.Syscall(procTranslateMessage, 1, uintptr(unsafe.Pointer(msg)), 0, 0) - ok = bool(r0 != 0) + done = bool(r0 != 0) return } @@ -198,10 +196,9 @@ func SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult i return } -func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (ok bool, errno int) { - r0, _, e1 := syscall.Syscall6(procPostMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - ok = bool(r0 != 0) - if !ok { +func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) { + r1, _, e1 := syscall.Syscall6(procPostMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 143a10611..be972057e 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -96,7 +96,7 @@ func newIntValue(val int, p *int) *intValue { } func (i *intValue) Set(s string) bool { - v, err := strconv.Atoi(s) + v, err := strconv.Btoi64(s, 0) *i = intValue(v) return err == nil } @@ -112,7 +112,7 @@ func newInt64Value(val int64, p *int64) *int64Value { } func (i *int64Value) Set(s string) bool { - v, err := strconv.Atoi64(s) + v, err := strconv.Btoi64(s, 0) *i = int64Value(v) return err == nil } @@ -128,7 +128,7 @@ func newUintValue(val uint, p *uint) *uintValue { } func (i *uintValue) Set(s string) bool { - v, err := strconv.Atoui(s) + v, err := strconv.Btoui64(s, 0) *i = uintValue(v) return err == nil } @@ -144,7 +144,7 @@ func newUint64Value(val uint64, p *uint64) *uint64Value { } func (i *uint64Value) Set(s string) bool { - v, err := strconv.Atoui64(s) + v, err := strconv.Btoui64(s, 0) *i = uint64Value(v) return err == nil } diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index b91a8b567..30a21e61a 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -106,7 +106,7 @@ func TestParse(t *testing.T) { "-bool", "-bool2=true", "--int", "22", - "--int64", "23", + "--int64", "0x23", "-uint", "24", "--uint64", "25", "-string", "hello", @@ -125,8 +125,8 @@ func TestParse(t *testing.T) { if *intFlag != 22 { t.Error("int flag should be 22, is ", *intFlag) } - if *int64Flag != 23 { - t.Error("int64 flag should be 23, is ", *int64Flag) + if *int64Flag != 0x23 { + t.Error("int64 flag should be 0x23, is ", *int64Flag) } if *uintFlag != 24 { t.Error("uint flag should be 24, is ", *uintFlag) diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index 02c29389d..b40e265ae 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -16,6 +16,7 @@ when printing structs, the plus flag (%+v) adds field names %#v a Go-syntax representation of the value %T a Go-syntax representation of the type of the value + %% a literal percent sign; consumes no value Boolean: %t the word true or false @@ -28,6 +29,8 @@ %X base 16, with upper-case letters for A-F %U Unicode format: U+1234; same as "U+%x" with 4 digits default Floating-point and complex constituents: + %b decimalless scientific notation with exponent a power + of two, in the manner of strconv.Ftoa32, e.g. -123456p-78 %e scientific notation, e.g. -1234.456e+78 %E scientific notation, e.g. -1234.456E+78 %f decimal point but no exponent, e.g. 123.456 @@ -136,6 +139,10 @@ %e %E %f %F %g %g are all equivalent and scan any floating point or complex value %s and %v on strings scan a space-delimited token + The familiar base-setting prefixes 0 (octal) and 0x + (hexadecimal) are accepted when scanning integers without a + format or with the %v verb. + Width is interpreted in the input text (%5s means at most five runes of input will be read to scan a string) but there is no syntax for scanning with a precision (no %5.2f, just diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index a408c42aa..53d88d574 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -7,6 +7,7 @@ package fmt import ( "bytes" "io" + "math" "os" "reflect" "strconv" @@ -15,18 +16,11 @@ import ( "utf8" ) -// readRuner is the interface to something that can read runes. If -// the object provided to Scan does not satisfy this interface, the -// object will be wrapped by a readRune object. -type readRuner interface { - ReadRune() (rune int, size int, err os.Error) -} - -// unreadRuner is the interface to something that can unread runes. +// runeUnreader is the interface to something that can unread runes. // If the object provided to Scan does not satisfy this interface, // a local buffer will be used to back up the input, but its contents // will be lost when Scan returns. -type unreadRuner interface { +type runeUnreader interface { UnreadRune() os.Error } @@ -36,7 +30,7 @@ type unreadRuner interface { type ScanState interface { // GetRune reads the next rune (Unicode code point) from the input. GetRune() (rune int, err os.Error) - // UngetRune causes the next call to GetRune to return the rune. + // UngetRune causes the next call to GetRune to return the same rune. UngetRune() // Width returns the value of the width option and whether it has been set. // The unit is Unicode code points. @@ -137,15 +131,15 @@ const EOF = -1 // ss is the internal implementation of ScanState. type ss struct { - rr readRuner // where to read input - buf bytes.Buffer // token accumulator - nlIsSpace bool // whether newline counts as white space - peekRune int // one-rune lookahead - prevRune int // last rune returned by GetRune - atEOF bool // already read EOF - maxWid int // max width of field, in runes - widPresent bool // width was specified - wid int // width consumed so far; used in accept() + rr io.RuneReader // where to read input + buf bytes.Buffer // token accumulator + nlIsSpace bool // whether newline counts as white space + peekRune int // one-rune lookahead + prevRune int // last rune returned by GetRune + atEOF bool // already read EOF + maxWid int // max width of field, in runes + widPresent bool // width was specified + wid int // width consumed so far; used in accept() } func (s *ss) GetRune() (rune int, err os.Error) { @@ -215,7 +209,7 @@ func (s *ss) mustGetRune() (rune int) { func (s *ss) UngetRune() { - if u, ok := s.rr.(unreadRuner); ok { + if u, ok := s.rr.(runeUnreader); ok { u.UnreadRune() } else { s.peekRune = s.prevRune @@ -246,7 +240,7 @@ func (s *ss) Token() (tok string, err os.Error) { // readRune is a structure to enable reading UTF-8 encoded code points // from an io.Reader. It is used if the Reader given to the scanner does -// not already implement ReadRuner. +// not already implement io.RuneReader. type readRune struct { reader io.Reader buf [utf8.UTFMax]byte // used only inside ReadRune @@ -308,7 +302,7 @@ var ssFree = newCache(func() interface{} { return new(ss) }) // Allocate a new ss struct or grab a cached one. func newScanState(r io.Reader, nlIsSpace bool) *ss { s := ssFree.get().(*ss) - if rr, ok := r.(readRuner); ok { + if rr, ok := r.(io.RuneReader); ok { s.rr = rr } else { s.rr = &readRune{reader: r} @@ -394,14 +388,12 @@ func (s *ss) consume(ok string, accept bool) bool { if rune == EOF { return false } - for i := 0; i < len(ok); i++ { - if int(ok[i]) == rune { - if accept { - s.buf.WriteRune(rune) - s.wid++ - } - return true + if strings.IndexRune(ok, rune) >= 0 { + if accept { + s.buf.WriteRune(rune) + s.wid++ } + return true } if rune != EOF && accept { s.UngetRune() @@ -409,6 +401,15 @@ func (s *ss) consume(ok string, accept bool) bool { return false } +// peek reports whether the next character is in the ok string, without consuming it. +func (s *ss) peek(ok string) bool { + rune := s.getRune() + if rune != EOF { + s.UngetRune() + } + return strings.IndexRune(ok, rune) >= 0 +} + // accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the // buffer and returns true. Otherwise it return false. func (s *ss) accept(ok string) bool { @@ -459,7 +460,7 @@ const ( hexadecimalDigits = "0123456789aAbBcCdDeEfF" sign = "+-" period = "." - exponent = "eE" + exponent = "eEp" ) // getBase returns the numeric base represented by the verb and its digit string. @@ -482,8 +483,8 @@ func (s *ss) getBase(verb int) (base int, digits string) { } // scanNumber returns the numerical string with specified digits starting here. -func (s *ss) scanNumber(digits string) string { - if !s.accept(digits) { +func (s *ss) scanNumber(digits string, haveDigits bool) string { + if !haveDigits && !s.accept(digits) { s.errorString("expected integer") } for s.accept(digits) { @@ -502,22 +503,44 @@ func (s *ss) scanRune(bitSize int) int64 { return rune } +// scanBasePrefix reports whether the integer begins with a 0 or 0x, +// and returns the base, digit string, and whether a zero was found. +// It is called only if the verb is %v. +func (s *ss) scanBasePrefix() (base int, digits string, found bool) { + if !s.peek("0") { + return 10, decimalDigits, false + } + s.accept("0") + found = true // We've put a digit into the token buffer. + // Special cases for '0' && '0x' + base, digits = 8, octalDigits + if s.peek("xX") { + s.consume("xX", false) + base, digits = 16, hexadecimalDigits + } + return +} + // scanInt returns the value of the integer represented by the next // token, checking for overflow. Any error is stored in s.err. func (s *ss) scanInt(verb int, bitSize int) int64 { if verb == 'c' { return s.scanRune(bitSize) } - base, digits := s.getBase(verb) s.skipSpace(false) + base, digits := s.getBase(verb) + haveDigits := false if verb == 'U' { if !s.consume("U", false) || !s.consume("+", false) { s.errorString("bad unicode format ") } } else { s.accept(sign) // If there's a sign, it will be left in the token buffer. + if verb == 'v' { + base, digits, haveDigits = s.scanBasePrefix() + } } - tok := s.scanNumber(digits) + tok := s.scanNumber(digits, haveDigits) i, err := strconv.Btoi64(tok, base) if err != nil { s.error(err) @@ -536,14 +559,17 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { if verb == 'c' { return uint64(s.scanRune(bitSize)) } - base, digits := s.getBase(verb) s.skipSpace(false) + base, digits := s.getBase(verb) + haveDigits := false if verb == 'U' { if !s.consume("U", false) || !s.consume("+", false) { s.errorString("bad unicode format ") } + } else if verb == 'v' { + base, digits, haveDigits = s.scanBasePrefix() } - tok := s.scanNumber(digits) + tok := s.scanNumber(digits, haveDigits) i, err := strconv.Btoui64(tok, base) if err != nil { s.error(err) @@ -617,6 +643,27 @@ func (s *ss) complexTokens() (real, imag string) { // convertFloat converts the string to a float64value. func (s *ss) convertFloat(str string, n int) float64 { + if p := strings.Index(str, "p"); p >= 0 { + // Atof doesn't handle power-of-2 exponents, + // but they're easy to evaluate. + f, err := strconv.AtofN(str[:p], n) + if err != nil { + // Put full string into error. + if e, ok := err.(*strconv.NumError); ok { + e.Num = str + } + s.error(err) + } + n, err := strconv.Atoi(str[p+1:]) + if err != nil { + // Put full string into error. + if e, ok := err.(*strconv.NumError); ok { + e.Num = str + } + s.error(err) + } + return math.Ldexp(f, n) + } f, err := strconv.AtofN(str, n) if err != nil { s.error(err) @@ -747,7 +794,7 @@ func (s *ss) hexString() string { return s.buf.String() } -const floatVerbs = "eEfFgGv" +const floatVerbs = "beEfFgGv" // scanOne scans a single value, deriving the scanner from the type of the argument. func (s *ss) scanOne(verb int, field interface{}) { diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 78b9fbb4a..478b10923 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -129,10 +129,20 @@ func newReader(s string) *myStringReader { } var scanTests = []ScanTest{ - // Numbers + // Basic types {"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written {"F\n", &boolVal, false}, // restored to zero value {"21\n", &intVal, 21}, + {"0\n", &intVal, 0}, + {"000\n", &intVal, 0}, + {"0x10\n", &intVal, 0x10}, + {"-0x10\n", &intVal, -0x10}, + {"0377\n", &intVal, 0377}, + {"-0377\n", &intVal, -0377}, + {"0\n", &uintVal, uint(0)}, + {"000\n", &uintVal, uint(0)}, + {"0x10\n", &uintVal, uint(0x10)}, + {"0377\n", &uintVal, uint(0377)}, {"22\n", &int8Val, int8(22)}, {"23\n", &int16Val, int16(23)}, {"24\n", &int32Val, int32(24)}, @@ -160,6 +170,10 @@ var scanTests = []ScanTest{ {"2.3\n", &float64Val, 2.3}, {"2.3e1\n", &float32Val, float32(2.3e1)}, {"2.3e2\n", &float64Val, 2.3e2}, + {"2.3p2\n", &float64Val, 2.3 * 4}, + {"2.3p+2\n", &float64Val, 2.3 * 4}, + {"2.3p+66\n", &float64Val, 2.3 * (1 << 32) * (1 << 32) * 4}, + {"2.3p-66\n", &float64Val, 2.3 / ((1 << 32) * (1 << 32) * 4)}, {"2.35\n", &stringVal, "2.35"}, {"2345678\n", &bytesVal, []byte("2345678")}, {"(3.4e1-2i)\n", &complex128Val, 3.4e1 - 2i}, @@ -197,6 +211,8 @@ var scanfTests = []ScanfTest{ {"%v", "TRUE\n", &boolVal, true}, {"%t", "false\n", &boolVal, false}, {"%v", "-71\n", &intVal, -71}, + {"%v", "0377\n", &intVal, 0377}, + {"%v", "0x44\n", &intVal, 0x44}, {"%d", "72\n", &intVal, 72}, {"%c", "a\n", &intVal, 'a'}, {"%c", "\u5072\n", &intVal, 0x5072}, diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 34b0c4e2d..48e2af1b7 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -34,18 +34,18 @@ const ( ) +const ( + esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8 + _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape +) + + var ( esc = []byte{tabwriter.Escape} htab = []byte{'\t'} htabs = []byte("\t\t\t\t\t\t\t\t") newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined by nlines formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined by nlines - - esc_quot = []byte(""") // shorter than """ - esc_apos = []byte("'") // shorter than "'" - esc_amp = []byte("&") - esc_lt = []byte("<") - esc_gt = []byte(">") ) @@ -145,18 +145,20 @@ func (p *printer) nlines(n, min int) int { // write0 does not indent after newlines, and does not HTML-escape or update p.pos. // func (p *printer) write0(data []byte) { - n, err := p.output.Write(data) - p.written += n - if err != nil { - p.errors <- err - runtime.Goexit() + if len(data) > 0 { + n, err := p.output.Write(data) + p.written += n + if err != nil { + p.errors <- err + runtime.Goexit() + } } } // write interprets data and writes it to p.output. It inserts indentation -// after a line break unless in a tabwriter escape sequence, and it HTML- -// escapes characters if GenHTML is set. It updates p.pos as a side-effect. +// after a line break unless in a tabwriter escape sequence. +// It updates p.pos as a side-effect. // func (p *printer) write(data []byte) { i0 := 0 @@ -189,36 +191,6 @@ func (p *printer) write(data []byte) { // next segment start i0 = i + 1 - case '"', '\'', '&', '<', '>': - if p.Mode&GenHTML != 0 { - // write segment ending in b - p.write0(data[i0:i]) - - // write HTML-escaped b - var esc []byte - switch b { - case '"': - esc = esc_quot - case '\'': - esc = esc_apos - case '&': - esc = esc_amp - case '<': - esc = esc_lt - case '>': - esc = esc_gt - } - p.write0(esc) - - // update p.pos - d := i + 1 - i0 - p.pos.Offset += d - p.pos.Column += d - - // next segment start - i0 = i + 1 - } - case tabwriter.Escape: p.mode ^= inLiteral @@ -251,29 +223,13 @@ func (p *printer) writeNewlines(n int, useFF bool) { } -func (p *printer) writeTaggedItem(data []byte, tag HTMLTag) { - // write start tag, if any - // (no html-escaping and no p.pos update for tags - use write0) - if tag.Start != "" { - p.write0([]byte(tag.Start)) - } - p.write(data) - // write end tag, if any - if tag.End != "" { - p.write0([]byte(tag.End)) - } -} - - // writeItem writes data at position pos. data is the text corresponding to // a single lexical token, but may also be comment text. pos is the actual // (or at least very accurately estimated) position of the data in the original -// source text. If tags are present and GenHTML is set, the tags are written -// before and after the data. writeItem updates p.last to the position -// immediately following the data. +// source text. writeItem updates p.last to the position immediately following +// the data. // -func (p *printer) writeItem(pos token.Position, data []byte, tag HTMLTag) { - fileChanged := false +func (p *printer) writeItem(pos token.Position, data []byte) { if pos.IsValid() { // continue with previous position if we don't have a valid pos if p.last.IsValid() && p.last.Filename != pos.Filename { @@ -283,7 +239,6 @@ func (p *printer) writeItem(pos token.Position, data []byte, tag HTMLTag) { p.indent = 0 p.mode = 0 p.buffer = p.buffer[0:0] - fileChanged = true } p.pos = pos } @@ -292,18 +247,7 @@ func (p *printer) writeItem(pos token.Position, data []byte, tag HTMLTag) { _, filename := path.Split(pos.Filename) p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column))) } - if p.Mode&GenHTML != 0 { - // write line tag if on a new line - // TODO(gri): should write line tags on each line at the start - // will be more useful (e.g. to show line numbers) - if p.Styler != nil && (pos.Line != p.lastTaggedLine || fileChanged) { - p.writeTaggedItem(p.Styler.LineTag(pos.Line)) - p.lastTaggedLine = pos.Line - } - p.writeTaggedItem(data, tag) - } else { - p.write(data) - } + p.write(data) p.last = p.pos } @@ -312,14 +256,13 @@ func (p *printer) writeItem(pos token.Position, data []byte, tag HTMLTag) { // If there is any pending whitespace, it consumes as much of // it as is likely to help position the comment nicely. // pos is the comment position, next the position of the item -// after all pending comments, isFirst indicates if this is the -// first comment in a group of comments, and isKeyword indicates -// if the next item is a keyword. +// after all pending comments, prev is the previous comment in +// a group of comments (or nil), and isKeyword indicates if the +// next item is a keyword. // -func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeyword bool) { - if !p.last.IsValid() { - // there was no preceeding item and the comment is the - // first item to be printed - don't write any whitespace +func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, isKeyword bool) { + if p.written == 0 { + // the comment is the first item to be printed - don't write any whitespace return } @@ -329,11 +272,12 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor return } - if pos.IsValid() && pos.Line == p.last.Line { + if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') { // comment on the same line as last item: // separate with at least one separator hasSep := false - if isFirst { + if prev == nil { + // first comment of a comment group j := 0 for i, ch := range p.buffer { switch ch { @@ -370,7 +314,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor } else { // comment on a different line: // separate with at least one line break - if isFirst { + if prev == nil { + // first comment of a comment group j := 0 for i, ch := range p.buffer { switch ch { @@ -402,10 +347,14 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor } // use formfeeds to break columns before a comment; // this is analogous to using formfeeds to separate - // individual lines of /*-style comments - // (if !pos.IsValid(), pos.Line == 0, and this will - // print no newlines) - p.writeNewlines(pos.Line-p.last.Line, true) + // individual lines of /*-style comments - but make + // sure there is at least one line break if the previous + // comment was a line comment + n := pos.Line - p.last.Line // if !pos.IsValid(), pos.Line == 0, and n will be 0 + if n <= 0 && prev != nil && prev.Text[1] == '/' { + n = 1 + } + p.writeNewlines(n, true) } } @@ -413,21 +362,10 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, isFirst, isKeywor func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) { // line must pass through unchanged, bracket it with tabwriter.Escape line = bytes.Join([][]byte{esc, line, esc}, nil) - - // apply styler, if any - var tag HTMLTag - if p.Styler != nil { - line, tag = p.Styler.Comment(comment, line) - } - - p.writeItem(pos, line, tag) + p.writeItem(pos, line) } -// TODO(gri): Similar (but not quite identical) functionality for -// comment processing can be found in go/doc/comment.go. -// Perhaps this can be factored eventually. - // Split comment text into lines func split(text []byte) [][]byte { // count lines (comment text never ends in a newline) @@ -680,7 +618,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro var last *ast.Comment for ; p.commentBefore(next); p.cindex++ { for _, c := range p.comments[p.cindex].List { - p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last == nil, tok.IsKeyword()) + p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, tok.IsKeyword()) p.writeComment(c) last = c } @@ -796,7 +734,6 @@ func (p *printer) print(args ...interface{}) { for _, f := range args { next := p.pos // estimated position of next item var data []byte - var tag HTMLTag var tok token.Token switch x := f.(type) { @@ -821,28 +758,31 @@ func (p *printer) print(args ...interface{}) { p.buffer = p.buffer[0 : i+1] p.buffer[i] = x case *ast.Ident: - if p.Styler != nil { - data, tag = p.Styler.Ident(x) - } else { - data = []byte(x.Name) - } + data = []byte(x.Name) tok = token.IDENT case *ast.BasicLit: - if p.Styler != nil { - data, tag = p.Styler.BasicLit(x) - } else { - data = x.Value - } // escape all literals so they pass through unchanged // (note that valid Go programs cannot contain // tabwriter.Escape bytes since they do not appear in // legal UTF-8 sequences) - escData := make([]byte, 0, len(data)+2) - escData = append(escData, tabwriter.Escape) - escData = append(escData, data...) - escData = append(escData, tabwriter.Escape) - data = escData + data = make([]byte, 0, len(x.Value)+2) + data = append(data, tabwriter.Escape) + data = append(data, x.Value...) + data = append(data, tabwriter.Escape) tok = x.Kind + // If we have a raw string that spans multiple lines and + // the opening quote (`) is on a line preceded only by + // indentation, we don't want to write that indentation + // because the following lines of the raw string are not + // indented. It's easiest to correct the output at the end + // via the trimmer (because of the complex handling of + // white space). + // Mark multi-line raw strings by replacing the opening + // quote with esc2 and have the trimmer take care of fixing + // it up. (Do this _after_ making a copy of data!) + if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 { + data[1] = esc2 + } case token.Token: s := x.String() if mayCombine(p.lastTok, s[0]) { @@ -858,11 +798,7 @@ func (p *printer) print(args ...interface{}) { p.buffer = p.buffer[0:1] p.buffer[0] = ' ' } - if p.Styler != nil { - data, tag = p.Styler.Token(x) - } else { - data = []byte(s) - } + data = []byte(s) tok = x case token.Pos: if x.IsValid() { @@ -885,7 +821,7 @@ func (p *printer) print(args ...interface{}) { // before p.writeNewlines(next.Line-p.pos.Line, droppedFF) - p.writeItem(next, data, tag) + p.writeItem(next, data) } } } @@ -927,21 +863,26 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { // through unchanged. // type trimmer struct { - output io.Writer - space bytes.Buffer - state int + output io.Writer + state int + space bytes.Buffer + hasText bool } // trimmer is implemented as a state machine. // It can be in one of the following states: const ( - inSpace = iota - inEscape - inText + inSpace = iota // inside space + atEscape // inside space and the last char was an opening tabwriter.Escape + inEscape // inside text bracketed by tabwriter.Escapes + inText // inside text ) +var backquote = []byte{'`'} + + // Design note: It is tempting to eliminate extra blanks occurring in // whitespace in this function as it could simplify some // of the blanks logic in the node printing functions. @@ -949,7 +890,13 @@ const ( // the tabwriter. func (p *trimmer) Write(data []byte) (n int, err os.Error) { - m := 0 // if p.state != inSpace, data[m:n] is unwritten + // invariants: + // p.state == inSpace, atEscape: + // p.space is unwritten + // p.hasText indicates if there is any text on this line + // p.state == inEscape, inText: + // data[m:n] is unwritten + m := 0 var b byte for n, b = range data { if b == '\v' { @@ -960,37 +907,55 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { switch b { case '\t', ' ': p.space.WriteByte(b) // WriteByte returns no errors - case '\f', '\n': + case '\n', '\f': p.space.Reset() // discard trailing space _, err = p.output.Write(newlines[0:1]) // write newline + p.hasText = false case tabwriter.Escape: - _, err = p.output.Write(p.space.Bytes()) - p.space.Reset() - p.state = inEscape - m = n + 1 // drop tabwriter.Escape + p.state = atEscape default: _, err = p.output.Write(p.space.Bytes()) - p.space.Reset() p.state = inText m = n } + case atEscape: + // discard indentation if we have a multi-line raw string + // (see printer.print for details) + if b != esc2 || p.hasText { + _, err = p.output.Write(p.space.Bytes()) + } + p.state = inEscape + m = n + if b == esc2 { + _, err = p.output.Write(backquote) // convert back + m++ + } case inEscape: if b == tabwriter.Escape { _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() + p.hasText = true } case inText: switch b { case '\t', ' ': _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() p.space.WriteByte(b) // WriteByte returns no errors - case '\f': - data[n] = '\n' // convert to newline + p.hasText = true + case '\n', '\f': + _, err = p.output.Write(data[m:n]) + p.state = inSpace + p.space.Reset() + _, err = p.output.Write(newlines[0:1]) // write newline + p.hasText = false case tabwriter.Escape: _, err = p.output.Write(data[m:n]) - p.state = inEscape - m = n + 1 // drop tabwriter.Escape + p.state = atEscape + p.space.Reset() + p.hasText = true } } if err != nil { @@ -999,9 +964,12 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { } n = len(data) - if p.state != inSpace { + switch p.state { + case inEscape, inText: _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() + p.hasText = true } return @@ -1013,36 +981,16 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { // General printing is controlled with these Config.Mode flags. const ( - GenHTML uint = 1 << iota // generate HTML - RawFormat // do not use a tabwriter; if set, UseSpaces is ignored + RawFormat uint = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored TabIndent // use tabs for indentation independent of UseSpaces UseSpaces // use spaces instead of tabs for alignment ) -// An HTMLTag specifies a start and end tag. -type HTMLTag struct { - Start, End string // empty if tags are absent -} - - -// A Styler specifies formatting of line tags and elementary Go words. -// A format consists of text and a (possibly empty) surrounding HTML tag. -// -type Styler interface { - LineTag(line int) ([]byte, HTMLTag) - Comment(c *ast.Comment, line []byte) ([]byte, HTMLTag) - BasicLit(x *ast.BasicLit) ([]byte, HTMLTag) - Ident(id *ast.Ident) ([]byte, HTMLTag) - Token(tok token.Token) ([]byte, HTMLTag) -} - - // A Config node controls the output of Fprint. type Config struct { - Mode uint // default: 0 - Tabwidth int // default: 8 - Styler Styler // default: nil + Mode uint // default: 0 + Tabwidth int // default: 8 } @@ -1070,9 +1018,6 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{ } twmode := tabwriter.DiscardEmptyColumns - if cfg.Mode&GenHTML != 0 { - twmode |= tabwriter.FilterHTML - } if cfg.Mode&TabIndent != 0 { minwidth = 0 twmode |= tabwriter.TabIndent diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index c66471b92..565075aa2 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -127,7 +127,7 @@ var data = []entry{ } -func Test(t *testing.T) { +func TestFiles(t *testing.T) { for _, e := range data { source := path.Join(dataDir, e.source) golden := path.Join(dataDir, e.golden) @@ -136,3 +136,38 @@ func Test(t *testing.T) { //check(t, golden, golden, e.mode); } } + + +// TestLineComments, using a simple test case, checks that consequtive line +// comments are properly terminated with a newline even if the AST position +// information is incorrect. +// +func TestLineComments(t *testing.T) { + const src = `// comment 1 + // comment 2 + // comment 3 + package main + ` + + fset := token.NewFileSet() + ast1, err1 := parser.ParseFile(fset, "", src, parser.ParseComments) + if err1 != nil { + panic(err1) + } + + var buf bytes.Buffer + fset = token.NewFileSet() // use the wrong file set + Fprint(&buf, fset, ast1) + + nlines := 0 + for _, ch := range buf.Bytes() { + if ch == '\n' { + nlines++ + } + } + + const expected = 3 + if nlines < expected { + t.Errorf("got %d, expected %d\n", nlines, expected) + } +} diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden index 882c7624c..7f18f338a 100644 --- a/src/pkg/go/printer/testdata/expressions.golden +++ b/src/pkg/go/printer/testdata/expressions.golden @@ -248,6 +248,77 @@ they must not be removed` func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + var _ = `` + var _ = `foo` + var _ = + // the next line should not be indented +`foo +bar` + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented +`foo +bar` + + var _ = /* comment */ `` + var _ = /* comment */ `foo` + var _ = /* comment */ `foo +bar` + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented +`foo +bar` + + var board = []int( +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + var state = S{ + "foo", + // the next line should not be indented +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } +} + + +func _() { // one-line function literals (body is on a single line) _ = func() {} _ = func() int { return 0 } diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input index 647706b09..6bcd9b5f8 100644 --- a/src/pkg/go/printer/testdata/expressions.input +++ b/src/pkg/go/printer/testdata/expressions.input @@ -244,6 +244,85 @@ they must not be removed` func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + +var _ = + `` +var _ = + `foo` +var _ = + // the next line should not be indented + `foo +bar` + + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented + `foo +bar` + + +var _ = /* comment */ `` +var _ = /* comment */ `foo` +var _ = /* comment */ `foo +bar` + + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented + `foo +bar` + + +var board = []int( + `........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + + var state = S{ + "foo", + // the next line should not be indented + `........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } +} + + +func _() { // one-line function literals (body is on a single line) _ = func() {} _ = func() int { return 0 } diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw index 62be00cc3..f1944c94b 100644 --- a/src/pkg/go/printer/testdata/expressions.raw +++ b/src/pkg/go/printer/testdata/expressions.raw @@ -243,7 +243,77 @@ func _() { _ = `foo bar` _ = `three spaces before the end of the line starting here: -they must not be removed` +they must not be removed`} + + +func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + var _ = `` + var _ = `foo` + var _ = + // the next line should not be indented +`foo +bar` + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented +`foo +bar` + + var _ = /* comment */ `` + var _ = /* comment */ `foo` + var _ = /* comment */ `foo +bar` + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented +`foo +bar` + + var board = []int( +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + var state = S{ + "foo", + // the next line should not be indented +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } } diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go index db8b96870..9667f6157 100644 --- a/src/pkg/gob/decode.go +++ b/src/pkg/gob/decode.go @@ -481,6 +481,19 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) { return nil } +func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) { + defer catchError(&err) + state := newDecodeState(dec, &dec.buf) + state.fieldnum = singletonField + delta := int(state.decodeUint()) + if delta != 0 { + errorf("gob decode: corrupted data: non-zero delta for singleton") + } + instr := &engine.instr[singletonField] + instr.op(instr, state, unsafe.Pointer(nil)) + return nil +} + func (dec *Decoder) decodeArrayHelper(state *decodeState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} for i := 0; i < length; i++ { @@ -653,8 +666,8 @@ func (dec *Decoder) ignoreInterface(state *decodeState) { if id < 0 { error(dec.err) } - // At this point, the decoder buffer contains the value. Just toss it. - state.b.Reset() + // At this point, the decoder buffer contains a delimited value. Just toss it. + state.b.Next(int(state.decodeUint())) } // Index by Go types. @@ -901,6 +914,16 @@ func (dec *Decoder) compileSingle(remoteId typeId, rt reflect.Type) (engine *dec return } +func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err os.Error) { + engine = new(decEngine) + engine.instr = make([]decInstr, 1) // one item + op := dec.decIgnoreOpFor(remoteId) + ovfl := overflow(dec.typeString(remoteId)) + engine.instr[0] = decInstr{op, 0, 0, 0, ovfl} + engine.numInstr = 1 + return +} + // Is this an exported - upper case - name? func isExported(name string) bool { rune, _ := utf8.DecodeRuneInString(name) @@ -984,7 +1007,12 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er // To handle recursive types, mark this engine as underway before compiling. enginePtr = new(*decEngine) dec.ignorerCache[wireId] = enginePtr - *enginePtr, err = dec.compileDec(wireId, emptyStructType) + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + *enginePtr, err = dec.compileDec(wireId, emptyStructType) + } else { + *enginePtr, err = dec.compileIgnoreSingle(wireId) + } if err != nil { dec.ignorerCache[wireId] = nil, false } @@ -993,6 +1021,10 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er } func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error { + // If the value is nil, it means we should just ignore this item. + if val == nil { + return dec.decodeIgnoredValue(wireId) + } // Dereference down to the underlying struct type. rt, indir := indirect(val.Type()) enginePtr, err := dec.getDecEnginePtr(wireId, rt) @@ -1010,6 +1042,18 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) os.Error { return dec.decodeSingle(engine, rt, uintptr(val.Addr()), indir) } +func (dec *Decoder) decodeIgnoredValue(wireId typeId) os.Error { + enginePtr, err := dec.getIgnoreEnginePtr(wireId) + if err != nil { + return err + } + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + return dec.ignoreStruct(*enginePtr) + } + return dec.ignoreSingle(*enginePtr) +} + func init() { var iop, uop decOp switch reflect.Typeof(int(0)).Bits() { diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go index 7527c5f1f..f7c994ffa 100644 --- a/src/pkg/gob/decoder.go +++ b/src/pkg/gob/decoder.go @@ -153,9 +153,13 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { // Decode reads the next value from the connection and stores // it in the data represented by the empty interface value. -// The value underlying e must be the correct type for the next +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must either be the correct type for the next // data item received, and must be a pointer. func (dec *Decoder) Decode(e interface{}) os.Error { + if e == nil { + return dec.DecodeValue(nil) + } value := reflect.NewValue(e) // If e represents a value as opposed to a pointer, the answer won't // get back to the caller. Make sure it's a pointer. @@ -169,7 +173,8 @@ func (dec *Decoder) Decode(e interface{}) os.Error { // DecodeValue reads the next value from the connection and stores // it in the data represented by the reflection value. // The value must be the correct type for the next -// data item received. +// data item received, or it may be nil, which means the +// value will be discarded. func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { // Make sure we're single-threaded through here. dec.mutex.Lock() @@ -178,7 +183,7 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { dec.buf.Reset() // In case data lingers from previous invocation. dec.err = nil id := dec.decodeTypeSequence(false) - if id >= 0 { + if dec.err == nil { dec.err = dec.decodeValue(id, value) } return dec.err diff --git a/src/pkg/gob/encoder_test.go b/src/pkg/gob/encoder_test.go index 1456ca00c..3e06db727 100644 --- a/src/pkg/gob/encoder_test.go +++ b/src/pkg/gob/encoder_test.go @@ -6,6 +6,7 @@ package gob import ( "bytes" + "fmt" "io" "os" "reflect" @@ -120,7 +121,7 @@ func corruptDataCheck(s string, err os.Error, t *testing.T) { dec := NewDecoder(b) err1 := dec.Decode(new(ET2)) if err1 != err { - t.Error("expected error", err, "got", err1) + t.Errorf("from %q expected error %s; got %s", s, err, err1) } } @@ -384,6 +385,75 @@ func TestInterfaceIndirect(t *testing.T) { } } +// Now follow various tests that decode into things that can't represent the +// encoded value, all of which should be legal. + +// Also, when the ignored object contains an interface value, it may define +// types. Make sure that skipping the value still defines the types by using +// the encoder/decoder pair to send a value afterwards. If an interface +// is sent, its type in the test is always NewType0, so this checks that the +// encoder and decoder don't skew with respect to type definitions. + +type Struct0 struct { + I interface{} +} + +type NewType0 struct { + S string +} + +type ignoreTest struct { + in, out interface{} +} + +var ignoreTests = []ignoreTest{ + // Decode normal struct into an empty struct + {&struct{ A int }{23}, &struct{}{}}, + // Decode normal struct into a nil. + {&struct{ A int }{23}, nil}, + // Decode singleton string into a nil. + {"hello, world", nil}, + // Decode singleton slice into a nil. + {[]int{1, 2, 3, 4}, nil}, + // Decode struct containing an interface into a nil. + {&Struct0{&NewType0{"value0"}}, nil}, + // Decode singleton slice of interfaces into a nil. + {[]interface{}{"hi", &NewType0{"value1"}, 23}, nil}, +} + +func TestDecodeIntoNothing(t *testing.T) { + Register(new(NewType0)) + for i, test := range ignoreTests { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(test.in) + if err != nil { + t.Errorf("%d: encode error %s:", i, err) + continue + } + dec := NewDecoder(b) + err = dec.Decode(test.out) + if err != nil { + t.Errorf("%d: decode error: %s", i, err) + continue + } + // Now see if the encoder and decoder are in a consistent state. + str := fmt.Sprintf("Value %d", i) + err = enc.Encode(&NewType0{str}) + if err != nil { + t.Fatalf("%d: NewType0 encode error: %s", i, err) + } + ns := new(NewType0) + err = dec.Decode(ns) + if err != nil { + t.Fatalf("%d: NewType0 decode error: %s", i, err) + } + if ns.S != str { + t.Fatalf("%d: expected %q got %q", i, str, ns.S) + } + } +} + // Another bug from golang-nuts, involving nested interfaces. type Bug0Outer struct { Bug0Field interface{} diff --git a/src/pkg/http/persist.go b/src/pkg/http/persist.go index 8bfc09755..000a4200e 100644 --- a/src/pkg/http/persist.go +++ b/src/pkg/http/persist.go @@ -6,14 +6,17 @@ package http import ( "bufio" - "container/list" "io" "net" + "net/textproto" "os" "sync" ) -var ErrPersistEOF = &ProtocolError{"persistent connection closed"} +var ( + ErrPersistEOF = &ProtocolError{"persistent connection closed"} + ErrPipeline = &ProtocolError{"pipeline error"} +) // A ServerConn reads requests and sends responses over an underlying // connection, until the HTTP keepalive logic commands an end. ServerConn @@ -26,8 +29,10 @@ type ServerConn struct { r *bufio.Reader clsd bool // indicates a graceful close re, we os.Error // read/write errors - lastBody io.ReadCloser + lastbody io.ReadCloser nread, nwritten int + pipe textproto.Pipeline + pipereq map[*Request]uint lk sync.Mutex // protected read/write to re,we } @@ -37,7 +42,7 @@ func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { if r == nil { r = bufio.NewReader(c) } - return &ServerConn{c: c, r: r} + return &ServerConn{c: c, r: r, pipereq: make(map[*Request]uint)} } // Close detaches the ServerConn and returns the underlying connection as well @@ -57,10 +62,25 @@ func (sc *ServerConn) Close() (c net.Conn, r *bufio.Reader) { // Read returns the next request on the wire. An ErrPersistEOF is returned if // it is gracefully determined that there are no more requests (e.g. after the // first request on an HTTP/1.0 connection, or after a Connection:close on a -// HTTP/1.1 connection). Read can be called concurrently with Write, but not -// with another Read. +// HTTP/1.1 connection). func (sc *ServerConn) Read() (req *Request, err os.Error) { + // Ensure ordered execution of Reads and Writes + id := sc.pipe.Next() + sc.pipe.StartRequest(id) + defer func() { + sc.pipe.EndRequest(id) + if req == nil { + sc.pipe.StartResponse(id) + sc.pipe.EndResponse(id) + } else { + // Remember the pipeline id of this request + sc.lk.Lock() + sc.pipereq[req] = id + sc.lk.Unlock() + } + }() + sc.lk.Lock() if sc.we != nil { // no point receiving if write-side broken or closed defer sc.lk.Unlock() @@ -73,12 +93,12 @@ func (sc *ServerConn) Read() (req *Request, err os.Error) { sc.lk.Unlock() // Make sure body is fully consumed, even if user does not call body.Close - if sc.lastBody != nil { + if sc.lastbody != nil { // body.Close is assumed to be idempotent and multiple calls to // it should return the error that its first invokation // returned. - err = sc.lastBody.Close() - sc.lastBody = nil + err = sc.lastbody.Close() + sc.lastbody = nil if err != nil { sc.lk.Lock() defer sc.lk.Unlock() @@ -102,7 +122,7 @@ func (sc *ServerConn) Read() (req *Request, err os.Error) { return } } - sc.lastBody = req.Body + sc.lastbody = req.Body sc.nread++ if req.Close { sc.lk.Lock() @@ -121,11 +141,24 @@ func (sc *ServerConn) Pending() int { return sc.nread - sc.nwritten } -// Write writes a repsonse. To close the connection gracefully, set the +// Write writes resp in response to req. To close the connection gracefully, set the // Response.Close field to true. Write should be considered operational until // it returns an error, regardless of any errors returned on the Read side. -// Write can be called concurrently with Read, but not with another Write. -func (sc *ServerConn) Write(resp *Response) os.Error { +func (sc *ServerConn) Write(req *Request, resp *Response) os.Error { + + // Retrieve the pipeline ID of this request/response pair + sc.lk.Lock() + id, ok := sc.pipereq[req] + sc.pipereq[req] = 0, false + if !ok { + sc.lk.Unlock() + return ErrPipeline + } + sc.lk.Unlock() + + // Ensure pipeline order + sc.pipe.StartResponse(id) + defer sc.pipe.EndResponse(id) sc.lk.Lock() if sc.we != nil { @@ -166,10 +199,11 @@ type ClientConn struct { c net.Conn r *bufio.Reader re, we os.Error // read/write errors - lastBody io.ReadCloser + lastbody io.ReadCloser nread, nwritten int - reqm list.List // request methods in order of execution - lk sync.Mutex // protects read/write to reqm,re,we + pipe textproto.Pipeline + pipereq map[*Request]uint + lk sync.Mutex // protects read/write to re,we,pipereq,etc. } // NewClientConn returns a new ClientConn reading and writing c. If r is not @@ -178,7 +212,7 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { if r == nil { r = bufio.NewReader(c) } - return &ClientConn{c: c, r: r} + return &ClientConn{c: c, r: r, pipereq: make(map[*Request]uint)} } // Close detaches the ClientConn and returns the underlying connection as well @@ -191,7 +225,6 @@ func (cc *ClientConn) Close() (c net.Conn, r *bufio.Reader) { r = cc.r cc.c = nil cc.r = nil - cc.reqm.Init() cc.lk.Unlock() return } @@ -201,8 +234,23 @@ func (cc *ClientConn) Close() (c net.Conn, r *bufio.Reader) { // keepalive connection is logically closed after this request and the opposing // server is informed. An ErrUnexpectedEOF indicates the remote closed the // underlying TCP connection, which is usually considered as graceful close. -// Write can be called concurrently with Read, but not with another Write. -func (cc *ClientConn) Write(req *Request) os.Error { +func (cc *ClientConn) Write(req *Request) (err os.Error) { + + // Ensure ordered execution of Writes + id := cc.pipe.Next() + cc.pipe.StartRequest(id) + defer func() { + cc.pipe.EndRequest(id) + if err != nil { + cc.pipe.StartResponse(id) + cc.pipe.EndResponse(id) + } else { + // Remember the pipeline id of this request + cc.lk.Lock() + cc.pipereq[req] = id + cc.lk.Unlock() + } + }() cc.lk.Lock() if cc.re != nil { // no point sending if read-side closed or broken @@ -223,7 +271,7 @@ func (cc *ClientConn) Write(req *Request) os.Error { cc.lk.Unlock() } - err := req.Write(cc.c) + err = req.Write(cc.c) if err != nil { cc.lk.Lock() defer cc.lk.Unlock() @@ -231,9 +279,6 @@ func (cc *ClientConn) Write(req *Request) os.Error { return err } cc.nwritten++ - cc.lk.Lock() - cc.reqm.PushBack(req.Method) - cc.lk.Unlock() return nil } @@ -250,7 +295,21 @@ func (cc *ClientConn) Pending() int { // returned together with an ErrPersistEOF, which means that the remote // requested that this be the last request serviced. Read can be called // concurrently with Write, but not with another Read. -func (cc *ClientConn) Read() (resp *Response, err os.Error) { +func (cc *ClientConn) Read(req *Request) (resp *Response, err os.Error) { + + // Retrieve the pipeline ID of this request/response pair + cc.lk.Lock() + id, ok := cc.pipereq[req] + cc.pipereq[req] = 0, false + if !ok { + cc.lk.Unlock() + return nil, ErrPipeline + } + cc.lk.Unlock() + + // Ensure pipeline order + cc.pipe.StartResponse(id) + defer cc.pipe.EndResponse(id) cc.lk.Lock() if cc.re != nil { @@ -259,17 +318,13 @@ func (cc *ClientConn) Read() (resp *Response, err os.Error) { } cc.lk.Unlock() - if cc.nread >= cc.nwritten { - return nil, os.NewError("persist client pipe count") - } - // Make sure body is fully consumed, even if user does not call body.Close - if cc.lastBody != nil { + if cc.lastbody != nil { // body.Close is assumed to be idempotent and multiple calls to // it should return the error that its first invokation // returned. - err = cc.lastBody.Close() - cc.lastBody = nil + err = cc.lastbody.Close() + cc.lastbody = nil if err != nil { cc.lk.Lock() defer cc.lk.Unlock() @@ -278,18 +333,14 @@ func (cc *ClientConn) Read() (resp *Response, err os.Error) { } } - cc.lk.Lock() - m := cc.reqm.Front() - cc.reqm.Remove(m) - cc.lk.Unlock() - resp, err = ReadResponse(cc.r, m.Value.(string)) + resp, err = ReadResponse(cc.r, req.Method) if err != nil { cc.lk.Lock() defer cc.lk.Unlock() cc.re = err return } - cc.lastBody = resp.Body + cc.lastbody = resp.Body cc.nread++ @@ -301,3 +352,12 @@ func (cc *ClientConn) Read() (resp *Response, err os.Error) { } return } + +// Do is convenience method that writes a request and reads a response. +func (cc *ClientConn) Do(req *Request) (resp *Response, err os.Error) { + err = cc.Write(req) + if err != nil { + return + } + return cc.Read(req) +} diff --git a/src/pkg/http/response_test.go b/src/pkg/http/response_test.go index 89a8c3b44..11bfdd08c 100644 --- a/src/pkg/http/response_test.go +++ b/src/pkg/http/response_test.go @@ -44,6 +44,47 @@ var respTests = []respTest{ "Body here\n", }, + // Unchunked HTTP/1.1 response without Content-Length or + // Connection headers. + { + "HTTP/1.1 200 OK\r\n" + + "\r\n" + + "Body here\n", + + Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + RequestMethod: "GET", + Close: true, + ContentLength: -1, + }, + + "Body here\n", + }, + + // Unchunked HTTP/1.1 204 response without Content-Length. + { + "HTTP/1.1 204 No Content\r\n" + + "\r\n" + + "Body should not be read!\n", + + Response{ + Status: "204 No Content", + StatusCode: 204, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + RequestMethod: "GET", + Close: false, + ContentLength: 0, + }, + + "", + }, + // Unchunked response with Content-Length. { "HTTP/1.0 200 OK\r\n" + diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index 7da3fc6f3..5594d512a 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -9,10 +9,13 @@ package http import ( "bufio" "bytes" + "fmt" "io" + "io/ioutil" "os" "net" "testing" + "time" ) type dummyAddr string @@ -189,7 +192,7 @@ func TestHostHandlers(t *testing.T) { t.Errorf("writing request: %v", err) continue } - r, err := cc.Read() + r, err := cc.Read(&req) if err != nil { t.Errorf("reading response: %v", err) continue @@ -283,3 +286,66 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) { } } } + +func TestServerTimeouts(t *testing.T) { + l, err := net.ListenTCP("tcp", &net.TCPAddr{Port: 0}) + if err != nil { + t.Fatalf("listen error: %v", err) + } + addr, _ := l.Addr().(*net.TCPAddr) + + reqNum := 0 + handler := HandlerFunc(func(res ResponseWriter, req *Request) { + reqNum++ + fmt.Fprintf(res, "req=%d", reqNum) + }) + + const second = 1000000000 /* nanos */ + server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second} + go server.Serve(l) + + url := fmt.Sprintf("http://localhost:%d/", addr.Port) + + // Hit the HTTP server successfully. + r, _, err := Get(url) + if err != nil { + t.Fatalf("http Get #1: %v", err) + } + got, _ := ioutil.ReadAll(r.Body) + expected := "req=1" + if string(got) != expected { + t.Errorf("Unexpected response for request #1; got %q; expected %q", + string(got), expected) + } + + // Slow client that should timeout. + t1 := time.Nanoseconds() + conn, err := net.Dial("tcp", "", fmt.Sprintf("localhost:%d", addr.Port)) + if err != nil { + t.Fatalf("Dial: %v", err) + } + buf := make([]byte, 1) + n, err := conn.Read(buf) + latency := time.Nanoseconds() - t1 + if n != 0 || err != os.EOF { + t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, os.EOF) + } + if latency < second*0.20 /* fudge from 0.25 above */ { + t.Errorf("got EOF after %d ns, want >= %d", latency, second*0.20) + } + + // Hit the HTTP server successfully again, verifying that the + // previous slow connection didn't run our handler. (that we + // get "req=2", not "req=3") + r, _, err = Get(url) + if err != nil { + t.Fatalf("http Get #2: %v", err) + } + got, _ = ioutil.ReadAll(r.Body) + expected = "req=2" + if string(got) != expected { + t.Errorf("Get #2 got %q, want %q", string(got), expected) + } + + l.Close() +} diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 6672c494b..0be270ad3 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -670,6 +670,39 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { // read requests and then call handler to reply to them. // Handler is typically nil, in which case the DefaultServeMux is used. func Serve(l net.Listener, handler Handler) os.Error { + srv := &Server{Handler: handler} + return srv.Serve(l) +} + +// A Server defines parameters for running an HTTP server. +type Server struct { + Addr string // TCP address to listen on, ":http" if empty + Handler Handler // handler to invoke, http.DefaultServeMux if nil + ReadTimeout int64 // the net.Conn.SetReadTimeout value for new connections + WriteTimeout int64 // the net.Conn.SetWriteTimeout value for new connections +} + +// ListenAndServe listens on the TCP network address srv.Addr and then +// calls Serve to handle requests on incoming connections. If +// srv.Addr is blank, ":http" is used. +func (srv *Server) ListenAndServe() os.Error { + addr := srv.Addr + if addr == "" { + addr = ":http" + } + l, e := net.Listen("tcp", addr) + if e != nil { + return e + } + return srv.Serve(l) +} + +// Serve accepts incoming connections on the Listener l, creating a +// new service thread for each. The service threads read requests and +// then call srv.Handler to reply to them. +func (srv *Server) Serve(l net.Listener) os.Error { + defer l.Close() + handler := srv.Handler if handler == nil { handler = DefaultServeMux } @@ -678,6 +711,12 @@ func Serve(l net.Listener, handler Handler) os.Error { if e != nil { return e } + if srv.ReadTimeout != 0 { + rw.SetReadTimeout(srv.ReadTimeout) + } + if srv.WriteTimeout != 0 { + rw.SetWriteTimeout(srv.WriteTimeout) + } c, err := newConn(rw, handler) if err != nil { continue @@ -715,13 +754,8 @@ func Serve(l net.Listener, handler Handler) os.Error { // } // } func ListenAndServe(addr string, handler Handler) os.Error { - l, e := net.Listen("tcp", addr) - if e != nil { - return e - } - e = Serve(l, handler) - l.Close() - return e + server := &Server{Addr: addr, Handler: handler} + return server.ListenAndServe() } // ListenAndServeTLS acts identically to ListenAndServe, except that it diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index e62885d62..f80f0ac63 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -172,6 +172,20 @@ type transferReader struct { Trailer map[string]string } +// bodyAllowedForStatus returns whether a given response status code +// permits a body. See RFC2616, section 4.4. +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == 204: + return false + case status == 304: + return false + } + return true +} + // msg is *Request or *Response. func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { t := &transferReader{} @@ -217,6 +231,19 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { return err } + // If there is no Content-Length or chunked Transfer-Encoding on a *Response + // and the status is not 1xx, 204 or 304, then the body is unbounded. + // See RFC2616, section 4.4. + switch msg.(type) { + case *Response: + if t.ContentLength == -1 && + !chunked(t.TransferEncoding) && + bodyAllowedForStatus(t.StatusCode) { + // Unbounded body. + t.Close = true + } + } + // Prepare body reader. ContentLength < 0 means chunked encoding // or close connection when finished, since multipart is not supported yet switch { diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go index 03cfafa7b..52d521d3d 100644 --- a/src/pkg/http/triv.go +++ b/src/pkg/http/triv.go @@ -99,15 +99,16 @@ func DateServer(rw http.ResponseWriter, req *http.Request) { fmt.Fprintf(rw, "pipe: %s\n", err) return } - pid, err := os.ForkExec("/bin/date", []string{"date"}, os.Environ(), "", []*os.File{nil, w, w}) + p, err := os.StartProcess("/bin/date", []string{"date"}, os.Environ(), "", []*os.File{nil, w, w}) defer r.Close() w.Close() if err != nil { fmt.Fprintf(rw, "fork/exec: %s\n", err) return } + defer p.Release() io.Copy(rw, r) - wait, err := os.Wait(pid, 0) + wait, err := p.Wait(0) if err != nil { fmt.Fprintf(rw, "wait: %s\n", err) return diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go index 1a6eca95a..3b8791897 100644 --- a/src/pkg/io/io.go +++ b/src/pkg/io/io.go @@ -150,14 +150,23 @@ type WriterAt interface { WriteAt(p []byte, off int64) (n int, err os.Error) } -// ReadByter is the interface that wraps the ReadByte method. +// ByteReader is the interface that wraps the ReadByte method. // // ReadByte reads and returns the next byte from the input. // If no byte is available, err will be set. -type ReadByter interface { +type ByteReader interface { ReadByte() (c byte, err os.Error) } +// RuneReader is the interface that wraps the ReadRune method. +// +// ReadRune reads a single UTF-8 encoded Unicode character +// and returns the rune and its size in bytes. If no character is +// available, err will be set. +type RuneReader interface { + ReadRune() (rune int, size int, err os.Error) +} + // WriteString writes the contents of the string s to w, which accepts an array of bytes. func WriteString(w Writer, s string) (n int, err os.Error) { return w.Write([]byte(s)) diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go index 759b49dbe..0fcc78aa8 100644 --- a/src/pkg/json/encode.go +++ b/src/pkg/json/encode.go @@ -35,9 +35,8 @@ import ( // // Struct values encode as JSON objects. Each struct field becomes // a member of the object. By default the object's key name is the -// struct field name converted to lower case. If the struct field -// has a tag, that tag will be used as the name instead. -// Only exported fields will be encoded. +// struct field name. If the struct field has a tag, that tag will +// be used as the name instead. Only exported fields will be encoded. // // Map values encode as JSON objects. // The map's key type must be string; the object keys are used directly diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 9b91eb398..d9c83831d 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -85,7 +85,7 @@ type ioPacket struct { func (s *pollServer) getCompletedIO() (ov *syscall.Overlapped, result *ioResult, err os.Error) { var r ioResult var o *syscall.Overlapped - _, e := syscall.GetQueuedCompletionStatus(s.iocp, &r.qty, &r.key, &o, syscall.INFINITE) + e := syscall.GetQueuedCompletionStatus(s.iocp, &r.qty, &r.key, &o, syscall.INFINITE) switch { case e == 0: // Dequeued successfully completed io packet. @@ -270,7 +270,7 @@ func timeoutIO() { case writeto: e = syscall.WSASendto(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, 0, *o.sa, &o.pckt.o, nil) case cancel: - _, e = syscall.CancelIo(uint32(o.fd.sysfd)) + e = syscall.CancelIo(uint32(o.fd.sysfd)) } o.c <- e } diff --git a/src/pkg/netchan/export.go b/src/pkg/netchan/export.go index 0b28536ed..675e252d5 100644 --- a/src/pkg/netchan/export.go +++ b/src/pkg/netchan/export.go @@ -118,7 +118,9 @@ func (client *expClient) run() { for { *hdr = header{} if err := client.decode(hdrValue); err != nil { - expLog("error decoding client header:", err) + if err != os.EOF { + expLog("error decoding client header:", err) + } break } switch hdr.PayloadType { diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile index f6caf084c..3a81afe39 100644 --- a/src/pkg/os/Makefile +++ b/src/pkg/os/Makefile @@ -22,21 +22,25 @@ GOFILES_freebsd=\ env_unix.go\ file_unix.go\ sys_bsd.go\ + exec_unix.go\ GOFILES_darwin=\ env_unix.go\ file_unix.go\ sys_bsd.go\ + exec_unix.go\ GOFILES_linux=\ env_unix.go\ file_unix.go\ sys_linux.go\ + exec_unix.go\ GOFILES_windows=\ env_windows.go\ file_windows.go\ sys_windows.go\ + exec_windows.go\ GOFILES+=$(GOFILES_$(GOOS)) diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go index d2b159dfb..a45d79be3 100644 --- a/src/pkg/os/env_windows.go +++ b/src/pkg/os/env_windows.go @@ -50,8 +50,8 @@ func Setenv(key, value string) Error { if len(value) > 0 { v = syscall.StringToUTF16Ptr(value) } - ok, e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v) - if !ok { + e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v) + if e != 0 { return NewSyscallError("SetEnvironmentVariable", e) } return nil diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index 100d984d1..dbdfacc58 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -5,17 +5,29 @@ package os import ( + "runtime" "syscall" ) -// ForkExec forks the current process and invokes Exec with the program, arguments, -// and environment specified by name, argv, and envv. It returns the process -// id of the forked process and an Error, if any. The fd array specifies the +// Process stores the information about a process created by StartProcess. +type Process struct { + Pid int + handle int +} + +func newProcess(pid, handle int) *Process { + p := &Process{pid, handle} + runtime.SetFinalizer(p, (*Process).Release) + return p +} + +// StartProcess starts a new process with the program, arguments, +// and environment specified by name, argv, and envv. The fd array specifies the // file descriptors to be set up in the new process: fd[0] will be Unix file // descriptor 0 (standard input), fd[1] descriptor 1, and so on. A nil entry // will cause the child to have no open file descriptor with that index. // If dir is not empty, the child chdirs into the directory before execing the program. -func ForkExec(name string, argv []string, envv []string, dir string, fd []*File) (pid int, err Error) { +func StartProcess(name string, argv []string, envv []string, dir string, fd []*File) (p *Process, err Error) { if envv == nil { envv = Environ() } @@ -29,17 +41,17 @@ func ForkExec(name string, argv []string, envv []string, dir string, fd []*File) } } - p, e := syscall.ForkExec(name, argv, envv, dir, intfd) + pid, h, e := syscall.StartProcess(name, argv, envv, dir, intfd) if e != 0 { - return 0, &PathError{"fork/exec", name, Errno(e)} + return nil, &PathError{"fork/exec", name, Errno(e)} } - return p, nil + return newProcess(pid, h), nil } // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. -// ForkExec is almost always a better way to execute a program. +// StartProcess is almost always a better way to execute a program. func Exec(name string, argv []string, envv []string) Error { if envv == nil { envv = Environ() @@ -65,37 +77,18 @@ type Waitmsg struct { Rusage *syscall.Rusage // System-dependent resource usage info. } -// Options for Wait. -const ( - WNOHANG = syscall.WNOHANG // Don't wait if no process has exited. - WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported. - WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED. - WRUSAGE = 1 << 20 // Record resource usage. -) - -// WRUSAGE must not be too high a bit, to avoid clashing with Linux's -// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of -// the options - // Wait waits for process pid to exit or stop, and then returns a // Waitmsg describing its status and an Error, if any. The options // (WNOHANG etc.) affect the behavior of the Wait call. +// Wait is equivalent to calling FindProcess and then Wait +// and Release on the result. func Wait(pid int, options int) (w *Waitmsg, err Error) { - var status syscall.WaitStatus - var rusage *syscall.Rusage - if options&WRUSAGE != 0 { - rusage = new(syscall.Rusage) - options ^= WRUSAGE - } - pid1, e := syscall.Wait4(pid, &status, options, rusage) - if e != 0 { - return nil, NewSyscallError("wait", e) + p, e := FindProcess(pid) + if e != nil { + return nil, e } - w = new(Waitmsg) - w.Pid = pid1 - w.WaitStatus = status - w.Rusage = rusage - return w, nil + defer p.Release() + return p.Wait(options) } // Convert i to decimal string. diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go new file mode 100644 index 000000000..8990d6a97 --- /dev/null +++ b/src/pkg/os/exec_unix.go @@ -0,0 +1,63 @@ +// 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 os + +import ( + "runtime" + "syscall" +) + +// Options for Wait. +const ( + WNOHANG = syscall.WNOHANG // Don't wait if no process has exited. + WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported. + WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED. + WRUSAGE = 1 << 20 // Record resource usage. +) + +// WRUSAGE must not be too high a bit, to avoid clashing with Linux's +// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of +// the options + +// Wait waits for the Process to exit or stop, and then returns a +// Waitmsg describing its status and an Error, if any. The options +// (WNOHANG etc.) affect the behavior of the Wait call. +func (p *Process) Wait(options int) (w *Waitmsg, err Error) { + if p.Pid == -1 { + return nil, EINVAL + } + var status syscall.WaitStatus + var rusage *syscall.Rusage + if options&WRUSAGE != 0 { + rusage = new(syscall.Rusage) + options ^= WRUSAGE + } + pid1, e := syscall.Wait4(p.Pid, &status, options, rusage) + if e != 0 { + return nil, NewSyscallError("wait", e) + } + w = new(Waitmsg) + w.Pid = pid1 + w.WaitStatus = status + w.Rusage = rusage + return w, nil +} + +// Release releases any resources associated with the Process. +func (p *Process) Release() Error { + // NOOP for unix. + p.Pid = -1 + // no need for a finalizer anymore + runtime.SetFinalizer(p, nil) + return nil +} + +// FindProcess looks for a running process by its pid. +// The Process it returns can be used to obtain information +// about the underlying operating system process. +func FindProcess(pid int) (p *Process, err Error) { + // NOOP for unix. + return newProcess(pid, 0), nil +} diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go new file mode 100644 index 000000000..ae8ffeab2 --- /dev/null +++ b/src/pkg/os/exec_windows.go @@ -0,0 +1,52 @@ +// 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 os + +import ( + "runtime" + "syscall" +) + +func (p *Process) Wait(options int) (w *Waitmsg, err Error) { + s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE) + switch s { + case syscall.WAIT_OBJECT_0: + break + case syscall.WAIT_FAILED: + return nil, NewSyscallError("WaitForSingleObject", e) + default: + return nil, ErrorString("os: unexpected result from WaitForSingleObject") + } + var ec uint32 + e = syscall.GetExitCodeProcess(uint32(p.handle), &ec) + if e != 0 { + return nil, NewSyscallError("GetExitCodeProcess", e) + } + return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil +} + +func (p *Process) Release() Error { + if p.handle == -1 { + return EINVAL + } + e := syscall.CloseHandle(int32(p.handle)) + if e != 0 { + return NewSyscallError("CloseHandle", e) + } + p.handle = -1 + // no need for a finalizer anymore + runtime.SetFinalizer(p, nil) + return nil +} + +func FindProcess(pid int) (p *Process, err Error) { + const da = syscall.STANDARD_RIGHTS_READ | + syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE + h, e := syscall.OpenProcess(da, false, uint32(pid)) + if e != 0 { + return nil, NewSyscallError("OpenProcess", e) + } + return newProcess(pid, int(h)), nil +} diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index bf710bb67..d14c38e17 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -83,9 +83,9 @@ func (file *File) Close() Error { } var e int if file.isdir() { - _, e = syscall.FindClose(int32(file.fd)) + e = syscall.FindClose(int32(file.fd)) } else { - _, e = syscall.CloseHandle(int32(file.fd)) + e = syscall.CloseHandle(int32(file.fd)) } var err Error if e != 0 { @@ -100,7 +100,8 @@ func (file *File) Close() Error { func (file *File) statFile(name string) (fi *FileInfo, err Error) { var stat syscall.ByHandleFileInformation - if ok, e := syscall.GetFileInformationByHandle(int32(file.fd), &stat); !ok { + e := syscall.GetFileInformationByHandle(int32(file.fd), &stat) + if e != 0 { return nil, &PathError{"stat", file.name, Errno(e)} } return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil @@ -142,7 +143,7 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) { if di.usefirststat { di.usefirststat = false } else { - _, e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata) + e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata) if e != 0 { if e == syscall.ERROR_NO_MORE_FILES { break diff --git a/src/pkg/os/inotify/inotify_linux.go b/src/pkg/os/inotify/inotify_linux.go index 9d7a07442..96c229e7b 100644 --- a/src/pkg/os/inotify/inotify_linux.go +++ b/src/pkg/os/inotify/inotify_linux.go @@ -8,11 +8,11 @@ This package implements a wrapper for the Linux inotify system. Example: watcher, err := inotify.NewWatcher() if err != nil { - log.Exit(err) + log.Fatal(err) } err = watcher.Watch("/tmp") if err != nil { - log.Exit(err) + log.Fatal(err) } for { select { diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go index 49b58c83c..2ea8acdc4 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -427,10 +427,11 @@ func TestForkExec(t *testing.T) { adir = "/" expect = "/\n" } - pid, err := ForkExec(cmd, args, nil, adir, []*File{nil, w, Stderr}) + p, err := StartProcess(cmd, args, nil, adir, []*File{nil, w, Stderr}) if err != nil { - t.Fatalf("ForkExec: %v", err) + t.Fatalf("StartProcess: %v", err) } + defer p.Release() w.Close() var b bytes.Buffer @@ -440,7 +441,7 @@ func TestForkExec(t *testing.T) { args[0] = cmd t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect) } - Wait(pid, 0) + p.Wait(0) } func checkMode(t *testing.T, path string, mode uint32) { @@ -750,15 +751,16 @@ func run(t *testing.T, cmd []string) string { if err != nil { t.Fatal(err) } - pid, err := ForkExec("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr}) + p, err := StartProcess("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr}) if err != nil { t.Fatal(err) } + defer p.Release() w.Close() var b bytes.Buffer io.Copy(&b, r) - Wait(pid, 0) + p.Wait(0) output := b.String() if n := len(output); n > 0 && output[n-1] == '\n' { output = output[0 : n-1] diff --git a/src/pkg/regexp/all_test.go b/src/pkg/regexp/all_test.go index aed733064..c7ee4c879 100644 --- a/src/pkg/regexp/all_test.go +++ b/src/pkg/regexp/all_test.go @@ -316,9 +316,9 @@ func TestNumSubexp(t *testing.T) { } func BenchmarkLiteral(b *testing.B) { - x := strings.Repeat("x", 50) + x := strings.Repeat("x", 50) + "y" b.StopTimer() - re := MustCompile(x) + re := MustCompile("y") b.StartTimer() for i := 0; i < b.N; i++ { if !re.MatchString(x) { @@ -329,9 +329,9 @@ func BenchmarkLiteral(b *testing.B) { } func BenchmarkNotLiteral(b *testing.B) { - x := strings.Repeat("x", 49) + x := strings.Repeat("x", 50) + "y" b.StopTimer() - re := MustCompile("^" + x) + re := MustCompile(".y") b.StartTimer() for i := 0; i < b.N; i++ { if !re.MatchString(x) { diff --git a/src/pkg/regexp/find_test.go b/src/pkg/regexp/find_test.go index 1690711dd..83b249e3c 100644 --- a/src/pkg/regexp/find_test.go +++ b/src/pkg/regexp/find_test.go @@ -6,6 +6,7 @@ package regexp import ( "fmt" + "strings" "testing" ) @@ -191,6 +192,12 @@ func TestFindStringIndex(t *testing.T) { } } +func TestFindReaderIndex(t *testing.T) { + for _, test := range findTests { + testFindIndex(&test, MustCompile(test.pat).FindReaderIndex(strings.NewReader(test.text)), t) + } +} + // Now come the simple All cases. func TestFindAll(t *testing.T) { @@ -381,12 +388,18 @@ func TestFindSubmatchIndex(t *testing.T) { } } -func TestFindStringSubmatchndex(t *testing.T) { +func TestFindStringSubmatchIndex(t *testing.T) { for _, test := range findTests { testFindSubmatchIndex(&test, MustCompile(test.pat).FindStringSubmatchIndex(test.text), t) } } +func TestFindReaderSubmatchIndex(t *testing.T) { + for _, test := range findTests { + testFindSubmatchIndex(&test, MustCompile(test.pat).FindReaderSubmatchIndex(strings.NewReader(test.text)), t) + } +} + // Now come the monster AllSubmatch cases. func TestFindAllSubmatch(t *testing.T) { @@ -452,7 +465,7 @@ func TestFindAllSubmatchIndex(t *testing.T) { } } -func TestFindAllStringSubmatchndex(t *testing.T) { +func TestFindAllStringSubmatchIndex(t *testing.T) { for _, test := range findTests { testFindAllSubmatchIndex(&test, MustCompile(test.pat).FindAllStringSubmatchIndex(test.text, -1), t) } diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go index d274ccdf5..e3221ac9d 100644 --- a/src/pkg/regexp/regexp.go +++ b/src/pkg/regexp/regexp.go @@ -54,6 +54,16 @@ // text of the match/submatch. If an index is negative, it means that // subexpression did not match any string in the input. // +// There is also a subset of the methods that can be applied to text read +// from a RuneReader: +// +// MatchReader, FindReaderIndex, FindReaderSubmatchIndex +// +// This set may grow. Note that regular expression matches may need to +// examine text beyond the text returned by a match, so the methods that +// match text from a RuneReader may read arbitrarily far into the input +// before returning. +// // (There are a few other methods that do not match this pattern.) // package regexp @@ -231,13 +241,13 @@ func (p *parser) error(err Error) { panic(err) } -const endOfFile = -1 +const endOfText = -1 func (p *parser) c() int { return p.ch } func (p *parser) nextc() int { if p.pos >= len(p.re.expr) { - p.ch = endOfFile + p.ch = endOfText } else { c, w := utf8.DecodeRuneInString(p.re.expr[p.pos:]) p.ch = c @@ -288,7 +298,7 @@ func (p *parser) checkBackslash() int { if c == '\\' { c = p.nextc() switch { - case c == endOfFile: + case c == endOfText: p.error(ErrExtraneousBackslash) case ispunct(c): // c is as delivered @@ -311,7 +321,7 @@ func (p *parser) charClass() *instr { left := -1 for { switch c := p.c(); c { - case ']', endOfFile: + case ']', endOfText: if left >= 0 { p.error(ErrBadRange) } @@ -356,7 +366,7 @@ func (p *parser) charClass() *instr { func (p *parser) term() (start, end *instr) { switch c := p.c(); c { - case '|', endOfFile: + case '|', endOfText: return nil, nil case '*', '+', '?': p.error(ErrBareClosure) @@ -638,8 +648,11 @@ func (re *Regexp) NumSubexp() int { return re.nbra } // match vectors away as we execute. Matches are ref counted and returned // to a free list when no longer active. Increases a simple benchmark by 22X. type matchArena struct { - head *matchVec - len int // length of match vector + head *matchVec + len int // length of match vector + pos int + atBOT bool // whether we're at beginning of text + atEOT bool // whether we're at end of text } type matchVec struct { @@ -699,21 +712,21 @@ type state struct { // Append new state to to-do list. Leftmost-longest wins so avoid // adding a state that's already active. The matchVec will be inc-ref'ed // if it is assigned to a state. -func (a *matchArena) addState(s []state, inst *instr, prefixed bool, match *matchVec, pos, end int) []state { +func (a *matchArena) addState(s []state, inst *instr, prefixed bool, match *matchVec) []state { switch inst.kind { case iBOT: - if pos == 0 { - s = a.addState(s, inst.next, prefixed, match, pos, end) + if a.atBOT { + s = a.addState(s, inst.next, prefixed, match) } return s case iEOT: - if pos == end { - s = a.addState(s, inst.next, prefixed, match, pos, end) + if a.atEOT { + s = a.addState(s, inst.next, prefixed, match) } return s case iBra: - match.m[inst.braNum] = pos - s = a.addState(s, inst.next, prefixed, match, pos, end) + match.m[inst.braNum] = a.pos + s = a.addState(s, inst.next, prefixed, match) return s } l := len(s) @@ -727,62 +740,157 @@ func (a *matchArena) addState(s []state, inst *instr, prefixed bool, match *matc s = append(s, state{inst, prefixed, match}) match.ref++ if inst.kind == iAlt { - s = a.addState(s, inst.left, prefixed, a.copy(match), pos, end) + s = a.addState(s, inst.left, prefixed, a.copy(match)) // give other branch a copy of this match vector - s = a.addState(s, inst.next, prefixed, a.copy(match), pos, end) + s = a.addState(s, inst.next, prefixed, a.copy(match)) } return s } -// Accepts either string or bytes - the logic is identical either way. -// If bytes == nil, scan str. -func (re *Regexp) doExecute(str string, bytestr []byte, pos int) []int { +// input abstracts different representations of the input text. It provides +// one-character lookahead. +type input interface { + step(pos int) (rune int, width int) // advance one rune + canCheckPrefix() bool // can we look ahead without losing info? + hasPrefix(re *Regexp) bool + index(re *Regexp, pos int) int +} + +// inputString scans a string. +type inputString struct { + str string +} + +func newInputString(str string) *inputString { + return &inputString{str: str} +} + +func (i *inputString) step(pos int) (int, int) { + if pos < len(i.str) { + return utf8.DecodeRuneInString(i.str[pos:len(i.str)]) + } + return endOfText, 0 +} + +func (i *inputString) canCheckPrefix() bool { + return true +} + +func (i *inputString) hasPrefix(re *Regexp) bool { + return strings.HasPrefix(i.str, re.prefix) +} + +func (i *inputString) index(re *Regexp, pos int) int { + return strings.Index(i.str[pos:], re.prefix) +} + +// inputBytes scans a byte slice. +type inputBytes struct { + str []byte +} + +func newInputBytes(str []byte) *inputBytes { + return &inputBytes{str: str} +} + +func (i *inputBytes) step(pos int) (int, int) { + if pos < len(i.str) { + return utf8.DecodeRune(i.str[pos:len(i.str)]) + } + return endOfText, 0 +} + +func (i *inputBytes) canCheckPrefix() bool { + return true +} + +func (i *inputBytes) hasPrefix(re *Regexp) bool { + return bytes.HasPrefix(i.str, re.prefixBytes) +} + +func (i *inputBytes) index(re *Regexp, pos int) int { + return bytes.Index(i.str[pos:], re.prefixBytes) +} + +// inputReader scans a RuneReader. +type inputReader struct { + r io.RuneReader + atEOT bool + pos int +} + +func newInputReader(r io.RuneReader) *inputReader { + return &inputReader{r: r} +} + +func (i *inputReader) step(pos int) (int, int) { + if !i.atEOT && pos != i.pos { + return endOfText, 0 + + } + r, w, err := i.r.ReadRune() + if err != nil { + i.atEOT = true + return endOfText, 0 + } + i.pos += w + return r, w +} + +func (i *inputReader) canCheckPrefix() bool { + return false +} + +func (i *inputReader) hasPrefix(re *Regexp) bool { + return false +} + +func (i *inputReader) index(re *Regexp, pos int) int { + return -1 +} + +// Search match starting from pos bytes into the input. +func (re *Regexp) doExecute(i input, pos int) []int { var s [2][]state s[0] = make([]state, 0, 10) s[1] = make([]state, 0, 10) in, out := 0, 1 var final state found := false - end := len(str) - if bytestr != nil { - end = len(bytestr) - } anchored := re.inst[0].next.kind == iBOT if anchored && pos > 0 { return nil } // fast check for initial plain substring - if re.prefix != "" { + if i.canCheckPrefix() && re.prefix != "" { advance := 0 if anchored { - if bytestr == nil { - if !strings.HasPrefix(str, re.prefix) { - return nil - } - } else { - if !bytes.HasPrefix(bytestr, re.prefixBytes) { - return nil - } + if !i.hasPrefix(re) { + return nil } } else { - if bytestr == nil { - advance = strings.Index(str[pos:], re.prefix) - } else { - advance = bytes.Index(bytestr[pos:], re.prefixBytes) + advance = i.index(re, pos) + if advance == -1 { + return nil } } - if advance == -1 { - return nil - } pos += advance } - arena := &matchArena{nil, 2 * (re.nbra + 1)} - for startPos := pos; pos <= end; { + // We look one character ahead so we can match $, which checks whether + // we are at EOT. + nextChar, nextWidth := i.step(pos) + arena := &matchArena{ + len: 2 * (re.nbra + 1), + pos: pos, + atBOT: pos == 0, + atEOT: nextChar == endOfText, + } + for c, startPos := 0, pos; c != endOfText; { if !found && (pos == startPos || !anchored) { // prime the pump if we haven't seen a match yet match := arena.noMatch() match.m[0] = pos - s[out] = arena.addState(s[out], re.start.next, false, match, pos, end) + s[out] = arena.addState(s[out], re.start.next, false, match) arena.free(match) // if addState saved it, ref was incremented } else if len(s[out]) == 0 { // machine has completed @@ -795,35 +903,32 @@ func (re *Regexp) doExecute(str string, bytestr []byte, pos int) []int { arena.free(state.match) } s[out] = old[0:0] // truncate state vector - charwidth := 1 - c := endOfFile - if pos < end { - if bytestr == nil { - c, charwidth = utf8.DecodeRuneInString(str[pos:end]) - } else { - c, charwidth = utf8.DecodeRune(bytestr[pos:end]) - } - } - pos += charwidth + c = nextChar + thisPos := pos + pos += nextWidth + nextChar, nextWidth = i.step(pos) + arena.atEOT = nextChar == endOfText + arena.atBOT = false + arena.pos = pos for _, st := range s[in] { switch st.inst.kind { case iBOT: case iEOT: case iChar: if c == st.inst.char { - s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match, pos, end) + s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match) } case iCharClass: if st.inst.cclass.matches(c) { - s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match, pos, end) + s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match) } case iAny: - if c != endOfFile { - s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match, pos, end) + if c != endOfText { + s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match) } case iNotNL: - if c != endOfFile && c != '\n' { - s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match, pos, end) + if c != endOfText && c != '\n' { + s[out] = arena.addState(s[out], st.inst.next, st.prefixed, st.match) } case iBra: case iAlt: @@ -831,13 +936,13 @@ func (re *Regexp) doExecute(str string, bytestr []byte, pos int) []int { // choose leftmost longest if !found || // first st.match.m[0] < final.match.m[0] || // leftmost - (st.match.m[0] == final.match.m[0] && pos-charwidth > final.match.m[1]) { // longest + (st.match.m[0] == final.match.m[0] && thisPos > final.match.m[1]) { // longest if final.match != nil { arena.free(final.match) } final = st final.match.ref++ - final.match.m[1] = pos - charwidth + final.match.m[1] = thisPos } found = true default: @@ -874,14 +979,31 @@ func (re *Regexp) LiteralPrefix() (prefix string, complete bool) { return string(c[:i]), true } +// MatchReader returns whether the Regexp matches the text read by the +// RuneReader. The return value is a boolean: true for match, false for no +// match. +func (re *Regexp) MatchReader(r io.RuneReader) bool { + return len(re.doExecute(newInputReader(r), 0)) > 0 +} + // MatchString returns whether the Regexp matches the string s. // The return value is a boolean: true for match, false for no match. -func (re *Regexp) MatchString(s string) bool { return len(re.doExecute(s, nil, 0)) > 0 } +func (re *Regexp) MatchString(s string) bool { return len(re.doExecute(newInputString(s), 0)) > 0 } // Match returns whether the Regexp matches the byte slice b. // The return value is a boolean: true for match, false for no match. -func (re *Regexp) Match(b []byte) bool { return len(re.doExecute("", b, 0)) > 0 } +func (re *Regexp) Match(b []byte) bool { return len(re.doExecute(newInputBytes(b), 0)) > 0 } +// MatchReader checks whether a textual regular expression matches the text +// read by the RuneReader. More complicated queries need to use Compile and +// the full Regexp interface. +func MatchReader(pattern string, r io.RuneReader) (matched bool, error os.Error) { + re, err := Compile(pattern) + if err != nil { + return false, err + } + return re.MatchReader(r), nil +} // MatchString checks whether a textual regular expression // matches a string. More complicated queries need @@ -921,7 +1043,7 @@ func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) str searchPos := 0 // position where we next look for a match buf := new(bytes.Buffer) for searchPos <= len(src) { - a := re.doExecute(src, nil, searchPos) + a := re.doExecute(newInputString(src), searchPos) if len(a) == 0 { break // no more matches } @@ -973,7 +1095,7 @@ func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte { searchPos := 0 // position where we next look for a match buf := new(bytes.Buffer) for searchPos <= len(src) { - a := re.doExecute("", src, searchPos) + a := re.doExecute(newInputBytes(src), searchPos) if len(a) == 0 { break // no more matches } @@ -1038,7 +1160,13 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { } for pos, i, prevMatchEnd := 0, 0, -1; i < n && pos <= end; { - matches := re.doExecute(s, b, pos) + var in input + if b == nil { + in = newInputString(s) + } else { + in = newInputBytes(b) + } + matches := re.doExecute(in, pos) if len(matches) == 0 { break } @@ -1052,6 +1180,7 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { accept = false } var width int + // TODO: use step() if b == nil { _, width = utf8.DecodeRuneInString(s[pos:end]) } else { @@ -1077,7 +1206,7 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { // Find returns a slice holding the text of the leftmost match in b of the regular expression. // A return value of nil indicates no match. func (re *Regexp) Find(b []byte) []byte { - a := re.doExecute("", b, 0) + a := re.doExecute(newInputBytes(b), 0) if a == nil { return nil } @@ -1089,7 +1218,7 @@ func (re *Regexp) Find(b []byte) []byte { // b[loc[0]:loc[1]]. // A return value of nil indicates no match. func (re *Regexp) FindIndex(b []byte) (loc []int) { - a := re.doExecute("", b, 0) + a := re.doExecute(newInputBytes(b), 0) if a == nil { return nil } @@ -1102,7 +1231,7 @@ func (re *Regexp) FindIndex(b []byte) (loc []int) { // an empty string. Use FindStringIndex or FindStringSubmatch if it is // necessary to distinguish these cases. func (re *Regexp) FindString(s string) string { - a := re.doExecute(s, nil, 0) + a := re.doExecute(newInputString(s), 0) if a == nil { return "" } @@ -1114,7 +1243,19 @@ func (re *Regexp) FindString(s string) string { // itself is at s[loc[0]:loc[1]]. // A return value of nil indicates no match. func (re *Regexp) FindStringIndex(s string) []int { - a := re.doExecute(s, nil, 0) + a := re.doExecute(newInputString(s), 0) + if a == nil { + return nil + } + return a[0:2] +} + +// FindReaderIndex returns a two-element slice of integers defining the +// location of the leftmost match of the regular expression in text read from +// the RuneReader. The match itself is at s[loc[0]:loc[1]]. A return +// value of nil indicates no match. +func (re *Regexp) FindReaderIndex(r io.RuneReader) []int { + a := re.doExecute(newInputReader(r), 0) if a == nil { return nil } @@ -1127,7 +1268,7 @@ func (re *Regexp) FindStringIndex(s string) []int { // comment. // A return value of nil indicates no match. func (re *Regexp) FindSubmatch(b []byte) [][]byte { - a := re.doExecute("", b, 0) + a := re.doExecute(newInputBytes(b), 0) if a == nil { return nil } @@ -1146,7 +1287,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte { // in the package comment. // A return value of nil indicates no match. func (re *Regexp) FindSubmatchIndex(b []byte) []int { - return re.doExecute("", b, 0) + return re.doExecute(newInputBytes(b), 0) } // FindStringSubmatch returns a slice of strings holding the text of the @@ -1155,7 +1296,7 @@ func (re *Regexp) FindSubmatchIndex(b []byte) []int { // package comment. // A return value of nil indicates no match. func (re *Regexp) FindStringSubmatch(s string) []string { - a := re.doExecute(s, nil, 0) + a := re.doExecute(newInputString(s), 0) if a == nil { return nil } @@ -1174,7 +1315,16 @@ func (re *Regexp) FindStringSubmatch(s string) []string { // 'Index' descriptions in the package comment. // A return value of nil indicates no match. func (re *Regexp) FindStringSubmatchIndex(s string) []int { - return re.doExecute(s, nil, 0) + return re.doExecute(newInputString(s), 0) +} + +// FindReaderSubmatchIndex returns a slice holding the index pairs +// identifying the leftmost match of the regular expression of text read by +// the RuneReader, and the matches, if any, of its subexpressions, as defined +// by the 'Submatch' and 'Index' descriptions in the package comment. A +// return value of nil indicates no match. +func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int { + return re.doExecute(newInputReader(r), 0) } const startSize = 10 // The size at which to start a slice in the 'All' routines. diff --git a/src/pkg/rpc/client.go b/src/pkg/rpc/client.go index 6f028c10d..6de6d1325 100644 --- a/src/pkg/rpc/client.go +++ b/src/pkg/rpc/client.go @@ -15,6 +15,16 @@ import ( "sync" ) +// ServerError represents an error that has been returned from +// the remote side of the RPC connection. +type ServerError string + +func (e ServerError) String() string { + return string(e) +} + +const ErrShutdown = os.ErrorString("connection is shut down") + // Call represents an active RPC. type Call struct { ServiceMethod string // The name of the service and method to call. @@ -30,12 +40,12 @@ type Call struct { // with a single Client. type Client struct { mutex sync.Mutex // protects pending, seq - shutdown os.Error // non-nil if the client is shut down sending sync.Mutex seq uint64 codec ClientCodec pending map[uint64]*Call closing bool + shutdown bool } // A ClientCodec implements writing of RPC requests and @@ -43,7 +53,9 @@ type Client struct { // The client calls WriteRequest to write a request to the connection // and calls ReadResponseHeader and ReadResponseBody in pairs // to read responses. The client calls Close when finished with the -// connection. +// connection. ReadResponseBody may be called with a nil +// argument to force the body of the response to be read and then +// discarded. type ClientCodec interface { WriteRequest(*Request, interface{}) os.Error ReadResponseHeader(*Response) os.Error @@ -55,8 +67,8 @@ type ClientCodec interface { func (client *Client) send(c *Call) { // Register this call. client.mutex.Lock() - if client.shutdown != nil { - c.Error = client.shutdown + if client.shutdown { + c.Error = ErrShutdown client.mutex.Unlock() c.done() return @@ -93,20 +105,27 @@ func (client *Client) input() { c := client.pending[seq] client.pending[seq] = c, false client.mutex.Unlock() - err = client.codec.ReadResponseBody(c.Reply) - if response.Error != "" { - c.Error = os.ErrorString(response.Error) - } else if err != nil { - c.Error = err + + if response.Error == "" { + err = client.codec.ReadResponseBody(c.Reply) + if err != nil { + c.Error = os.ErrorString("reading body " + err.String()) + } } else { - // Empty strings should turn into nil os.Errors - c.Error = nil + // We've got an error response. Give this to the request; + // any subsequent requests will get the ReadResponseBody + // error if there is one. + c.Error = ServerError(response.Error) + err = client.codec.ReadResponseBody(nil) + if err != nil { + err = os.ErrorString("reading error body: " + err.String()) + } } c.done() } // Terminate pending calls. client.mutex.Lock() - client.shutdown = err + client.shutdown = true for _, call := range client.pending { call.Error = err call.done() @@ -209,10 +228,11 @@ func Dial(network, address string) (*Client, os.Error) { } func (client *Client) Close() os.Error { - if client.shutdown != nil || client.closing { - return os.ErrorString("rpc: already closed") - } client.mutex.Lock() + if client.shutdown || client.closing { + client.mutex.Unlock() + return ErrShutdown + } client.closing = true client.mutex.Unlock() return client.codec.Close() @@ -239,8 +259,8 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface } } c.Done = done - if client.shutdown != nil { - c.Error = client.shutdown + if client.shutdown { + c.Error = ErrShutdown c.done() return c } @@ -250,8 +270,8 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface // Call invokes the named function, waits for it to complete, and returns its error status. func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) os.Error { - if client.shutdown != nil { - return client.shutdown + if client.shutdown { + return ErrShutdown } call := <-client.Go(serviceMethod, args, reply, nil).Done return call.Error diff --git a/src/pkg/rpc/debug.go b/src/pkg/rpc/debug.go index 44b32e04b..32dc8a18b 100644 --- a/src/pkg/rpc/debug.go +++ b/src/pkg/rpc/debug.go @@ -83,7 +83,7 @@ func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) { } server.Unlock() sort.Sort(services) - err := debug.Execute(services, w) + err := debug.Execute(w, services) if err != nil { fmt.Fprintln(w, "rpc: error executing template:", err.String()) } diff --git a/src/pkg/rpc/jsonrpc/client.go b/src/pkg/rpc/jsonrpc/client.go index dcaa69f9d..5b806bd6e 100644 --- a/src/pkg/rpc/jsonrpc/client.go +++ b/src/pkg/rpc/jsonrpc/client.go @@ -98,6 +98,9 @@ func (c *clientCodec) ReadResponseHeader(r *rpc.Response) os.Error { } func (c *clientCodec) ReadResponseBody(x interface{}) os.Error { + if x == nil { + return nil + } return json.Unmarshal(*c.resp.Result, x) } diff --git a/src/pkg/rpc/jsonrpc/server.go b/src/pkg/rpc/jsonrpc/server.go index bf53bda8d..9c6b8b40d 100644 --- a/src/pkg/rpc/jsonrpc/server.go +++ b/src/pkg/rpc/jsonrpc/server.go @@ -85,6 +85,9 @@ func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error { } func (c *serverCodec) ReadRequestBody(x interface{}) os.Error { + if x == nil { + return nil + } // JSON params is array value. // RPC params is struct. // Unmarshal into array containing struct for now. diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go index 91e9cd5c8..9dcda4148 100644 --- a/src/pkg/rpc/server.go +++ b/src/pkg/rpc/server.go @@ -299,10 +299,10 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E // A value sent as a placeholder for the response when the server receives an invalid request. type InvalidRequest struct { - marker int + Marker int } -var invalidRequest = InvalidRequest{1} +var invalidRequest = InvalidRequest{} func _new(t *reflect.PtrType) *reflect.PtrValue { v := reflect.MakeZero(t).(*reflect.PtrValue) @@ -316,6 +316,7 @@ func sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec Se resp.ServiceMethod = req.ServiceMethod if errmsg != "" { resp.Error = errmsg + reply = invalidRequest } resp.Seq = req.Seq sending.Lock() @@ -389,54 +390,74 @@ func (server *Server) ServeConn(conn io.ReadWriteCloser) { func (server *Server) ServeCodec(codec ServerCodec) { sending := new(sync.Mutex) for { - // Grab the request header. - req := new(Request) - err := codec.ReadRequestHeader(req) + req, service, mtype, err := server.readRequest(codec) if err != nil { + if err != os.EOF { + log.Println("rpc:", err) + } if err == os.EOF || err == io.ErrUnexpectedEOF { - if err == io.ErrUnexpectedEOF { - log.Println("rpc:", err) - } break } - s := "rpc: server cannot decode request: " + err.String() - sendResponse(sending, req, invalidRequest, codec, s) - break - } - serviceMethod := strings.Split(req.ServiceMethod, ".", -1) - if len(serviceMethod) != 2 { - s := "rpc: service/method request ill-formed: " + req.ServiceMethod - sendResponse(sending, req, invalidRequest, codec, s) - continue - } - // Look up the request. - server.Lock() - service, ok := server.serviceMap[serviceMethod[0]] - server.Unlock() - if !ok { - s := "rpc: can't find service " + req.ServiceMethod - sendResponse(sending, req, invalidRequest, codec, s) - continue - } - mtype, ok := service.method[serviceMethod[1]] - if !ok { - s := "rpc: can't find method " + req.ServiceMethod - sendResponse(sending, req, invalidRequest, codec, s) + // discard body + codec.ReadRequestBody(nil) + + // send a response if we actually managed to read a header. + if req != nil { + sendResponse(sending, req, invalidRequest, codec, err.String()) + } continue } + // Decode the argument value. argv := _new(mtype.ArgType) replyv := _new(mtype.ReplyType) err = codec.ReadRequestBody(argv.Interface()) if err != nil { - log.Println("rpc: tearing down", serviceMethod[0], "connection:", err) + if err == os.EOF || err == io.ErrUnexpectedEOF { + if err == io.ErrUnexpectedEOF { + log.Println("rpc:", err) + } + break + } sendResponse(sending, req, replyv.Interface(), codec, err.String()) - break + continue } go service.call(sending, mtype, req, argv, replyv, codec) } codec.Close() } +func (server *Server) readRequest(codec ServerCodec) (req *Request, service *service, mtype *methodType, err os.Error) { + // Grab the request header. + req = new(Request) + err = codec.ReadRequestHeader(req) + if err != nil { + req = nil + if err == os.EOF || err == io.ErrUnexpectedEOF { + return + } + err = os.ErrorString("rpc: server cannot decode request: " + err.String()) + return + } + + serviceMethod := strings.Split(req.ServiceMethod, ".", -1) + if len(serviceMethod) != 2 { + err = os.ErrorString("rpc: service/method request ill-formed: " + req.ServiceMethod) + return + } + // Look up the request. + server.Lock() + service = server.serviceMap[serviceMethod[0]] + server.Unlock() + if service == nil { + err = os.ErrorString("rpc: can't find service " + req.ServiceMethod) + return + } + mtype = service.method[serviceMethod[1]] + if mtype == nil { + err = os.ErrorString("rpc: can't find method " + req.ServiceMethod) + } + return +} // Accept accepts connections on the listener and serves requests // for each incoming connection. Accept blocks; the caller typically @@ -465,7 +486,8 @@ func RegisterName(name string, rcvr interface{}) os.Error { // The server calls ReadRequestHeader and ReadRequestBody in pairs // to read requests from the connection, and it calls WriteResponse to // write a response back. The server calls Close when finished with the -// connection. +// connection. ReadRequestBody may be called with a nil +// argument to force the body of the request to be read and discarded. type ServerCodec interface { ReadRequestHeader(*Request) os.Error ReadRequestBody(interface{}) os.Error diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go index 1f080faa5..05aaebceb 100644 --- a/src/pkg/rpc/server_test.go +++ b/src/pkg/rpc/server_test.go @@ -134,14 +134,25 @@ func testRPC(t *testing.T, addr string) { t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) } - args = &Args{7, 8} + // Nonexistent method + args = &Args{7, 0} reply = new(Reply) - err = client.Call("Arith.Mul", args, reply) - if err != nil { - t.Errorf("Mul: expected no error but got string %q", err.String()) + err = client.Call("Arith.BadOperation", args, reply) + // expect an error + if err == nil { + t.Error("BadOperation: expected error") + } else if !strings.HasPrefix(err.String(), "rpc: can't find method ") { + t.Errorf("BadOperation: expected can't find method error; got %q", err) } - if reply.C != args.A*args.B { - t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B) + + // Unknown service + args = &Args{7, 8} + reply = new(Reply) + err = client.Call("Arith.Unknown", args, reply) + if err == nil { + t.Error("expected error calling unknown service") + } else if strings.Index(err.String(), "method") < 0 { + t.Error("expected error about method; got", err) } // Out of order. @@ -178,6 +189,15 @@ func testRPC(t *testing.T, addr string) { t.Error("Div: expected divide by zero error; got", err) } + // Bad type. + reply = new(Reply) + err = client.Call("Arith.Add", reply, reply) // args, reply would be the correct thing to use + if err == nil { + t.Error("expected error calling Arith.Add with wrong arg type") + } else if strings.Index(err.String(), "type") < 0 { + t.Error("expected error about type; got", err) + } + // Non-struct argument const Val = 12345 str := fmt.Sprint(Val) @@ -200,9 +220,19 @@ func testRPC(t *testing.T, addr string) { if str != expect { t.Errorf("String: expected %s got %s", expect, str) } + + args = &Args{7, 8} + reply = new(Reply) + err = client.Call("Arith.Mul", args, reply) + if err != nil { + t.Errorf("Mul: expected no error but got string %q", err.String()) + } + if reply.C != args.A*args.B { + t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B) + } } -func TestHTTPRPC(t *testing.T) { +func TestHTTP(t *testing.T) { once.Do(startServer) testHTTPRPC(t, "") newOnce.Do(startNewServer) @@ -233,65 +263,6 @@ func testHTTPRPC(t *testing.T, path string) { } } -func TestCheckUnknownService(t *testing.T) { - once.Do(startServer) - - conn, err := net.Dial("tcp", "", serverAddr) - if err != nil { - t.Fatal("dialing:", err) - } - - client := NewClient(conn) - - args := &Args{7, 8} - reply := new(Reply) - err = client.Call("Unknown.Add", args, reply) - if err == nil { - t.Error("expected error calling unknown service") - } else if strings.Index(err.String(), "service") < 0 { - t.Error("expected error about service; got", err) - } -} - -func TestCheckUnknownMethod(t *testing.T) { - once.Do(startServer) - - conn, err := net.Dial("tcp", "", serverAddr) - if err != nil { - t.Fatal("dialing:", err) - } - - client := NewClient(conn) - - args := &Args{7, 8} - reply := new(Reply) - err = client.Call("Arith.Unknown", args, reply) - if err == nil { - t.Error("expected error calling unknown service") - } else if strings.Index(err.String(), "method") < 0 { - t.Error("expected error about method; got", err) - } -} - -func TestCheckBadType(t *testing.T) { - once.Do(startServer) - - conn, err := net.Dial("tcp", "", serverAddr) - if err != nil { - t.Fatal("dialing:", err) - } - - client := NewClient(conn) - - reply := new(Reply) - err = client.Call("Arith.Add", reply, reply) // args, reply would be the correct thing to use - if err == nil { - t.Error("expected error calling Arith.Add with wrong arg type") - } else if strings.Index(err.String(), "type") < 0 { - t.Error("expected error about type; got", err) - } -} - type ArgNotPointer int type ReplyNotPointer int type ArgNotPublic int diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s index 63d582606..74e1df0da 100644 --- a/src/pkg/runtime/386/asm.s +++ b/src/pkg/runtime/386/asm.s @@ -5,6 +5,14 @@ #include "386/asm.h" TEXT _rt0_386(SB),7,$0 + // Linux, Windows start the FPU in extended double precision. + // Other operating systems use double precision. + // Change to double precision to match them, + // and to match other hardware that only has double. + PUSHL $0x27F + FLDCW 0(SP) + POPL AX + // copy arguments forward on an even stack MOVL 0(SP), AX // argc LEAL 4(SP), BX // argv diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile index e9488cfb5..521c095b9 100644 --- a/src/pkg/runtime/Makefile +++ b/src/pkg/runtime/Makefile @@ -26,8 +26,12 @@ GOFILES=\ softfloat64.go\ type.go\ version.go\ + version_$(GOOS).go\ + version_$(GOARCH).go\ runtime_defs.go\ +CLEANFILES+=version.go version_*.go + OFILES_windows=\ syscall.$O\ @@ -107,7 +111,7 @@ include ../../Make.pkg $(pkgdir)/%.h: %.h @test -d $(QUOTED_GOROOT)/pkg && mkdir -p $(pkgdir) - cp $< $@ + cp $< "$@" clean: clean-local @@ -127,8 +131,14 @@ mkversion: mkversion.c version.go: mkversion ./mkversion >version.go +version_$(GOARCH).go: + (echo 'package runtime'; echo 'const theGoarch = "$(GOARCH)"') >$@ + +version_$(GOOS).go: + (echo 'package runtime'; echo 'const theGoos = "$(GOOS)"') >$@ + %.c: %.goc goc2c - ./goc2c `pwd`/$< > $@.tmp + ./goc2c "`pwd`/$<" > $@.tmp mv -f $@.tmp $@ %.$O: $(GOARCH)/%.c diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c index 86e96f348..d3aae0db9 100644 --- a/src/pkg/runtime/amd64/traceback.c +++ b/src/pkg/runtime/amd64/traceback.c @@ -8,6 +8,8 @@ static uintptr isclosureentry(uintptr); void runtime·deferproc(void); void runtime·newproc(void); +void runtime·newstack(void); +void runtime·morestack(void); // This code is also used for the 386 tracebacks. // Use uintptr for an appropriate word-sized integer. @@ -17,15 +19,32 @@ void runtime·newproc(void); // A little clunky to merge the two but avoids duplicating // the code and all its subtlety. static int32 -gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) +gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 max) { byte *p; - int32 i, n, iter, nascent; - uintptr pc, tracepc, *fp; + int32 i, n, iter, sawnewstack; + uintptr pc, lr, tracepc; + byte *fp; Stktop *stk; Func *f; - + pc = (uintptr)pc0; + lr = 0; + fp = nil; + + // If the PC is goexit, the goroutine hasn't started yet. + if(pc0 == g->sched.pc && sp == g->sched.sp && pc0 == (byte*)runtime·goexit) { + fp = sp; + lr = pc; + pc = (uintptr)g->entry; + } + + // If the PC is zero, it's likely a nil function call. + // Start in the caller's frame. + if(pc == 0) { + pc = lr; + lr = 0; + } // If the PC is zero, it's likely a nil function call. // Start in the caller's frame. @@ -33,26 +52,29 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) pc = *(uintptr*)sp; sp += sizeof(uintptr); } - - nascent = 0; - if(pc0 == g->sched.pc && sp == g->sched.sp && pc0 == (byte*)runtime·goexit) { - // Hasn't started yet. g->sched is set up for goexit - // but goroutine will start at g->entry. - nascent = 1; - pc = (uintptr)g->entry; - } - + n = 0; + sawnewstack = 0; stk = (Stktop*)g->stackbase; - for(iter = 0; iter < 100 && n < m; iter++) { // iter avoids looping forever + for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever + // Typically: + // pc is the PC of the running function. + // sp is the stack pointer at that program counter. + // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown. + // stk is the stack containing sp. + // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp. + if(pc == (uintptr)runtime·lessstack) { // Hit top of stack segment. Unwind to next segment. pc = (uintptr)stk->gobuf.pc; sp = stk->gobuf.sp; + lr = 0; + fp = nil; + if(pcbuf == nil) + runtime·printf("----- stack segment boundary -----\n"); stk = (Stktop*)stk->stackbase; continue; } - if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) { // Dangerous, but worthwhile: see if this is a closure: // ADDQ $wwxxyyzz, SP; RET @@ -66,17 +88,32 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) sp += *(uint32*)(p+2); pc = *(uintptr*)sp; sp += sizeof(uintptr); + lr = 0; + fp = nil; continue; } - if(nascent && (pc = isclosureentry(pc)) != 0) + // Closure at top of stack, not yet started. + if(lr == (uintptr)runtime·goexit && (pc = isclosureentry(pc)) != 0) { + fp = sp; continue; + } - // Unknown pc; stop. + // Unknown pc: stop. break; } - // Found an actual function worth reporting. + // Found an actual function. + if(fp == nil) { + fp = sp; + if(pc > f->entry && f->frame >= sizeof(uintptr)) + fp += f->frame - sizeof(uintptr); + if(lr == 0) + lr = *(uintptr*)fp; + fp += sizeof(uintptr); + } else if(lr == 0) + lr = *(uintptr*)fp; + if(skip > 0) skip--; else if(pcbuf != nil) @@ -93,15 +130,10 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) tracepc--; runtime·printf(" %S:%d\n", f->src, runtime·funcline(f, tracepc)); runtime·printf("\t%S(", f->name); - fp = (uintptr*)sp; - if(f->frame < sizeof(uintptr)) - fp++; - else - fp += f->frame/sizeof(uintptr); for(i = 0; i < f->args; i++) { if(i != 0) runtime·prints(", "); - runtime·printhex(fp[i]); + runtime·printhex(((uintptr*)fp)[i]); if(i >= 4) { runtime·prints(", ..."); break; @@ -111,20 +143,32 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) n++; } - if(nascent) { - pc = (uintptr)g->sched.pc; - sp = g->sched.sp; - nascent = 0; + if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc) + fp += 2*sizeof(uintptr); + + if(f->entry == (uintptr)runtime·newstack) + sawnewstack = 1; + + if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && g == m->g0 && sawnewstack) { + // The fact that we saw newstack means that morestack + // has managed to record its information in m, so we can + // use it to keep unwinding the stack. + runtime·printf("----- morestack called from goroutine %d -----\n", m->curg->goid); + pc = (uintptr)m->morepc; + sp = m->morebuf.sp - sizeof(void*); + lr = (uintptr)m->morebuf.pc; + fp = m->morebuf.sp; + sawnewstack = 0; + g = m->curg; + stk = (Stktop*)g->stackbase; continue; } - if(f->frame < sizeof(uintptr)) // assembly functions lie - sp += sizeof(uintptr); - else - sp += f->frame; - pc = *((uintptr*)sp - 1); - if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc) - sp += 2*sizeof(uintptr); + // Unwind to next frame. + pc = lr; + lr = 0; + sp = fp; + fp = nil; } return n; } @@ -156,7 +200,17 @@ isclosureentry(uintptr pc) p = (byte*)pc; if(p < runtime·mheap.arena_start || p+32 > runtime·mheap.arena_used) return 0; + + if(*p == 0xe8) { + // CALL fn + return pc+5+*(int32*)(p+1); + } + if(sizeof(uintptr) == 8 && p[0] == 0x48 && p[1] == 0xb9 && p[10] == 0xff && p[11] == 0xd1) { + // MOVQ $fn, CX; CALL *CX + return *(uintptr*)(p+2); + } + // SUBQ $siz, SP if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x81 || *p++ != 0xec) return 0; diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c index 8289fdb28..2307e98e8 100644 --- a/src/pkg/runtime/arm/traceback.c +++ b/src/pkg/runtime/arm/traceback.c @@ -3,19 +3,27 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "malloc.h" + +void runtime·deferproc(void); +void runtime·newproc(void); +void runtime·newstack(void); +void runtime·morestack(void); static int32 -gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 m) +gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max) { int32 i, n, iter; - uintptr pc, lr, tracepc; + uintptr pc, lr, tracepc, x; + byte *fp, *p; Stktop *stk; Func *f; pc = (uintptr)pc0; lr = (uintptr)lr0; - - // If the PC is goexit, it hasn't started yet. + fp = nil; + + // If the PC is goexit, the goroutine hasn't started yet. if(pc == (uintptr)runtime·goexit) { pc = (uintptr)g->entry; lr = (uintptr)runtime·goexit; @@ -30,21 +38,73 @@ gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, i n = 0; stk = (Stktop*)g->stackbase; - for(iter = 0; iter < 100 && n < m; iter++) { // iter avoids looping forever + for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever + // Typically: + // pc is the PC of the running function. + // sp is the stack pointer at that program counter. + // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown. + // stk is the stack containing sp. + // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp. + if(pc == (uintptr)runtime·lessstack) { // Hit top of stack segment. Unwind to next segment. pc = (uintptr)stk->gobuf.pc; sp = stk->gobuf.sp; - lr = *(uintptr*)sp; + lr = 0; + fp = nil; + if(pcbuf == nil) + runtime·printf("----- stack segment boundary -----\n"); stk = (Stktop*)stk->stackbase; continue; } - if(pc <= 0x1000 || (f = runtime·findfunc(pc-4)) == nil) { - // TODO: Check for closure. + + if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) { + // Dangerous, but worthwhile: see if this is a closure by + // decoding the instruction stream. + // + // We check p < p+4 to avoid wrapping and faulting if + // we have lost track of where we are. + p = (byte*)pc; + if((pc&3) == 0 && p < p+4 && + runtime·mheap.arena_start < p && + p+4 < runtime·mheap.arena_used) { + x = *(uintptr*)p; + if((x&0xfffff000) == 0xe49df000) { + // End of closure: + // MOVW.P frame(R13), R15 + pc = *(uintptr*)sp; + lr = 0; + sp += x & 0xfff; + fp = nil; + continue; + } + if((x&0xfffff000) == 0xe52de000 && lr == (uintptr)runtime·goexit) { + // Beginning of closure. + // Closure at top of stack, not yet started. + p += 5*4; + if((x&0xfff) != 4) { + // argument copying + p += 7*4; + } + if((byte*)pc < p && p < p+4 && p+4 < runtime·mheap.arena_used) { + pc = *(uintptr*)p; + fp = nil; + continue; + } + } + } break; } - // Found an actual function worth reporting. + // Found an actual function. + if(lr == 0) + lr = *(uintptr*)sp; + if(fp == nil) { + fp = sp; + if(pc > f->entry && f->frame >= 0) + fp += f->frame; + } + if(skip > 0) skip--; else if(pcbuf != nil) @@ -64,7 +124,7 @@ gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, i for(i = 0; i < f->args; i++) { if(i != 0) runtime·prints(", "); - runtime·printhex(((uintptr*)sp)[1+i]); + runtime·printhex(((uintptr*)fp)[1+i]); if(i >= 4) { runtime·prints(", ..."); break; @@ -73,17 +133,28 @@ gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, i runtime·prints(")\n"); n++; } + + if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && g == m->g0) { + runtime·printf("----- newstack called from goroutine %d -----\n", m->curg->goid); + pc = (uintptr)m->morepc; + sp = (byte*)m->moreargp - sizeof(void*); + lr = (uintptr)m->morebuf.pc; + fp = m->morebuf.sp; + g = m->curg; + stk = (Stktop*)g->stackbase; + continue; + } - if(lr == 0) - lr = *(uintptr*)sp; + // Unwind to next frame. pc = lr; lr = 0; - if(f->frame >= 0) - sp += f->frame; + sp = fp; + fp = nil; } return n; } + void runtime·traceback(byte *pc0, byte *sp, byte *lr, G *g) { diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index e6ece9542..74e5a3085 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -53,13 +53,12 @@ runtime·cgocall(void (*fn)(void*), void *arg) // (arg/argsize) on to the stack, calls the function, copies the // arguments back where they came from, and finally returns to the old // stack. -uintptr +void runtime·cgocallback(void (*fn)(void), void *arg, int32 argsize) { Gobuf oldsched, oldg1sched; G *g1; void *sp; - uintptr ret; if(g != m->g0) runtime·throw("bad g in cgocallback"); @@ -71,11 +70,11 @@ runtime·cgocallback(void (*fn)(void), void *arg, int32 argsize) runtime·startcgocallback(g1); sp = g1->sched.sp - argsize; - if(sp < g1->stackguard - StackGuard + 4) // +4 for return address + if(sp < g1->stackguard - StackGuard + 8) // +8 for return address runtime·throw("g stack overflow in cgocallback"); runtime·mcpy(sp, arg, argsize); - ret = runtime·runcgocallback(g1, sp, fn); + runtime·runcgocallback(g1, sp, fn); runtime·mcpy(arg, sp, argsize); @@ -83,8 +82,6 @@ runtime·cgocallback(void (*fn)(void), void *arg, int32 argsize) m->sched = oldsched; g1->sched = oldg1sched; - - return ret; } void diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h index 7c24e167b..1ad954eb1 100644 --- a/src/pkg/runtime/cgocall.h +++ b/src/pkg/runtime/cgocall.h @@ -7,6 +7,6 @@ */ void runtime·cgocall(void (*fn)(void*), void*); -uintptr runtime·cgocallback(void (*fn)(void), void*, int32); +void runtime·cgocallback(void (*fn)(void), void*, int32); void *runtime·cmalloc(uintptr); void runtime·cfree(void*); diff --git a/src/pkg/runtime/debug.go b/src/pkg/runtime/debug.go index d09db1be6..5117e1a55 100644 --- a/src/pkg/runtime/debug.go +++ b/src/pkg/runtime/debug.go @@ -69,7 +69,8 @@ type MemStatsType struct { // Per-size allocation statistics. // Not locked during update; approximate. - BySize [67]struct { + // 61 is NumSizeClasses in the C code. + BySize [61]struct { Size uint32 Mallocs uint64 Frees uint64 diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go index dba28324c..c6e664abb 100644 --- a/src/pkg/runtime/extern.go +++ b/src/pkg/runtime/extern.go @@ -60,31 +60,47 @@ func (f *Func) Entry() uintptr { return f.entry } // counter within f. func (f *Func) FileLine(pc uintptr) (file string, line int) { // NOTE(rsc): If you edit this function, also edit - // symtab.c:/^funcline. + // symtab.c:/^funcline. That function also has the + // comments explaining the logic. + targetpc := pc + var pcQuant uintptr = 1 if GOARCH == "arm" { pcQuant = 4 } - targetpc := pc p := f.pcln pc = f.pc0 line = int(f.ln0) - file = f.src - for i := 0; i < len(p) && pc <= targetpc; i++ { - switch { - case p[i] == 0: + i := 0 + //print("FileLine start pc=", pc, " targetpc=", targetpc, " line=", line, + // " tab=", p, " ", p[0], " quant=", pcQuant, " GOARCH=", GOARCH, "\n") + for { + for i < len(p) && p[i] > 128 { + pc += pcQuant * uintptr(p[i]-128) + i++ + } + //print("pc<", pc, " targetpc=", targetpc, " line=", line, "\n") + if pc > targetpc || i >= len(p) { + break + } + if p[i] == 0 { + if i+5 > len(p) { + break + } line += int(p[i+1]<<24) | int(p[i+2]<<16) | int(p[i+3]<<8) | int(p[i+4]) - i += 4 - case p[i] <= 64: + i += 5 + } else if p[i] <= 64 { line += int(p[i]) - case p[i] <= 128: + i++ + } else { line -= int(p[i] - 64) - default: - pc += pcQuant * uintptr(p[i]-129) + i++ } + //print("pc=", pc, " targetpc=", targetpc, " line=", line, "\n") pc += pcQuant } + file = f.src return } diff --git a/src/pkg/runtime/freebsd/mem.c b/src/pkg/runtime/freebsd/mem.c index cbae18718..f5bbfa6fa 100644 --- a/src/pkg/runtime/freebsd/mem.c +++ b/src/pkg/runtime/freebsd/mem.c @@ -33,6 +33,12 @@ runtime·SysFree(void *v, uintptr n) void* runtime·SysReserve(void *v, uintptr n) { + // On 64-bit, people with ulimit -v set complain if we reserve too + // much address space. Instead, assume that the reservation is okay + // and check the assumption in SysMap. + if(sizeof(void*) == 8) + return v; + return runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); } @@ -42,6 +48,17 @@ runtime·SysMap(void *v, uintptr n) void *p; mstats.sys += n; + + // On 64-bit, we don't actually have v reserved, so tread carefully. + if(sizeof(void*) == 8) { + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p != v) { + runtime·printf("runtime: address space conflict: map(%v) = %v\n", v, p); + runtime·throw("runtime: address space conflict"); + } + return; + } + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c index aa36df68e..3dec45e2b 100644 --- a/src/pkg/runtime/iface.c +++ b/src/pkg/runtime/iface.c @@ -702,7 +702,7 @@ unsafe·New(Eface typ, void *ret) t = (Type*)((Eface*)typ.data-1); if(t->kind&KindNoPointers) - ret = runtime·mallocgc(t->size, RefNoPointers, 1, 1); + ret = runtime·mallocgc(t->size, FlagNoPointers, 1, 1); else ret = runtime·mal(t->size); FLUSH(&ret); @@ -722,7 +722,7 @@ unsafe·NewArray(Eface typ, uint32 n, void *ret) size = n*t->size; if(t->kind&KindNoPointers) - ret = runtime·mallocgc(size, RefNoPointers, 1, 1); + ret = runtime·mallocgc(size, FlagNoPointers, 1, 1); else ret = runtime·mal(size); FLUSH(&ret); diff --git a/src/pkg/runtime/linux/386/rt0.s b/src/pkg/runtime/linux/386/rt0.s index 0f82d6a1c..223e6d2ea 100644 --- a/src/pkg/runtime/linux/386/rt0.s +++ b/src/pkg/runtime/linux/386/rt0.s @@ -5,13 +5,5 @@ // Darwin and Linux use the same linkage to main TEXT _rt0_386_linux(SB),7,$0 - // Linux starts the FPU in extended double precision. - // Other operating systems use double precision. - // Change to double precision to match them, - // and to match other hardware that only has double. - PUSHL $0x27F - FLDCW 0(SP) - POPL AX - JMP _rt0_386(SB) diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c index 3a83e7394..633ad0c62 100644 --- a/src/pkg/runtime/linux/mem.c +++ b/src/pkg/runtime/linux/mem.c @@ -39,6 +39,12 @@ runtime·SysFree(void *v, uintptr n) void* runtime·SysReserve(void *v, uintptr n) { + // On 64-bit, people with ulimit -v set complain if we reserve too + // much address space. Instead, assume that the reservation is okay + // and check the assumption in SysMap. + if(sizeof(void*) == 8) + return v; + return runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); } @@ -48,6 +54,17 @@ runtime·SysMap(void *v, uintptr n) void *p; mstats.sys += n; + + // On 64-bit, we don't actually have v reserved, so tread carefully. + if(sizeof(void*) == 8) { + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p != v) { + runtime·printf("runtime: address space conflict: map(%v) = %v\n", v, p); + runtime·throw("runtime: address space conflict"); + } + return; + } + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc index cc28b943d..70b85d68d 100644 --- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -36,14 +36,13 @@ fastrand1(void) // Small objects are allocated from the per-thread cache's free lists. // Large objects (> 32 kB) are allocated straight from the heap. void* -runtime·mallocgc(uintptr size, uint32 refflag, int32 dogc, int32 zeroed) +runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) { int32 sizeclass, rate; MCache *c; uintptr npages; MSpan *s; void *v; - uint32 *ref; if(runtime·gcwaiting && g != m->g0 && m->locks == 0) runtime·gosched(); @@ -65,12 +64,6 @@ runtime·mallocgc(uintptr size, uint32 refflag, int32 dogc, int32 zeroed) mstats.alloc += size; mstats.total_alloc += size; mstats.by_size[sizeclass].nmalloc++; - - if(!runtime·mlookup(v, nil, nil, nil, &ref)) { - runtime·printf("malloc %D; runtime·mlookup failed\n", (uint64)size); - runtime·throw("malloc runtime·mlookup"); - } - *ref = RefNone | refflag; } else { // TODO(rsc): Report tracebacks for very large allocations. @@ -87,13 +80,14 @@ runtime·mallocgc(uintptr size, uint32 refflag, int32 dogc, int32 zeroed) v = (void*)(s->start << PageShift); // setup for mark sweep - s->gcref0 = RefNone | refflag; - ref = &s->gcref0; + runtime·markspan(v, 0, 0, true); } + if(!(flag & FlagNoGC)) + runtime·markallocated(v, size, (flag&FlagNoPointers) != 0); m->mallocing = 0; - if(!(refflag & RefNoProfiling) && (rate = runtime·MemProfileRate) > 0) { + if(!(flag & FlagNoProfiling) && (rate = runtime·MemProfileRate) > 0) { if(size >= rate) goto profile; if(m->mcache->next_sample > size) @@ -104,7 +98,7 @@ runtime·mallocgc(uintptr size, uint32 refflag, int32 dogc, int32 zeroed) rate = 0x3fffffff; m->mcache->next_sample = fastrand1() % (2*rate); profile: - *ref |= RefProfiled; + runtime·setblockspecial(v); runtime·MProf_Malloc(v, size); } } @@ -124,33 +118,35 @@ runtime·malloc(uintptr size) void runtime·free(void *v) { - int32 sizeclass, size; + int32 sizeclass; MSpan *s; MCache *c; - uint32 prof, *ref; + uint32 prof; + uintptr size; if(v == nil) return; + + // If you change this also change mgc0.c:/^sweepspan, + // which has a copy of the guts of free. if(m->mallocing) runtime·throw("malloc/free - deadlock"); m->mallocing = 1; - if(!runtime·mlookup(v, nil, nil, &s, &ref)) { + if(!runtime·mlookup(v, nil, nil, &s)) { runtime·printf("free %p: not an allocated block\n", v); runtime·throw("free runtime·mlookup"); } - prof = *ref & RefProfiled; - *ref = RefFree; + prof = runtime·blockspecial(v); // Find size class for v. sizeclass = s->sizeclass; if(sizeclass == 0) { // Large object. - if(prof) - runtime·MProf_Free(v, s->npages<<PageShift); - mstats.alloc -= s->npages<<PageShift; - runtime·memclr(v, s->npages<<PageShift); + size = s->npages<<PageShift; + *(uintptr*)(s->start<<PageShift) = 1; // mark as "needs to be zeroed" + runtime·unmarkspan(v, 1<<PageShift); runtime·MHeap_Free(&runtime·mheap, s, 1); } else { // Small object. @@ -158,19 +154,20 @@ runtime·free(void *v) size = runtime·class_to_size[sizeclass]; if(size > sizeof(uintptr)) ((uintptr*)v)[1] = 1; // mark as "needs to be zeroed" - if(prof) - runtime·MProf_Free(v, size); - mstats.alloc -= size; mstats.by_size[sizeclass].nfree++; runtime·MCache_Free(c, v, sizeclass, size); } + runtime·markfreed(v, size); + mstats.alloc -= size; + if(prof) + runtime·MProf_Free(v, size); m->mallocing = 0; } int32 -runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp, uint32 **ref) +runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp) { - uintptr n, nobj, i; + uintptr n, i; byte *p; MSpan *s; @@ -179,12 +176,11 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp, uint32 **ref) if(sp) *sp = s; if(s == nil) { + runtime·checkfreed(v, 1); if(base) *base = nil; if(size) *size = 0; - if(ref) - *ref = 0; return 0; } @@ -195,14 +191,11 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp, uint32 **ref) *base = p; if(size) *size = s->npages<<PageShift; - if(ref) - *ref = &s->gcref0; return 1; } - if((byte*)v >= (byte*)s->gcref) { - // pointers into the gc ref counts - // do not count as pointers. + if((byte*)v >= (byte*)s->limit) { + // pointers past the last block do not count as pointers. return 0; } @@ -213,21 +206,6 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp, uint32 **ref) if(size) *size = n; - // good for error checking, but expensive - if(0) { - nobj = (s->npages << PageShift) / (n + RefcountOverhead); - if((byte*)s->gcref < p || (byte*)(s->gcref+nobj) > p+(s->npages<<PageShift)) { - runtime·printf("odd span state=%d span=%p base=%p sizeclass=%d n=%D size=%D npages=%D\n", - s->state, s, p, s->sizeclass, (uint64)nobj, (uint64)n, (uint64)s->npages); - runtime·printf("s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%D size=%D end=%p end=%p\n", - s->sizeclass, v, p, s->gcref, (uint64)s->npages<<PageShift, - (uint64)nobj, (uint64)n, s->gcref + nobj, p+(s->npages<<PageShift)); - runtime·throw("bad gcref"); - } - } - if(ref) - *ref = &s->gcref[i]; - return 1; } @@ -246,14 +224,20 @@ runtime·allocmcache(void) int32 runtime·sizeof_C_MStats = sizeof(MStats); +#define MaxArena32 (2U<<30) + void runtime·mallocinit(void) { byte *p; - uintptr arena_size; + uintptr arena_size, bitmap_size; + extern byte end[]; runtime·InitSizes(); + // Set up the allocation arena, a contiguous area of memory where + // allocated data will be found. The arena begins with a bitmap large + // enough to hold 4 bits per allocated word. if(sizeof(void*) == 8) { // On a 64-bit machine, allocate from a single contiguous reservation. // 16 GB should be big enough for now. @@ -273,19 +257,53 @@ runtime·mallocinit(void) // odds of the conservative garbage collector not collecting memory // because some non-pointer block of memory had a bit pattern // that matched a memory address. + // + // Actually we reserve 17 GB (because the bitmap ends up being 1 GB) + // but it hardly matters: fc is not valid UTF-8 either, and we have to + // allocate 15 GB before we get that far. arena_size = 16LL<<30; - p = runtime·SysReserve((void*)(0x00f8ULL<<32), arena_size); + bitmap_size = arena_size / (sizeof(void*)*8/4); + p = runtime·SysReserve((void*)(0x00f8ULL<<32), bitmap_size + arena_size); if(p == nil) runtime·throw("runtime: cannot reserve arena virtual address space"); - runtime·mheap.arena_start = p; - runtime·mheap.arena_used = p; - runtime·mheap.arena_end = p + arena_size; } else { - // On a 32-bit machine, we'll take what we can get for each allocation - // and maintain arena_start and arena_end as min, max we've seen. - runtime·mheap.arena_start = (byte*)0xffffffff; - runtime·mheap.arena_end = 0; + // On a 32-bit machine, we can't typically get away + // with a giant virtual address space reservation. + // Instead we map the memory information bitmap + // immediately after the data segment, large enough + // to handle another 2GB of mappings (256 MB), + // along with a reservation for another 512 MB of memory. + // When that gets used up, we'll start asking the kernel + // for any memory anywhere and hope it's in the 2GB + // following the bitmap (presumably the executable begins + // near the bottom of memory, so we'll have to use up + // most of memory before the kernel resorts to giving out + // memory before the beginning of the text segment). + // + // Alternatively we could reserve 512 MB bitmap, enough + // for 4GB of mappings, and then accept any memory the + // kernel threw at us, but normally that's a waste of 512 MB + // of address space, which is probably too much in a 32-bit world. + bitmap_size = MaxArena32 / (sizeof(void*)*8/4); + arena_size = 512<<20; + + // SysReserve treats the address we ask for, end, as a hint, + // not as an absolute requirement. If we ask for the end + // of the data segment but the operating system requires + // a little more space before we can start allocating, it will + // give out a slightly higher pointer. That's fine. + // Run with what we get back. + p = runtime·SysReserve(end, bitmap_size + arena_size); + if(p == nil) + runtime·throw("runtime: cannot reserve arena virtual address space"); } + if((uintptr)p & (((uintptr)1<<PageShift)-1)) + runtime·throw("runtime: SysReserve returned unaligned address"); + + runtime·mheap.bitmap = p; + runtime·mheap.arena_start = p + bitmap_size; + runtime·mheap.arena_used = runtime·mheap.arena_start; + runtime·mheap.arena_end = runtime·mheap.arena_start + arena_size; // Initialize the rest of the allocator. runtime·MHeap_Init(&runtime·mheap, runtime·SysAlloc); @@ -299,26 +317,41 @@ void* runtime·MHeap_SysAlloc(MHeap *h, uintptr n) { byte *p; - - if(sizeof(void*) == 8) { + + if(n <= h->arena_end - h->arena_used) { // Keep taking from our reservation. - if(h->arena_end - h->arena_used < n) - return nil; p = h->arena_used; runtime·SysMap(p, n); h->arena_used += n; + runtime·MHeap_MapBits(h); return p; - } else { - // Take what we can get from the OS. - p = runtime·SysAlloc(n); - if(p == nil) - return nil; - if(p+n > h->arena_used) - h->arena_used = p+n; - if(p > h->arena_end) - h->arena_end = p; - return p; } + + // On 64-bit, our reservation is all we have. + if(sizeof(void*) == 8) + return nil; + + // On 32-bit, once the reservation is gone we can + // try to get memory at a location chosen by the OS + // and hope that it is in the range we allocated bitmap for. + p = runtime·SysAlloc(n); + if(p == nil) + return nil; + + if(p < h->arena_start || p+n - h->arena_start >= MaxArena32) { + runtime·printf("runtime: memory allocated by OS not in usable range"); + runtime·SysFree(p, n); + return nil; + } + + if(p+n > h->arena_used) { + h->arena_used = p+n; + if(h->arena_used > h->arena_end) + h->arena_end = h->arena_used; + runtime·MHeap_MapBits(h); + } + + return p; } // Runtime stubs. @@ -353,7 +386,6 @@ void* runtime·stackalloc(uint32 n) { void *v; - uint32 *ref; if(m->mallocing || m->gcing || n == FixedStack) { runtime·lock(&stacks); @@ -369,11 +401,7 @@ runtime·stackalloc(uint32 n) runtime·unlock(&stacks); return v; } - v = runtime·mallocgc(n, RefNoProfiling, 0, 0); - if(!runtime·mlookup(v, nil, nil, nil, &ref)) - runtime·throw("stackalloc runtime·mlookup"); - *ref = RefStack; - return v; + return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0); } void @@ -399,7 +427,7 @@ func Free(p *byte) { } func Lookup(p *byte) (base *byte, size uintptr) { - runtime·mlookup(p, &base, &size, nil, nil); + runtime·mlookup(p, &base, &size, nil); } func GC() { @@ -422,7 +450,7 @@ func SetFinalizer(obj Eface, finalizer Eface) { runtime·printf("runtime.SetFinalizer: first argument is %S, not pointer\n", *obj.type->string); goto throw; } - if(!runtime·mlookup(obj.data, &base, &size, nil, nil) || obj.data != base) { + if(!runtime·mlookup(obj.data, &base, &size, nil) || obj.data != base) { runtime·printf("runtime.SetFinalizer: pointer not at beginning of allocated block\n"); goto throw; } diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h index e2472e8d2..4e2794570 100644 --- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -97,8 +97,14 @@ typedef uintptr PageID; // address >> PageShift enum { + // Computed constant. The definition of MaxSmallSize and the + // algorithm in msize.c produce some number of different allocation + // size classes. NumSizeClasses is that number. It's needed here + // because there are static arrays of this length; when msize runs its + // size choosing algorithm it double-checks that NumSizeClasses agrees. + NumSizeClasses = 61, + // Tunable constants. - NumSizeClasses = 67, // Number of size classes (must match msize.c) MaxSmallSize = 32<<10, FixAllocChunk = 128<<10, // Chunk size for FixAlloc @@ -290,10 +296,7 @@ struct MSpan uint32 ref; // number of allocated objects in this span uint32 sizeclass; // size class uint32 state; // MSpanInUse etc - union { - uint32 *gcref; // sizeclass > 0 - uint32 gcref0; // sizeclass == 0 - }; + byte *limit; // end of data in span }; void runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages); @@ -336,6 +339,7 @@ struct MHeap // range of addresses we might see in the heap byte *bitmap; + uintptr bitmap_mapped; byte *arena_start; byte *arena_used; byte *arena_end; @@ -359,26 +363,29 @@ MSpan* runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct void runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct); MSpan* runtime·MHeap_Lookup(MHeap *h, void *v); MSpan* runtime·MHeap_LookupMaybe(MHeap *h, void *v); -void runtime·MGetSizeClassInfo(int32 sizeclass, int32 *size, int32 *npages, int32 *nobj); +void runtime·MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj); void* runtime·MHeap_SysAlloc(MHeap *h, uintptr n); +void runtime·MHeap_MapBits(MHeap *h); void* runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed); -int32 runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **s, uint32 **ref); +int32 runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **s); void runtime·gc(int32 force); +void runtime·markallocated(void *v, uintptr n, bool noptr); +void runtime·checkallocated(void *v, uintptr n); +void runtime·markfreed(void *v, uintptr n); +void runtime·checkfreed(void *v, uintptr n); +int32 runtime·checking; +void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover); +void runtime·unmarkspan(void *v, uintptr size); +bool runtime·blockspecial(void*); +void runtime·setblockspecial(void*); enum { - RefcountOverhead = 4, // one uint32 per object - - RefFree = 0, // must be zero - RefStack, // stack segment - don't free and don't scan for pointers - RefNone, // no references - RefSome, // some references - RefNoPointers = 0x80000000U, // flag - no pointers here - RefHasFinalizer = 0x40000000U, // flag - has finalizer - RefProfiled = 0x20000000U, // flag - is in profiling table - RefNoProfiling = 0x10000000U, // flag - must not profile - RefFlags = 0xFFFF0000U, + // flags to malloc + FlagNoPointers = 1<<0, // no pointers here + FlagNoProfiling = 1<<1, // must not profile + FlagNoGC = 1<<2, // must not free or scan for pointers }; void runtime·MProf_Malloc(void*, uintptr); diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c index f1ad119d3..29b03b58f 100644 --- a/src/pkg/runtime/mcentral.c +++ b/src/pkg/runtime/mcentral.c @@ -113,8 +113,7 @@ static void MCentral_Free(MCentral *c, void *v) { MSpan *s; - PageID page; - MLink *p, *next; + MLink *p; int32 size; // Find span for v. @@ -138,16 +137,8 @@ MCentral_Free(MCentral *c, void *v) if(--s->ref == 0) { size = runtime·class_to_size[c->sizeclass]; runtime·MSpanList_Remove(s); - // The second word of each freed block indicates - // whether it needs to be zeroed. The first word - // is the link pointer and must always be cleared. - for(p=s->freelist; p; p=next) { - next = p->next; - if(size > sizeof(uintptr) && ((uintptr*)p)[1] != 0) - runtime·memclr((byte*)p, size); - else - p->next = nil; - } + runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift); + *(uintptr*)(s->start<<PageShift) = 1; // needs zeroing s->freelist = nil; c->nfree -= (s->npages << PageShift) / size; runtime·unlock(c); @@ -157,7 +148,7 @@ MCentral_Free(MCentral *c, void *v) } void -runtime·MGetSizeClassInfo(int32 sizeclass, int32 *sizep, int32 *npagesp, int32 *nobj) +runtime·MGetSizeClassInfo(int32 sizeclass, uintptr *sizep, int32 *npagesp, int32 *nobj) { int32 size; int32 npages; @@ -166,7 +157,7 @@ runtime·MGetSizeClassInfo(int32 sizeclass, int32 *sizep, int32 *npagesp, int32 size = runtime·class_to_size[sizeclass]; *npagesp = npages; *sizep = size; - *nobj = (npages << PageShift) / (size + RefcountOverhead); + *nobj = (npages << PageShift) / size; } // Fetch a new span from the heap and @@ -174,7 +165,8 @@ runtime·MGetSizeClassInfo(int32 sizeclass, int32 *sizep, int32 *npagesp, int32 static bool MCentral_Grow(MCentral *c) { - int32 i, n, npages, size; + int32 i, n, npages; + uintptr size; MLink **tailp, *v; byte *p; MSpan *s; @@ -191,7 +183,7 @@ MCentral_Grow(MCentral *c) // Carve span into sequence of blocks. tailp = &s->freelist; p = (byte*)(s->start << PageShift); - s->gcref = (uint32*)(p + size*n); + s->limit = p + size*n; for(i=0; i<n; i++) { v = (MLink*)p; *tailp = v; @@ -199,6 +191,7 @@ MCentral_Grow(MCentral *c) p += size; } *tailp = nil; + runtime·markspan((byte*)(s->start<<PageShift), size, n, size*n < (s->npages<<PageShift)); runtime·lock(c); c->nfree += n; diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c index f73561b3c..03ee777c0 100644 --- a/src/pkg/runtime/mfinal.c +++ b/src/pkg/runtime/mfinal.c @@ -5,6 +5,7 @@ #include "runtime.h" #include "malloc.h" +// TODO(rsc): Why not just use mheap.Lock? static Lock finlock; // Finalizer hash table. Direct hash, linear scan, at most 3/4 full. @@ -101,24 +102,21 @@ runtime·addfinalizer(void *p, void (*f)(void*), int32 nret) } runtime·lock(&finlock); - if(!runtime·mlookup(p, &base, nil, nil, &ref) || p != base) { + if(!runtime·mlookup(p, &base, nil, nil) || p != base) { runtime·unlock(&finlock); runtime·throw("addfinalizer on invalid pointer"); } if(f == nil) { - if(*ref & RefHasFinalizer) { - lookfintab(&fintab, p, 1); - *ref &= ~RefHasFinalizer; - } + lookfintab(&fintab, p, 1); runtime·unlock(&finlock); return; } - if(*ref & RefHasFinalizer) { + if(lookfintab(&fintab, p, 0)) { runtime·unlock(&finlock); runtime·throw("double finalizer"); } - *ref |= RefHasFinalizer; + runtime·setblockspecial(p); if(fintab.nkey >= fintab.max/2+fintab.max/4) { // keep table at most 3/4 full: @@ -134,7 +132,7 @@ runtime·addfinalizer(void *p, void (*f)(void*), int32 nret) newtab.max *= 3; } - newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], RefNoPointers, 0, 1); + newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], FlagNoPointers, 0, 1); newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1); for(i=0; i<fintab.max; i++) { diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index af1c721e8..232c6cdcd 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -2,28 +2,66 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Garbage collector -- step 0. -// -// Stop the world, mark and sweep garbage collector. -// NOT INTENDED FOR PRODUCTION USE. -// -// A mark and sweep collector provides a way to exercise -// and test the memory allocator and the stack walking machinery -// without also needing to get reference counting -// exactly right. +// Garbage collector. #include "runtime.h" #include "malloc.h" enum { - Debug = 0 + Debug = 0, + UseCas = 1, + PtrSize = sizeof(void*), + + // Four bits per word (see #defines below). + wordsPerBitmapWord = sizeof(void*)*8/4, + bitShift = sizeof(void*)*8/4, }; -typedef struct BlockList BlockList; -struct BlockList +// Bits in per-word bitmap. +// #defines because enum might not be able to hold the values. +// +// Each word in the bitmap describes wordsPerBitmapWord words +// of heap memory. There are 4 bitmap bits dedicated to each heap word, +// so on a 64-bit system there is one bitmap word per 16 heap words. +// The bits in the word are packed together by type first, then by +// heap location, so each 64-bit bitmap word consists of, from top to bottom, +// the 16 bitSpecial bits for the corresponding heap words, then the 16 bitMarked bits, +// then the 16 bitNoPointers/bitBlockBoundary bits, then the 16 bitAllocated bits. +// This layout makes it easier to iterate over the bits of a given type. +// +// The bitmap starts at mheap.arena_start and extends *backward* from +// there. On a 64-bit system the off'th word in the arena is tracked by +// the off/16+1'th word before mheap.arena_start. (On a 32-bit system, +// the only difference is that the divisor is 8.) +// +// To pull out the bits corresponding to a given pointer p, we use: +// +// off = p - (uintptr*)mheap.arena_start; // word offset +// b = (uintptr*)mheap.arena_start - off/wordsPerBitmapWord - 1; +// shift = off % wordsPerBitmapWord +// bits = *b >> shift; +// /* then test bits & bitAllocated, bits & bitMarked, etc. */ +// +#define bitAllocated ((uintptr)1<<(bitShift*0)) +#define bitNoPointers ((uintptr)1<<(bitShift*1)) /* when bitAllocated is set */ +#define bitMarked ((uintptr)1<<(bitShift*2)) /* when bitAllocated is set */ +#define bitSpecial ((uintptr)1<<(bitShift*3)) /* when bitAllocated is set - has finalizer or being profiled */ +#define bitBlockBoundary ((uintptr)1<<(bitShift*1)) /* when bitAllocated is NOT set */ + +#define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial) + +static uint64 nlookup; +static uint64 nsizelookup; +static uint64 naddrlookup; +static uint64 nhandoff; +static int32 gctrace; + +typedef struct Workbuf Workbuf; +struct Workbuf { - byte *obj; - uintptr size; + Workbuf *next; + uintptr nw; + byte *w[2048-2]; }; extern byte data[]; @@ -33,72 +71,258 @@ extern byte end[]; static G *fing; static Finalizer *finq; static int32 fingwait; -static BlockList *bl, *ebl; +static uint32 nfullwait; static void runfinq(void); - -enum { - PtrSize = sizeof(void*) -}; - +static bool bitlookup(void*, uintptr**, uintptr*, int32*); +static Workbuf* getempty(Workbuf*); +static Workbuf* getfull(Workbuf*); + +// scanblock scans a block of n bytes starting at pointer b for references +// to other objects, scanning any it finds recursively until there are no +// unscanned objects left. Instead of using an explicit recursion, it keeps +// a work list in the Workbuf* structures and loops in the main function +// body. Keeping an explicit work list is easier on the stack allocator and +// more efficient. static void scanblock(byte *b, int64 n) { - int32 off; - void *obj; - uintptr size; - uint32 *refp, ref; + byte *obj, *arena_start, *p; void **vp; - int64 i; - BlockList *w; - - w = bl; - w->obj = b; - w->size = n; - w++; + uintptr size, *bitp, bits, shift, i, j, x, xbits, off; + MSpan *s; + PageID k; + void **bw, **w, **ew; + Workbuf *wbuf; - while(w > bl) { - w--; - b = w->obj; - n = w->size; + // Memory arena parameters. + arena_start = runtime·mheap.arena_start; + + wbuf = nil; // current work buffer + ew = nil; // end of work buffer + bw = nil; // beginning of work buffer + w = nil; // current pointer into work buffer + + // Align b to a word boundary. + off = (uintptr)b & (PtrSize-1); + if(off != 0) { + b += PtrSize - off; + n -= PtrSize - off; + } + for(;;) { + // Each iteration scans the block b of length n, queueing pointers in + // the work buffer. if(Debug > 1) runtime·printf("scanblock %p %D\n", b, n); - off = (uint32)(uintptr)b & (PtrSize-1); - if(off) { - b += PtrSize - off; - n -= PtrSize - off; - } - + vp = (void**)b; n /= PtrSize; for(i=0; i<n; i++) { - obj = vp[i]; - if(obj == nil) + obj = (byte*)vp[i]; + + // Words outside the arena cannot be pointers. + if((byte*)obj < arena_start || (byte*)obj >= runtime·mheap.arena_used) continue; - if(runtime·mheap.arena_start <= (byte*)obj && (byte*)obj < runtime·mheap.arena_end) { - if(runtime·mlookup(obj, &obj, &size, nil, &refp)) { - ref = *refp; - switch(ref & ~RefFlags) { - case RefNone: - if(Debug > 1) - runtime·printf("found at %p: ", &vp[i]); - *refp = RefSome | (ref & RefFlags); - if(!(ref & RefNoPointers)) { - if(w >= ebl) - runtime·throw("scanblock: garbage collection stack overflow"); - w->obj = obj; - w->size = size; - w++; - } - break; - } + + // obj may be a pointer to a live object. + // Try to find the beginning of the object. + + // Round down to word boundary. + obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); + + // Find bits for this word. + off = (uintptr*)obj - (uintptr*)arena_start; + bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + xbits = *bitp; + bits = xbits >> shift; + + // Pointing at the beginning of a block? + if((bits & (bitAllocated|bitBlockBoundary)) != 0) + goto found; + + // Pointing just past the beginning? + // Scan backward a little to find a block boundary. + for(j=shift; j-->0; ) { + if(((xbits>>j) & (bitAllocated|bitBlockBoundary)) != 0) { + obj = (byte*)obj - (shift-j)*PtrSize; + shift = j; + bits = xbits>>shift; + goto found; } } + + // Otherwise consult span table to find beginning. + // (Manually inlined copy of MHeap_LookupMaybe.) + nlookup++; + naddrlookup++; + k = (uintptr)obj>>PageShift; + x = k; + if(sizeof(void*) == 8) + x -= (uintptr)arena_start>>PageShift; + s = runtime·mheap.map[x]; + if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse) + continue; + p = (byte*)((uintptr)s->start<<PageShift); + if(s->sizeclass == 0) { + obj = p; + } else { + if((byte*)obj >= (byte*)s->limit) + continue; + size = runtime·class_to_size[s->sizeclass]; + int32 i = ((byte*)obj - p)/size; + obj = p+i*size; + } + + // Now that we know the object header, reload bits. + off = (uintptr*)obj - (uintptr*)arena_start; + bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + xbits = *bitp; + bits = xbits >> shift; + + found: + // Now we have bits, bitp, and shift correct for + // obj pointing at the base of the object. + // If not allocated or already marked, done. + if((bits & bitAllocated) == 0 || (bits & bitMarked) != 0) + continue; + *bitp |= bitMarked<<shift; + + // If object has no pointers, don't need to scan further. + if((bits & bitNoPointers) != 0) + continue; + + // If buffer is full, get a new one. + if(w >= ew) { + wbuf = getempty(wbuf); + bw = wbuf->w; + w = bw; + ew = bw + nelem(wbuf->w); + } + *w++ = obj; + } + + // Done scanning [b, b+n). Prepare for the next iteration of + // the loop by setting b and n to the parameters for the next block. + + // Fetch b from the work buffers. + if(w <= bw) { + // Emptied our buffer: refill. + wbuf = getfull(wbuf); + if(wbuf == nil) + break; + bw = wbuf->w; + ew = wbuf->w + nelem(wbuf->w); + w = bw+wbuf->nw; } + b = *--w; + + // Figure out n = size of b. Start by loading bits for b. + off = (uintptr*)b - (uintptr*)arena_start; + bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + xbits = *bitp; + bits = xbits >> shift; + + // Might be small; look for nearby block boundary. + // A block boundary is marked by either bitBlockBoundary + // or bitAllocated being set (see notes near their definition). + enum { + boundary = bitBlockBoundary|bitAllocated + }; + // Look for a block boundary both after and before b + // in the same bitmap word. + // + // A block boundary j words after b is indicated by + // bits>>j & boundary + // assuming shift+j < bitShift. (If shift+j >= bitShift then + // we'll be bleeding other bit types like bitMarked into our test.) + // Instead of inserting the conditional shift+j < bitShift into the loop, + // we can let j range from 1 to bitShift as long as we first + // apply a mask to keep only the bits corresponding + // to shift+j < bitShift aka j < bitShift-shift. + bits &= (boundary<<(bitShift-shift)) - boundary; + + // A block boundary j words before b is indicated by + // xbits>>(shift-j) & boundary + // (assuming shift >= j). There is no cleverness here + // avoid the test, because when j gets too large the shift + // turns negative, which is undefined in C. + + for(j=1; j<bitShift; j++) { + if(((bits>>j)&boundary) != 0 || shift>=j && ((xbits>>(shift-j))&boundary) != 0) { + n = j*PtrSize; + goto scan; + } + } + + // Fall back to asking span about size class. + // (Manually inlined copy of MHeap_Lookup.) + nlookup++; + nsizelookup++; + x = (uintptr)b>>PageShift; + if(sizeof(void*) == 8) + x -= (uintptr)arena_start>>PageShift; + s = runtime·mheap.map[x]; + if(s->sizeclass == 0) + n = s->npages<<PageShift; + else + n = runtime·class_to_size[s->sizeclass]; + scan:; + } +} + +static struct { + Workbuf *full; + Workbuf *empty; + byte *chunk; + uintptr nchunk; +} work; + +// Get an empty work buffer off the work.empty list, +// allocating new buffers as needed. +static Workbuf* +getempty(Workbuf *b) +{ + if(b != nil) { + b->nw = nelem(b->w); + b->next = work.full; + work.full = b; + } + b = work.empty; + if(b != nil) { + work.empty = b->next; + return b; + } + + if(work.nchunk < sizeof *b) { + work.nchunk = 1<<20; + work.chunk = runtime·SysAlloc(work.nchunk); + } + b = (Workbuf*)work.chunk; + work.chunk += sizeof *b; + work.nchunk -= sizeof *b; + return b; +} + +// Get a full work buffer off the work.full list, or return nil. +static Workbuf* +getfull(Workbuf *b) +{ + if(b != nil) { + b->nw = 0; + b->next = work.empty; + work.empty = b; } + b = work.full; + if(b != nil) + work.full = b->next; + return b; } +// Scanstack calls scanblock on each of gp's stack segments. static void scanstack(G *gp) { @@ -119,46 +343,26 @@ scanstack(G *gp) } } +// Markfin calls scanblock on the blocks that have finalizers: +// the things pointed at cannot be freed until the finalizers have run. static void markfin(void *v) { uintptr size; - uint32 *refp; size = 0; - refp = nil; - if(!runtime·mlookup(v, &v, &size, nil, &refp) || !(*refp & RefHasFinalizer)) + if(!runtime·mlookup(v, &v, &size, nil) || !runtime·blockspecial(v)) runtime·throw("mark - finalizer inconsistency"); - + // do not mark the finalizer block itself. just mark the things it points at. scanblock(v, size); } +// Mark static void mark(void) { G *gp; - uintptr blsize, nobj; - - // Figure out how big an object stack we need. - // Get a new one if we need more than we have - // or we need significantly less than we have. - nobj = mstats.heap_objects; - if(nobj > ebl - bl || nobj < (ebl-bl)/4) { - if(bl != nil) - runtime·SysFree(bl, (byte*)ebl - (byte*)bl); - - // While we're allocated a new object stack, - // add 20% headroom and also round up to - // the nearest page boundary, since mmap - // will anyway. - nobj = nobj * 12/10; - blsize = nobj * sizeof *bl; - blsize = (blsize + 4095) & ~4095; - nobj = blsize / sizeof *bl; - bl = runtime·SysAlloc(blsize); - ebl = bl + nobj; - } // mark data+bss. // skip runtime·mheap itself, which has no interesting pointers @@ -192,97 +396,85 @@ mark(void) runtime·walkfintab(markfin); } -// free RefNone, free & queue finalizers for RefNone|RefHasFinalizer, reset RefSome +// Sweep frees or calls finalizers for blocks not marked in the mark phase. +// It clears the mark bits in preparation for the next GC round. static void -sweepspan(MSpan *s) +sweep(void) { - int32 n, npages, size; + MSpan *s; + int32 cl, n, npages; + uintptr size; byte *p; - uint32 ref, *gcrefp, *gcrefep; MCache *c; Finalizer *f; - p = (byte*)(s->start << PageShift); - if(s->sizeclass == 0) { - // Large block. - ref = s->gcref0; - switch(ref & ~(RefFlags^RefHasFinalizer)) { - case RefNone: - // Free large object. - mstats.alloc -= s->npages<<PageShift; - mstats.nfree++; - runtime·memclr(p, s->npages<<PageShift); - if(ref & RefProfiled) - runtime·MProf_Free(p, s->npages<<PageShift); - s->gcref0 = RefFree; - runtime·MHeap_Free(&runtime·mheap, s, 1); - break; - case RefNone|RefHasFinalizer: - f = runtime·getfinalizer(p, 1); - if(f == nil) - runtime·throw("finalizer inconsistency"); - f->arg = p; - f->next = finq; - finq = f; - ref &= ~RefHasFinalizer; - // fall through - case RefSome: - case RefSome|RefHasFinalizer: - s->gcref0 = RefNone | (ref&RefFlags); - break; + for(s = runtime·mheap.allspans; s != nil; s = s->allnext) { + if(s->state != MSpanInUse) + continue; + + p = (byte*)(s->start << PageShift); + cl = s->sizeclass; + if(cl == 0) { + size = s->npages<<PageShift; + n = 1; + } else { + // Chunk full of small blocks. + size = runtime·class_to_size[cl]; + npages = runtime·class_to_allocnpages[cl]; + n = (npages << PageShift) / size; } - return; - } + + // sweep through n objects of given size starting at p. + for(; n > 0; n--, p += size) { + uintptr off, *bitp, shift, bits; - // Chunk full of small blocks. - runtime·MGetSizeClassInfo(s->sizeclass, &size, &npages, &n); - gcrefp = s->gcref; - gcrefep = s->gcref + n; - for(; gcrefp < gcrefep; gcrefp++, p += size) { - ref = *gcrefp; - if(ref < RefNone) // RefFree or RefStack - continue; - switch(ref & ~(RefFlags^RefHasFinalizer)) { - case RefNone: - // Free small object. - if(ref & RefProfiled) + off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; + bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + bits = *bitp>>shift; + + if((bits & bitAllocated) == 0) + continue; + + if((bits & bitMarked) != 0) { + *bitp &= ~(bitMarked<<shift); + continue; + } + + if((bits & bitSpecial) != 0) { + // Special means it has a finalizer or is being profiled. + f = runtime·getfinalizer(p, 1); + if(f != nil) { + f->arg = p; + f->next = finq; + finq = f; + continue; + } runtime·MProf_Free(p, size); - *gcrefp = RefFree; - c = m->mcache; - if(size > sizeof(uintptr)) - ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed" + } + + // Mark freed; restore block boundary bit. + *bitp = (*bitp & ~(bitMask<<shift)) | (bitBlockBoundary<<shift); + + if(s->sizeclass == 0) { + // Free large span. + runtime·unmarkspan(p, 1<<PageShift); + *(uintptr*)p = 1; // needs zeroing + runtime·MHeap_Free(&runtime·mheap, s, 1); + } else { + // Free small object. + c = m->mcache; + if(size > sizeof(uintptr)) + ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed" + mstats.by_size[s->sizeclass].nfree++; + runtime·MCache_Free(c, p, s->sizeclass, size); + } mstats.alloc -= size; mstats.nfree++; - mstats.by_size[s->sizeclass].nfree++; - runtime·MCache_Free(c, p, s->sizeclass, size); - break; - case RefNone|RefHasFinalizer: - f = runtime·getfinalizer(p, 1); - if(f == nil) - runtime·throw("finalizer inconsistency"); - f->arg = p; - f->next = finq; - finq = f; - ref &= ~RefHasFinalizer; - // fall through - case RefSome: - case RefSome|RefHasFinalizer: - *gcrefp = RefNone | (ref&RefFlags); - break; } } } -static void -sweep(void) -{ - MSpan *s; - - for(s = runtime·mheap.allspans; s != nil; s = s->allnext) - if(s->state == MSpanInUse) - sweepspan(s); -} - // Semaphore, not Lock, so that the goroutine // reschedules when there is contention rather // than spinning. @@ -326,7 +518,8 @@ cachestats(void) void runtime·gc(int32 force) { - int64 t0, t1; + int64 t0, t1, t2, t3; + uint64 heap0, heap1, obj0, obj1; byte *p; Finalizer *fp; @@ -349,23 +542,41 @@ runtime·gc(int32 force) gcpercent = -1; else gcpercent = runtime·atoi(p); + + p = runtime·getenv("GOGCTRACE"); + if(p != nil) + gctrace = runtime·atoi(p); } if(gcpercent < 0) return; runtime·semacquire(&gcsema); + if(!force && mstats.heap_alloc < mstats.next_gc) { + runtime·semrelease(&gcsema); + return; + } + t0 = runtime·nanotime(); + nlookup = 0; + nsizelookup = 0; + naddrlookup = 0; + m->gcing = 1; runtime·stoptheworld(); if(runtime·mheap.Lock.key != 0) runtime·throw("runtime·mheap locked during gc"); - if(force || mstats.heap_alloc >= mstats.next_gc) { - cachestats(); - mark(); - sweep(); - stealcache(); - mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100; - } + + cachestats(); + heap0 = mstats.heap_alloc; + obj0 = mstats.nmalloc - mstats.nfree; + + mark(); + t1 = runtime·nanotime(); + sweep(); + t2 = runtime·nanotime(); + stealcache(); + + mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100; m->gcing = 0; m->locks++; // disable gc during the mallocs in newproc @@ -381,18 +592,34 @@ runtime·gc(int32 force) } m->locks--; - t1 = runtime·nanotime(); + cachestats(); + heap1 = mstats.heap_alloc; + obj1 = mstats.nmalloc - mstats.nfree; + + t3 = runtime·nanotime(); + mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0; + mstats.pause_total_ns += t3 - t0; mstats.numgc++; - mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t1 - t0; - mstats.pause_total_ns += t1 - t0; if(mstats.debuggc) - runtime·printf("pause %D\n", t1-t0); + runtime·printf("pause %D\n", t3-t0); + + if(gctrace) { + runtime·printf("gc%d: %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D pointer lookups (%D size, %D addr)\n", + mstats.numgc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000, + heap0>>20, heap1>>20, obj0, obj1, + mstats.nmalloc, mstats.nfree, + nlookup, nsizelookup, naddrlookup); + } + runtime·semrelease(&gcsema); runtime·starttheworld(); // give the queued finalizers, if any, a chance to run if(fp != nil) runtime·gosched(); + + if(gctrace > 1 && !force) + runtime·gc(1); } static void @@ -430,3 +657,157 @@ runfinq(void) runtime·gc(1); // trigger another gc to clean up the finalized objects, if possible } } + +// mark the block at v of size n as allocated. +// If noptr is true, mark it as having no pointers. +void +runtime·markallocated(void *v, uintptr n, bool noptr) +{ + uintptr *b, bits, off, shift; + + if(0) + runtime·printf("markallocated %p+%p\n", v, n); + + if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + runtime·throw("markallocated: bad pointer"); + + off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + + bits = (*b & ~(bitMask<<shift)) | (bitAllocated<<shift); + if(noptr) + bits |= bitNoPointers<<shift; + *b = bits; +} + +// mark the block at v of size n as freed. +void +runtime·markfreed(void *v, uintptr n) +{ + uintptr *b, off, shift; + + if(0) + runtime·printf("markallocated %p+%p\n", v, n); + + if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + runtime·throw("markallocated: bad pointer"); + + off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + + *b = (*b & ~(bitMask<<shift)) | (bitBlockBoundary<<shift); +} + +// check that the block at v of size n is marked freed. +void +runtime·checkfreed(void *v, uintptr n) +{ + uintptr *b, bits, off, shift; + + if(!runtime·checking) + return; + + if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + return; // not allocated, so okay + + off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + + bits = *b>>shift; + if((bits & bitAllocated) != 0) { + runtime·printf("checkfreed %p+%p: off=%p have=%p\n", + v, n, off, bits & bitMask); + runtime·throw("checkfreed: not freed"); + } +} + +// mark the span of memory at v as having n blocks of the given size. +// if leftover is true, there is left over space at the end of the span. +void +runtime·markspan(void *v, uintptr size, uintptr n, bool leftover) +{ + uintptr *b, off, shift; + byte *p; + + if((byte*)v+size*n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + runtime·throw("markspan: bad pointer"); + + p = v; + if(leftover) // mark a boundary just past end of last block too + n++; + for(; n-- > 0; p += size) { + off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; // word offset + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + *b = (*b & ~(bitMask<<shift)) | (bitBlockBoundary<<shift); + } +} + +// unmark the span of memory at v of length n bytes. +void +runtime·unmarkspan(void *v, uintptr n) +{ + uintptr *p, *b, off; + + if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + runtime·throw("markspan: bad pointer"); + + p = v; + off = p - (uintptr*)runtime·mheap.arena_start; // word offset + if(off % wordsPerBitmapWord != 0) + runtime·throw("markspan: unaligned pointer"); + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + n /= PtrSize; + if(n%wordsPerBitmapWord != 0) + runtime·throw("unmarkspan: unaligned length"); + n /= wordsPerBitmapWord; + while(n-- > 0) + *b-- = 0; +} + +bool +runtime·blockspecial(void *v) +{ + uintptr *b, off, shift; + + off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + + return (*b & (bitSpecial<<shift)) != 0; +} + +void +runtime·setblockspecial(void *v) +{ + uintptr *b, off, shift; + + off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; + b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + + *b |= bitSpecial<<shift; +} + +void +runtime·MHeap_MapBits(MHeap *h) +{ + // Caller has added extra mappings to the arena. + // Add extra mappings of bitmap words as needed. + // We allocate extra bitmap pieces in chunks of bitmapChunk. + enum { + bitmapChunk = 8192 + }; + uintptr n; + + n = (h->arena_used - h->arena_start) / wordsPerBitmapWord; + n = (n+bitmapChunk-1) & ~(bitmapChunk-1); + if(h->bitmap_mapped >= n) + return; + + runtime·SysMap(h->arena_start - n, n - h->bitmap_mapped); + h->bitmap_mapped = n; +} diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c index 0c9ac0a09..8061b7cf8 100644 --- a/src/pkg/runtime/mheap.c +++ b/src/pkg/runtime/mheap.c @@ -180,7 +180,9 @@ MHeap_Grow(MHeap *h, uintptr npage) // Allocate a multiple of 64kB (16 pages). npage = (npage+15)&~15; ask = npage<<PageShift; - if(ask < HeapAllocChunk) + if(ask > h->arena_end - h->arena_used) + return false; + if(ask < HeapAllocChunk && HeapAllocChunk <= h->arena_end - h->arena_used) ask = HeapAllocChunk; v = runtime·MHeap_SysAlloc(h, ask); @@ -194,11 +196,6 @@ MHeap_Grow(MHeap *h, uintptr npage) } mstats.heap_sys += ask; - if((byte*)v < h->arena_start || h->arena_start == nil) - h->arena_start = v; - if((byte*)v+ask > h->arena_end) - h->arena_end = (byte*)v+ask; - // Create a fake "in use" span and free it, so that the // right coalescing happens. s = runtime·FixAlloc_Alloc(&h->spanalloc); @@ -370,10 +367,14 @@ runtime·MSpanList_IsEmpty(MSpan *list) void runtime·MSpanList_Insert(MSpan *list, MSpan *span) { - if(span->next != nil || span->prev != nil) + if(span->next != nil || span->prev != nil) { + runtime·printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev); runtime·throw("MSpanList_Insert"); + } span->next = list->next; span->prev = list; span->next->prev = span; span->prev->next = span; } + + diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh index 3ed5f74c9..91d1bbe5d 100755 --- a/src/pkg/runtime/mkasmh.sh +++ b/src/pkg/runtime/mkasmh.sh @@ -25,9 +25,9 @@ case "$GOARCH" in echo '#define m(r) 4(r)' ;; plan9) - echo '#define get_tls(r)' - echo '#define g(r) 0xdfffefc0' - echo '#define m(r) 0xdfffefc4' + echo '#define get_tls(r) MOVL _tos(SB), r ' + echo '#define g(r) -8(r)' + echo '#define m(r) -4(r)' ;; linux) # On Linux systems, what we call 0(GS) and 4(GS) for g and m @@ -84,6 +84,7 @@ esac echo awk ' +{ gsub(/\r/, ""); } /^aggr G$/ { aggr="g" } /^aggr M$/ { aggr = "m" } /^aggr Gobuf$/ { aggr = "gobuf" } diff --git a/src/pkg/runtime/mkversion.c b/src/pkg/runtime/mkversion.c index 9790d3f09..56afa1892 100644 --- a/src/pkg/runtime/mkversion.c +++ b/src/pkg/runtime/mkversion.c @@ -5,13 +5,11 @@ char *template = "// generated by mkversion.c; do not edit.\n" "package runtime\n" "const defaultGoroot = \"%s\"\n" - "const theVersion = \"%s\"\n" - "const theGoarch = \"%s\"\n" - "const theGoos = \"%s\"\n"; + "const theVersion = \"%s\"\n"; void main(void) { - print(template, getgoroot(), getgoversion(), getgoarch(), getgoos()); + print(template, getgoroot(), getgoversion()); exits(0); } diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc index f4581e98d..aae3d183f 100644 --- a/src/pkg/runtime/mprof.goc +++ b/src/pkg/runtime/mprof.goc @@ -65,7 +65,7 @@ stkbucket(uintptr *stk, int32 nstk) runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0) return b; - b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], RefNoProfiling, 0, 1); + b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1); bucketmem += sizeof *b + nstk*sizeof stk[0]; runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); b->hash = h; @@ -132,7 +132,7 @@ setaddrbucket(uintptr addr, Bucket *b) if(ah->addr == (addr>>20)) goto found; - ah = runtime·mallocgc(sizeof *ah, RefNoProfiling, 0, 1); + ah = runtime·mallocgc(sizeof *ah, FlagNoProfiling, 0, 1); addrmem += sizeof *ah; ah->next = addrhash[h]; ah->addr = addr>>20; @@ -140,7 +140,7 @@ setaddrbucket(uintptr addr, Bucket *b) found: if((e = addrfree) == nil) { - e = runtime·mallocgc(64*sizeof *e, RefNoProfiling, 0, 0); + e = runtime·mallocgc(64*sizeof *e, FlagNoProfiling, 0, 0); addrmem += 64*sizeof *e; for(i=0; i+1<64; i++) e[i].next = &e[i+1]; diff --git a/src/pkg/runtime/msize.c b/src/pkg/runtime/msize.c index ec85eb373..770ef38ce 100644 --- a/src/pkg/runtime/msize.c +++ b/src/pkg/runtime/msize.c @@ -57,7 +57,7 @@ runtime·SizeToClass(int32 size) void runtime·InitSizes(void) { - int32 align, sizeclass, size, osize, nextsize, n; + int32 align, sizeclass, size, nextsize, n; uint32 i; uintptr allocsize, npages; @@ -81,8 +81,7 @@ runtime·InitSizes(void) // the leftover is less than 1/8 of the total, // so wasted space is at most 12.5%. allocsize = PageSize; - osize = size + RefcountOverhead; - while(allocsize%osize > (allocsize/8)) + while(allocsize%size > allocsize/8) allocsize += PageSize; npages = allocsize >> PageShift; @@ -93,7 +92,7 @@ runtime·InitSizes(void) // different sizes. if(sizeclass > 1 && npages == runtime·class_to_allocnpages[sizeclass-1] - && allocsize/osize == allocsize/(runtime·class_to_size[sizeclass-1]+RefcountOverhead)) { + && allocsize/size == allocsize/runtime·class_to_size[sizeclass-1]) { runtime·class_to_size[sizeclass-1] = size; continue; } diff --git a/src/pkg/runtime/plan9/386/defs.h b/src/pkg/runtime/plan9/386/defs.h index 5df757613..58fd9d94d 100644 --- a/src/pkg/runtime/plan9/386/defs.h +++ b/src/pkg/runtime/plan9/386/defs.h @@ -1 +1,2 @@ // nothing to see here +#define tos_pid 48 diff --git a/src/pkg/runtime/plan9/386/sys.s b/src/pkg/runtime/plan9/386/sys.s index 867b8940f..f760b782f 100644 --- a/src/pkg/runtime/plan9/386/sys.s +++ b/src/pkg/runtime/plan9/386/sys.s @@ -58,9 +58,10 @@ TEXT runtime·rfork(SB),7,$0 MOVL BX, m(AX) // Initialize AX from _tos->pid - MOVL 0xdfffeff8, AX + MOVL _tos(SB), AX + MOVL tos_pid(AX), AX MOVL AX, m_procid(BX) // save pid as m->procid - + CALL runtime·stackcheck(SB) // smashes AX, CX MOVL 0(DX), DX // paranoia; check they are not nil diff --git a/src/pkg/runtime/plan9/mem.c b/src/pkg/runtime/plan9/mem.c index 651e6728e..b840de984 100644 --- a/src/pkg/runtime/plan9/mem.c +++ b/src/pkg/runtime/plan9/mem.c @@ -10,40 +10,47 @@ static byte *bloc = { end }; enum { - Round = 7 + Round = 4095 }; void* -runtime·SysAlloc(uintptr ask) +runtime·SysAlloc(uintptr nbytes) { uintptr bl; // Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c bl = ((uintptr)bloc + Round) & ~Round; - if(runtime·brk_((void*)(bl + ask)) < 0) + if(runtime·brk_((void*)(bl + nbytes)) < 0) return (void*)-1; - bloc = (byte*)bl + ask; + bloc = (byte*)bl + nbytes; return (void*)bl; } void -runtime·SysFree(void *v, uintptr n) +runtime·SysFree(void *v, uintptr nbytes) { // from tiny/mem.c // Push pointer back if this is a free // of the most recent SysAlloc. - n += (n + Round) & ~Round; - if(bloc == (byte*)v+n) - bloc -= n; + nbytes += (nbytes + Round) & ~Round; + if(bloc == (byte*)v+nbytes) + bloc -= nbytes; } void -runtime·SysUnused(void *v, uintptr n) +runtime·SysUnused(void *v, uintptr nbytes) { - USED(v, n); + USED(v, nbytes); } void -runtime·SysMemInit(void) +runtime·SysMap(void *v, uintptr nbytes) { + USED(v, nbytes); +} + +void* +runtime·SysReserve(void *v, uintptr nbytes) +{ + return runtime·SysAlloc(nbytes); } diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 998cbc7bc..26c1f13a4 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -752,8 +752,8 @@ runtime·newstack(void) free = framesize; } -//printf("newstack frame=%d args=%d morepc=%p morefp=%p gobuf=%p, %p newstk=%p\n", -//frame, args, m->morepc, m->morefp, g->sched.pc, g->sched.sp, stk); +//runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", +//framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, g1->stackbase); top->stackbase = g1->stackbase; top->stackguard = g1->stackguard; @@ -761,7 +761,7 @@ runtime·newstack(void) top->argp = m->moreargp; top->argsize = argsize; top->free = free; - + // copy flag from panic top->panic = g1->ispanic; g1->ispanic = false; diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c index 284b1e458..e3a20d48a 100644 --- a/src/pkg/runtime/runtime.c +++ b/src/pkg/runtime/runtime.c @@ -528,14 +528,22 @@ void runtime·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool retbool) { Func *f; + uintptr pc; - if(runtime·callers(1+skip, &retpc, 1) == 0 || (f = runtime·findfunc(retpc-1)) == nil) { + if(runtime·callers(1+skip, &retpc, 1) == 0) { retfile = runtime·emptystring; retline = 0; retbool = false; + } else if((f = runtime·findfunc(retpc)) == nil) { + retfile = runtime·emptystring; + retline = 0; + retbool = true; // have retpc at least } else { retfile = f->src; - retline = runtime·funcline(f, retpc-1); + pc = retpc; + if(pc > f->entry) + pc--; + retline = runtime·funcline(f, pc); retbool = true; } FLUSH(&retfile); diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 2c19f851e..cea07e4a7 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -419,7 +419,7 @@ void runtime·signalstack(byte*, int32); G* runtime·malg(int32); void runtime·minit(void); Func* runtime·findfunc(uintptr); -int32 runtime·funcline(Func*, uint64); +int32 runtime·funcline(Func*, uintptr); void* runtime·stackalloc(uint32); void runtime·stackfree(void*, uintptr); MCache* runtime·allocmcache(void); @@ -443,7 +443,7 @@ void runtime·breakpoint(void); void runtime·gosched(void); void runtime·goexit(void); void runtime·runcgo(void (*fn)(void*), void*); -uintptr runtime·runcgocallback(G*, void*, void (*fn)()); +void runtime·runcgocallback(G*, void*, void (*fn)()); void runtime·entersyscall(void); void runtime·exitsyscall(void); void runtime·startcgocallback(G*); diff --git a/src/pkg/runtime/slice.c b/src/pkg/runtime/slice.c index 051075479..1fee923e4 100644 --- a/src/pkg/runtime/slice.c +++ b/src/pkg/runtime/slice.c @@ -41,7 +41,7 @@ makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret) ret->cap = cap; if((t->elem->kind&KindNoPointers)) - ret->array = runtime·mallocgc(size, RefNoPointers, 1, 1); + ret->array = runtime·mallocgc(size, FlagNoPointers, 1, 1); else ret->array = runtime·mal(size); } diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc index 916559eb2..b72aa937c 100644 --- a/src/pkg/runtime/string.goc +++ b/src/pkg/runtime/string.goc @@ -225,7 +225,7 @@ func slicebytetostring(b Slice) (s String) { } func stringtoslicebyte(s String) (b Slice) { - b.array = runtime·mallocgc(s.len, RefNoPointers, 1, 1); + b.array = runtime·mallocgc(s.len, FlagNoPointers, 1, 1); b.len = s.len; b.cap = s.len; runtime·mcpy(b.array, s.str, s.len); @@ -268,7 +268,7 @@ func stringtosliceint(s String) (b Slice) { n++; } - b.array = runtime·mallocgc(n*sizeof(r[0]), RefNoPointers, 1, 1); + b.array = runtime·mallocgc(n*sizeof(r[0]), FlagNoPointers, 1, 1); b.len = n; b.cap = n; p = s.str; diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c index b2cccd3cf..6f0eea0e7 100644 --- a/src/pkg/runtime/symtab.c +++ b/src/pkg/runtime/symtab.c @@ -258,28 +258,49 @@ splitpcln(void) ef = func + nfunc; pc = func[0].entry; // text base f->pcln.array = p; - f->pc0 = pc - pcquant; + f->pc0 = pc; line = 0; - for(; p < ep; p++) { - if(f < ef && pc > (f+1)->entry) { + for(;;) { + while(p < ep && *p > 128) + pc += pcquant * (*p++ - 128); + // runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line); + if(*p == 0) { + if(p+5 > ep) + break; + // 4 byte add to line + line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; + p += 5; + } else if(*p <= 64) + line += *p++; + else + line -= *p++ - 64; + + // pc, line now match. + // Because the state machine begins at pc==entry and line==0, + // it can happen - just at the beginning! - that the update may + // have updated line but left pc alone, to tell us the true line + // number for pc==entry. In that case, update f->ln0. + // Having the correct initial line number is important for choosing + // the correct file in dosrcline above. + if(f == func && pc == f->pc0) { + f->pcln.array = p; + f->pc0 = pc + pcquant; + f->ln0 = line; + } + + if(f < ef && pc >= (f+1)->entry) { f->pcln.len = p - f->pcln.array; f->pcln.cap = f->pcln.len; f++; f->pcln.array = p; - f->pc0 = pc; + // pc0 and ln0 are the starting values for + // the loop over f->pcln, so pc must be + // adjusted by the same pcquant update + // that we're going to do as we continue our loop. + f->pc0 = pc + pcquant; f->ln0 = line; } - if(*p == 0) { - // 4 byte add to line - line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; - p += 4; - } else if(*p <= 64) { - line += *p; - } else if(*p <= 128) { - line -= *p - 64; - } else { - pc += pcquant*(*p - 129); - } + pc += pcquant; } if(f < ef) { @@ -293,13 +314,17 @@ splitpcln(void) // (Source file is f->src.) // NOTE(rsc): If you edit this function, also edit extern.go:/FileLine int32 -runtime·funcline(Func *f, uint64 targetpc) +runtime·funcline(Func *f, uintptr targetpc) { byte *p, *ep; uintptr pc; int32 line; int32 pcquant; + enum { + debug = 0 + }; + switch(thechar) { case '5': pcquant = 4; @@ -313,17 +338,41 @@ runtime·funcline(Func *f, uint64 targetpc) ep = p + f->pcln.len; pc = f->pc0; line = f->ln0; - for(; p < ep && pc <= targetpc; p++) { + if(debug && !runtime·panicking) + runtime·printf("funcline start pc=%p targetpc=%p line=%d tab=%p+%d\n", + pc, targetpc, line, p, (int32)f->pcln.len); + for(;;) { + // Table is a sequence of updates. + + // Each update says first how to adjust the pc, + // in possibly multiple instructions... + while(p < ep && *p > 128) + pc += pcquant * (*p++ - 128); + + if(debug && !runtime·panicking) + runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line); + + // If the pc has advanced too far or we're out of data, + // stop and the last known line number. + if(pc > targetpc || p >= ep) + break; + + // ... and then how to adjust the line number, + // in a single instruction. if(*p == 0) { + if(p+5 > ep) + break; line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; - p += 4; - } else if(*p <= 64) { - line += *p; - } else if(*p <= 128) { - line -= *p - 64; - } else { - pc += pcquant*(*p - 129); - } + p += 5; + } else if(*p <= 64) + line += *p++; + else + line -= *p++ - 64; + // Now pc, line pair is consistent. + if(debug && !runtime·panicking) + runtime·printf("pc=%p targetpc=%p line=%d\n", pc, targetpc, line); + + // PC increments implicitly on each iteration. pc += pcquant; } return line; diff --git a/src/pkg/runtime/windows/386/defs.h b/src/pkg/runtime/windows/386/defs.h index a2a882103..49fc19504 100644 --- a/src/pkg/runtime/windows/386/defs.h +++ b/src/pkg/runtime/windows/386/defs.h @@ -10,6 +10,9 @@ enum { PROT_EXEC = 0x4, MAP_ANON = 0x1, MAP_PRIVATE = 0x2, + SIGINT = 0x2, + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 0x1, EXCEPTION_ACCESS_VIOLATION = 0xc0000005, EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, diff --git a/src/pkg/runtime/windows/386/signal.c b/src/pkg/runtime/windows/386/signal.c index 69178cdd0..903636910 100644 --- a/src/pkg/runtime/windows/386/signal.c +++ b/src/pkg/runtime/windows/386/signal.c @@ -27,12 +27,7 @@ runtime·dumpregs(Context *r) void runtime·initsig(int32) { -} - -String -runtime·signame(int32) -{ - return runtime·emptystring; + runtime·siginit(); } uint32 diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s index d1a8a49a9..bca48febe 100644 --- a/src/pkg/runtime/windows/386/sys.s +++ b/src/pkg/runtime/windows/386/sys.s @@ -99,6 +99,45 @@ TEXT runtime·sigtramp1(SB),0,$16-28 sigdone: RET +// Windows runs the ctrl handler in a new thread. +TEXT runtime·ctrlhandler(SB),7,$0 + PUSHL BP + MOVL SP, BP + PUSHL BX + PUSHL SI + PUSHL DI + PUSHL 0x2c(FS) + MOVL SP, BX + + // setup dummy m, g + SUBL $(m_sehframe+4), SP // at least space for m_sehframe + LEAL m_tls(SP), CX + MOVL CX, 0x2c(FS) + MOVL SP, m(CX) + MOVL SP, DX + SUBL $8, SP // space for g_stack{guard,base} + MOVL SP, g(CX) + MOVL SP, m_g0(DX) + LEAL -4096(SP), CX + MOVL CX, g_stackguard(SP) + MOVL BX, g_stackbase(SP) + + PUSHL 8(BP) + CALL runtime·ctrlhandler1(SB) + POPL CX + + get_tls(CX) + MOVL g(CX), CX + MOVL g_stackbase(CX), SP + POPL 0x2c(FS) + POPL DI + POPL SI + POPL BX + POPL BP + MOVL 0(SP), CX + ADDL $8, SP + JMP CX + // Called from dynamic function created by ../thread.c compilecallback, // running on Windows stack (not Go stack). // BX, BP, SI, DI registers and DF flag are preserved @@ -107,7 +146,11 @@ sigdone: // DX = total size of arguments // TEXT runtime·callbackasm+0(SB),7,$0 + // preserve whatever's at the memory location that + // the callback will use to store the return value LEAL 8(SP), CX + PUSHL 0(CX)(DX*1) + ADDL $4, DX // extend argsize by size of return value // save registers as required for windows callback PUSHL 0(FS) @@ -129,7 +172,7 @@ TEXT runtime·callbackasm+0(SB),7,$0 CALL runtime·cgocallback(SB) // restore registers as required for windows callback - POPL CX + POPL AX POPL CX POPL DX POPL BX @@ -139,6 +182,8 @@ TEXT runtime·callbackasm+0(SB),7,$0 POPL 0(FS) CLD + MOVL -4(CX)(DX*1), AX + POPL -4(CX)(DX*1) RET // void tstart(M *newm); diff --git a/src/pkg/runtime/windows/defs.c b/src/pkg/runtime/windows/defs.c index 5aac03c81..3b2824940 100644 --- a/src/pkg/runtime/windows/defs.c +++ b/src/pkg/runtime/windows/defs.c @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include <signal.h> #include <stdarg.h> #include <windef.h> #include <winbase.h> +#include <wincon.h> enum { $PROT_NONE = 0, @@ -15,6 +17,10 @@ enum { $MAP_ANON = 1, $MAP_PRIVATE = 2, + $SIGINT = SIGINT, + $CTRL_C_EVENT = CTRL_C_EVENT, + $CTRL_BREAK_EVENT = CTRL_BREAK_EVENT, + $EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION, $EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT, $EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND, diff --git a/src/pkg/runtime/windows/mem.c b/src/pkg/runtime/windows/mem.c index 19d11ce8d..54d77da37 100644 --- a/src/pkg/runtime/windows/mem.c +++ b/src/pkg/runtime/windows/mem.c @@ -48,7 +48,14 @@ runtime·SysFree(void *v, uintptr n) void* runtime·SysReserve(void *v, uintptr n) { - return runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_RESERVE, 0); + // v is just a hint. + // First try at v. + v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if(v != nil) + return v; + + // Next let the kernel choose the address. + return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE); } void diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h index 391eace5a..77881e86e 100644 --- a/src/pkg/runtime/windows/os.h +++ b/src/pkg/runtime/windows/os.h @@ -20,6 +20,7 @@ uint32 runtime·tstart_stdcall(M *newm); uint32 runtime·issigpanic(uint32); void runtime·sigpanic(void); +uint32 runtime·ctrlhandler(uint32 type); // Windows dll function to go callback entry. byte *runtime·compilecallback(Eface fn, bool cleanstack); diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index 278a5da69..aedd24200 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -18,6 +18,7 @@ #pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll" #pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll" #pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll" +#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll" #pragma dynimport runtime·SetEvent SetEvent "kernel32.dll" #pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll" #pragma dynimport runtime·WriteFile WriteFile "kernel32.dll" @@ -33,6 +34,7 @@ extern void *runtime·GetStdHandle; extern void *runtime·LoadLibraryEx; extern void *runtime·QueryPerformanceCounter; extern void *runtime·QueryPerformanceFrequency; +extern void *runtime·SetConsoleCtrlHandler; extern void *runtime·SetEvent; extern void *runtime·WaitForSingleObject; extern void *runtime·WriteFile; @@ -43,6 +45,7 @@ void runtime·osinit(void) { runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq); + runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, 1); } void @@ -161,6 +164,7 @@ runtime·destroylock(Lock *l) void runtime·noteclear(Note *n) { + n->lock.key = 0; // memset(n, 0, sizeof *n) eventlock(&n->lock); } @@ -180,11 +184,17 @@ runtime·notesleep(Note *n) void runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) { + void *thandle; + USED(stk); USED(g); // assuming g = m->g0 USED(fn); // assuming fn = mstart - runtime·stdcall(runtime·CreateThread, 6, 0, 0, runtime·tstart_stdcall, m, 0, 0); + thandle = runtime·stdcall(runtime·CreateThread, 6, 0, 0, runtime·tstart_stdcall, m, 0, 0); + if(thandle == 0) { + runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); + runtime·throw("runtime.newosproc"); + } } // Called to initialize a new m (including the bootstrap m). @@ -279,6 +289,41 @@ runtime·sigpanic(void) runtime·throw("fault"); } +String +runtime·signame(int32 sig) +{ + int8 *s; + + switch(sig) { + case SIGINT: + s = "SIGINT: interrupt"; + break; + default: + return runtime·emptystring; + } + return runtime·gostringnocopy((byte*)s); +} + +uint32 +runtime·ctrlhandler1(uint32 type) +{ + int32 s; + + switch(type) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + s = SIGINT; + break; + default: + return 0; + } + + if(runtime·sigsend(s)) + return 1; + runtime·exit(2); // SIGINT, SIGTERM, etc + return 0; +} + // Call back from windows dll into go. byte * runtime·compilecallback(Eface fn, bool cleanstack) diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go index 4ec3cdbb9..b6049c545 100644 --- a/src/pkg/strconv/ftoa.go +++ b/src/pkg/strconv/ftoa.go @@ -64,7 +64,7 @@ func FtoaN(f float64, fmt byte, prec int, n int) string { } func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { - neg := bits>>flt.expbits>>flt.mantbits != 0 + neg := bits>>(flt.expbits+flt.mantbits) != 0 exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1) mant := bits & (uint64(1)<<flt.mantbits - 1) diff --git a/src/pkg/strconv/ftoa_test.go b/src/pkg/strconv/ftoa_test.go index bc327600e..6d361a138 100644 --- a/src/pkg/strconv/ftoa_test.go +++ b/src/pkg/strconv/ftoa_test.go @@ -121,9 +121,8 @@ var ftoatests = []ftoaTest{ // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, - // TODO: uncomment after fixing issue 1463. // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ - // {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, + {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, } func TestFtoa(t *testing.T) { diff --git a/src/pkg/sync/Makefile b/src/pkg/sync/Makefile index f843795b0..fd8e5d998 100644 --- a/src/pkg/sync/Makefile +++ b/src/pkg/sync/Makefile @@ -9,6 +9,7 @@ GOFILES=\ mutex.go\ once.go \ rwmutex.go\ + waitgroup.go\ # 386-specific object files OFILES_386=\ diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go index c4d82af00..2a1270b9c 100644 --- a/src/pkg/sync/mutex.go +++ b/src/pkg/sync/mutex.go @@ -3,10 +3,10 @@ // license that can be found in the LICENSE file. // The sync package provides basic synchronization primitives -// such as mutual exclusion locks. Other than the Once type, -// most are intended for use by low-level library routines. -// Higher-level synchronization is better done via channels -// and communication. +// such as mutual exclusion locks. Other than the Once and +// WaitGroup types, most are intended for use by low-level +// library routines. Higher-level synchronization is better +// done via channels and communication. package sync import "runtime" @@ -53,9 +53,14 @@ func (m *Mutex) Lock() { // It is allowed for one goroutine to lock a Mutex and then // arrange for another goroutine to unlock it. func (m *Mutex) Unlock() { - if xadd(&m.key, -1) == 0 { + switch v := xadd(&m.key, -1); { + case v == 0: // changed from 1 to 0; no contention return + case int32(v) == -1: + // changed from 0 to -1: wasn't locked + // (or there are 4 billion goroutines waiting) + panic("sync: unlock of unlocked mutex") } runtime.Semrelease(&m.sema) } diff --git a/src/pkg/sync/mutex_test.go b/src/pkg/sync/mutex_test.go index d0e048ed7..f5c20ca49 100644 --- a/src/pkg/sync/mutex_test.go +++ b/src/pkg/sync/mutex_test.go @@ -89,3 +89,16 @@ func BenchmarkContendedMutex(b *testing.B) { <-c <-c } + +func TestMutexPanic(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatalf("unlock of unlocked mutex did not panic") + } + }() + + var mu Mutex + mu.Lock() + mu.Unlock() + mu.Unlock() +} diff --git a/src/pkg/sync/rwmutex.go b/src/pkg/sync/rwmutex.go index 06fd0b0ff..25696aca2 100644 --- a/src/pkg/sync/rwmutex.go +++ b/src/pkg/sync/rwmutex.go @@ -64,12 +64,10 @@ func (rw *RWMutex) Lock() { rw.r.Unlock() } -// Unlock unlocks rw for writing. -// It is a run-time error if rw is not locked for writing -// on entry to Unlock. +// Unlock unlocks rw for writing. It is a run-time error if rw is +// not locked for writing on entry to Unlock. // -// Like for Mutexes, -// a locked RWMutex is not associated with a particular goroutine. -// It is allowed for one goroutine to RLock (Lock) an RWMutex and then +// As with Mutexes, a locked RWMutex is not associated with a particular +// goroutine. One goroutine may RLock (Lock) an RWMutex and then // arrange for another goroutine to RUnlock (Unlock) it. func (rw *RWMutex) Unlock() { rw.w.Unlock() } diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go new file mode 100644 index 000000000..68e1d509f --- /dev/null +++ b/src/pkg/sync/waitgroup.go @@ -0,0 +1,86 @@ +// 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 sync + +import "runtime" + +// A WaitGroup waits for a collection of goroutines to finish. +// The main goroutine calls Add to set the number of +// goroutines to wait for. Then each of the goroutines +// runs and calls Done when finished. At the same time, +// Wait can be used to block until all goroutines have finished. +// +// For example: +// +// for i := 0; i < n; i++ { +// if !condition(i) { +// continue +// } +// wg.Add(1) +// go func() { +// // Do something. +// wg.Done() +// } +// } +// wg.Wait() +// +type WaitGroup struct { + m Mutex + counter int + waiters int + sema *uint32 +} + +// WaitGroup creates a new semaphore each time the old semaphore +// is released. This is to avoid the following race: +// +// G1: Add(1) +// G1: go G2() +// G1: Wait() // Context switch after Unlock() and before Semacquire(). +// G2: Done() // Release semaphore: sema == 1, waiters == 0. G1 doesn't run yet. +// G3: Wait() // Finds counter == 0, waiters == 0, doesn't block. +// G3: Add(1) // Makes counter == 1, waiters == 0. +// G3: go G4() +// G3: Wait() // G1 still hasn't run, G3 finds sema == 1, unblocked! Bug. + +// Add adds delta, which may be negative, to the WaitGroup counter. +// If the counter becomes zero, all goroutines blocked on Wait() are released. +func (wg *WaitGroup) Add(delta int) { + wg.m.Lock() + if delta < -wg.counter { + wg.m.Unlock() + panic("sync: negative WaitGroup count") + } + wg.counter += delta + if wg.counter == 0 && wg.waiters > 0 { + for i := 0; i < wg.waiters; i++ { + runtime.Semrelease(wg.sema) + } + wg.waiters = 0 + wg.sema = nil + } + wg.m.Unlock() +} + +// Done decrements the WaitGroup counter. +func (wg *WaitGroup) Done() { + wg.Add(-1) +} + +// Wait blocks until the WaitGroup counter is zero. +func (wg *WaitGroup) Wait() { + wg.m.Lock() + if wg.counter == 0 { + wg.m.Unlock() + return + } + wg.waiters++ + if wg.sema == nil { + wg.sema = new(uint32) + } + s := wg.sema + wg.m.Unlock() + runtime.Semacquire(s) +} diff --git a/src/pkg/sync/waitgroup_test.go b/src/pkg/sync/waitgroup_test.go new file mode 100644 index 000000000..fe35732e7 --- /dev/null +++ b/src/pkg/sync/waitgroup_test.go @@ -0,0 +1,60 @@ +// 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 sync_test + +import ( + . "sync" + "testing" +) + +func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) { + n := 16 + wg1.Add(n) + wg2.Add(n) + exited := make(chan bool, n) + for i := 0; i != n; i++ { + go func(i int) { + wg1.Done() + wg2.Wait() + exited <- true + }(i) + } + wg1.Wait() + for i := 0; i != n; i++ { + select { + case <-exited: + t.Fatal("WaitGroup released group too soon") + default: + } + wg2.Done() + } + for i := 0; i != n; i++ { + <-exited // Will block if barrier fails to unlock someone. + } +} + +func TestWaitGroup(t *testing.T) { + wg1 := &WaitGroup{} + wg2 := &WaitGroup{} + + // Run the same test a few times to ensure barrier is in a proper state. + for i := 0; i != 8; i++ { + testWaitGroup(t, wg1, wg2) + } +} + +func TestWaitGroupMisuse(t *testing.T) { + defer func() { + err := recover() + if err != "sync: negative WaitGroup count" { + t.Fatalf("Unexpected panic: %#v", err) + } + }() + wg := &WaitGroup{} + wg.Add(1) + wg.Done() + wg.Done() + t.Fatal("Should panic") +} diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go index c7f7893e7..04c066918 100644 --- a/src/pkg/syscall/exec_unix.go +++ b/src/pkg/syscall/exec_unix.go @@ -310,3 +310,9 @@ func Exec(argv0 string, argv []string, envv []string) (err int) { uintptr(unsafe.Pointer(&StringArrayPtr(envv)[0]))) return int(err1) } + +// StartProcess wraps ForkExec for package os. +func StartProcess(argv0 string, argv []string, envv []string, dir string, fd []int) (pid, handle int, err int) { + pid, err = forkExec(argv0, argv, envv, false, dir, fd) + return pid, 0, err +} diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go index c3ed3ba98..7256c3ae3 100644 --- a/src/pkg/syscall/exec_windows.go +++ b/src/pkg/syscall/exec_windows.go @@ -107,7 +107,7 @@ func escapeAddQuotes(s string) string { func CloseOnExec(fd int) { - return + SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0) } func SetNonblock(fd int, nonblocking bool) (errno int) { @@ -117,13 +117,9 @@ func SetNonblock(fd int, nonblocking bool) (errno int) { // TODO(kardia): Add trace //The command and arguments are passed via the Command line parameter. -func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir string, fd []int) (pid int, err int) { - if traceme == true { - return 0, EWINDOWS - } - +func StartProcess(argv0 string, argv []string, envv []string, dir string, fd []int) (pid, handle int, err int) { if len(fd) > 3 { - return 0, EWINDOWS + return 0, 0, EWINDOWS } //CreateProcess will throw an error if the dir is not set to a valid dir @@ -144,22 +140,31 @@ func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir stri startupInfo.StdOutput = 0 startupInfo.StdErr = 0 + // Acquire the fork lock so that no other threads + // create new fds that are not yet close-on-exec + // before we fork. + ForkLock.Lock() + defer ForkLock.Unlock() + var currentProc, _ = GetCurrentProcess() if len(fd) > 0 && fd[0] > 0 { - if ok, err := DuplicateHandle(currentProc, int32(fd[0]), currentProc, &startupInfo.StdInput, 0, true, DUPLICATE_SAME_ACCESS); !ok { - return 0, err + err := DuplicateHandle(currentProc, int32(fd[0]), currentProc, &startupInfo.StdInput, 0, true, DUPLICATE_SAME_ACCESS) + if err != 0 { + return 0, 0, err } defer CloseHandle(int32(startupInfo.StdInput)) } if len(fd) > 1 && fd[1] > 0 { - if ok, err := DuplicateHandle(currentProc, int32(fd[1]), currentProc, &startupInfo.StdOutput, 0, true, DUPLICATE_SAME_ACCESS); !ok { - return 0, err + err := DuplicateHandle(currentProc, int32(fd[1]), currentProc, &startupInfo.StdOutput, 0, true, DUPLICATE_SAME_ACCESS) + if err != 0 { + return 0, 0, err } defer CloseHandle(int32(startupInfo.StdOutput)) } if len(fd) > 2 && fd[2] > 0 { - if ok, err := DuplicateHandle(currentProc, int32(fd[2]), currentProc, &startupInfo.StdErr, 0, true, DUPLICATE_SAME_ACCESS); !ok { - return 0, err + err := DuplicateHandle(currentProc, int32(fd[2]), currentProc, &startupInfo.StdErr, 0, true, DUPLICATE_SAME_ACCESS) + if err != 0 { + return 0, 0, err } defer CloseHandle(int32(startupInfo.StdErr)) } @@ -168,7 +173,7 @@ func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir stri } // argv0 must not be longer then 256 chars // but the entire cmd line can have up to 32k chars (msdn) - ok, err := CreateProcess( + err = CreateProcess( nil, StringToUTF16Ptr(escapeAddQuotes(argv0)+" "+stringJoin(argv[1:], " ", escapeAddQuotes)), nil, //ptr to struct lpProcessAttributes @@ -180,23 +185,14 @@ func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir stri startupInfo, processInfo) - if ok { + if err != 0 { pid = int(processInfo.ProcessId) - CloseHandle(processInfo.Process) + handle = int(processInfo.Process) CloseHandle(processInfo.Thread) } return } -func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) { - return forkExec(argv0, argv, envv, false, dir, fd) -} - -// PtraceForkExec is like ForkExec, but starts the child in a traced state. -func PtraceForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) { - return forkExec(argv0, argv, envv, true, dir, fd) -} - // Ordinary exec. func Exec(argv0 string, argv []string, envv []string) (err int) { return EWINDOWS diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh index 41acf95ec..a13c0e91b 100755 --- a/src/pkg/syscall/mkerrors.sh +++ b/src/pkg/syscall/mkerrors.sh @@ -38,23 +38,28 @@ includes_Darwin=' #define _DARWIN_C_SOURCE #define KERNEL #define _DARWIN_USE_64_BIT_INODE -#include <sys/cdefs.h> -#include <sys/wait.h> +#include <sys/types.h> #include <sys/event.h> #include <sys/socket.h> #include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/wait.h> #include <net/if.h> +#include <net/route.h> +#include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_mroute.h> ' includes_FreeBSD=' -#include <sys/wait.h> +#include <sys/types.h> #include <sys/event.h> #include <sys/socket.h> #include <sys/sockio.h> -#include <net/route.h> +#include <sys/sysctl.h> +#include <sys/wait.h> #include <net/if.h> +#include <net/route.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_mroute.h> @@ -105,16 +110,22 @@ done $2 ~ /^(MAP_FAILED)$/ {next} $2 !~ /^ETH_/ && + $2 !~ /^EPROC_/ && + $2 !~ /^EQUIV_/ && + $2 !~ /^EXPR_/ && $2 ~ /^E[A-Z0-9_]+$/ || $2 ~ /^SIG[^_]/ || $2 ~ /^IN_/ || - $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|IFF)_/ || + $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|IFF|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || $2 == "SOMAXCONN" || $2 == "NAME_MAX" || $2 == "IFNAMSIZ" || + $2 == "CTL_NET" || + $2 == "CTL_MAXNAME" || $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || $2 ~ /^(O|F|FD|NAME|S|PTRACE)_/ || - $2 ~ /^SIO/ || + $2 ~ /^SIOC/ || + $2 !~ "WMESGLEN" && $2 ~ /^W[A-Z0-9]+$/ {printf("\t$%s = %s,\n", $2, $2)} $2 ~ /^__WCOREFLAG$/ {next} $2 ~ /^__W[A-Z0-9]+$/ {printf("\t$%s = %s,\n", substr($2,3), $2)} diff --git a/src/pkg/syscall/mksyscall.sh b/src/pkg/syscall/mksyscall.sh index 79d19d0d0..0f8098098 100755 --- a/src/pkg/syscall/mksyscall.sh +++ b/src/pkg/syscall/mksyscall.sh @@ -98,14 +98,11 @@ while(<>) { } elsif($type =~ /^\[\](.*)/) { # Convert slice into pointer, length. # Have to be careful not to take address of &a[0] if len == 0: - # pass nil in that case. + # pass dummy pointer in that case. + # Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). $text .= "\tvar _p$n unsafe.Pointer\n"; $text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}"; - if($nacl) { - # NaCl rejects zero length write with nil pointer, - # so use non-nil pointer. - $text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero[0])\n\t}"; - } + $text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}"; $text .= "\n"; push @args, "uintptr(_p$n)", "uintptr(len($name))"; $n++; diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go index 1647d69e5..96975376f 100644 --- a/src/pkg/syscall/syscall.go +++ b/src/pkg/syscall/syscall.go @@ -24,3 +24,7 @@ func StringByteSlice(s string) []byte { // StringBytePtr returns a pointer to a NUL-terminated array of bytes // containing the text of s. func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] } + +// Single-word zero for use when we need a valid pointer to 0 bytes. +// See mksyscall.sh. +var _zero uintptr diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go index 3c4ac51dc..1f5b2ba9a 100644 --- a/src/pkg/syscall/syscall_bsd.go +++ b/src/pkg/syscall/syscall_bsd.go @@ -223,8 +223,40 @@ func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, int) { return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), 0 } +func (sa *SockaddrDatalink) sockaddr() (uintptr, _Socklen, int) { + if sa.Index == 0 { + return 0, 0, EINVAL + } + sa.raw.Len = sa.Len + sa.raw.Family = AF_LINK + sa.raw.Index = sa.Index + sa.raw.Type = sa.Type + sa.raw.Nlen = sa.Nlen + sa.raw.Alen = sa.Alen + sa.raw.Slen = sa.Slen + for i := 0; i < len(sa.raw.Data); i++ { + sa.raw.Data[i] = sa.Data[i] + } + return uintptr(unsafe.Pointer(&sa.raw)), SizeofSockaddrDatalink, 0 +} + func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { switch rsa.Addr.Family { + case AF_LINK: + pp := (*RawSockaddrDatalink)(unsafe.Pointer(rsa)) + sa := new(SockaddrDatalink) + sa.Len = pp.Len + sa.Family = pp.Family + sa.Index = pp.Index + sa.Type = pp.Type + sa.Nlen = pp.Nlen + sa.Alen = pp.Alen + sa.Slen = pp.Slen + for i := 0; i < len(sa.Data); i++ { + sa.Data[i] = pp.Data[i] + } + return sa, 0 + case AF_UNIX: pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)) if pp.Len < 3 || pp.Len > SizeofSockaddrUnix { @@ -343,6 +375,10 @@ func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l)) } +func SetsockoptIpMreq(fd, level, opt int, mreq *IpMreq) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq)) +} + func SetsockoptString(fd, level, opt int, s string) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), len(s)) } @@ -395,7 +431,6 @@ func Kevent(kq int, changes, events []Kevent_t, timeout *Timespec) (n int, errno // Translate "kern.hostname" to []_C_int{0,1,2,3}. func nametomib(name string) (mib []_C_int, errno int) { - const CTL_MAXNAME = 12 const siz = uintptr(unsafe.Sizeof(mib[0])) // NOTE(rsc): It seems strange to set the buffer to have @@ -467,6 +502,27 @@ func SysctlUint32(name string) (value uint32, errno int) { return *(*uint32)(unsafe.Pointer(&buf[0])), 0 } +func SysctlNetRoute(fourth, fifth, sixth int) (value []byte, errno int) { + mib := []_C_int{CTL_NET, AF_ROUTE, 0, _C_int(fourth), _C_int(fifth), _C_int(sixth)} + + // Find size. + n := uintptr(0) + if errno = sysctl(mib, nil, &n, nil, 0); errno != 0 { + return nil, errno + } + if n == 0 { + return nil, 0 + } + + // Read into buffer of that size. + b := make([]byte, n) + if errno = sysctl(mib, &b[0], &n, nil, 0); errno != 0 { + return nil, errno + } + + return b[0:n], 0 +} + //sys utimes(path string, timeval *[2]Timeval) (errno int) func Utimes(path string, tv []Timeval) (errno int) { if len(tv) != 2 { diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go index ab83af5c1..552c9c154 100644 --- a/src/pkg/syscall/syscall_darwin.go +++ b/src/pkg/syscall/syscall_darwin.go @@ -14,6 +14,18 @@ package syscall const OS = "darwin" +type SockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 + raw RawSockaddrDatalink +} + /* * Wrapped */ diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go index ee947be20..ed310663a 100644 --- a/src/pkg/syscall/syscall_freebsd.go +++ b/src/pkg/syscall/syscall_freebsd.go @@ -14,6 +14,18 @@ package syscall const OS = "freebsd" +type SockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [46]int8 + raw RawSockaddrDatalink +} + /* * Exposed directly */ diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go index d20c035b5..30ad89646 100644 --- a/src/pkg/syscall/syscall_linux.go +++ b/src/pkg/syscall/syscall_linux.go @@ -428,6 +428,10 @@ func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l)) } +func SetsockoptIpMreq(fd, level, opt int, mreq *IpMreq) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq)) +} + func SetsockoptString(fd, level, opt int, s string) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), len(s)) } diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 762ed53db..0cd89d426 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -106,57 +106,59 @@ func NewCallback(fn interface{}) uintptr //sys GetLastError() (lasterrno int) //sys LoadLibrary(libname string) (handle uint32, errno int) = LoadLibraryW -//sys FreeLibrary(handle uint32) (ok bool, errno int) +//sys FreeLibrary(handle uint32) (errno int) //sys GetProcAddress(module uint32, procname string) (proc uint32, errno int) //sys GetVersion() (ver uint32, errno int) //sys FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) = FormatMessageW //sys ExitProcess(exitcode uint32) -//sys CreateFile(name *uint16, access uint32, mode uint32, sa *byte, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) [failretval==-1] = CreateFileW -//sys ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (ok bool, errno int) -//sys WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (ok bool, errno int) +//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) [failretval==-1] = CreateFileW +//sys ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) +//sys WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) //sys SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) [failretval==0xffffffff] -//sys CloseHandle(handle int32) (ok bool, errno int) +//sys CloseHandle(handle int32) (errno int) //sys GetStdHandle(stdhandle int32) (handle int32, errno int) [failretval==-1] //sys FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) [failretval==-1] = FindFirstFileW -//sys FindNextFile(handle int32, data *Win32finddata) (ok bool, errno int) = FindNextFileW -//sys FindClose(handle int32) (ok bool, errno int) -//sys GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (ok bool, errno int) +//sys FindNextFile(handle int32, data *Win32finddata) (errno int) = FindNextFileW +//sys FindClose(handle int32) (errno int) +//sys GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int) //sys GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) = GetCurrentDirectoryW -//sys SetCurrentDirectory(path *uint16) (ok bool, errno int) = SetCurrentDirectoryW -//sys CreateDirectory(path *uint16, sa *byte) (ok bool, errno int) = CreateDirectoryW -//sys RemoveDirectory(path *uint16) (ok bool, errno int) = RemoveDirectoryW -//sys DeleteFile(path *uint16) (ok bool, errno int) = DeleteFileW -//sys MoveFile(from *uint16, to *uint16) (ok bool, errno int) = MoveFileW -//sys GetComputerName(buf *uint16, n *uint32) (ok bool, errno int) = GetComputerNameW -//sys SetEndOfFile(handle int32) (ok bool, errno int) +//sys SetCurrentDirectory(path *uint16) (errno int) = SetCurrentDirectoryW +//sys CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) = CreateDirectoryW +//sys RemoveDirectory(path *uint16) (errno int) = RemoveDirectoryW +//sys DeleteFile(path *uint16) (errno int) = DeleteFileW +//sys MoveFile(from *uint16, to *uint16) (errno int) = MoveFileW +//sys GetComputerName(buf *uint16, n *uint32) (errno int) = GetComputerNameW +//sys SetEndOfFile(handle int32) (errno int) //sys GetSystemTimeAsFileTime(time *Filetime) //sys sleep(msec uint32) = Sleep //sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval==0xffffffff] //sys CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int) -//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (ok bool, errno int) -//sys CancelIo(s uint32) (ok bool, errno int) -//sys CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (ok bool, errno int) = CreateProcessW -//sys GetStartupInfo(startupInfo *StartupInfo) (ok bool, errno int) = GetStartupInfoW +//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) +//sys CancelIo(s uint32) (errno int) +//sys CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW +//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) +//sys GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) +//sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW //sys GetCurrentProcess() (pseudoHandle int32, errno int) -//sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (ok bool, errno int) +//sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) //sys WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) [failretval==0xffffffff] //sys GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) = GetTempPathW -//sys CreatePipe(readhandle *uint32, writehandle *uint32, lpsa *byte, size uint32) (ok bool, errno int) +//sys CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int) //sys GetFileType(filehandle uint32) (n uint32, errno int) -//sys CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (ok bool, errno int) = advapi32.CryptAcquireContextW -//sys CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) = advapi32.CryptReleaseContext -//sys CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (ok bool, errno int) = advapi32.CryptGenRandom -//sys OpenProcess(da uint32,b int, pid uint32) (handle uint32, errno int) -//sys GetExitCodeProcess(h uint32, c *uint32) (ok bool, errno int) +//sys CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) = advapi32.CryptAcquireContextW +//sys CryptReleaseContext(provhandle uint32, flags uint32) (errno int) = advapi32.CryptReleaseContext +//sys CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) = advapi32.CryptGenRandom //sys GetEnvironmentStrings() (envs *uint16, errno int) [failretval==nil] = kernel32.GetEnvironmentStringsW -//sys FreeEnvironmentStrings(envs *uint16) (ok bool, errno int) = kernel32.FreeEnvironmentStringsW +//sys FreeEnvironmentStrings(envs *uint16) (errno int) = kernel32.FreeEnvironmentStringsW //sys GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, errno int) = kernel32.GetEnvironmentVariableW -//sys SetEnvironmentVariable(name *uint16, value *uint16) (ok bool, errno int) = kernel32.SetEnvironmentVariableW -//sys SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (ok bool, errno int) +//sys SetEnvironmentVariable(name *uint16, value *uint16) (errno int) = kernel32.SetEnvironmentVariableW +//sys SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) //sys GetFileAttributes(name *uint16) (attrs uint32, errno int) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW //sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW //sys CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) [failretval==nil] = shell32.CommandLineToArgvW //sys LocalFree(hmem uint32) (handle uint32, errno int) [failretval!=0] +//sys SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) +//sys FlushFileBuffers(handle int32) (errno int) // syscall interface implementation for other packages @@ -181,6 +183,13 @@ func Errstr(errno int) string { func Exit(code int) { ExitProcess(uint32(code)) } +func makeInheritSa() *SecurityAttributes { + var sa SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + return &sa +} + func Open(path string, mode int, perm uint32) (fd int, errno int) { if len(path) == 0 { return -1, ERROR_FILE_NOT_FOUND @@ -202,6 +211,10 @@ func Open(path string, mode int, perm uint32) (fd int, errno int) { access |= FILE_APPEND_DATA } sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE) + var sa *SecurityAttributes + if mode&O_CLOEXEC == 0 { + sa = makeInheritSa() + } var createmode uint32 switch { case mode&O_CREAT != 0: @@ -215,13 +228,14 @@ func Open(path string, mode int, perm uint32) (fd int, errno int) { default: createmode = OPEN_EXISTING } - h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, nil, createmode, FILE_ATTRIBUTE_NORMAL, 0) + h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0) return int(h), int(e) } func Read(fd int, p []byte) (n int, errno int) { var done uint32 - if ok, e := ReadFile(int32(fd), p, &done, nil); !ok { + e := ReadFile(int32(fd), p, &done, nil) + if e != 0 { if e == ERROR_BROKEN_PIPE { // NOTE(brainman): work around ERROR_BROKEN_PIPE is returned on reading EOF from stdin return 0, 0 @@ -245,7 +259,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { o.OffsetHigh = uint32(offset >> 32) o.Offset = uint32(offset) var done uint32 - if ok, e := ReadFile(int32(fd), p, &done, &o); !ok { + e = ReadFile(int32(fd), p, &done, &o) + if e != 0 { return 0, e } return int(done), 0 @@ -253,7 +268,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { func Write(fd int, p []byte) (n int, errno int) { var done uint32 - if ok, e := WriteFile(int32(fd), p, &done, nil); !ok { + e := WriteFile(int32(fd), p, &done, nil) + if e != 0 { return 0, e } return int(done), 0 @@ -269,7 +285,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { o.OffsetHigh = uint32(offset >> 32) o.Offset = uint32(offset) var done uint32 - if ok, e := WriteFile(int32(fd), p, &done, &o); !ok { + e = WriteFile(int32(fd), p, &done, &o) + if e != 0 { return 0, e } return int(done), 0 @@ -300,10 +317,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) { } func Close(fd int) (errno int) { - if ok, e := CloseHandle(int32(fd)); !ok { - return e - } - return 0 + return CloseHandle(int32(fd)) } var ( @@ -362,46 +376,32 @@ func Getwd() (wd string, errno int) { } func Chdir(path string) (errno int) { - if ok, e := SetCurrentDirectory(&StringToUTF16(path)[0]); !ok { - return e - } - return 0 + return SetCurrentDirectory(&StringToUTF16(path)[0]) } func Mkdir(path string, mode uint32) (errno int) { - if ok, e := CreateDirectory(&StringToUTF16(path)[0], nil); !ok { - return e - } - return 0 + return CreateDirectory(&StringToUTF16(path)[0], nil) } func Rmdir(path string) (errno int) { - if ok, e := RemoveDirectory(&StringToUTF16(path)[0]); !ok { - return e - } - return 0 + return RemoveDirectory(&StringToUTF16(path)[0]) } func Unlink(path string) (errno int) { - if ok, e := DeleteFile(&StringToUTF16(path)[0]); !ok { - return e - } - return 0 + return DeleteFile(&StringToUTF16(path)[0]) } func Rename(oldpath, newpath string) (errno int) { from := &StringToUTF16(oldpath)[0] to := &StringToUTF16(newpath)[0] - if ok, e := MoveFile(from, to); !ok { - return e - } - return 0 + return MoveFile(from, to) } func ComputerName() (name string, errno int) { var n uint32 = MAX_COMPUTERNAME_LENGTH + 1 b := make([]uint16, n) - if ok, e := GetComputerName(&b[0], &n); !ok { + e := GetComputerName(&b[0], &n) + if e != 0 { return "", e } return string(utf16.Decode(b[0:n])), 0 @@ -413,10 +413,12 @@ func Ftruncate(fd int, length int64) (errno int) { return e } defer Seek(fd, curoffset, 0) - if _, e := Seek(fd, length, 0); e != 0 { + _, e = Seek(fd, length, 0) + if e != 0 { return e } - if _, e := SetEndOfFile(int32(fd)); e != 0 { + e = SetEndOfFile(int32(fd)) + if e != 0 { return e } return 0 @@ -439,8 +441,9 @@ func Pipe(p []int) (errno int) { return EINVAL } var r, w uint32 - if ok, errno := CreatePipe(&r, &w, nil, 0); !ok { - return errno + e := CreatePipe(&r, &w, makeInheritSa(), 0) + if e != 0 { + return e } p[0] = int(r) p[1] = int(w) @@ -460,10 +463,11 @@ func Utimes(path string, tv []Timeval) (errno int) { defer Close(int(h)) a := NsecToFiletime(tv[0].Nanoseconds()) w := NsecToFiletime(tv[1].Nanoseconds()) - if ok, e := SetFileTime(h, nil, &a, &w); !ok { - return e - } - return 0 + return SetFileTime(h, nil, &a, &w) +} + +func Fsync(fd int) (errno int) { + return FlushFileBuffers(int32(fd)) } // net api calls @@ -479,7 +483,7 @@ func Utimes(path string, tv []Timeval) (errno int) { //sys listen(s int32, backlog int32) (errno int) [failretval==-1] = wsock32.listen //sys shutdown(s int32, how int32) (errno int) [failretval==-1] = wsock32.shutdown //sys Closesocket(s int32) (errno int) [failretval==-1] = wsock32.closesocket -//sys AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (ok bool, errno int) = wsock32.AcceptEx +//sys AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx //sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = wsock32.GetAcceptExSockaddrs //sys WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv //sys WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend @@ -637,7 +641,7 @@ func AcceptIOCP(iocpfd, fd int, o *Overlapped) (attrs *byte, errno int) { attrs = (*byte)(unsafe.Pointer(&rsa[0])) alen := uint32(unsafe.Sizeof(rsa[0])) var done uint32 - _, errno = AcceptEx(uint32(iocpfd), uint32(fd), attrs, 0, alen, alen, &done, o) + errno = AcceptEx(uint32(iocpfd), uint32(fd), attrs, 0, alen, alen, &done, o) return } @@ -660,6 +664,32 @@ func WSASendto(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 return } +// Invented structures to support what package os expects. +type Rusage struct{} + +type WaitStatus struct { + Status uint32 + ExitCode uint32 +} + +func (w WaitStatus) Exited() bool { return true } + +func (w WaitStatus) ExitStatus() int { return int(w.ExitCode) } + +func (w WaitStatus) Signal() int { return -1 } + +func (w WaitStatus) CoreDump() bool { return false } + +func (w WaitStatus) Stopped() bool { return false } + +func (w WaitStatus) Continued() bool { return false } + +func (w WaitStatus) StopSignal() int { return -1 } + +func (w WaitStatus) Signaled() bool { return true } + +func (w WaitStatus) TrapCause() int { return -1 } + // TODO(brainman): fix all needed for net func Accept(fd int) (nfd int, sa Sockaddr, errno int) { return 0, nil, EWINDOWS } @@ -694,9 +724,6 @@ func Chown(path string, uid int, gid int) (errno int) { return EWINDOWS } func Lchown(path string, uid int, gid int) (errno int) { return EWINDOWS } func Fchown(fd int, uid int, gid int) (errno int) { return EWINDOWS } -// TODO(brainman): use FlushFileBuffers Windows api to implement Fsync. -func Fsync(fd int) (errno int) { return EWINDOWS } - func Getuid() (uid int) { return -1 } func Geteuid() (euid int) { return -1 } func Getgid() (gid int) { return -1 } @@ -723,67 +750,3 @@ const ( SYS_EXIT SYS_READ ) - -type Rusage struct { - Utime Timeval - Stime Timeval - Maxrss int32 - Ixrss int32 - Idrss int32 - Isrss int32 - Minflt int32 - Majflt int32 - Nswap int32 - Inblock int32 - Oublock int32 - Msgsnd int32 - Msgrcv int32 - Nsignals int32 - Nvcsw int32 - Nivcsw int32 -} - -type WaitStatus struct { - Status uint32 - ExitCode uint32 -} - -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { - const da = STANDARD_RIGHTS_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE - handle, errno := OpenProcess(da, 0, uint32(pid)) - if errno != 0 { - return 0, errno - } - defer CloseHandle(int32(handle)) - e, errno := WaitForSingleObject(int32(handle), INFINITE) - var c uint32 - if ok, errno := GetExitCodeProcess(handle, &c); !ok { - return 0, errno - } - *wstatus = WaitStatus{e, c} - return pid, 0 -} - - -func (w WaitStatus) Exited() bool { return w.Status == WAIT_OBJECT_0 } - -func (w WaitStatus) ExitStatus() int { - if w.Status == WAIT_OBJECT_0 { - return int(w.ExitCode) - } - return -1 -} - -func (WaitStatus) Signal() int { return -1 } - -func (WaitStatus) CoreDump() bool { return false } - -func (WaitStatus) Stopped() bool { return false } - -func (WaitStatus) Continued() bool { return false } - -func (WaitStatus) StopSignal() int { return -1 } - -func (w WaitStatus) Signaled() bool { return w.Status == WAIT_OBJECT_0 } - -func (WaitStatus) TrapCause() int { return -1 } diff --git a/src/pkg/syscall/types_darwin.c b/src/pkg/syscall/types_darwin.c index d7f7a7460..4096bcfd9 100644 --- a/src/pkg/syscall/types_darwin.c +++ b/src/pkg/syscall/types_darwin.c @@ -11,12 +11,10 @@ Input to godefs. See also mkerrors.sh and mkall.sh #define _DARWIN_USE_64_BIT_INODE #include <dirent.h> #include <fcntl.h> +#include <signal.h> +#include <unistd.h> #include <mach/mach.h> #include <mach/message.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <signal.h> -#include <stdio.h> #include <sys/event.h> #include <sys/mman.h> #include <sys/mount.h> @@ -25,12 +23,18 @@ Input to godefs. See also mkerrors.sh and mkall.sh #include <sys/resource.h> #include <sys/select.h> #include <sys/signal.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <sys/un.h> #include <sys/wait.h> -#include <unistd.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_var.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/tcp.h> // Machine characteristics; for internal use. @@ -89,6 +93,7 @@ union sockaddr_all { struct sockaddr_in s2; // these pad it out struct sockaddr_in6 s3; struct sockaddr_un s4; + struct sockaddr_dl s5; }; struct sockaddr_any { @@ -99,11 +104,13 @@ struct sockaddr_any { typedef struct sockaddr_in $RawSockaddrInet4; typedef struct sockaddr_in6 $RawSockaddrInet6; typedef struct sockaddr_un $RawSockaddrUnix; +typedef struct sockaddr_dl $RawSockaddrDatalink; typedef struct sockaddr $RawSockaddr; typedef struct sockaddr_any $RawSockaddrAny; typedef socklen_t $_Socklen; typedef struct linger $Linger; typedef struct iovec $Iovec; +typedef struct ip_mreq $IpMreq; typedef struct msghdr $Msghdr; typedef struct cmsghdr $Cmsghdr; @@ -112,7 +119,9 @@ enum { $SizeofSockaddrInet6 = sizeof(struct sockaddr_in6), $SizeofSockaddrAny = sizeof(struct sockaddr_any), $SizeofSockaddrUnix = sizeof(struct sockaddr_un), + $SizeofSockaddrDatalink = sizeof(struct sockaddr_dl), $SizeofLinger = sizeof(struct linger), + $SizeofIpMreq = sizeof(struct ip_mreq), $SizeofMsghdr = sizeof(struct msghdr), $SizeofCmsghdr = sizeof(struct cmsghdr), }; @@ -132,3 +141,19 @@ typedef struct kevent $Kevent_t; // Select typedef fd_set $FdSet; + +// Routing and interface messages + +enum { + $SizeofIfMsghdr = sizeof(struct if_msghdr), + $SizeofIfData = sizeof(struct if_data), + $SizeofIfaMsghdr = sizeof(struct ifa_msghdr), + $SizeofRtMsghdr = sizeof(struct rt_msghdr), + $SizeofRtMetrics = sizeof(struct rt_metrics), +}; + +typedef struct if_msghdr $IfMsghdr; +typedef struct if_data $IfData; +typedef struct ifa_msghdr $IfaMsghdr; +typedef struct rt_msghdr $RtMsghdr; +typedef struct rt_metrics $RtMetrics; diff --git a/src/pkg/syscall/types_freebsd.c b/src/pkg/syscall/types_freebsd.c index 13bec9f9f..6fc814134 100644 --- a/src/pkg/syscall/types_freebsd.c +++ b/src/pkg/syscall/types_freebsd.c @@ -7,14 +7,11 @@ Input to godefs. See also mkerrors.sh and mkall.sh */ #define KERNEL -#include <sys/cdefs.h> #include <dirent.h> #include <fcntl.h> -#include <sys/types.h> -#include <netinet/in.h> -#include <netinet/tcp.h> #include <signal.h> #include <stdio.h> +#include <unistd.h> #include <sys/event.h> #include <sys/mman.h> #include <sys/mount.h> @@ -26,9 +23,14 @@ Input to godefs. See also mkerrors.sh and mkall.sh #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> +#include <sys/types.h> #include <sys/un.h> #include <sys/wait.h> -#include <unistd.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/tcp.h> // Machine characteristics; for internal use. @@ -101,6 +103,7 @@ union sockaddr_all { struct sockaddr_in s2; // these pad it out struct sockaddr_in6 s3; struct sockaddr_un s4; + struct sockaddr_dl s5; }; struct sockaddr_any { @@ -111,11 +114,13 @@ struct sockaddr_any { typedef struct sockaddr_in $RawSockaddrInet4; typedef struct sockaddr_in6 $RawSockaddrInet6; typedef struct sockaddr_un $RawSockaddrUnix; +typedef struct sockaddr_dl $RawSockaddrDatalink; typedef struct sockaddr $RawSockaddr; typedef struct sockaddr_any $RawSockaddrAny; typedef socklen_t $_Socklen; typedef struct linger $Linger; typedef struct iovec $Iovec; +typedef struct ip_mreq $IpMreq; typedef struct msghdr $Msghdr; typedef struct cmsghdr $Cmsghdr; @@ -124,7 +129,9 @@ enum { $SizeofSockaddrInet6 = sizeof(struct sockaddr_in6), $SizeofSockaddrAny = sizeof(struct sockaddr_any), $SizeofSockaddrUnix = sizeof(struct sockaddr_un), + $SizeofSockaddrDatalink = sizeof(struct sockaddr_dl), $SizeofLinger = sizeof(struct linger), + $SizeofIpMreq = sizeof(struct ip_mreq), $SizeofMsghdr = sizeof(struct msghdr), $SizeofCmsghdr = sizeof(struct cmsghdr), }; @@ -144,3 +151,19 @@ typedef struct kevent $Kevent_t; // Select typedef fd_set $FdSet; + +// Routing and interface messages + +enum { + $SizeofIfMsghdr = sizeof(struct if_msghdr), + $SizeofIfData = sizeof(struct if_data), + $SizeofIfaMsghdr = sizeof(struct ifa_msghdr), + $SizeofRtMsghdr = sizeof(struct rt_msghdr), + $SizeofRtMetrics = sizeof(struct rt_metrics), +}; + +typedef struct if_msghdr $IfMsghdr; +typedef struct if_data $IfData; +typedef struct ifa_msghdr $IfaMsghdr; +typedef struct rt_msghdr $RtMsghdr; +typedef struct rt_metrics $RtMetrics; diff --git a/src/pkg/syscall/types_linux.c b/src/pkg/syscall/types_linux.c index 4752e3122..ec94c84df 100644 --- a/src/pkg/syscall/types_linux.c +++ b/src/pkg/syscall/types_linux.c @@ -109,6 +109,7 @@ typedef struct sockaddr_any $RawSockaddrAny; typedef socklen_t $_Socklen; typedef struct linger $Linger; typedef struct iovec $Iovec; +typedef struct ip_mreq $IpMreq; typedef struct msghdr $Msghdr; typedef struct cmsghdr $Cmsghdr; typedef struct ucred $Ucred; @@ -120,6 +121,7 @@ enum { $SizeofSockaddrUnix = sizeof(struct sockaddr_un), $SizeofSockaddrLinklayer = sizeof(struct sockaddr_ll), $SizeofLinger = sizeof(struct linger), + $SizeofIpMreq = sizeof(struct ip_mreq), $SizeofMsghdr = sizeof(struct msghdr), $SizeofCmsghdr = sizeof(struct cmsghdr), $SizeofUcred = sizeof(struct ucred), diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go index 8f5f69b5b..52b986228 100644 --- a/src/pkg/syscall/zerrors_darwin_386.go +++ b/src/pkg/syscall/zerrors_darwin_386.go @@ -45,6 +45,8 @@ const ( AF_SYSTEM = 0x20 AF_UNIX = 0x1 AF_UNSPEC = 0 + CTL_MAXNAME = 0xc + CTL_NET = 0x4 E2BIG = 0x7 EACCES = 0xd EADDRINUSE = 0x30 @@ -475,6 +477,15 @@ const ( MSG_TRUNC = 0x10 MSG_WAITALL = 0x40 MSG_WAITSTREAM = 0x200 + NAME_MAX = 0xff + NET_RT_DUMP = 0x1 + NET_RT_DUMP2 = 0x7 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFLIST2 = 0x6 + NET_RT_MAXID = 0x8 + NET_RT_STAT = 0x4 + NET_RT_TRASH = 0x5 O_ACCMODE = 0x3 O_ALERT = 0x20000000 O_APPEND = 0x8 @@ -498,6 +509,77 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_CLONING = 0x100 + RTF_CONDEMNED = 0x2000000 + RTF_DELCLONE = 0x80 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_IFREF = 0x4000000 + RTF_IFSCOPE = 0x1000000 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_STATIC = 0x800 + RTF_UP = 0x1 + RTF_WASCLONED = 0x20000 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_GET2 = 0x14 + RTM_IFINFO = 0xe + RTM_IFINFO2 = 0x12 + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_NEWMADDR2 = 0x13 + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go index 75174a0d2..4e7a174ea 100644 --- a/src/pkg/syscall/zerrors_darwin_amd64.go +++ b/src/pkg/syscall/zerrors_darwin_amd64.go @@ -45,6 +45,8 @@ const ( AF_SYSTEM = 0x20 AF_UNIX = 0x1 AF_UNSPEC = 0 + CTL_MAXNAME = 0xc + CTL_NET = 0x4 E2BIG = 0x7 EACCES = 0xd EADDRINUSE = 0x30 @@ -475,6 +477,15 @@ const ( MSG_TRUNC = 0x10 MSG_WAITALL = 0x40 MSG_WAITSTREAM = 0x200 + NAME_MAX = 0xff + NET_RT_DUMP = 0x1 + NET_RT_DUMP2 = 0x7 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFLIST2 = 0x6 + NET_RT_MAXID = 0x8 + NET_RT_STAT = 0x4 + NET_RT_TRASH = 0x5 O_ACCMODE = 0x3 O_ALERT = 0x20000000 O_APPEND = 0x8 @@ -498,6 +509,77 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_CLONING = 0x100 + RTF_CONDEMNED = 0x2000000 + RTF_DELCLONE = 0x80 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_IFREF = 0x4000000 + RTF_IFSCOPE = 0x1000000 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_STATIC = 0x800 + RTF_UP = 0x1 + RTF_WASCLONED = 0x20000 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_GET2 = 0x14 + RTM_IFINFO = 0xe + RTM_IFINFO2 = 0x12 + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_NEWMADDR2 = 0x13 + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go index 5af1d4a1a..d3d46ce03 100644 --- a/src/pkg/syscall/zerrors_freebsd_386.go +++ b/src/pkg/syscall/zerrors_freebsd_386.go @@ -94,6 +94,8 @@ const ( AF_VENDOR45 = 0x81 AF_VENDOR46 = 0x83 AF_VENDOR47 = 0x85 + CTL_MAXNAME = 0x18 + CTL_NET = 0x4 E2BIG = 0x7 EACCES = 0xd EADDRINUSE = 0x30 @@ -540,6 +542,11 @@ const ( MSG_PEEK = 0x2 MSG_TRUNC = 0x10 MSG_WAITALL = 0x40 + NET_RT_DUMP = 0x1 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFMALIST = 0x4 + NET_RT_MAXID = 0x5 O_ACCMODE = 0x3 O_APPEND = 0x8 O_ASYNC = 0x40 @@ -561,6 +568,75 @@ const ( O_TRUNC = 0x400 O_TTY_INIT = 0x80000 O_WRONLY = 0x1 + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_FMASK = 0x1004d808 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_LLDATA = 0x400 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_RNH_LOCKED = 0x40000000 + RTF_STATIC = 0x800 + RTF_STICKY = 0x10000000 + RTF_UP = 0x1 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_IEEE80211 = 0x12 + RTM_IFANNOUNCE = 0x11 + RTM_IFINFO = 0xe + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RTV_WEIGHT = 0x100 SCM_BINTIME = 0x4 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go index 7e9d85754..ce3ff5402 100644 --- a/src/pkg/syscall/zerrors_freebsd_amd64.go +++ b/src/pkg/syscall/zerrors_freebsd_amd64.go @@ -94,6 +94,8 @@ const ( AF_VENDOR45 = 0x81 AF_VENDOR46 = 0x83 AF_VENDOR47 = 0x85 + CTL_MAXNAME = 0x18 + CTL_NET = 0x4 E2BIG = 0x7 EACCES = 0xd EADDRINUSE = 0x30 @@ -540,6 +542,11 @@ const ( MSG_PEEK = 0x2 MSG_TRUNC = 0x10 MSG_WAITALL = 0x40 + NET_RT_DUMP = 0x1 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFMALIST = 0x4 + NET_RT_MAXID = 0x5 O_ACCMODE = 0x3 O_APPEND = 0x8 O_ASYNC = 0x40 @@ -561,6 +568,75 @@ const ( O_TRUNC = 0x400 O_TTY_INIT = 0x80000 O_WRONLY = 0x1 + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_FMASK = 0x1004d808 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_LLDATA = 0x400 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_RNH_LOCKED = 0x40000000 + RTF_STATIC = 0x800 + RTF_STICKY = 0x10000000 + RTF_UP = 0x1 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_IEEE80211 = 0x12 + RTM_IFANNOUNCE = 0x11 + RTM_IFINFO = 0xe + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RTV_WEIGHT = 0x100 SCM_BINTIME = 0x4 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go index fe45d23f1..43d27827c 100644 --- a/src/pkg/syscall/zerrors_linux_386.go +++ b/src/pkg/syscall/zerrors_linux_386.go @@ -640,7 +640,6 @@ const ( SIOCSIFTXQLEN = 0x8943 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 - SIOGIFINDEX = 0x8933 SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go index f9404d089..04f4dad2c 100644 --- a/src/pkg/syscall/zerrors_linux_amd64.go +++ b/src/pkg/syscall/zerrors_linux_amd64.go @@ -641,7 +641,6 @@ const ( SIOCSIFTXQLEN = 0x8943 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 - SIOGIFINDEX = 0x8933 SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go index 9718e5def..973f00ef8 100644 --- a/src/pkg/syscall/zsyscall_darwin_386.go +++ b/src/pkg/syscall/zsyscall_darwin_386.go @@ -121,6 +121,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -134,6 +136,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) @@ -155,6 +159,8 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) var _p0 unsafe.Pointer if len(mib) > 0 { _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) errno = int(e1) @@ -377,6 +383,8 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) n = int(r0) @@ -414,6 +422,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETFSSTAT64, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) n = int(r0) @@ -601,6 +611,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) @@ -614,6 +626,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) @@ -627,6 +641,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -640,6 +656,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -871,6 +889,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go index 6dca1987c..f7a37b63e 100644 --- a/src/pkg/syscall/zsyscall_darwin_amd64.go +++ b/src/pkg/syscall/zsyscall_darwin_amd64.go @@ -121,6 +121,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -134,6 +136,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) @@ -155,6 +159,8 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) var _p0 unsafe.Pointer if len(mib) > 0 { _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) errno = int(e1) @@ -377,6 +383,8 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) n = int(r0) @@ -414,6 +422,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETFSSTAT64, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) n = int(r0) @@ -601,6 +611,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -614,6 +626,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -627,6 +641,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -640,6 +656,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -871,6 +889,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go index 627a9a24d..1fab5e2d2 100644 --- a/src/pkg/syscall/zsyscall_freebsd_386.go +++ b/src/pkg/syscall/zsyscall_freebsd_386.go @@ -121,6 +121,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -134,6 +136,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) @@ -155,6 +159,8 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) var _p0 unsafe.Pointer if len(mib) > 0 { _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) errno = int(e1) @@ -361,6 +367,8 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) n = int(r0) @@ -398,6 +406,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) n = int(r0) @@ -609,6 +619,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) @@ -622,6 +634,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) @@ -635,6 +649,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -648,6 +664,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -871,6 +889,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go index 8872367b1..53434b263 100644 --- a/src/pkg/syscall/zsyscall_freebsd_amd64.go +++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go @@ -121,6 +121,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -134,6 +136,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) @@ -155,6 +159,8 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) var _p0 unsafe.Pointer if len(mib) > 0 { _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) errno = int(e1) @@ -361,6 +367,8 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) n = int(r0) @@ -398,6 +406,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) n = int(r0) @@ -609,6 +619,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -622,6 +634,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -635,6 +649,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -648,6 +664,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -871,6 +889,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go index aa8c41a31..005cc1542 100644 --- a/src/pkg/syscall/zsyscall_linux_386.go +++ b/src/pkg/syscall/zsyscall_linux_386.go @@ -53,6 +53,8 @@ func Getcwd(buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0) n = int(r0) @@ -184,6 +186,8 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) { var _p0 unsafe.Pointer if len(events) > 0 { _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) n = int(r0) @@ -277,6 +281,8 @@ func Getdents(fd int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -391,6 +397,8 @@ func Klogctl(typ int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -468,6 +476,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -481,6 +491,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -518,6 +530,8 @@ func Setdomainname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -530,6 +544,8 @@ func Sethostname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -688,6 +704,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -823,6 +841,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) @@ -836,6 +856,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go index 2759c5c8b..d449a3bfe 100644 --- a/src/pkg/syscall/zsyscall_linux_amd64.go +++ b/src/pkg/syscall/zsyscall_linux_amd64.go @@ -53,6 +53,8 @@ func Getcwd(buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0) n = int(r0) @@ -184,6 +186,8 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) { var _p0 unsafe.Pointer if len(events) > 0 { _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) n = int(r0) @@ -277,6 +281,8 @@ func Getdents(fd int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -391,6 +397,8 @@ func Klogctl(typ int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -468,6 +476,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -481,6 +491,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -518,6 +530,8 @@ func Setdomainname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -530,6 +544,8 @@ func Sethostname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -688,6 +704,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -839,6 +857,8 @@ func Pread(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -852,6 +872,8 @@ func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) @@ -1071,6 +1093,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -1084,6 +1108,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go index 711108577..22b736bfa 100644 --- a/src/pkg/syscall/zsyscall_linux_arm.go +++ b/src/pkg/syscall/zsyscall_linux_arm.go @@ -53,6 +53,8 @@ func Getcwd(buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0) n = int(r0) @@ -184,6 +186,8 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) { var _p0 unsafe.Pointer if len(events) > 0 { _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) n = int(r0) @@ -277,6 +281,8 @@ func Getdents(fd int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -391,6 +397,8 @@ func Klogctl(typ int, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -468,6 +476,8 @@ func Read(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -481,6 +491,8 @@ func Readlink(path string, buf []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) n = int(r0) @@ -518,6 +530,8 @@ func Setdomainname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -530,6 +544,8 @@ func Sethostname(p []byte) (errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0) errno = int(e1) @@ -688,6 +704,8 @@ func Write(fd int, p []byte) (n int, errno int) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) @@ -802,6 +820,8 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) n = int(r0) @@ -815,6 +835,8 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) errno = int(e1) diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index b71177e42..46e16f43c 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -45,6 +45,8 @@ var ( procGetQueuedCompletionStatus = getSysProcAddr(modkernel32, "GetQueuedCompletionStatus") procCancelIo = getSysProcAddr(modkernel32, "CancelIo") procCreateProcessW = getSysProcAddr(modkernel32, "CreateProcessW") + procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess") + procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess") procGetStartupInfoW = getSysProcAddr(modkernel32, "GetStartupInfoW") procGetCurrentProcess = getSysProcAddr(modkernel32, "GetCurrentProcess") procDuplicateHandle = getSysProcAddr(modkernel32, "DuplicateHandle") @@ -55,8 +57,6 @@ var ( procCryptAcquireContextW = getSysProcAddr(modadvapi32, "CryptAcquireContextW") procCryptReleaseContext = getSysProcAddr(modadvapi32, "CryptReleaseContext") procCryptGenRandom = getSysProcAddr(modadvapi32, "CryptGenRandom") - procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess") - procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess") procGetEnvironmentStringsW = getSysProcAddr(modkernel32, "GetEnvironmentStringsW") procFreeEnvironmentStringsW = getSysProcAddr(modkernel32, "FreeEnvironmentStringsW") procGetEnvironmentVariableW = getSysProcAddr(modkernel32, "GetEnvironmentVariableW") @@ -66,6 +66,8 @@ var ( procGetCommandLineW = getSysProcAddr(modkernel32, "GetCommandLineW") procCommandLineToArgvW = getSysProcAddr(modshell32, "CommandLineToArgvW") procLocalFree = getSysProcAddr(modkernel32, "LocalFree") + procSetHandleInformation = getSysProcAddr(modkernel32, "SetHandleInformation") + procFlushFileBuffers = getSysProcAddr(modkernel32, "FlushFileBuffers") procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup") procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup") procsocket = getSysProcAddr(modwsock32, "socket") @@ -111,10 +113,9 @@ func LoadLibrary(libname string) (handle uint32, errno int) { return } -func FreeLibrary(handle uint32) (ok bool, errno int) { - r0, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0) - ok = bool(r0 != 0) - if !ok { +func FreeLibrary(handle uint32) (errno int) { + r1, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -180,7 +181,7 @@ func ExitProcess(exitcode uint32) { return } -func CreateFile(name *uint16, access uint32, mode uint32, sa *byte, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) { +func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) { r0, _, e1 := Syscall9(procCreateFileW, 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) handle = int32(r0) if handle == -1 { @@ -195,14 +196,13 @@ func CreateFile(name *uint16, access uint32, mode uint32, sa *byte, createmode u return } -func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (ok bool, errno int) { +func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] } - r0, _, e1 := Syscall6(procReadFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - ok = bool(r0 != 0) - if !ok { + r1, _, e1 := Syscall6(procReadFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -214,14 +214,13 @@ func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (o return } -func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (ok bool, errno int) { +func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] } - r0, _, e1 := Syscall6(procWriteFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - ok = bool(r0 != 0) - if !ok { + r1, _, e1 := Syscall6(procWriteFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -248,10 +247,9 @@ func SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence return } -func CloseHandle(handle int32) (ok bool, errno int) { - r0, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0) - ok = bool(r0 != 0) - if !ok { +func CloseHandle(handle int32) (errno int) { + r1, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -293,10 +291,9 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) return } -func FindNextFile(handle int32, data *Win32finddata) (ok bool, errno int) { - r0, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - ok = bool(r0 != 0) - if !ok { +func FindNextFile(handle int32, data *Win32finddata) (errno int) { + r1, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -308,10 +305,9 @@ func FindNextFile(handle int32, data *Win32finddata) (ok bool, errno int) { return } -func FindClose(handle int32) (ok bool, errno int) { - r0, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0) - ok = bool(r0 != 0) - if !ok { +func FindClose(handle int32) (errno int) { + r1, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -323,10 +319,9 @@ func FindClose(handle int32) (ok bool, errno int) { return } -func GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (ok bool, errno int) { - r0, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - ok = bool(r0 != 0) - if !ok { +func GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int) { + r1, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -353,10 +348,9 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) { return } -func SetCurrentDirectory(path *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procSetCurrentDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func SetCurrentDirectory(path *uint16) (errno int) { + r1, _, e1 := Syscall(procSetCurrentDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -368,10 +362,9 @@ func SetCurrentDirectory(path *uint16) (ok bool, errno int) { return } -func CreateDirectory(path *uint16, sa *byte) (ok bool, errno int) { - r0, _, e1 := Syscall(procCreateDirectoryW, 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) - ok = bool(r0 != 0) - if !ok { +func CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) { + r1, _, e1 := Syscall(procCreateDirectoryW, 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -383,10 +376,9 @@ func CreateDirectory(path *uint16, sa *byte) (ok bool, errno int) { return } -func RemoveDirectory(path *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procRemoveDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func RemoveDirectory(path *uint16) (errno int) { + r1, _, e1 := Syscall(procRemoveDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -398,10 +390,9 @@ func RemoveDirectory(path *uint16) (ok bool, errno int) { return } -func DeleteFile(path *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procDeleteFileW, 1, uintptr(unsafe.Pointer(path)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func DeleteFile(path *uint16) (errno int) { + r1, _, e1 := Syscall(procDeleteFileW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -413,10 +404,9 @@ func DeleteFile(path *uint16) (ok bool, errno int) { return } -func MoveFile(from *uint16, to *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procMoveFileW, 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) - ok = bool(r0 != 0) - if !ok { +func MoveFile(from *uint16, to *uint16) (errno int) { + r1, _, e1 := Syscall(procMoveFileW, 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -428,10 +418,9 @@ func MoveFile(from *uint16, to *uint16) (ok bool, errno int) { return } -func GetComputerName(buf *uint16, n *uint32) (ok bool, errno int) { - r0, _, e1 := Syscall(procGetComputerNameW, 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) - ok = bool(r0 != 0) - if !ok { +func GetComputerName(buf *uint16, n *uint32) (errno int) { + r1, _, e1 := Syscall(procGetComputerNameW, 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -443,10 +432,9 @@ func GetComputerName(buf *uint16, n *uint32) (ok bool, errno int) { return } -func SetEndOfFile(handle int32) (ok bool, errno int) { - r0, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0) - ok = bool(r0 != 0) - if !ok { +func SetEndOfFile(handle int32) (errno int) { + r1, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -498,10 +486,9 @@ func CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, thread return } -func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (ok bool, errno int) { - r0, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) - ok = bool(r0 != 0) - if !ok { +func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) { + r1, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -513,10 +500,9 @@ func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlap return } -func CancelIo(s uint32) (ok bool, errno int) { - r0, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0) - ok = bool(r0 != 0) - if !ok { +func CancelIo(s uint32) (errno int) { + r1, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -528,16 +514,15 @@ func CancelIo(s uint32) (ok bool, errno int) { return } -func CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (ok bool, errno int) { +func CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) { var _p0 uint32 if inheritHandles { _p0 = 1 } else { _p0 = 0 } - r0, _, e1 := Syscall12(procCreateProcessW, 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) - ok = bool(r0 != 0) - if !ok { + r1, _, e1 := Syscall12(procCreateProcessW, 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -549,10 +534,16 @@ func CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, thr return } -func GetStartupInfo(startupInfo *StartupInfo) (ok bool, errno int) { - r0, _, e1 := Syscall(procGetStartupInfoW, 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid)) + handle = uint32(r0) + if handle == 0 { if e1 != 0 { errno = int(e1) } else { @@ -564,10 +555,9 @@ func GetStartupInfo(startupInfo *StartupInfo) (ok bool, errno int) { return } -func GetCurrentProcess() (pseudoHandle int32, errno int) { - r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0) - pseudoHandle = int32(r0) - if pseudoHandle == 0 { +func GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) { + r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -579,16 +569,9 @@ func GetCurrentProcess() (pseudoHandle int32, errno int) { return } -func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (ok bool, errno int) { - var _p0 uint32 - if bInheritHandle { - _p0 = 1 - } else { - _p0 = 0 - } - r0, _, e1 := Syscall9(procDuplicateHandle, 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) - ok = bool(r0 != 0) - if !ok { +func GetStartupInfo(startupInfo *StartupInfo) (errno int) { + r1, _, e1 := Syscall(procGetStartupInfoW, 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -600,10 +583,10 @@ func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetPro return } -func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) { - r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0) - event = uint32(r0) - if event == 0xffffffff { +func GetCurrentProcess() (pseudoHandle int32, errno int) { + r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0) + pseudoHandle = int32(r0) + if pseudoHandle == 0 { if e1 != 0 { errno = int(e1) } else { @@ -615,10 +598,15 @@ func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, e return } -func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) { - r0, _, e1 := Syscall(procGetTempPathW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) - n = uint32(r0) - if n == 0 { +func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) { + var _p0 uint32 + if bInheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := Syscall9(procDuplicateHandle, 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -630,10 +618,10 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) { return } -func CreatePipe(readhandle *uint32, writehandle *uint32, lpsa *byte, size uint32) (ok bool, errno int) { - r0, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(lpsa)), uintptr(size), 0, 0) - ok = bool(r0 != 0) - if !ok { +func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) { + r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0) + event = uint32(r0) + if event == 0xffffffff { if e1 != 0 { errno = int(e1) } else { @@ -645,8 +633,8 @@ func CreatePipe(readhandle *uint32, writehandle *uint32, lpsa *byte, size uint32 return } -func GetFileType(filehandle uint32) (n uint32, errno int) { - r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0) +func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetTempPathW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) n = uint32(r0) if n == 0 { if e1 != 0 { @@ -660,10 +648,9 @@ func GetFileType(filehandle uint32) (n uint32, errno int) { return } -func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (ok bool, errno int) { - r0, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) - ok = bool(r0 != 0) - if !ok { +func CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int) { + r1, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -675,10 +662,10 @@ func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16 return } -func CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) { - r0, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0) - ok = bool(r0 != 0) - if !ok { +func GetFileType(filehandle uint32) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0) + n = uint32(r0) + if n == 0 { if e1 != 0 { errno = int(e1) } else { @@ -690,10 +677,9 @@ func CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) { return } -func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (ok bool, errno int) { - r0, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) - ok = bool(r0 != 0) - if !ok { +func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) { + r1, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -705,10 +691,9 @@ func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (ok bool, errno return } -func OpenProcess(da uint32, b int, pid uint32) (handle uint32, errno int) { - r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(b), uintptr(pid)) - handle = uint32(r0) - if handle == 0 { +func CryptReleaseContext(provhandle uint32, flags uint32) (errno int) { + r1, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -720,10 +705,9 @@ func OpenProcess(da uint32, b int, pid uint32) (handle uint32, errno int) { return } -func GetExitCodeProcess(h uint32, c *uint32) (ok bool, errno int) { - r0, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(h), uintptr(unsafe.Pointer(c)), 0) - ok = bool(r0 != 0) - if !ok { +func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) { + r1, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -750,10 +734,9 @@ func GetEnvironmentStrings() (envs *uint16, errno int) { return } -func FreeEnvironmentStrings(envs *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procFreeEnvironmentStringsW, 1, uintptr(unsafe.Pointer(envs)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func FreeEnvironmentStrings(envs *uint16) (errno int) { + r1, _, e1 := Syscall(procFreeEnvironmentStringsW, 1, uintptr(unsafe.Pointer(envs)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -780,10 +763,9 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32 return } -func SetEnvironmentVariable(name *uint16, value *uint16) (ok bool, errno int) { - r0, _, e1 := Syscall(procSetEnvironmentVariableW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) - ok = bool(r0 != 0) - if !ok { +func SetEnvironmentVariable(name *uint16, value *uint16) (errno int) { + r1, _, e1 := Syscall(procSetEnvironmentVariableW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -795,10 +777,9 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (ok bool, errno int) { return } -func SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (ok bool, errno int) { - r0, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) - ok = bool(r0 != 0) - if !ok { +func SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) { + r1, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { @@ -861,6 +842,34 @@ func LocalFree(hmem uint32) (handle uint32, errno int) { return } +func SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) { + r1, _, e1 := Syscall(procSetHandleInformation, 3, uintptr(handle), uintptr(mask), uintptr(flags)) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FlushFileBuffers(handle int32) (errno int) { + r1, _, e1 := Syscall(procFlushFileBuffers, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) { r0, _, _ := Syscall(procWSAStartup, 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) sockerrno = int(r0) @@ -1008,10 +1017,9 @@ func Closesocket(s int32) (errno int) { return } -func AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (ok bool, errno int) { - r0, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) - ok = bool(r0 != 0) - if !ok { +func AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) { + r1, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { if e1 != 0 { errno = int(e1) } else { diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go index 708bba400..736c654ab 100644 --- a/src/pkg/syscall/ztypes_darwin_386.go +++ b/src/pkg/syscall/ztypes_darwin_386.go @@ -6,22 +6,29 @@ package syscall // Constants const ( - sizeofPtr = 0x4 - sizeofShort = 0x2 - sizeofInt = 0x4 - sizeofLong = 0x4 - sizeofLongLong = 0x8 - O_CLOEXEC = 0 - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofLinger = 0x8 - SizeofMsghdr = 0x1c - SizeofCmsghdr = 0xc - PTRACE_TRACEME = 0 - PTRACE_CONT = 0x7 - PTRACE_KILL = 0x8 + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + O_CLOEXEC = 0 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofLinger = 0x8 + SizeofIpMreq = 0x8 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc + PTRACE_TRACEME = 0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 + SizeofIfMsghdr = 0x70 + SizeofIfData = 0x60 + SizeofIfaMsghdr = 0x14 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 ) // Types @@ -144,13 +151,13 @@ type Log2phys_t struct { } type Dirent struct { - Ino uint64 - Seekoff uint64 - Reclen uint16 - Namlen uint16 - Type uint8 - Name [1024]int8 - Pad0 [3]byte + Ino uint64 + Seekoff uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [1024]int8 + Pad_godefs_0 [3]byte } type RawSockaddrInet4 struct { @@ -176,6 +183,17 @@ type RawSockaddrUnix struct { Path [104]int8 } +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 +} + type RawSockaddr struct { Len uint8 Family uint8 @@ -199,6 +217,11 @@ type Iovec struct { Len uint32 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { Name *byte Namelen uint32 @@ -227,3 +250,87 @@ type Kevent_t struct { type FdSet struct { Bits [32]int32 } + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Typelen uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Recvquota uint8 + Xmitquota uint8 + Unused1 uint8 + Mtu uint32 + Metric uint32 + Baudrate uint32 + Ipackets uint32 + Ierrors uint32 + Opackets uint32 + Oerrors uint32 + Collisions uint32 + Ibytes uint32 + Obytes uint32 + Imcasts uint32 + Omcasts uint32 + Iqdrops uint32 + Noproto uint32 + Recvtiming uint32 + Xmittiming uint32 + Lastchange Timeval + Unused2 uint32 + Hwassist uint32 + Reserved1 uint32 + Reserved2 uint32 +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Metric int32 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_godefs_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits uint32 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint32 + Mtu uint32 + Hopcount uint32 + Expire int32 + Recvpipe uint32 + Sendpipe uint32 + Ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Pksent uint32 + Filler [4]uint32 +} diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go index 5b273768f..91ee45796 100644 --- a/src/pkg/syscall/ztypes_darwin_amd64.go +++ b/src/pkg/syscall/ztypes_darwin_amd64.go @@ -6,22 +6,29 @@ package syscall // Constants const ( - sizeofPtr = 0x8 - sizeofShort = 0x2 - sizeofInt = 0x4 - sizeofLong = 0x8 - sizeofLongLong = 0x8 - O_CLOEXEC = 0 - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofLinger = 0x8 - SizeofMsghdr = 0x30 - SizeofCmsghdr = 0xc - PTRACE_TRACEME = 0 - PTRACE_CONT = 0x7 - PTRACE_KILL = 0x8 + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + O_CLOEXEC = 0 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofLinger = 0x8 + SizeofIpMreq = 0x8 + SizeofMsghdr = 0x30 + SizeofCmsghdr = 0xc + PTRACE_TRACEME = 0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 + SizeofIfMsghdr = 0x70 + SizeofIfData = 0x60 + SizeofIfaMsghdr = 0x14 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 ) // Types @@ -40,9 +47,9 @@ type Timespec struct { } type Timeval struct { - Sec int64 - Usec int32 - Pad0 [4]byte + Sec int64 + Usec int32 + Pad_godefs_0 [4]byte } type Rusage struct { @@ -79,7 +86,7 @@ type Stat_t struct { Uid uint32 Gid uint32 Rdev int32 - Pad0 [4]byte + Pad_godefs_0 [4]byte Atimespec Timespec Mtimespec Timespec Ctimespec Timespec @@ -129,9 +136,9 @@ type Fstore_t struct { } type Radvisory_t struct { - Offset int64 - Count int32 - Pad0 [4]byte + Offset int64 + Count int32 + Pad_godefs_0 [4]byte } type Fbootstraptransfer_t struct { @@ -147,13 +154,13 @@ type Log2phys_t struct { } type Dirent struct { - Ino uint64 - Seekoff uint64 - Reclen uint16 - Namlen uint16 - Type uint8 - Name [1024]int8 - Pad0 [3]byte + Ino uint64 + Seekoff uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [1024]int8 + Pad_godefs_0 [3]byte } type RawSockaddrInet4 struct { @@ -179,6 +186,17 @@ type RawSockaddrUnix struct { Path [104]int8 } +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 +} + type RawSockaddr struct { Len uint8 Family uint8 @@ -202,16 +220,21 @@ type Iovec struct { Len uint64 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { - Name *byte - Namelen uint32 - Pad0 [4]byte - Iov *Iovec - Iovlen int32 - Pad1 [4]byte - Control *byte - Controllen uint32 - Flags int32 + Name *byte + Namelen uint32 + Pad_godefs_0 [4]byte + Iov *Iovec + Iovlen int32 + Pad_godefs_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 } type Cmsghdr struct { @@ -232,3 +255,87 @@ type Kevent_t struct { type FdSet struct { Bits [32]int32 } + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Typelen uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Recvquota uint8 + Xmitquota uint8 + Unused1 uint8 + Mtu uint32 + Metric uint32 + Baudrate uint32 + Ipackets uint32 + Ierrors uint32 + Opackets uint32 + Oerrors uint32 + Collisions uint32 + Ibytes uint32 + Obytes uint32 + Imcasts uint32 + Omcasts uint32 + Iqdrops uint32 + Noproto uint32 + Recvtiming uint32 + Xmittiming uint32 + Lastchange [8]byte /* timeval32 */ + Unused2 uint32 + Hwassist uint32 + Reserved1 uint32 + Reserved2 uint32 +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Metric int32 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_godefs_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits uint32 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint32 + Mtu uint32 + Hopcount uint32 + Expire int32 + Recvpipe uint32 + Sendpipe uint32 + Ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Pksent uint32 + Filler [4]uint32 +} diff --git a/src/pkg/syscall/ztypes_freebsd_386.go b/src/pkg/syscall/ztypes_freebsd_386.go index 39e74522d..0f8e37abb 100644 --- a/src/pkg/syscall/ztypes_freebsd_386.go +++ b/src/pkg/syscall/ztypes_freebsd_386.go @@ -6,36 +6,43 @@ package syscall // Constants const ( - sizeofPtr = 0x4 - sizeofShort = 0x2 - sizeofInt = 0x4 - sizeofLong = 0x4 - sizeofLongLong = 0x8 - O_CLOEXEC = 0 - S_IFMT = 0xf000 - S_IFIFO = 0x1000 - S_IFCHR = 0x2000 - S_IFDIR = 0x4000 - S_IFBLK = 0x6000 - S_IFREG = 0x8000 - S_IFLNK = 0xa000 - S_IFSOCK = 0xc000 - S_ISUID = 0x800 - S_ISGID = 0x400 - S_ISVTX = 0x200 - S_IRUSR = 0x100 - S_IWUSR = 0x80 - S_IXUSR = 0x40 - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofLinger = 0x8 - SizeofMsghdr = 0x1c - SizeofCmsghdr = 0xc - PTRACE_TRACEME = 0 - PTRACE_CONT = 0x7 - PTRACE_KILL = 0x8 + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + O_CLOEXEC = 0 + S_IFMT = 0xf000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 + S_ISUID = 0x800 + S_ISGID = 0x400 + S_ISVTX = 0x200 + S_IRUSR = 0x100 + S_IWUSR = 0x80 + S_IXUSR = 0x40 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x36 + SizeofLinger = 0x8 + SizeofIpMreq = 0x8 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc + PTRACE_TRACEME = 0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 + SizeofIfMsghdr = 0x60 + SizeofIfData = 0x50 + SizeofIfaMsghdr = 0x14 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 ) // Types @@ -102,8 +109,8 @@ type Stat_t struct { Gen uint32 Lspare int32 Birthtimespec Timespec - Pad0 uint32 - Pad1 uint32 + Pad_godefs_0 uint32 + Pad_godefs_1 uint32 } type Statfs_t struct { @@ -171,6 +178,17 @@ type RawSockaddrUnix struct { Path [104]int8 } +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [46]int8 +} + type RawSockaddr struct { Len uint8 Family uint8 @@ -194,6 +212,11 @@ type Iovec struct { Len uint32 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { Name *byte Namelen uint32 @@ -222,3 +245,84 @@ type Kevent_t struct { type FdSet struct { X__fds_bits [32]uint32 } + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Spare_char1 uint8 + Spare_char2 uint8 + Datalen uint8 + Mtu uint32 + Metric uint32 + Baudrate uint32 + Ipackets uint32 + Ierrors uint32 + Opackets uint32 + Oerrors uint32 + Collisions uint32 + Ibytes uint32 + Obytes uint32 + Imcasts uint32 + Omcasts uint32 + Iqdrops uint32 + Noproto uint32 + Hwassist uint32 + Epoch int32 + Lastchange Timeval +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Metric int32 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_godefs_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Fmask int32 + Inits uint32 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint32 + Mtu uint32 + Hopcount uint32 + Expire uint32 + Recvpipe uint32 + Sendpipe uint32 + Ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Pksent uint32 + Weight uint32 + Filler [3]uint32 +} diff --git a/src/pkg/syscall/ztypes_freebsd_amd64.go b/src/pkg/syscall/ztypes_freebsd_amd64.go index eea7a0517..83a54f66b 100644 --- a/src/pkg/syscall/ztypes_freebsd_amd64.go +++ b/src/pkg/syscall/ztypes_freebsd_amd64.go @@ -6,36 +6,43 @@ package syscall // Constants const ( - sizeofPtr = 0x8 - sizeofShort = 0x2 - sizeofInt = 0x4 - sizeofLong = 0x8 - sizeofLongLong = 0x8 - O_CLOEXEC = 0 - S_IFMT = 0xf000 - S_IFIFO = 0x1000 - S_IFCHR = 0x2000 - S_IFDIR = 0x4000 - S_IFBLK = 0x6000 - S_IFREG = 0x8000 - S_IFLNK = 0xa000 - S_IFSOCK = 0xc000 - S_ISUID = 0x800 - S_ISGID = 0x400 - S_ISVTX = 0x200 - S_IRUSR = 0x100 - S_IWUSR = 0x80 - S_IXUSR = 0x40 - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofLinger = 0x8 - SizeofMsghdr = 0x30 - SizeofCmsghdr = 0xc - PTRACE_TRACEME = 0 - PTRACE_CONT = 0x7 - PTRACE_KILL = 0x8 + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + O_CLOEXEC = 0 + S_IFMT = 0xf000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 + S_ISUID = 0x800 + S_ISGID = 0x400 + S_ISVTX = 0x200 + S_IRUSR = 0x100 + S_IWUSR = 0x80 + S_IXUSR = 0x40 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x36 + SizeofLinger = 0x8 + SizeofIpMreq = 0x8 + SizeofMsghdr = 0x30 + SizeofCmsghdr = 0xc + PTRACE_TRACEME = 0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 + SizeofIfMsghdr = 0xa8 + SizeofIfData = 0x98 + SizeofIfaMsghdr = 0x14 + SizeofRtMsghdr = 0x98 + SizeofRtMetrics = 0x70 ) // Types @@ -102,8 +109,8 @@ type Stat_t struct { Gen uint32 Lspare int32 Birthtimespec Timespec - Pad0 uint8 - Pad1 uint8 + Pad_godefs_0 uint8 + Pad_godefs_1 uint8 } type Statfs_t struct { @@ -132,13 +139,13 @@ type Statfs_t struct { } type Flock_t struct { - Start int64 - Len int64 - Pid int32 - Type int16 - Whence int16 - Sysid int32 - Pad0 [4]byte + Start int64 + Len int64 + Pid int32 + Type int16 + Whence int16 + Sysid int32 + Pad_godefs_0 [4]byte } type Dirent struct { @@ -172,6 +179,17 @@ type RawSockaddrUnix struct { Path [104]int8 } +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [46]int8 +} + type RawSockaddr struct { Len uint8 Family uint8 @@ -195,16 +213,21 @@ type Iovec struct { Len uint64 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { - Name *byte - Namelen uint32 - Pad0 [4]byte - Iov *Iovec - Iovlen int32 - Pad1 [4]byte - Control *byte - Controllen uint32 - Flags int32 + Name *byte + Namelen uint32 + Pad_godefs_0 [4]byte + Iov *Iovec + Iovlen int32 + Pad_godefs_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 } type Cmsghdr struct { @@ -225,3 +248,84 @@ type Kevent_t struct { type FdSet struct { X__fds_bits [16]uint64 } + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Spare_char1 uint8 + Spare_char2 uint8 + Datalen uint8 + Mtu uint64 + Metric uint64 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Hwassist uint64 + Epoch int64 + Lastchange Timeval +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_godefs_0 [2]byte + Metric int32 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_godefs_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Fmask int32 + Inits uint64 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint64 + Mtu uint64 + Hopcount uint64 + Expire uint64 + Recvpipe uint64 + Sendpipe uint64 + Ssthresh uint64 + Rtt uint64 + Rttvar uint64 + Pksent uint64 + Weight uint64 + Filler [3]uint64 +} diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go index 0603168aa..d98d6af05 100644 --- a/src/pkg/syscall/ztypes_linux_386.go +++ b/src/pkg/syscall/ztypes_linux_386.go @@ -18,6 +18,7 @@ const ( SizeofSockaddrUnix = 0x6e SizeofSockaddrLinklayer = 0x14 SizeofLinger = 0x8 + SizeofIpMreq = 0x8 SizeofMsghdr = 0x1c SizeofCmsghdr = 0xc SizeofUcred = 0xc @@ -45,37 +46,37 @@ type Timeval struct { } type Timex struct { - Modes uint32 - Offset int32 - Freq int32 - Maxerror int32 - Esterror int32 - Status int32 - Constant int32 - Precision int32 - Tolerance int32 - Time Timeval - Tick int32 - Ppsfreq int32 - Jitter int32 - Shift int32 - Stabil int32 - Jitcnt int32 - Calcnt int32 - Errcnt int32 - Stbcnt int32 - Tai int32 - Pad0 int32 - Pad1 int32 - Pad2 int32 - Pad3 int32 - Pad4 int32 - Pad5 int32 - Pad6 int32 - Pad7 int32 - Pad8 int32 - Pad9 int32 - Pad10 int32 + Modes uint32 + Offset int32 + Freq int32 + Maxerror int32 + Esterror int32 + Status int32 + Constant int32 + Precision int32 + Tolerance int32 + Time Timeval + Tick int32 + Ppsfreq int32 + Jitter int32 + Shift int32 + Stabil int32 + Jitcnt int32 + Calcnt int32 + Errcnt int32 + Stbcnt int32 + Tai int32 + Pad_godefs_0 int32 + Pad_godefs_1 int32 + Pad_godefs_2 int32 + Pad_godefs_3 int32 + Pad_godefs_4 int32 + Pad_godefs_5 int32 + Pad_godefs_6 int32 + Pad_godefs_7 int32 + Pad_godefs_8 int32 + Pad_godefs_9 int32 + Pad_godefs_10 int32 } type Time_t int32 @@ -119,24 +120,24 @@ type Rlimit struct { type _Gid_t uint32 type Stat_t struct { - Dev uint64 - X__pad1 uint16 - Pad0 [2]byte - X__st_ino uint32 - Mode uint32 - Nlink uint32 - Uid uint32 - Gid uint32 - Rdev uint64 - X__pad2 uint16 - Pad1 [2]byte - Size int64 - Blksize int32 - Blocks int64 - Atim Timespec - Mtim Timespec - Ctim Timespec - Ino uint64 + Dev uint64 + X__pad1 uint16 + Pad_godefs_0 [2]byte + X__st_ino uint32 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint64 + X__pad2 uint16 + Pad_godefs_1 [2]byte + Size int64 + Blksize int32 + Blocks int64 + Atim Timespec + Mtim Timespec + Ctim Timespec + Ino uint64 } type Statfs_t struct { @@ -154,12 +155,12 @@ type Statfs_t struct { } type Dirent struct { - Ino uint64 - Off int64 - Reclen uint16 - Type uint8 - Name [256]int8 - Pad0 [1]byte + Ino uint64 + Off int64 + Reclen uint16 + Type uint8 + Name [256]int8 + Pad_godefs_0 [1]byte } type RawSockaddrInet4 struct { @@ -214,6 +215,11 @@ type Iovec struct { Len uint32 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { Name *byte Namelen uint32 diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go index b975a8732..db5c32cae 100644 --- a/src/pkg/syscall/ztypes_linux_amd64.go +++ b/src/pkg/syscall/ztypes_linux_amd64.go @@ -18,6 +18,7 @@ const ( SizeofSockaddrUnix = 0x6e SizeofSockaddrLinklayer = 0x14 SizeofLinger = 0x8 + SizeofIpMreq = 0x8 SizeofMsghdr = 0x38 SizeofCmsghdr = 0x10 SizeofUcred = 0xc @@ -45,40 +46,40 @@ type Timeval struct { } type Timex struct { - Modes uint32 - Pad0 [4]byte - Offset int64 - Freq int64 - Maxerror int64 - Esterror int64 - Status int32 - Pad1 [4]byte - Constant int64 - Precision int64 - Tolerance int64 - Time Timeval - Tick int64 - Ppsfreq int64 - Jitter int64 - Shift int32 - Pad2 [4]byte - Stabil int64 - Jitcnt int64 - Calcnt int64 - Errcnt int64 - Stbcnt int64 - Tai int32 - Pad3 int32 - Pad4 int32 - Pad5 int32 - Pad6 int32 - Pad7 int32 - Pad8 int32 - Pad9 int32 - Pad10 int32 - Pad11 int32 - Pad12 int32 - Pad13 int32 + Modes uint32 + Pad_godefs_0 [4]byte + Offset int64 + Freq int64 + Maxerror int64 + Esterror int64 + Status int32 + Pad_godefs_1 [4]byte + Constant int64 + Precision int64 + Tolerance int64 + Time Timeval + Tick int64 + Ppsfreq int64 + Jitter int64 + Shift int32 + Pad_godefs_2 [4]byte + Stabil int64 + Jitcnt int64 + Calcnt int64 + Errcnt int64 + Stbcnt int64 + Tai int32 + Pad_godefs_3 int32 + Pad_godefs_4 int32 + Pad_godefs_5 int32 + Pad_godefs_6 int32 + Pad_godefs_7 int32 + Pad_godefs_8 int32 + Pad_godefs_9 int32 + Pad_godefs_10 int32 + Pad_godefs_11 int32 + Pad_godefs_12 int32 + Pad_godefs_13 int32 } type Time_t int64 @@ -154,12 +155,12 @@ type Statfs_t struct { } type Dirent struct { - Ino uint64 - Off int64 - Reclen uint16 - Type uint8 - Name [256]int8 - Pad0 [5]byte + Ino uint64 + Off int64 + Reclen uint16 + Type uint8 + Name [256]int8 + Pad_godefs_0 [5]byte } type RawSockaddrInet4 struct { @@ -214,16 +215,21 @@ type Iovec struct { Len uint64 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { - Name *byte - Namelen uint32 - Pad0 [4]byte - Iov *Iovec - Iovlen uint64 - Control *byte - Controllen uint64 - Flags int32 - Pad1 [4]byte + Name *byte + Namelen uint32 + Pad_godefs_0 [4]byte + Iov *Iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_godefs_1 [4]byte } type Cmsghdr struct { @@ -280,22 +286,22 @@ type FdSet struct { } type Sysinfo_t struct { - Uptime int64 - Loads [3]uint64 - Totalram uint64 - Freeram uint64 - Sharedram uint64 - Bufferram uint64 - Totalswap uint64 - Freeswap uint64 - Procs uint16 - Pad uint16 - Pad0 [4]byte - Totalhigh uint64 - Freehigh uint64 - Unit uint32 - X_f [2]int8 - Pad1 [4]byte + Uptime int64 + Loads [3]uint64 + Totalram uint64 + Freeram uint64 + Sharedram uint64 + Bufferram uint64 + Totalswap uint64 + Freeswap uint64 + Procs uint16 + Pad uint16 + Pad_godefs_0 [4]byte + Totalhigh uint64 + Freehigh uint64 + Unit uint32 + X_f [0]int8 + Pad_godefs_1 [4]byte } type Utsname struct { @@ -308,12 +314,12 @@ type Utsname struct { } type Ustat_t struct { - Tfree int32 - Pad0 [4]byte - Tinode uint64 - Fname [6]int8 - Fpack [6]int8 - Pad1 [4]byte + Tfree int32 + Pad_godefs_0 [4]byte + Tinode uint64 + Fname [6]int8 + Fpack [6]int8 + Pad_godefs_1 [4]byte } type EpollEvent struct { diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go index 450f2008b..236155b20 100644 --- a/src/pkg/syscall/ztypes_linux_arm.go +++ b/src/pkg/syscall/ztypes_linux_arm.go @@ -23,6 +23,7 @@ const ( SizeofSockaddrUnix = 0x6e SizeofSockaddrLinklayer = 0x14 SizeofLinger = 0x8 + SizeofIpMreq = 0x8 SizeofMsghdr = 0x1c SizeofCmsghdr = 0xc SizeofUcred = 0xc @@ -221,6 +222,11 @@ type Iovec struct { Len uint32 } +type IpMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + type Msghdr struct { Name *byte Namelen uint32 diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go index b1271aff1..e9ab35461 100644 --- a/src/pkg/syscall/ztypes_windows_386.go +++ b/src/pkg/syscall/ztypes_windows_386.go @@ -74,6 +74,7 @@ const ( OPEN_ALWAYS = 4 TRUNCATE_EXISTING = 5 + HANDLE_FLAG_INHERIT = 0x00000001 STARTF_USESTDHANDLES = 0x00000100 DUPLICATE_CLOSE_SOURCE = 0x00000001 DUPLICATE_SAME_ACCESS = 0x00000002 @@ -172,6 +173,12 @@ func NsecToTimeval(nsec int64) (tv Timeval) { return } +type SecurityAttributes struct { + Length uint32 + SecurityDescriptor uintptr + InheritHandle uint32 +} + type Overlapped struct { Internal uint32 InternalHigh uint32 diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go index 078463aaf..36fd06dc2 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/template.go @@ -47,12 +47,20 @@ {field1 field2 ...} {field|formatter} {field1 field2...|formatter} + {field|formatter1|formatter2} Insert the value of the fields into the output. Each field is first looked for in the cursor, as in .section and .repeated. If it is not found, the search continues in outer sections until the top level is reached. + If the field value is a pointer, leading asterisks indicate + that the value to be inserted should be evaluated through the + pointer. For example, if x.p is of type *int, {x.p} will + insert the value of the pointer but {*x.p} will insert the + value of the underlying integer. If the value is nil or not a + pointer, asterisks have no effect. + If a formatter is specified, it must be named in the formatter map passed to the template set up routines or in the default set ("html","str","") and is used to process the data for @@ -62,10 +70,15 @@ values at the instantiation, and formatter is its name at the invocation site. The default formatter just concatenates the string representations of the fields. + + Multiple formatters separated by the pipeline character | are + executed sequentially, with each formatter receiving the bytes + emitted by the one to its left. */ package template import ( + "bytes" "container/vector" "fmt" "io" @@ -131,9 +144,9 @@ type literalElement struct { // A variable invocation to be evaluated type variableElement struct { - linenum int - word []string // The fields in the invocation. - formatter string // TODO(r): implement pipelines + linenum int + word []string // The fields in the invocation. + fmts []string // Names of formatters to apply. len(fmts) > 0 } // A .section block, possibly with a .or @@ -169,13 +182,14 @@ type Template struct { // the data item descends into the fields associated with sections, etc. // Parent is used to walk upwards to find variables higher in the tree. type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output + parent *state // parent in hierarchy + data reflect.Value // the driver data for this section etc. + wr io.Writer // where to send output + buf [2]bytes.Buffer // alternating buffers used when chaining formatters } func (parent *state) clone(data reflect.Value) *state { - return &state{parent, data, parent.wr} + return &state{parent: parent, data: data, wr: parent.wr} } // New creates a new template with the specified formatter map (which @@ -402,38 +416,43 @@ func (t *Template) analyze(item []byte) (tok int, w []string) { return } +// formatter returns the Formatter with the given name in the Template, or nil if none exists. +func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) { + if t.fmap != nil { + if fn := t.fmap[name]; fn != nil { + return fn + } + } + return builtins[name] +} + // -- Parsing // Allocate a new variable-evaluation element. -func (t *Template) newVariable(words []string) (v *variableElement) { - // The words are tokenized elements from the {item}. The last one may be of - // the form "|fmt". For example: {a b c|d} - formatter := "" +func (t *Template) newVariable(words []string) *variableElement { + // After the final space-separated argument, formatters may be specified separated + // by pipe symbols, for example: {a b c|d|e} + + // Until we learn otherwise, formatters contains a single name: "", the default formatter. + formatters := []string{""} lastWord := words[len(words)-1] - bar := strings.Index(lastWord, "|") + bar := strings.IndexRune(lastWord, '|') if bar >= 0 { words[len(words)-1] = lastWord[0:bar] - formatter = lastWord[bar+1:] + formatters = strings.Split(lastWord[bar+1:], "|", -1) } - // Probably ok, so let's build it. - v = &variableElement{t.linenum, words, formatter} // We could remember the function address here and avoid the lookup later, // but it's more dynamic to let the user change the map contents underfoot. // We do require the name to be present, though. // Is it in user-supplied map? - if t.fmap != nil { - if _, ok := t.fmap[formatter]; ok { - return + for _, f := range formatters { + if t.formatter(f) == nil { + t.parseError("unknown formatter: %q", f) } } - // Is it in builtin map? - if _, ok := builtins[formatter]; ok { - return - } - t.parseError("unknown formatter: %s", formatter) - return + return &variableElement{t.linenum, words, formatters} } // Grab the next item. If it's simple, just append it to the template. @@ -633,6 +652,23 @@ func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value return v } +// indirectPtr returns the item numLevels levels of indirection below the value. +// It is forgiving: if the value is not a pointer, it returns it rather than giving +// an error. If the pointer is nil, it is returned as is. +func indirectPtr(v reflect.Value, numLevels int) reflect.Value { + for i := numLevels; v != nil && i > 0; i++ { + if p, ok := v.(*reflect.PtrValue); ok { + if p.IsNil() { + return v + } + v = p.Elem() + } else { + break + } + } + return v +} + // Walk v through pointers and interfaces, extracting the elements within. func indirect(v reflect.Value) reflect.Value { loop: @@ -654,12 +690,16 @@ loop: // The special name "@" (the "cursor") denotes the current data. // The value coming in (st.data) might need indirecting to reach // a struct while the return value is not indirected - that is, -// it represents the actual named field. +// it represents the actual named field. Leading stars indicate +// levels of indirection to be applied to the value. func (t *Template) findVar(st *state, s string) reflect.Value { + data := st.data + flattenedName := strings.TrimLeft(s, "*") + numStars := len(s) - len(flattenedName) + s = flattenedName if s == "@" { - return st.data + return indirectPtr(data, numStars) } - data := st.data for _, elem := range strings.Split(s, ".", -1) { // Look up field; data must be a struct or map. data = t.lookup(st, data, elem) @@ -667,7 +707,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value { return nil } } - return data + return indirectPtr(data, numStars) } // Is there no data to look at? @@ -705,28 +745,31 @@ func (t *Template) varValue(name string, st *state) reflect.Value { return field } +func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { + fn := t.formatter(fmt) + if fn == nil { + t.execError(st, v.linenum, "missing formatter %s for variable %s", fmt, v.word[0]) + } + fn(wr, fmt, val...) +} + // Evaluate a variable, looking up through the parent if necessary. // If it has a formatter attached ({var|formatter}) run that too. func (t *Template) writeVariable(v *variableElement, st *state) { - formatter := v.formatter // Turn the words of the invocation into values. val := make([]interface{}, len(v.word)) for i, word := range v.word { val[i] = t.varValue(word, st).Interface() } - // is it in user-supplied map? - if t.fmap != nil { - if fn, ok := t.fmap[formatter]; ok { - fn(st.wr, formatter, val...) - return - } - } - // is it in builtin map? - if fn, ok := builtins[formatter]; ok { - fn(st.wr, formatter, val...) - return + + for i, fmt := range v.fmts[:len(v.fmts)-1] { + b := &st.buf[i&1] + b.Reset() + t.format(b, fmt, val, v, st) + val = val[0:1] + val[0] = b.Bytes() } - t.execError(st, v.linenum, "missing formatter %s for variable %s", formatter, v.word[0]) + t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) } // Execute element i. Return next index to execute. @@ -929,12 +972,12 @@ func (t *Template) ParseFile(filename string) (err os.Error) { // Execute applies a parsed template to the specified data object, // generating output to wr. -func (t *Template) Execute(data interface{}, wr io.Writer) (err os.Error) { +func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { // Extract the driver data. val := reflect.NewValue(data) defer checkError(&err) t.p = 0 - t.execute(0, t.elems.Len(), &state{nil, val, wr}) + t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr}) return nil } diff --git a/src/pkg/template/template_test.go b/src/pkg/template/template_test.go index 3842b6d6b..d21a5397a 100644 --- a/src/pkg/template/template_test.go +++ b/src/pkg/template/template_test.go @@ -31,8 +31,10 @@ type U struct { type S struct { Header string + HeaderPtr *string Integer int - Raw string + IntegerPtr *int + NilPtr *int InnerT T InnerPointerT *T Data []T @@ -47,7 +49,7 @@ type S struct { JSON interface{} Innermap U Stringmap map[string]string - Bytes []byte + Ptrmap map[string]*string Iface interface{} Ifaceptr interface{} } @@ -118,6 +120,24 @@ var tests = []*Test{ out: "Header=77\n", }, + &Test{ + in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n", + + out: "Pointers: Header=77\n", + }, + + &Test{ + in: "Stars but not pointers: {*Header}={*Integer}\n", + + out: "Stars but not pointers: Header=77\n", + }, + + &Test{ + in: "nil pointer: {*NilPtr}={*Integer}\n", + + out: "nil pointer: <nil>=77\n", + }, + // Method at top level &Test{ in: "ptrmethod={PointerMethod}\n", @@ -312,38 +332,6 @@ var tests = []*Test{ out: "ItemNumber1=ValueNumber1\n", }, - - // Formatters - &Test{ - in: "{.section Pdata }\n" + - "{Header|uppercase}={Integer|+1}\n" + - "{Header|html}={Integer|str}\n" + - "{.end}\n", - - out: "HEADER=78\n" + - "Header=77\n", - }, - - &Test{ - in: "{.section Pdata }\n" + - "{Header|uppercase}={Integer Header|multiword}\n" + - "{Header|html}={Header Integer|multiword}\n" + - "{Header|html}={Header Integer}\n" + - "{.end}\n", - - out: "HEADER=<77><Header>\n" + - "Header=<Header><77>\n" + - "Header=Header77\n", - }, - - &Test{ - in: "{Raw}\n" + - "{Raw|html}\n", - - out: "&<>!@ #$%^\n" + - "&<>!@ #$%^\n", - }, - &Test{ in: "{.section Emptystring}emptystring{.end}\n" + "{.section Header}header{.end}\n", @@ -358,12 +346,6 @@ var tests = []*Test{ out: "1\n4\n", }, - &Test{ - in: "{Bytes}", - - out: "hello", - }, - // Maps &Test{ @@ -407,6 +389,20 @@ var tests = []*Test{ out: "\tstringresult\n" + "\tstringresult\n", }, + &Test{ + in: "{*Ptrmap.stringkey1}\n", + + out: "pointedToString\n", + }, + &Test{ + in: "{.repeated section Ptrmap}\n" + + "{*@}\n" + + "{.end}", + + out: "pointedToString\n" + + "pointedToString\n", + }, + // Interface values @@ -460,8 +456,9 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { s := new(S) // initialized by hand for clarity. s.Header = "Header" + s.HeaderPtr = &s.Header s.Integer = 77 - s.Raw = "&<>!@ #$%^" + s.IntegerPtr = &s.Integer s.InnerT = t1 s.Data = []T{t1, t2} s.Pdata = []*T{&t1, &t2} @@ -480,7 +477,10 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { s.Stringmap = make(map[string]string) s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent s.Stringmap["stringkey2"] = "stringresult" - s.Bytes = []byte("hello") + s.Ptrmap = make(map[string]*string) + x := "pointedToString" + s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent + s.Ptrmap["stringkey2"] = &x s.Iface = []int{1, 2, 3} s.Ifaceptr = &T{"Item", "Value"} @@ -492,7 +492,7 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { t.Error("unexpected parse error: ", err) continue } - err = tmpl.Execute(s, &buf) + err = tmpl.Execute(&buf, s) if test.err == "" { if err != nil { t.Error("unexpected execute error:", err) @@ -517,7 +517,7 @@ func TestMapDriverType(t *testing.T) { t.Error("unexpected parse error:", err) } var b bytes.Buffer - err = tmpl.Execute(mp, &b) + err = tmpl.Execute(&b, mp) if err != nil { t.Error("unexpected execute error:", err) } @@ -535,7 +535,7 @@ func TestMapNoEntry(t *testing.T) { t.Error("unexpected parse error:", err) } var b bytes.Buffer - err = tmpl.Execute(mp, &b) + err = tmpl.Execute(&b, mp) if err != nil { t.Error("unexpected execute error:", err) } @@ -552,7 +552,7 @@ func TestStringDriverType(t *testing.T) { t.Error("unexpected parse error:", err) } var b bytes.Buffer - err = tmpl.Execute("hello", &b) + err = tmpl.Execute(&b, "hello") if err != nil { t.Error("unexpected execute error:", err) } @@ -569,7 +569,7 @@ func TestTwice(t *testing.T) { t.Error("unexpected parse error:", err) } var b bytes.Buffer - err = tmpl.Execute("hello", &b) + err = tmpl.Execute(&b, "hello") if err != nil { t.Error("unexpected parse error:", err) } @@ -578,7 +578,7 @@ func TestTwice(t *testing.T) { if s != expect { t.Errorf("failed passing string as data: expected %q got %q", expect, s) } - err = tmpl.Execute("hello", &b) + err = tmpl.Execute(&b, "hello") if err != nil { t.Error("unexpected parse error:", err) } @@ -614,7 +614,7 @@ func TestCustomDelims(t *testing.T) { continue } var b bytes.Buffer - err = tmpl.Execute("hello", &b) + err = tmpl.Execute(&b, "hello") s := b.String() if s != "template: hello"+ldelim+rdelim { t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s) @@ -635,7 +635,7 @@ func TestVarIndirection(t *testing.T) { if err != nil { t.Fatal("unexpected parse error:", err) } - err = tmpl.Execute(s, &buf) + err = tmpl.Execute(&buf, s) if err != nil { t.Fatal("unexpected execute error:", err) } @@ -669,7 +669,7 @@ func TestReferenceToUnexported(t *testing.T) { if err != nil { t.Fatal("unexpected parse error:", err) } - err = tmpl.Execute(u, &buf) + err = tmpl.Execute(&buf, u) if err == nil { t.Fatal("expected execute error, got none") } @@ -677,3 +677,87 @@ func TestReferenceToUnexported(t *testing.T) { t.Fatal("expected unexported error; got", err) } } + +var formatterTests = []Test{ + { + in: "{Header|uppercase}={Integer|+1}\n" + + "{Header|html}={Integer|str}\n", + + out: "HEADER=78\n" + + "Header=77\n", + }, + + { + in: "{Header|uppercase}={Integer Header|multiword}\n" + + "{Header|html}={Header Integer|multiword}\n" + + "{Header|html}={Header Integer}\n", + + out: "HEADER=<77><Header>\n" + + "Header=<Header><77>\n" + + "Header=Header77\n", + }, + { + in: "{Raw}\n" + + "{Raw|html}\n", + + out: "a <&> b\n" + + "a <&> b\n", + }, + { + in: "{Bytes}", + out: "hello", + }, + { + in: "{Raw|uppercase|html|html}", + out: "A &lt;&amp;&gt; B", + }, + { + in: "{Header Integer|multiword|html}", + out: "<Header><77>", + }, + { + in: "{Integer|no_formatter|html}", + err: `unknown formatter: "no_formatter"`, + }, + { + in: "{Integer|||||}", // empty string is a valid formatter + out: "77", + }, +} + +func TestFormatters(t *testing.T) { + data := map[string]interface{}{ + "Header": "Header", + "Integer": 77, + "Raw": "a <&> b", + "Bytes": []byte("hello"), + } + for _, c := range formatterTests { + tmpl, err := Parse(c.in, formatters) + if err != nil { + if c.err == "" { + t.Error("unexpected parse error:", err) + continue + } + if strings.Index(err.String(), c.err) < 0 { + t.Errorf("unexpected error: expected %q, got %q", c.err, err.String()) + continue + } + } else { + if c.err != "" { + t.Errorf("For %q, expected error, got none.", c.in) + continue + } + buf := bytes.NewBuffer(nil) + err = tmpl.Execute(buf, data) + if err != nil { + t.Error("unexpected Execute error: ", err) + continue + } + actual := buf.String() + if actual != c.out { + t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual) + } + } + } +} diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go index 0e04935ce..edbf0847c 100644 --- a/src/pkg/testing/testing.go +++ b/src/pkg/testing/testing.go @@ -43,6 +43,7 @@ import ( "fmt" "os" "runtime" + "time" ) // Report as tests are run; default is silent for success. @@ -153,16 +154,19 @@ func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe if *chatty { println("=== RUN ", tests[i].Name) } + ns := -time.Nanoseconds() t := new(T) t.ch = make(chan *T) go tRunner(t, &tests[i]) <-t.ch + ns += time.Nanoseconds() + tstr := fmt.Sprintf("(%.1f seconds)", float64(ns)/1e9) if t.failed { - println("--- FAIL:", tests[i].Name) + println("--- FAIL:", tests[i].Name, tstr) print(t.errors) ok = false } else if *chatty { - println("--- PASS:", tests[i].Name) + println("--- PASS:", tests[i].Name, tstr) print(t.errors) } } diff --git a/src/pkg/xml/xml.go b/src/pkg/xml/xml.go index 4d9c672d2..417b4edfd 100644 --- a/src/pkg/xml/xml.go +++ b/src/pkg/xml/xml.go @@ -163,7 +163,7 @@ type Parser struct { // "quot": `"`, Entity map[string]string - r io.ReadByter + r io.ByteReader buf bytes.Buffer saved *bytes.Buffer stk *stack @@ -191,7 +191,7 @@ func NewParser(r io.Reader) *Parser { // Assume that if reader has its own // ReadByte, it's efficient enough. // Otherwise, use bufio. - if rb, ok := r.(io.ReadByter); ok { + if rb, ok := r.(io.ByteReader); ok { p.r = rb } else { p.r = bufio.NewReader(r) diff --git a/src/run.bash b/src/run.bash index 731e07e49..a3e90cc4f 100755 --- a/src/run.bash +++ b/src/run.bash @@ -30,26 +30,17 @@ xcd() { builtin cd "$GOROOT"/src/$1 } -maketest() { - for i - do - ( - xcd $i - if $rebuild; then - gomake clean - time gomake - gomake install - fi - gomake test - ) || exit $? - done -} - -maketest \ - pkg \ +if $rebuild; then + (xcd pkg + gomake clean + time gomake + gomake install + ) || exit $i +fi -# all of these are subtly different -# from what maketest does. +(xcd pkg +gomake test +) || exit $? (xcd pkg/sync; if $rebuild; then @@ -77,19 +68,17 @@ time gomake test ) || exit $? [ "$GOARCH" == arm ] || +[ "$GOHOSTOS" == windows ] || (xcd ../misc/cgo/stdio -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - gomake clean - ./test.bash -fi +gomake clean +./test.bash ) || exit $? [ "$GOARCH" == arm ] || +[ "$GOHOSTOS" == windows ] || (xcd ../misc/cgo/life -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - gomake clean - ./test.bash -fi +gomake clean +./test.bash ) || exit $? (xcd pkg/exp/ogle @@ -97,16 +86,16 @@ gomake clean time gomake ogle ) || exit $? +[ "$GOHOSTOS" == windows ] || (xcd ../doc/progs -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - time ./run -fi +time ./run ) || exit $? +[ "$GOHOSTOS" == windows ] || (xcd ../doc/codelab/wiki -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - gomake test -fi +gomake clean +gomake +gomake test ) || exit $? for i in ../misc/dashboard/builder ../misc/goplay @@ -118,15 +107,15 @@ do done [ "$GOARCH" == arm ] || +[ "$GOHOSTOS" == windows ] || (xcd ../test/bench -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - ./timing.sh -test -fi +./timing.sh -test ) || exit $? +[ "$GOHOSTOS" == windows ] || (xcd ../test -if [[ $(uname | tr A-Z a-z | sed 's/mingw/windows/') != *windows* ]]; then - ./run -fi +./run ) || exit $? +echo +echo ALL TESTS PASSED diff --git a/src/version.bash b/src/version.bash index 2d29825ff..0e6483150 100755 --- a/src/version.bash +++ b/src/version.bash @@ -11,17 +11,16 @@ fi # Get numerical revision VERSION=$(hg identify -n 2>/dev/null) -if [ $? = 0 ]; then - TAG=$(hg identify -t | sed 's!/release!!') -else +if [ $? != 0 ]; then OLD=$(hg identify | sed 1q) VERSION=$(echo $OLD | awk '{print $1}') - TAG=$(echo $OLD | awk '{print $2}' | sed 's!/release!!') fi -# Append tag if not 'tip' -if [[ "$TAG" != "tip" ]]; then - VERSION="$VERSION $TAG" +# Find most recent known release tag. +TAG=$(hg tags | awk '$1~/^release\./ {print $1}' | sed -n 1p) + +if [ "$TAG" != "" ]; then + VERSION="$TAG $VERSION" fi echo $VERSION diff --git a/test/bugs/bug322.dir/lib.go b/test/bugs/bug322.dir/lib.go new file mode 100644 index 000000000..0de56d3d6 --- /dev/null +++ b/test/bugs/bug322.dir/lib.go @@ -0,0 +1,15 @@ +// 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 lib + +type T struct { + x int // non-exported field +} + +func (t T) M() { +} + +func (t *T) PM() { +} diff --git a/test/bugs/bug322.dir/main.go b/test/bugs/bug322.dir/main.go new file mode 100644 index 000000000..a99ed3bc2 --- /dev/null +++ b/test/bugs/bug322.dir/main.go @@ -0,0 +1,47 @@ +// 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 "./lib" + +type I interface { + M() +} + +type PI interface { + PM() +} + +func main() { + var t lib.T + t.M() + t.PM() + + var i1 I = t + i1.M() + + // This combination is illegal because + // PM requires a pointer receiver. + // var pi1 PI = t + // pi1.PM() + + var pt = &t + pt.M() + pt.PM() + + var i2 I = pt + i2.M() + + var pi2 PI = pt + pi2.PM() +} + +/* +These should not be errors anymore: + +bug322.dir/main.go:19: implicit assignment of unexported field 'x' of lib.T in method receiver +bug322.dir/main.go:22: implicit assignment of unexported field 'x' of lib.T in assignment +bug322.dir/main.go:31: implicit assignment of unexported field 'x' of lib.T in method receiver +*/
\ No newline at end of file diff --git a/test/bugs/bug322.go b/test/bugs/bug322.go new file mode 100644 index 000000000..ad0e62dc8 --- /dev/null +++ b/test/bugs/bug322.go @@ -0,0 +1,8 @@ +// $G $D/$F.dir/lib.go && $G $D/$F.dir/main.go && $L main.$A && ./$A.out || echo BUG: fails incorrectly + +// 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. + +// Test case for issue 1402. +ignored diff --git a/test/chan/select5.go b/test/chan/select5.go index 0678b8dab..e7ca9e015 100644 --- a/test/chan/select5.go +++ b/test/chan/select5.go @@ -49,7 +49,7 @@ func main() { } func run(t *template.Template, a interface{}, out io.Writer) { - if err := t.Execute(a, out); err != nil { + if err := t.Execute(out, a); err != nil { panic(err) } } diff --git a/test/fixedbugs/bug320.go b/test/fixedbugs/bug320.go new file mode 100644 index 000000000..06d41f2ed --- /dev/null +++ b/test/fixedbugs/bug320.go @@ -0,0 +1,45 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out + +// 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 main() { + c := make(chan int, 1) + dummy := make(chan int) + v := 0x12345678 + for i := 0; i < 10; i++ { + // 6g had a bug that caused select to pass &t to + // selectrecv before allocating the memory for t, + // which caused non-deterministic crashes. + // This test looks for the bug by checking that the + // value received actually ends up in t. + // If the allocation happens after storing through + // whatever garbage &t holds, the later reference + // to t in the case body will use the new pointer and + // not see the received value. + v += 0x1020304 + c <- v + select { + case t := <-c: + go func() { + f(t) + }() + escape(&t) + if t != v { + println(i, v, t) + panic("wrong values") + } + case dummy <- 1: + } + } +} + +func escape(*int) { +} + +func f(int) { +} + diff --git a/test/fixedbugs/bug321.go b/test/fixedbugs/bug321.go new file mode 100644 index 000000000..d0595ff59 --- /dev/null +++ b/test/fixedbugs/bug321.go @@ -0,0 +1,30 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: bug321 + +// 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. + +// Troublesome floating point constants. Issue 1463. + +package main + +import "fmt" + +func check(test string, got, want float64) bool { + if got != want { + fmt.Println(test, "got", got, "want", want) + return false + } + return true +} + +func main() { + good := true + // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + good = good && check("2.2250738585072012e-308", 2.2250738585072012e-308, 2.2250738585072014e-308) + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + good = good && check("2.2250738585072011e-308", 2.2250738585072011e-308, 2.225073858507201e-308) + if !good { + panic("fail") + } +} diff --git a/test/fixedbugs/bug322.go b/test/fixedbugs/bug322.go new file mode 100644 index 000000000..bfb528318 --- /dev/null +++ b/test/fixedbugs/bug322.go @@ -0,0 +1,20 @@ +// errchk $G $D/$F.go + +// 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 + +type T struct{} +type P *T + +func (t *T) Meth() {} +func (t T) Meth2() {} + +func main() { + t := &T{} + p := P(t) + p.Meth() // ERROR "undefined \(type P" + p.Meth2() // ERROR "undefined \(type P" +}
\ No newline at end of file diff --git a/test/golden.out b/test/golden.out index 425771b4a..7883973e0 100644 --- a/test/golden.out +++ b/test/golden.out @@ -158,3 +158,9 @@ panic: interface conversion: interface is main.T, not main.T == bugs/ + +=========== bugs/bug322.go +bugs/bug322.dir/main.go:19: implicit assignment of unexported field 'x' of lib.T in method receiver +bugs/bug322.dir/main.go:22: implicit assignment of unexported field 'x' of lib.T in assignment +bugs/bug322.dir/main.go:31: implicit assignment of unexported field 'x' of lib.T in method receiver +BUG: fails incorrectly @@ -42,9 +42,7 @@ TMP2FILE=/tmp/gotest2-$$-$USER # don't run the machine out of memory: limit individual processes to 4GB. # on thresher, 3GB suffices to run the tests; with 2GB, peano fails. -# Linux charges reserved but not mapped addresses to ulimit -v -# so we have to use ulimit -m. -ulimit -m 4000000 +ulimit -v 4000000 # no core files please ulimit -c 0 |