From f8b0a7105b0f2271789076529403aeef78e86748 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 20 Jun 2024 14:17:37 +0800 Subject: [PATCH] patch: reflect (type) --- _demo/rtype/rtype.go | 8 + cl/import.go | 5 +- internal/abi/type.go | 4 +- internal/build/build.go | 1 + internal/lib/reflect/reflect.go | 22 + internal/lib/reflect/type.go | 1011 +++++++++++++++++++++++++++++++ internal/lib/reflect/value.go | 120 ++++ internal/runtime/z_face.go | 8 +- internal/runtime/z_type.go | 51 +- 9 files changed, 1204 insertions(+), 26 deletions(-) create mode 100644 _demo/rtype/rtype.go create mode 100644 internal/lib/reflect/reflect.go create mode 100644 internal/lib/reflect/type.go create mode 100644 internal/lib/reflect/value.go diff --git a/_demo/rtype/rtype.go b/_demo/rtype/rtype.go new file mode 100644 index 00000000..5d114161 --- /dev/null +++ b/_demo/rtype/rtype.go @@ -0,0 +1,8 @@ +package main + +import "reflect" + +func main() { + tyIntSlice := reflect.SliceOf(reflect.TypeOf(0)) + println(tyIntSlice.String()) +} diff --git a/cl/import.go b/cl/import.go index e780e5e5..bb8f03f0 100644 --- a/cl/import.go +++ b/cl/import.go @@ -519,10 +519,9 @@ func ignoreName(name string) bool { } */ return (strings.HasPrefix(name, "internal/") && !supportedInternal(name)) || - strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "reflect.") || + strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "runtime/") || strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || - strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "runtime/") || - strings.HasPrefix(name, "plugin.") + strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "plugin.") } func supportedInternal(name string) bool { diff --git a/internal/abi/type.go b/internal/abi/type.go index 3e7250b9..b65b578e 100644 --- a/internal/abi/type.go +++ b/internal/abi/type.go @@ -337,8 +337,8 @@ func (t *Type) Align() int { return int(t.Align_) } func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } -// Name returns the name of type t. -func (t *Type) Name() string { +// String returns string form of type t. +func (t *Type) String() string { if t.TFlag&TFlagExtraStar != 0 { return "*" + t.Str_ } diff --git a/internal/build/build.go b/internal/build/build.go index 7e73151b..01e0dcb9 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -727,6 +727,7 @@ var hasAltPkg = map[string]none{ "io/fs": {}, "math": {}, "math/cmplx": {}, + "reflect": {}, "sync": {}, "sync/atomic": {}, "syscall": {}, diff --git a/internal/lib/reflect/reflect.go b/internal/lib/reflect/reflect.go new file mode 100644 index 00000000..1cbda820 --- /dev/null +++ b/internal/lib/reflect/reflect.go @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +// llgo:skipall +import ( + _ "unsafe" +) diff --git a/internal/lib/reflect/type.go b/internal/lib/reflect/type.go new file mode 100644 index 00000000..92d95359 --- /dev/null +++ b/internal/lib/reflect/type.go @@ -0,0 +1,1011 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 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 ( + "strconv" + "unsafe" + + "github.com/goplus/llgo/internal/abi" + "github.com/goplus/llgo/internal/runtime" +) + +// Type is the representation of a Go type. +// +// Not all methods apply to all kinds of types. Restrictions, +// if any, are noted in the documentation for each method. +// Use the Kind method to find out the kind of type before +// calling kind-specific methods. Calling a method +// inappropriate to the kind of type causes a run-time panic. +// +// Type values are comparable, such as with the == operator, +// so they can be used as map keys. +// Two Type values are equal if they represent identical types. +type Type interface { + // Methods applicable to all types. + + // Align returns the alignment in bytes of a value of + // this type when allocated in memory. + Align() int + + // FieldAlign returns the alignment in bytes of a value of + // this type when used as a field in a struct. + FieldAlign() int + + // Method returns the i'th method in the type's method set. + // It panics if i is not in the range [0, NumMethod()). + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver, + // and only exported methods are accessible. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + // + // Methods are sorted in lexicographic order. + Method(int) Method + + // MethodByName returns the method with that name in the type's + // method set and a boolean indicating if the method was found. + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + MethodByName(string) (Method, bool) + + // NumMethod returns the number of methods accessible using Method. + // + // For a non-interface type, it returns the number of exported methods. + // + // For an interface type, it returns the number of exported and unexported methods. + NumMethod() int + + // Name returns the type's name within its package for a defined type. + // For other (non-defined) types it returns the empty string. + Name() string + + // PkgPath returns a defined type's package path, that is, the import path + // that uniquely identifies the package, such as "encoding/base64". + // If the type was predeclared (string, error) or not defined (*T, struct{}, + // []int, or A where A is an alias for a non-defined type), the package path + // will be the empty string. + PkgPath() string + + // Size returns the number of bytes needed to store + // a value of the given type; it is analogous to unsafe.Sizeof. + Size() uintptr + + // String returns a string representation of the type. + // The string representation may use shortened package names + // (e.g., base64 instead of "encoding/base64") and is not + // guaranteed to be unique among types. To test for type identity, + // compare the Types directly. + String() string + + // Kind returns the specific kind of this type. + Kind() Kind + + // Implements reports whether the type implements the interface type u. + Implements(u Type) bool + + // AssignableTo reports whether a value of the type is assignable to type u. + AssignableTo(u Type) bool + + // ConvertibleTo reports whether a value of the type is convertible to type u. + // Even if ConvertibleTo returns true, the conversion may still panic. + // For example, a slice of type []T is convertible to *[N]T, + // but the conversion will panic if its length is less than N. + ConvertibleTo(u Type) bool + + // Comparable reports whether values of this type are comparable. + // Even if Comparable returns true, the comparison may still panic. + // For example, values of interface type are comparable, + // but the comparison will panic if their dynamic type is not comparable. + Comparable() bool + + // Methods applicable only to some types, depending on Kind. + // The methods allowed for each kind are: + // + // Int*, Uint*, Float*, Complex*: Bits + // Array: Elem, Len + // Chan: ChanDir, Elem + // Func: In, NumIn, Out, NumOut, IsVariadic. + // Map: Key, Elem + // Pointer: Elem + // Slice: Elem + // Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField + + // Bits returns the size of the type in bits. + // It panics if the type's Kind is not one of the + // sized or unsized Int, Uint, Float, or Complex kinds. + Bits() int + + // ChanDir returns a channel type's direction. + // It panics if the type's Kind is not Chan. + ChanDir() ChanDir + + // IsVariadic reports whether a function type's final input parameter + // is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's + // implicit actual type []T. + // + // For concreteness, if t represents func(x int, y ... float64), then + // + // t.NumIn() == 2 + // t.In(0) is the reflect.Type for "int" + // t.In(1) is the reflect.Type for "[]float64" + // t.IsVariadic() == true + // + // IsVariadic panics if the type's Kind is not Func. + IsVariadic() bool + + // Elem returns a type's element type. + // It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice. + Elem() Type + + // Field returns a struct type's i'th field. + // It panics if the type's Kind is not Struct. + // It panics if i is not in the range [0, NumField()). + Field(i int) StructField + + // FieldByIndex returns the nested field corresponding + // to the index sequence. It is equivalent to calling Field + // successively for each index i. + // It panics if the type's Kind is not Struct. + FieldByIndex(index []int) StructField + + // FieldByName returns the struct field with the given name + // and a boolean indicating if the field was found. + FieldByName(name string) (StructField, bool) + + // FieldByNameFunc returns the struct field with a name + // that satisfies the match function and a boolean indicating if + // the field was found. + // + // FieldByNameFunc considers the fields in the struct itself + // and then the fields in any embedded structs, in breadth first order, + // stopping at the shallowest nesting depth containing one or more + // fields satisfying the match function. If multiple fields at that depth + // satisfy the match function, they cancel each other + // and FieldByNameFunc returns no match. + // This behavior mirrors Go's handling of name lookup in + // structs containing embedded fields. + FieldByNameFunc(match func(string) bool) (StructField, bool) + + // In returns the type of a function type's i'th input parameter. + // It panics if the type's Kind is not Func. + // It panics if i is not in the range [0, NumIn()). + In(i int) Type + + // Key returns a map type's key type. + // It panics if the type's Kind is not Map. + Key() Type + + // Len returns an array type's length. + // It panics if the type's Kind is not Array. + Len() int + + // NumField returns a struct type's field count. + // It panics if the type's Kind is not Struct. + NumField() int + + // NumIn returns a function type's input parameter count. + // It panics if the type's Kind is not Func. + NumIn() int + + // NumOut returns a function type's output parameter count. + // It panics if the type's Kind is not Func. + NumOut() int + + // Out returns the type of a function type's i'th output parameter. + // It panics if the type's Kind is not Func. + // It panics if i is not in the range [0, NumOut()). + Out(i int) Type + + common() *abi.Type + uncommon() *uncommonType +} + +// A Kind represents the specific kind of type that a Type represents. +// The zero Kind is not a valid kind. +type Kind uint + +const ( + Invalid Kind = iota + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + Array + Chan + Func + Interface + Map + Pointer + Slice + String + Struct + UnsafePointer +) + +// Ptr is the old name for the Pointer kind. +const Ptr = Pointer + +// uncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). +// Using a pointer to this struct reduces the overall size required +// to describe a non-defined type with no methods. +type uncommonType = abi.UncommonType + +// Embed this type to get common/uncommon +type common struct { + abi.Type +} + +// rtype is the common implementation of most values. +// It is embedded in other struct types. +type rtype struct { + t abi.Type +} + +func (t *rtype) common() *abi.Type { + return &t.t +} + +func (t *rtype) uncommon() *abi.UncommonType { + return t.t.Uncommon() +} + +// ChanDir represents a channel type's direction. +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan +) + +// arrayType represents a fixed array type. +type arrayType = abi.ArrayType + +// chanType represents a channel type. +type chanType = abi.ChanType + +// funcType represents a function type. +// +// A *rtype for each in and out parameter is stored in an array that +// directly follows the funcType (and possibly its uncommonType). So +// a function type with one method, one input, and one output is: +// +// struct { +// funcType +// uncommonType +// [2]*rtype // [0] is in, [1] is out +// } +type funcType = abi.FuncType + +// interfaceType represents an interface type. +type interfaceType struct { + abi.InterfaceType // can embed directly because not a public type. +} + +func (t *interfaceType) common() *abi.Type { + return &t.Type +} + +func (t *interfaceType) uncommon() *abi.UncommonType { + return t.Uncommon() +} + +// mapType represents a map type. +type mapType struct { + abi.MapType +} + +// ptrType represents a pointer type. +type ptrType struct { + abi.PtrType +} + +// sliceType represents a slice type. +type sliceType struct { + abi.SliceType +} + +// Struct field +type structField = abi.StructField + +// structType represents a struct type. +type structType struct { + abi.StructType +} + +/* + * The compiler knows the exact layout of all the data structures above. + * The compiler does not know about the data structures and methods below. + */ + +// Method represents a single method. +type Method struct { + // Name is the method name. + Name string + + // PkgPath is the package path that qualifies a lower case (unexported) + // method name. It is empty for upper case (exported) method names. + // The combination of PkgPath and Name uniquely identifies a method + // in a method set. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + PkgPath string + + Type Type // method type + Func Value // func with receiver as first argument + Index int // index for Type.Method +} + +// IsExported reports whether the method is exported. +func (m Method) IsExported() bool { + return m.PkgPath == "" +} + +const ( + kindDirectIface = 1 << 5 + kindGCProg = 1 << 6 // Type.gc points to GC program + kindMask = (1 << 5) - 1 +) + +// String returns the name of k. +func (k Kind) String() string { + if uint(k) < uint(len(kindNames)) { + return kindNames[uint(k)] + } + return "kind" + strconv.Itoa(int(k)) +} + +var kindNames = []string{ + Invalid: "invalid", + Bool: "bool", + Int: "int", + Int8: "int8", + Int16: "int16", + Int32: "int32", + Int64: "int64", + Uint: "uint", + Uint8: "uint8", + Uint16: "uint16", + Uint32: "uint32", + Uint64: "uint64", + Uintptr: "uintptr", + Float32: "float32", + Float64: "float64", + Complex64: "complex64", + Complex128: "complex128", + Array: "array", + Chan: "chan", + Func: "func", + Interface: "interface", + Map: "map", + Pointer: "ptr", + Slice: "slice", + String: "string", + Struct: "struct", + UnsafePointer: "unsafe.Pointer", +} + +func (t *rtype) String() string { + /* + s := t.nameOff(t.t.Str).Name() + if t.t.TFlag&abi.TFlagExtraStar != 0 { + return s[1:] + } + return s + */ + panic("todo") +} + +func (t *rtype) Size() uintptr { return t.t.Size() } + +func (t *rtype) Bits() int { + if t == nil { + panic("reflect: Bits of nil Type") + } + k := t.Kind() + if k < Int || k > Complex128 { + panic("reflect: Bits of non-arithmetic Type " + t.String()) + } + return int(t.t.Size_) * 8 +} + +func (t *rtype) Align() int { return t.t.Align() } + +func (t *rtype) FieldAlign() int { return t.t.FieldAlign() } + +func (t *rtype) Kind() Kind { return Kind(t.t.Kind()) } + +func (t *rtype) exportedMethods() []abi.Method { + ut := t.uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t *rtype) NumMethod() int { + /* + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.NumMethod() + } + return len(t.exportedMethods()) + */ + panic("todo") +} + +func (t *rtype) Method(i int) (m Method) { + /* + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.Method(i) + } + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + pname := t.nameOff(p.Name) + m.Name = pname.Name() + fl := flag(Func) + mtyp := t.typeOff(p.Mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+ft.NumIn()) + in = append(in, t) + for _, arg := range ft.InSlice() { + in = append(in, toRType(arg)) + } + out := make([]Type, 0, ft.NumOut()) + for _, ret := range ft.OutSlice() { + out = append(out, toRType(ret)) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + tfn := t.textOff(p.Tfn) + fn := unsafe.Pointer(&tfn) + m.Func = Value{&mt.(*rtype).t, fn, fl} + + m.Index = i + return m + */ + panic("todo") +} + +func (t *rtype) MethodByName(name string) (m Method, ok bool) { + /* + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.MethodByName(name) + } + ut := t.uncommon() + if ut == nil { + return Method{}, false + } + + methods := ut.ExportedMethods() + + // We are looking for the first index i where the string becomes >= s. + // This is a copy of sort.Search, with f(h) replaced by (t.nameOff(methods[h].name).name() >= name). + i, j := 0, len(methods) + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + // i ≤ h < j + if !(t.nameOff(methods[h].Name).Name() >= name) { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + if i < len(methods) && name == t.nameOff(methods[i].Name).Name() { + return t.Method(i), true + } + + return Method{}, false + */ + panic("todo") +} + +func (t *rtype) PkgPath() string { + if t.t.TFlag&abi.TFlagNamed == 0 { + return "" + } + ut := t.uncommon() + if ut == nil { + return "" + } + return ut.PkgPath_ +} + +func pkgPathFor(t *abi.Type) string { + return toRType(t).PkgPath() +} + +func (t *rtype) Name() string { + /* + if !t.t.HasName() { + return "" + } + s := t.String() + i := len(s) - 1 + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } + i-- + } + return s[i+1:] + */ + panic("todo") +} + +func nameFor(t *abi.Type) string { + return toRType(t).Name() +} + +func (t *rtype) ChanDir() ChanDir { + if t.Kind() != Chan { + panic("reflect: ChanDir of non-chan type " + t.String()) + } + tt := (*abi.ChanType)(unsafe.Pointer(t)) + return ChanDir(tt.Dir) +} + +func toRType(t *abi.Type) *rtype { + return (*rtype)(unsafe.Pointer(t)) +} + +func elem(t *abi.Type) *abi.Type { + et := t.Elem() + if et != nil { + return et + } + // TODO(xsw): + // panic("reflect: Elem of invalid type " + stringFor(t)) + panic("todo") +} + +func (t *rtype) Elem() Type { + return toType(elem(t.common())) +} + +func (t *rtype) Field(i int) StructField { + /* + if t.Kind() != Struct { + panic("reflect: Field of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.Field(i) + */ + panic("todo") +} + +func (t *rtype) FieldByIndex(index []int) StructField { + /* + if t.Kind() != Struct { + panic("reflect: FieldByIndex of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByIndex(index) + */ + panic("todo") +} + +func (t *rtype) FieldByName(name string) (StructField, bool) { + /* + if t.Kind() != Struct { + panic("reflect: FieldByName of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByName(name) + */ + panic("todo") +} + +func (t *rtype) FieldByNameFunc(match func(string) bool) (StructField, bool) { + /* + if t.Kind() != Struct { + panic("reflect: FieldByNameFunc of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return tt.FieldByNameFunc(match) + */ + panic("todo") +} + +func (t *rtype) Key() Type { + if t.Kind() != Map { + panic("reflect: Key of non-map type " + t.String()) + } + tt := (*mapType)(unsafe.Pointer(t)) + return toType(tt.Key) +} + +func (t *rtype) Len() int { + if t.Kind() != Array { + panic("reflect: Len of non-array type " + t.String()) + } + tt := (*arrayType)(unsafe.Pointer(t)) + return int(tt.Len) +} + +func (t *rtype) NumField() int { + if t.Kind() != Struct { + panic("reflect: NumField of non-struct type " + t.String()) + } + tt := (*structType)(unsafe.Pointer(t)) + return len(tt.Fields) +} + +func (t *rtype) In(i int) Type { + /* + if t.Kind() != Func { + panic("reflect: In of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return toType(tt.InSlice()[i]) + */ + panic("todo") +} + +func (t *rtype) NumIn() int { + /* + if t.Kind() != Func { + panic("reflect: NumIn of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.NumIn() + */ + panic("todo") +} + +func (t *rtype) NumOut() int { + /* + if t.Kind() != Func { + panic("reflect: NumOut of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.NumOut() + */ + panic("todo") +} + +func (t *rtype) Out(i int) Type { + /* + if t.Kind() != Func { + panic("reflect: Out of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return toType(tt.OutSlice()[i]) + */ + panic("todo") +} + +func (t *rtype) IsVariadic() bool { + /* + if t.Kind() != Func { + panic("reflect: IsVariadic of non-func type " + t.String()) + } + tt := (*abi.FuncType)(unsafe.Pointer(t)) + return tt.IsVariadic() + */ + panic("todo") +} + +// A StructField describes a single field in a struct. +type StructField struct { + // Name is the field name. + Name string + + // PkgPath is the package path that qualifies a lower case (unexported) + // field name. It is empty for upper case (exported) field names. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + PkgPath string + + Type Type // field type + Tag StructTag // field tag string + Offset uintptr // offset within struct, in bytes + Index []int // index sequence for Type.FieldByIndex + Anonymous bool // is an embedded field +} + +// IsExported reports whether the field is exported. +func (f StructField) IsExported() bool { + return f.PkgPath == "" +} + +// A StructTag is the tag string in a struct field. +// +// By convention, tag strings are a concatenation of +// optionally space-separated key:"value" pairs. +// Each key is a non-empty string consisting of non-control +// characters other than space (U+0020 ' '), quote (U+0022 '"'), +// and colon (U+003A ':'). Each value is quoted using U+0022 '"' +// characters and Go string literal syntax. +type StructTag string + +// Get returns the value associated with key in the tag string. +// If there is no such key in the tag, Get returns the empty string. +// If the tag does not have the conventional format, the value +// returned by Get is unspecified. To determine whether a tag is +// explicitly set to the empty string, use Lookup. +func (tag StructTag) Get(key string) string { + v, _ := tag.Lookup(key) + return v +} + +// Lookup returns the value associated with key in the tag string. +// If the key is present in the tag the value (which may be empty) +// is returned. Otherwise the returned value will be the empty string. +// The ok return value reports whether the value was explicitly set in +// the tag string. If the tag does not have the conventional format, +// the value returned by Lookup is unspecified. +func (tag StructTag) Lookup(key string) (value string, ok bool) { + // When modifying this code, also update the validateStructTag code + // in cmd/vet/structtag.go. + + 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 := strconv.Unquote(qvalue) + if err != nil { + break + } + return value, true + } + } + return "", false +} + +// TypeOf returns the reflection Type that represents the dynamic type of i. +// If i is a nil interface value, TypeOf returns nil. +func TypeOf(i any) Type { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + // Noescape so this doesn't make i to escape. See the comment + // at Value.typ for why this is safe. + return toType((*abi.Type)(unsafe.Pointer(eface.typ))) +} + +/* TODO(xsw): +// rtypeOf directly extracts the *rtype of the provided value. +func rtypeOf(i any) *abi.Type { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + return eface.typ +} + +// ptrMap is the cache for PointerTo. +var ptrMap sync.Map // map[*rtype]*ptrType +*/ + +// PtrTo returns the pointer type with element t. +// For example, if t represents type Foo, PtrTo(t) represents *Foo. +// +// PtrTo is the old spelling of PointerTo. +// The two functions behave identically. +func PtrTo(t Type) Type { return PointerTo(t) } + +// PointerTo returns the pointer type with element t. +// For example, if t represents type Foo, PointerTo(t) represents *Foo. +func PointerTo(t Type) Type { + return toRType(t.(*rtype).ptrTo()) +} + +func (t *rtype) ptrTo() *abi.Type { + /* + at := &t.t + if at.PtrToThis != 0 { + return t.typeOff(at.PtrToThis) + } + + // Check the cache. + if pi, ok := ptrMap.Load(t); ok { + return &pi.(*ptrType).Type + } + + // Look in known types. + s := "*" + t.String() + for _, tt := range typesByString(s) { + p := (*ptrType)(unsafe.Pointer(tt)) + if p.Elem != &t.t { + continue + } + pi, _ := ptrMap.LoadOrStore(t, p) + return &pi.(*ptrType).Type + } + + // Create a new ptrType starting with the description + // of an *unsafe.Pointer. + var iptr any = (*unsafe.Pointer)(nil) + prototype := *(**ptrType)(unsafe.Pointer(&iptr)) + pp := *prototype + + pp.Str = resolveReflectName(newName(s, "", false, false)) + pp.PtrToThis = 0 + + // For the type structures linked into the binary, the + // compiler provides a good hash of the string. + // Create a good hash for the new string by using + // the FNV-1 hash's mixing function to combine the + // old hash and the new "*". + pp.Hash = fnv1(t.t.Hash, '*') + + pp.Elem = at + + pi, _ := ptrMap.LoadOrStore(t, &pp) + return &pi.(*ptrType).Type + */ + panic("todo") +} + +func ptrTo(t *abi.Type) *abi.Type { + return toRType(t).ptrTo() +} + +// TODO(xsw): check if this is needed +// fnv1 incorporates the list of bytes into the hash x using the FNV-1 hash function. +func fnv1(x uint32, list ...byte) uint32 { + for _, b := range list { + x = x*16777619 ^ uint32(b) + } + return x +} + +func (t *rtype) Implements(u Type) bool { + /* + if u == nil { + panic("reflect: nil type passed to Type.Implements") + } + if u.Kind() != Interface { + panic("reflect: non-interface type passed to Type.Implements") + } + return implements(u.common(), t.common()) + */ + panic("todo") +} + +func (t *rtype) AssignableTo(u Type) bool { + /* + if u == nil { + panic("reflect: nil type passed to Type.AssignableTo") + } + uu := u.common() + return directlyAssignable(uu, t.common()) || implements(uu, t.common()) + */ + panic("todo") +} + +func (t *rtype) ConvertibleTo(u Type) bool { + /* + if u == nil { + panic("reflect: nil type passed to Type.ConvertibleTo") + } + return convertOp(u.common(), t.common()) != nil + */ + panic("todo") +} + +func (t *rtype) Comparable() bool { + return t.t.Equal != nil +} + +// SliceOf returns the slice type with element type t. +// For example, if t represents int, SliceOf(t) represents []int. +func SliceOf(t Type) Type { + typ := t.common() + + /* TODO(xsw): no cache + // Look in cache. + ckey := cacheKey{Slice, typ, nil, 0} + if slice, ok := lookupCache.Load(ckey); ok { + return slice.(Type) + } + + // Look in known types. + s := "[]" + stringFor(typ) + for _, tt := range typesByString(s) { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.Elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } + } + */ + + slice := runtime.SliceOf(typ) + + // TODO(xsw): + // ti, _ := lookupCache.LoadOrStore(ckey, toRType(&slice.Type)) + // return ti.(Type) + + return toType(slice) +} + +// toType converts from a *rtype to a Type that can be returned +// to the client of package reflect. In gc, the only concern is that +// a nil *rtype must be replaced by a nil Type, but in gccgo this +// function takes care of ensuring that multiple *rtype for the same +// type are coalesced into a single Type. +func toType(t *abi.Type) Type { + if t == nil { + return nil + } + return toRType(t) +} diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go new file mode 100644 index 00000000..405e6600 --- /dev/null +++ b/internal/lib/reflect/value.go @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reflect + +import ( + "unsafe" + + "github.com/goplus/llgo/internal/abi" +) + +// Value is the reflection interface to a Go value. +// +// Not all methods apply to all kinds of values. Restrictions, +// if any, are noted in the documentation for each method. +// Use the Kind method to find out the kind of value before +// calling kind-specific methods. Calling a method +// inappropriate to the kind of type causes a run time panic. +// +// The zero Value represents no value. +// Its IsValid method returns false, its Kind method returns Invalid, +// its String method returns "", and all other methods panic. +// Most functions and methods never return an invalid value. +// If one does, its documentation states the conditions explicitly. +// +// A Value can be used concurrently by multiple goroutines provided that +// the underlying Go value can be used concurrently for the equivalent +// direct operations. +// +// To compare two Values, compare the results of the Interface method. +// Using == on two Values does not compare the underlying values +// they represent. +type Value struct { + // typ_ holds the type of the value represented by a Value. + // Access using the typ method to avoid escape of v. + typ_ *abi.Type + + // Pointer-valued data or, if flagIndir is set, pointer to data. + // Valid when either flagIndir is set or typ.pointers() is true. + ptr unsafe.Pointer + + // flag holds metadata about the value. + // + // The lowest five bits give the Kind of the value, mirroring typ.Kind(). + // + // The next set of bits are flag bits: + // - flagStickyRO: obtained via unexported not embedded field, so read-only + // - flagEmbedRO: obtained via unexported embedded field, so read-only + // - flagIndir: val holds a pointer to the data + // - flagAddr: v.CanAddr is true (implies flagIndir and ptr is non-nil) + // - flagMethod: v is a method value. + // If ifaceIndir(typ), code can assume that flagIndir is set. + // + // The remaining 22+ bits give a method number for method values. + // If flag.kind() != Func, code can assume that flagMethod is unset. + flag + + // A method value represents a curried method invocation + // like r.Read for some receiver r. The typ+val+flag bits describe + // the receiver r, but the flag's Kind bits say Func (methods are + // functions), and the top bits of the flag give the method number + // in r's type's method table. +} + +type flag uintptr + +const ( + flagKindWidth = 5 // there are 27 kinds + flagKindMask flag = 1<