diff --git a/src/os/env.go b/src/os/env.go new file mode 100644 index 00000000..3a250df0 --- /dev/null +++ b/src/os/env.go @@ -0,0 +1,5 @@ +package os + +func Getenv(key string) string { + return "" +} diff --git a/src/os/errors.go b/src/os/errors.go new file mode 100644 index 00000000..9b346554 --- /dev/null +++ b/src/os/errors.go @@ -0,0 +1,39 @@ +package os + +import ( + "errors" +) + +var ( + ErrInvalid = errors.New("invalid argument") + ErrPermission = errors.New("permission denied") + ErrClosed = errors.New("file already closed") + + // Portable analogs of some common system call errors. + // Note that these are exported for use in the Filesystem interface. + ErrUnsupported = errors.New("operation not supported") + ErrNotImplemented = errors.New("operation not implemented") + ErrNotExist = errors.New("file not found") + ErrExist = errors.New("file exists") +) + +func IsPermission(err error) bool { + return err == ErrPermission +} + +func NewSyscallError(syscall string, err error) error { + if err == nil { + return nil + } + return &SyscallError{syscall, err} +} + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Err error +} + +func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() } + +func (e *SyscallError) Unwrap() error { return e.Err } diff --git a/src/os/exec.go b/src/os/exec.go new file mode 100644 index 00000000..66cc7999 --- /dev/null +++ b/src/os/exec.go @@ -0,0 +1,6 @@ +package os + +type Signal interface { + String() string + Signal() // to distinguish from other Stringers +} diff --git a/src/os/file.go b/src/os/file.go index 6f4a8cc1..a059a421 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -6,16 +6,7 @@ package os import ( - "errors" -) - -// Portable analogs of some common system call errors. -// Note that these are exported for use in the Filesystem interface. -var ( - ErrUnsupported = errors.New("operation not supported") - ErrNotImplemented = errors.New("operation not implemented") - ErrNotExist = errors.New("file not found") - ErrExist = errors.New("file exists") + "syscall" ) // Mkdir creates a directory. If the operation fails, it will return an error of @@ -91,6 +82,10 @@ func (f *File) Read(b []byte) (n int, err error) { return } +func (f *File) ReadAt(b []byte, off int64) (n int, err error) { + return 0, ErrNotImplemented +} + // Write writes len(b) bytes to the File. It returns the number of bytes written // and an error, if any. Write returns a non-nil error when n != len(b). func (f *File) Write(b []byte) (n int, err error) { @@ -125,6 +120,20 @@ func (f *File) Stat() (FileInfo, error) { return nil, &PathError{"stat", f.name, ErrNotImplemented} } +// Sync is a stub, not yet implemented +func (f *File) Sync() error { + return ErrNotImplemented +} + +func (f *File) SyscallConn() (syscall.RawConn, error) { + return nil, ErrNotImplemented +} + +// Fd returns the file handle referencing the open file. +func (f *File) Fd() uintptr { + panic("unimplemented: os.file.Fd()") +} + const ( PathSeparator = '/' // OS-specific path separator PathListSeparator = ':' // OS-specific path list separator @@ -194,7 +203,7 @@ type FileInfo interface { Name() string // base name of the file Size() int64 // length in bytes for regular files; system-dependent for others Mode() FileMode // file mode bits - // ModTime() time.Time // modification time + // TODO ModTime() time.Time // modification time IsDir() bool // abbreviation for Mode().IsDir() Sys() interface{} // underlying data source (can return nil) } diff --git a/src/os/sys.go b/src/os/sys.go new file mode 100644 index 00000000..ba300e40 --- /dev/null +++ b/src/os/sys.go @@ -0,0 +1,5 @@ +package os + +func Hostname() (name string, err error) { + return "", ErrNotImplemented +} diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go new file mode 100644 index 00000000..32719c34 --- /dev/null +++ b/src/reflect/deepequal.go @@ -0,0 +1,9 @@ +package reflect + +func DeepEqual(x, y interface{}) bool { + if x == nil || y == nil { + return x == y + } + + panic("unimplemented: reflect.DeepEqual()") +} diff --git a/src/reflect/strconv.go b/src/reflect/strconv.go new file mode 100644 index 00000000..9b07cb45 --- /dev/null +++ b/src/reflect/strconv.go @@ -0,0 +1,248 @@ +// 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 reflect + +import ( + "unicode/utf8" +) + +// errSyntax indicates that a value does not have the right syntax for the target type. +var errSyntax = badSyntax{} + +type badSyntax struct{} + +func (badSyntax) Error() string { + return "invalid syntax" +} + +func unhex(b byte) (v rune, ok bool) { + c := rune(b) + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + return +} + +// unquoteChar decodes the first character or byte in the escaped string +// or character literal represented by the string s. +// It returns four values: +// +// 1) value, the decoded Unicode code point or byte value; +// 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation; +// 3) tail, the remainder of the string after the character; and +// 4) an error that will be nil if the character is syntactically valid. +// +// The second argument, quote, specifies the type of literal being parsed +// and therefore which escaped quote character is permitted. +// If set to a single quote, it permits the sequence \' and disallows unescaped '. +// If set to a double quote, it permits \" and disallows unescaped ". +// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped. +func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { + // easy cases + if len(s) == 0 { + err = errSyntax + return + } + switch c := s[0]; { + case c == quote && (quote == '\'' || quote == '"'): + err = errSyntax + return + case c >= utf8.RuneSelf: + r, size := utf8.DecodeRuneInString(s) + return r, true, s[size:], nil + case c != '\\': + return rune(s[0]), false, s[1:], nil + } + + // hard case: c is backslash + if len(s) <= 1 { + err = errSyntax + return + } + c := s[1] + s = s[2:] + + switch c { + case 'a': + value = '\a' + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case 'x', 'u', 'U': + n := 0 + switch c { + case 'x': + n = 2 + case 'u': + n = 4 + case 'U': + n = 8 + } + var v rune + if len(s) < n { + err = errSyntax + return + } + for j := 0; j < n; j++ { + x, ok := unhex(s[j]) + if !ok { + err = errSyntax + return + } + v = v<<4 | x + } + s = s[n:] + if c == 'x' { + // single-byte string, possibly not UTF-8 + value = v + break + } + if v > utf8.MaxRune { + err = errSyntax + return + } + value = v + multibyte = true + case '0', '1', '2', '3', '4', '5', '6', '7': + v := rune(c) - '0' + if len(s) < 2 { + err = errSyntax + return + } + for j := 0; j < 2; j++ { // one digit already; two more + x := rune(s[j]) - '0' + if x < 0 || x > 7 { + err = errSyntax + return + } + v = (v << 3) | x + } + s = s[2:] + if v > 255 { + err = errSyntax + return + } + value = v + case '\\': + value = '\\' + case '\'', '"': + if c != quote { + err = errSyntax + return + } + value = rune(c) + default: + err = errSyntax + return + } + tail = s + return +} + +// unquote interprets s as a single-quoted, double-quoted, +// or backquoted Go string literal, returning the string value +// that s quotes. (If s is single-quoted, it would be a Go +// character literal; unquote returns the corresponding +// one-character string.) +func unquote(s string) (string, error) { + n := len(s) + if n < 2 { + return "", errSyntax + } + quote := s[0] + if quote != s[n-1] { + return "", errSyntax + } + s = s[1 : n-1] + + if quote == '`' { + if contains(s, '`') { + return "", errSyntax + } + if contains(s, '\r') { + // -1 because we know there is at least one \r to remove. + buf := make([]byte, 0, len(s)-1) + for i := 0; i < len(s); i++ { + if s[i] != '\r' { + buf = append(buf, s[i]) + } + } + return string(buf), nil + } + return s, nil + } + if quote != '"' && quote != '\'' { + return "", errSyntax + } + if contains(s, '\n') { + return "", errSyntax + } + + // Is it trivial? Avoid allocation. + if !contains(s, '\\') && !contains(s, quote) { + switch quote { + case '"': + if utf8.ValidString(s) { + return s, nil + } + case '\'': + r, size := utf8.DecodeRuneInString(s) + if size == len(s) && (r != utf8.RuneError || size != 1) { + return s, nil + } + } + } + + var runeTmp [utf8.UTFMax]byte + buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. + for len(s) > 0 { + c, multibyte, ss, err := unquoteChar(s, quote) + if err != nil { + return "", err + } + s = ss + if c < utf8.RuneSelf || !multibyte { + buf = append(buf, byte(c)) + } else { + n := utf8.EncodeRune(runeTmp[:], c) + buf = append(buf, runeTmp[:n]...) + } + if quote == '\'' && len(s) != 0 { + // single-quoted must be single character + return "", errSyntax + } + } + return string(buf), nil +} + +// contains reports whether the string contains the byte c. +func contains(s string, c byte) bool { + return indexByteString(s, c) != -1 +} + +// Index finds the index of the first instance of the specified byte in the string. +// If the byte is not found, this returns -1. +func indexByteString(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/src/reflect/type.go b/src/reflect/type.go index 44dc10cb..237a5a6a 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -1,3 +1,7 @@ +// 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 reflect import ( @@ -227,7 +231,7 @@ func (t Type) Field(i int) StructField { // There is a tag. var tagNum uintptr tagNum, p = readVarint(p) - field.Tag = readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum) + field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum)) } else { // There is no tag. field.Tag = "" @@ -445,6 +449,22 @@ func (t Type) Comparable() bool { } } +func (t Type) ConvertibleTo(u Type) bool { + panic("unimplemented: (reflect.Type).ConvertibleTo()") +} + +func (t Type) NumMethod() int { + panic("unimplemented: (reflect.Type).NumMethod()") +} + +func (t Type) Name() string { + panic("unimplemented: (reflect.Type).Name()") +} + +func (t Type) Key() Type { + panic("unimplemented: (reflect.Type).Key()") +} + // A StructField describes a single field in a struct. type StructField struct { // Name indicates the field name. @@ -455,11 +475,75 @@ type StructField struct { PkgPath string Type Type - Tag string + Tag StructTag // field tag string Anonymous bool Offset uintptr } +// A StructTag is the tag string in a struct field. +type StructTag string + +// TODO: it would be feasible to do the key/value splitting at compile time, +// avoiding the code size cost of doing it at runtime + +// Get returns the value associated with key in the tag string. +func (tag StructTag) Get(key string) string { + v, _ := tag.Lookup(key) + return v +} + +// Lookup returns the value associated with key in the tag string. +func (tag StructTag) Lookup(key string) (value string, ok bool) { + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if key == name { + value, err := unquote(qvalue) + if err != nil { + break + } + return value, true + } + } + return "", false +} + // TypeError is the error that is used in a panic when invoking a method on a // type that is not applicable to that type. type TypeError struct { diff --git a/src/reflect/value.go b/src/reflect/value.go index e71eaee8..087ddfa1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -661,6 +661,18 @@ func (v Value) checkAddressable() { } } +func (v Value) OverflowInt(x int64) bool { + panic("unimplemented: reflect.OverflowInt()") +} + +func (v Value) OverflowUint(x uint64) bool { + panic("unimplemented: reflect.OverflowUint()") +} + +func (v Value) Convert(t Type) Value { + panic("unimplemented: (reflect.Value).Convert()") +} + func MakeSlice(typ Type, len, cap int) Value { panic("unimplemented: reflect.MakeSlice()") } @@ -700,3 +712,33 @@ func (e *ValueError) Error() string { // Calls to this function are converted to LLVM intrinsic calls such as // llvm.memcpy.p0i8.p0i8.i32(). func memcpy(dst, src unsafe.Pointer, size uintptr) + +// Copy copies the contents of src into dst until either +// dst has been filled or src has been exhausted. +func Copy(dst, src Value) int { + panic("unimplemented: reflect.Copy()") +} + +// Append appends the values x to a slice s and returns the resulting slice. +// As in Go, each x's value must be assignable to the slice's element type. +func Append(s Value, x ...Value) Value { + panic("unimplemented: reflect.Append()") +} + +func (v Value) SetMapIndex(key, elem Value) { + panic("unimplemented: (reflect.Value).SetMapIndex()") +} + +// FieldByIndex returns the nested field corresponding to index. +func (v Value) FieldByIndex(index []int) Value { + panic("unimplemented: (reflect.Value).FieldByIndex()") +} + +func (v Value) FieldByName(name string) Value { + panic("unimplemented: (reflect.Value).FieldByName()") +} + +// MakeMap creates a new map with the specified type. +func MakeMap(typ Type) Value { + panic("unimplemented: reflect.MakeMap()") +} diff --git a/src/runtime/error.go b/src/runtime/error.go new file mode 100644 index 00000000..3ae5ea3a --- /dev/null +++ b/src/runtime/error.go @@ -0,0 +1,8 @@ +package runtime + +// The Error interface identifies a run time error. +type Error interface { + error + + RuntimeError() +} diff --git a/src/runtime/stack.go b/src/runtime/stack.go index c2cff3d0..5ef9b7ad 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -14,3 +14,7 @@ func (f *Func) Name() string { func Caller(skip int) (pc uintptr, file string, line int, ok bool) { return 0, "", 0, false } + +func Stack(buf []byte, all bool) int { + return 0 +} diff --git a/src/sync/map.go b/src/sync/map.go index 7d3cde8a..5eeff9d3 100644 --- a/src/sync/map.go +++ b/src/sync/map.go @@ -42,3 +42,18 @@ func (m *Map) Store(key, value interface{}) { } m.m[key] = value } + +func (m *Map) Range(f func(key, value interface{}) bool) { + m.lock.Lock() + defer m.lock.Unlock() + + if m.m == nil { + return + } + + for k, v := range m.m { + if !f(k, v) { + break + } + } +} diff --git a/src/sync/mutex.go b/src/sync/mutex.go index acc02de3..7284300f 100644 --- a/src/sync/mutex.go +++ b/src/sync/mutex.go @@ -74,3 +74,14 @@ type Locker interface { Lock() Unlock() } + +// RLocker returns a Locker interface that implements +// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. +func (rw *RWMutex) RLocker() Locker { + return (*rlocker)(rw) +} + +type rlocker RWMutex + +func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } +func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } diff --git a/src/syscall/net.go b/src/syscall/net.go new file mode 100644 index 00000000..531fa80d --- /dev/null +++ b/src/syscall/net.go @@ -0,0 +1,34 @@ +// Copyright 2017 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 syscall + +// A RawConn is a raw network connection. +type RawConn interface { + // Control invokes f on the underlying connection's file + // descriptor or handle. + // The file descriptor fd is guaranteed to remain valid while + // f executes but not after f returns. + Control(f func(fd uintptr)) error + + // Read invokes f on the underlying connection's file + // descriptor or handle; f is expected to try to read from the + // file descriptor. + // If f returns true, Read returns. Otherwise Read blocks + // waiting for the connection to be ready for reading and + // tries again repeatedly. + // The file descriptor is guaranteed to remain valid while f + // executes but not after f returns. + Read(f func(fd uintptr) (done bool)) error + + // Write is like Read but for writing. + Write(f func(fd uintptr) (done bool)) error +} + +// Conn is implemented by some types in the net and os packages to provide +// access to the underlying file descriptor or handle. +type Conn interface { + // SyscallConn returns a raw network connection. + SyscallConn() (RawConn, error) +} diff --git a/testdata/reflect.go b/testdata/reflect.go index 40b1969c..c4df8460 100644 --- a/testdata/reflect.go +++ b/testdata/reflect.go @@ -264,6 +264,9 @@ func main() { default: println("type assertion failed (but should succeed)") } + + println("\nstruct tags") + TestStructTag() } func emptyFunc() { @@ -352,3 +355,14 @@ func assertSize(ok bool, typ string) { } type unreferencedType int + +func TestStructTag() { + type S struct { + F string `species:"gopher" color:"blue"` + } + + s := S{} + st := reflect.TypeOf(s) + field := st.Field(0) + println(field.Tag.Get("color"), field.Tag.Get("species")) +} diff --git a/testdata/reflect.txt b/testdata/reflect.txt index 28361306..eeaa9c6f 100644 --- a/testdata/reflect.txt +++ b/testdata/reflect.txt @@ -336,3 +336,6 @@ float64 8 64 complex64 8 64 complex128 16 128 type assertion succeeded for unreferenced type + +struct tags +blue gopher