From 05031e097910062a8b985eba680dc2a19e3f6ee0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 20 Jun 2024 23:40:35 +0800 Subject: [PATCH] patch reflect: Zero/Len --- _demo/rtype/rtype.go | 3 + internal/abi/type.go | 18 +- internal/lib/reflect/unsafeheader.go | 37 +++ internal/lib/reflect/value.go | 428 +++++++++++++++++++++++++++ internal/runtime/z_rt.go | 20 ++ 5 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 internal/lib/reflect/unsafeheader.go diff --git a/_demo/rtype/rtype.go b/_demo/rtype/rtype.go index 5d114161..48938568 100644 --- a/_demo/rtype/rtype.go +++ b/_demo/rtype/rtype.go @@ -5,4 +5,7 @@ import "reflect" func main() { tyIntSlice := reflect.SliceOf(reflect.TypeOf(0)) println(tyIntSlice.String()) + + v := reflect.Zero(tyIntSlice) + println(v.Len()) } diff --git a/internal/abi/type.go b/internal/abi/type.go index b65b578e..a02f3be7 100644 --- a/internal/abi/type.go +++ b/internal/abi/type.go @@ -329,6 +329,22 @@ func (p *Imethod) PkgPath() string { func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) } +func (t *Type) HasName() bool { + return t.TFlag&TFlagNamed != 0 +} + +func (t *Type) Pointers() bool { return t.PtrBytes != 0 } + +// IfaceIndir reports whether t is stored indirectly in an interface value. +func (t *Type) IfaceIndir() bool { + return t.Kind_&KindDirectIface == 0 +} + +// isDirectIface reports whether t is stored directly in an interface value. +func (t *Type) IsDirectIface() bool { + return t.Kind_&KindDirectIface != 0 +} + // Size returns the size of data with type t. func (t *Type) Size() uintptr { return t.Size_ } @@ -340,7 +356,7 @@ func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } // String returns string form of type t. func (t *Type) String() string { if t.TFlag&TFlagExtraStar != 0 { - return "*" + t.Str_ + return "*" + t.Str_ // TODO(xsw): misunderstand } return t.Str_ } diff --git a/internal/lib/reflect/unsafeheader.go b/internal/lib/reflect/unsafeheader.go new file mode 100644 index 00000000..15da82ea --- /dev/null +++ b/internal/lib/reflect/unsafeheader.go @@ -0,0 +1,37 @@ +// Copyright 2020 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 unsafeheader contains header declarations for the Go runtime's slice +// and string implementations. +// +// This package allows packages that cannot import "reflect" to use types that +// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. +package reflect + +import ( + "unsafe" +) + +// unsafeheaderSlice is the runtime representation of a slice. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type unsafeheaderSlice struct { + Data unsafe.Pointer + Len int + Cap int +} + +// unsafeheaderString is the runtime representation of a string. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type unsafeheaderString struct { + Data unsafe.Pointer + Len int +} diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go index 405e6600..4daf3e44 100644 --- a/internal/lib/reflect/value.go +++ b/internal/lib/reflect/value.go @@ -20,6 +20,7 @@ import ( "unsafe" "github.com/goplus/llgo/internal/abi" + "github.com/goplus/llgo/internal/runtime" ) // Value is the reflection interface to a Go value. @@ -100,6 +101,21 @@ func (f flag) ro() flag { return 0 } +// A ValueError occurs when a Value method is invoked on +// a Value that does not support it. Such cases are documented +// in the description of each method. +type ValueError struct { + Method string + Kind Kind +} + +func (e *ValueError) Error() string { + if e.Kind == 0 { + return "reflect: call of " + e.Method + " on zero Value" + } + return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" +} + // emptyInterface is the header for an interface{} value. type emptyInterface struct { typ *abi.Type @@ -118,3 +134,415 @@ type nonEmptyInterface struct { } word unsafe.Pointer } + +// CanFloat reports whether Float can be used without panicking. +func (v Value) CanFloat() bool { + switch v.kind() { + case Float32, Float64: + return true + default: + return false + } +} + +// Float returns v's underlying value, as a float64. +// It panics if v's Kind is not Float32 or Float64 +func (v Value) Float() float64 { + k := v.kind() + switch k { + case Float32: + return float64(*(*float32)(v.ptr)) + case Float64: + return *(*float64)(v.ptr) + } + panic(&ValueError{"reflect.Value.Float", v.kind()}) +} + +// TODO(xsw): +// var uint8Type = rtypeOf(uint8(0)) + +// Index returns v's i'th element. +// It panics if v's Kind is not Array, Slice, or String or i is out of range. +func (v Value) Index(i int) Value { + /* + switch v.kind() { + case Array: + tt := (*arrayType)(unsafe.Pointer(v.typ())) + if uint(i) >= uint(tt.Len) { + panic("reflect: array index out of range") + } + typ := tt.Elem + offset := uintptr(i) * typ.Size() + + // Either flagIndir is set and v.ptr points at array, + // or flagIndir is not set and v.ptr is the actual array data. + // In the former case, we want v.ptr + offset. + // In the latter case, we must be doing Index(0), so offset = 0, + // so v.ptr + offset is still the correct address. + val := add(v.ptr, offset, "same as &v[i], i < tt.len") + fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) // bits same as overall array + return Value{typ, val, fl} + + case Slice: + // Element flag same as Elem of Pointer. + // Addressable, indirect, possibly read-only. + s := (*unsafeheader.Slice)(v.ptr) + if uint(i) >= uint(s.Len) { + panic("reflect: slice index out of range") + } + tt := (*sliceType)(unsafe.Pointer(v.typ())) + typ := tt.Elem + val := arrayAt(s.Data, i, typ.Size(), "i < s.Len") + fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) + return Value{typ, val, fl} + + case String: + s := (*unsafeheader.String)(v.ptr) + if uint(i) >= uint(s.Len) { + panic("reflect: string index out of range") + } + p := arrayAt(s.Data, i, 1, "i < s.Len") + fl := v.flag.ro() | flag(Uint8) | flagIndir + return Value{uint8Type, p, fl} + } + panic(&ValueError{"reflect.Value.Index", v.kind()}) + */ + panic("todo") +} + +// CanInt reports whether Int can be used without panicking. +func (v Value) CanInt() bool { + switch v.kind() { + case Int, Int8, Int16, Int32, Int64: + return true + default: + return false + } +} + +// Int returns v's underlying value, as an int64. +// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. +func (v Value) Int() int64 { + k := v.kind() + p := v.ptr + switch k { + case Int: + return int64(*(*int)(p)) + case Int8: + return int64(*(*int8)(p)) + case Int16: + return int64(*(*int16)(p)) + case Int32: + return int64(*(*int32)(p)) + case Int64: + return *(*int64)(p) + } + panic(&ValueError{"reflect.Value.Int", v.kind()}) +} + +// CanInterface reports whether Interface can be used without panicking. +func (v Value) CanInterface() bool { + if v.flag == 0 { + panic(&ValueError{"reflect.Value.CanInterface", Invalid}) + } + return v.flag&flagRO == 0 +} + +// Interface returns v's current value as an interface{}. +// It is equivalent to: +// +// var i interface{} = (v's underlying value) +// +// It panics if the Value was obtained by accessing +// unexported struct fields. +func (v Value) Interface() (i any) { + return valueInterface(v, true) +} + +func valueInterface(v Value, safe bool) any { + /* + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", Invalid}) + } + if safe && v.flag&flagRO != 0 { + // Do not allow access to unexported values via Interface, + // because they might be pointers that should not be + // writable or methods or function that should not be callable. + panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") + } + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } + + if v.kind() == Interface { + // Special case: return the element inside the interface. + // Empty interface has one layout, all interfaces with + // methods have a second layout. + if v.NumMethod() == 0 { + return *(*any)(v.ptr) + } + return *(*interface { + M() + })(v.ptr) + } + + // TODO: pass safe to packEface so we don't need to copy if safe==true? + return packEface(v) + */ + panic("todo") +} + +// InterfaceData returns a pair of unspecified uintptr values. +// It panics if v's Kind is not Interface. +// +// In earlier versions of Go, this function returned the interface's +// value as a uintptr pair. As of Go 1.4, the implementation of +// interface values precludes any defined use of InterfaceData. +// +// Deprecated: The memory representation of interface values is not +// compatible with InterfaceData. +func (v Value) InterfaceData() [2]uintptr { + /* + v.mustBe(Interface) + // The compiler loses track as it converts to uintptr. Force escape. + escapes(v.ptr) + // We treat this as a read operation, so we allow + // it even for unexported data, because the caller + // has to import "unsafe" to turn it into something + // that can be abused. + // Interface value is always bigger than a word; assume flagIndir. + return *(*[2]uintptr)(v.ptr) + */ + panic("todo") +} + +// IsNil reports whether its argument v is nil. The argument must be +// a chan, func, interface, map, pointer, or slice value; if it is +// not, IsNil panics. Note that IsNil is not always equivalent to a +// regular comparison with nil in Go. For example, if v was created +// by calling ValueOf with an uninitialized interface variable i, +// i==nil will be true but v.IsNil will panic as v will be the zero +// Value. +func (v Value) IsNil() bool { + k := v.kind() + switch k { + case Chan, Func, Map, Pointer, UnsafePointer: + if v.flag&flagMethod != 0 { + return false + } + ptr := v.ptr + if v.flag&flagIndir != 0 { + ptr = *(*unsafe.Pointer)(ptr) + } + return ptr == nil + case Interface, Slice: + // Both interface and slice are nil if first word is 0. + // Both are always bigger than a word; assume flagIndir. + return *(*unsafe.Pointer)(v.ptr) == nil + } + panic(&ValueError{"reflect.Value.IsNil", v.kind()}) +} + +// IsValid reports whether v represents a value. +// It returns false if v is the zero Value. +// If IsValid returns false, all other methods except String panic. +// Most functions and methods never return an invalid Value. +// If one does, its documentation states the conditions explicitly. +func (v Value) IsValid() bool { + return v.flag != 0 +} + +// IsZero reports whether v is the zero value for its type. +// It panics if the argument is invalid. +func (v Value) IsZero() bool { + /* + switch v.kind() { + case Bool: + return !v.Bool() + case Int, Int8, Int16, Int32, Int64: + return v.Int() == 0 + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return v.Uint() == 0 + case Float32, Float64: + return math.Float64bits(v.Float()) == 0 + case Complex64, Complex128: + c := v.Complex() + return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 + case Array: + // If the type is comparable, then compare directly with zero. + if v.typ().Equal != nil && v.typ().Size() <= maxZero { + if v.flag&flagIndir == 0 { + return v.ptr == nil + } + // v.ptr doesn't escape, as Equal functions are compiler generated + // and never escape. The escape analysis doesn't know, as it is a + // function pointer call. + return v.typ().Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0])) + } + + n := v.Len() + for i := 0; i < n; i++ { + if !v.Index(i).IsZero() { + return false + } + } + return true + case Chan, Func, Interface, Map, Pointer, Slice, UnsafePointer: + return v.IsNil() + case String: + return v.Len() == 0 + case Struct: + // If the type is comparable, then compare directly with zero. + if v.typ().Equal != nil && v.typ().Size() <= maxZero { + if v.flag&flagIndir == 0 { + return v.ptr == nil + } + // See noescape justification above. + return v.typ().Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0])) + } + + n := v.NumField() + for i := 0; i < n; i++ { + if !v.Field(i).IsZero() { + return false + } + } + return true + default: + // This should never happen, but will act as a safeguard for later, + // as a default value doesn't makes sense here. + panic(&ValueError{"reflect.Value.IsZero", v.Kind()}) + } + */ + panic("todo") +} + +// SetZero sets v to be the zero value of v's type. +// It panics if CanSet returns false. +func (v Value) SetZero() { + /* + v.mustBeAssignable() + switch v.kind() { + case Bool: + *(*bool)(v.ptr) = false + case Int: + *(*int)(v.ptr) = 0 + case Int8: + *(*int8)(v.ptr) = 0 + case Int16: + *(*int16)(v.ptr) = 0 + case Int32: + *(*int32)(v.ptr) = 0 + case Int64: + *(*int64)(v.ptr) = 0 + case Uint: + *(*uint)(v.ptr) = 0 + case Uint8: + *(*uint8)(v.ptr) = 0 + case Uint16: + *(*uint16)(v.ptr) = 0 + case Uint32: + *(*uint32)(v.ptr) = 0 + case Uint64: + *(*uint64)(v.ptr) = 0 + case Uintptr: + *(*uintptr)(v.ptr) = 0 + case Float32: + *(*float32)(v.ptr) = 0 + case Float64: + *(*float64)(v.ptr) = 0 + case Complex64: + *(*complex64)(v.ptr) = 0 + case Complex128: + *(*complex128)(v.ptr) = 0 + case String: + *(*string)(v.ptr) = "" + case Slice: + *(*unsafeheader.Slice)(v.ptr) = unsafeheader.Slice{} + case Interface: + *(*[2]unsafe.Pointer)(v.ptr) = [2]unsafe.Pointer{} + case Chan, Func, Map, Pointer, UnsafePointer: + *(*unsafe.Pointer)(v.ptr) = nil + case Array, Struct: + typedmemclr(v.typ(), v.ptr) + default: + // This should never happen, but will act as a safeguard for later, + // as a default value doesn't makes sense here. + panic(&ValueError{"reflect.Value.SetZero", v.Kind()}) + } + */ + panic("todo") +} + +// Kind returns v's Kind. +// If v is the zero Value (IsValid returns false), Kind returns Invalid. +func (v Value) Kind() Kind { + return v.kind() +} + +// Len returns v's length. +// It panics if v's Kind is not Array, Chan, Map, Slice, String, or pointer to Array. +func (v Value) Len() int { + // lenNonSlice is split out to keep Len inlineable for slice kinds. + if v.kind() == Slice { + return (*unsafeheaderSlice)(v.ptr).Len + } + return v.lenNonSlice() +} + +func (v Value) lenNonSlice() int { + /* + switch k := v.kind(); k { + case Array: + tt := (*arrayType)(unsafe.Pointer(v.typ())) + return int(tt.Len) + case Chan: + return chanlen(v.pointer()) + case Map: + return maplen(v.pointer()) + case String: + // String is bigger than a word; assume flagIndir. + return (*unsafeheader.String)(v.ptr).Len + case Ptr: + if v.typ().Elem().Kind() == abi.Array { + return v.typ().Elem().Len() + } + panic("reflect: call of reflect.Value.Len on ptr to non-array Value") + } + panic(&ValueError{"reflect.Value.Len", v.kind()}) + */ + panic("todo") +} + +//go:linkname unsafe_New github.com/goplus/llgo/internal/runtime.New +func unsafe_New(*abi.Type) unsafe.Pointer + +//go:linkname unsafe_NewArray github.com/goplus/llgo/internal/runtime.NewArray +func unsafe_NewArray(*abi.Type, int) unsafe.Pointer + +// Zero returns a Value representing the zero value for the specified type. +// The result is different from the zero value of the Value struct, +// which represents no value at all. +// For example, Zero(TypeOf(42)) returns a Value with Kind Int and value 0. +// The returned value is neither addressable nor settable. +func Zero(typ Type) Value { + if typ == nil { + panic("reflect: Zero(nil)") + } + t := &typ.(*rtype).t + fl := flag(t.Kind()) + if t.IfaceIndir() { + var p unsafe.Pointer + if t.Size() <= maxZero { + p = unsafe.Pointer(&runtime.ZeroVal[0]) + } else { + p = unsafe_New(t) + } + return Value{t, p, fl | flagIndir} + } + return Value{t, nil, fl} +} + +// TODO(xsw): check this +// must match declarations in runtime/map.go. +const maxZero = runtime.MaxZero diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index eb9bbac9..2c8a414d 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -135,10 +135,12 @@ func TracePanic(v any) { } } +/* func stringTracef(fp c.FilePtr, format *c.Char, s String) { cs := c.Alloca(uintptr(s.len) + 1) c.Fprintf(fp, format, CStrCopy(cs, s)) } +*/ // ----------------------------------------------------------------------------- @@ -147,4 +149,22 @@ func Zeroinit(p unsafe.Pointer, size uintptr) unsafe.Pointer { return c.Memset(p, 0, size) } +// New allocates memory and initializes it to zero. +func New(t *Type) unsafe.Pointer { + return AllocZ(t.Size_) +} + +// NewArray allocates memory for an array and initializes it to zero. +func NewArray(t *Type, n int) unsafe.Pointer { + return AllocZ(uintptr(n) * t.Size_) +} + +// ----------------------------------------------------------------------------- + +// TODO(xsw): check this +// must match declarations in runtime/map.go. +const MaxZero = 1024 + +var ZeroVal [MaxZero]byte + // -----------------------------------------------------------------------------