1
0
mirror of https://github.com/strongdm/comply synced 2024-07-08 09:21:47 +00:00
comply/vendor/github.com/trivago/tgo/treflect/reflection.go
Justin McCarthy 4d63cf559b
dep ensure
2018-06-01 17:37:01 -07:00

374 lines
11 KiB
Go

// Copyright 2015-2016 trivago GmbH
//
// 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 treflect
import (
"fmt"
"reflect"
"unsafe"
)
// GetMissingMethods checks if a given object implements all methods of a
// given interface. It returns the interface coverage [0..1] as well as an array
// of error messages. If the interface is correctly implemented the coverage is
// 1 and the error message array is empty.
func GetMissingMethods(objType reflect.Type, ifaceType reflect.Type) (float32, []string) {
missing := []string{}
if objType.Implements(ifaceType) {
return 1.0, missing
}
methodCount := ifaceType.NumMethod()
for mIdx := 0; mIdx < methodCount; mIdx++ {
ifaceMethod := ifaceType.Method(mIdx)
objMethod, exists := objType.MethodByName(ifaceMethod.Name)
signatureMismatch := false
switch {
case !exists:
missing = append(missing, fmt.Sprintf("Missing: \"%s\" %v", ifaceMethod.Name, ifaceMethod.Type))
continue // ### continue, error found ###
case ifaceMethod.Type.NumOut() != objMethod.Type.NumOut():
signatureMismatch = true
case ifaceMethod.Type.NumIn()+1 != objMethod.Type.NumIn():
signatureMismatch = true
default:
for oIdx := 0; !signatureMismatch && oIdx < ifaceMethod.Type.NumOut(); oIdx++ {
signatureMismatch = ifaceMethod.Type.Out(oIdx) != objMethod.Type.Out(oIdx)
}
for iIdx := 0; !signatureMismatch && iIdx < ifaceMethod.Type.NumIn(); iIdx++ {
signatureMismatch = ifaceMethod.Type.In(iIdx) != objMethod.Type.In(iIdx+1)
}
}
if signatureMismatch {
missing = append(missing, fmt.Sprintf("Invalid: \"%s\" %v is not %v", ifaceMethod.Name, objMethod.Type, ifaceMethod.Type))
}
}
return float32(methodCount-len(missing)) / float32(methodCount), missing
}
// Int64 converts any signed number type to an int64.
// The second parameter is returned as false if a non-number type was given.
func Int64(v interface{}) (int64, bool) {
switch reflect.TypeOf(v).Kind() {
case reflect.Int:
return int64(v.(int)), true
case reflect.Int8:
return int64(v.(int8)), true
case reflect.Int16:
return int64(v.(int16)), true
case reflect.Int32:
return int64(v.(int32)), true
case reflect.Int64:
return v.(int64), true
case reflect.Float32:
return int64(v.(float32)), true
case reflect.Float64:
return int64(v.(float64)), true
}
fmt.Printf("%t\n%#v\n%#v\n", v, v, reflect.TypeOf(v).Kind())
return 0, false
}
// Uint64 converts any unsigned number type to an uint64.
// The second parameter is returned as false if a non-number type was given.
func Uint64(v interface{}) (uint64, bool) {
switch reflect.TypeOf(v).Kind() {
case reflect.Uint:
return uint64(v.(uint)), true
case reflect.Uint8:
return uint64(v.(uint8)), true
case reflect.Uint16:
return uint64(v.(uint16)), true
case reflect.Uint32:
return uint64(v.(uint32)), true
case reflect.Uint64:
return v.(uint64), true
}
return 0, false
}
// Float32 converts any number type to an float32.
// The second parameter is returned as false if a non-number type was given.
func Float32(v interface{}) (float32, bool) {
switch reflect.TypeOf(v).Kind() {
case reflect.Int:
return float32(v.(int)), true
case reflect.Uint:
return float32(v.(uint)), true
case reflect.Int8:
return float32(v.(int8)), true
case reflect.Uint8:
return float32(v.(uint8)), true
case reflect.Int16:
return float32(v.(int16)), true
case reflect.Uint16:
return float32(v.(uint16)), true
case reflect.Int32:
return float32(v.(int32)), true
case reflect.Uint32:
return float32(v.(uint32)), true
case reflect.Int64:
return float32(v.(int64)), true
case reflect.Uint64:
return float32(v.(uint64)), true
case reflect.Float32:
return v.(float32), true
case reflect.Float64:
return float32(v.(float64)), true
}
return 0, false
}
// Float64 converts any number type to an float64.
// The second parameter is returned as false if a non-number type was given.
func Float64(v interface{}) (float64, bool) {
switch reflect.TypeOf(v).Kind() {
case reflect.Int:
return float64(v.(int)), true
case reflect.Uint:
return float64(v.(uint)), true
case reflect.Int8:
return float64(v.(int8)), true
case reflect.Uint8:
return float64(v.(uint8)), true
case reflect.Int16:
return float64(v.(int16)), true
case reflect.Uint16:
return float64(v.(uint16)), true
case reflect.Int32:
return float64(v.(int32)), true
case reflect.Uint32:
return float64(v.(uint32)), true
case reflect.Int64:
return float64(v.(int64)), true
case reflect.Uint64:
return float64(v.(uint64)), true
case reflect.Float32:
return float64(v.(float32)), true
case reflect.Float64:
return v.(float64), true
}
return 0, false
}
// RemovePtrFromType will return the type of t and strips away any pointer(s)
// in front of the actual type.
func RemovePtrFromType(t interface{}) reflect.Type {
var v reflect.Type
if rt, isType := t.(reflect.Type); isType {
v = rt
} else {
v = reflect.TypeOf(t)
}
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
// RemovePtrFromValue will return the value of t and strips away any pointer(s)
// in front of the actual type.
func RemovePtrFromValue(t interface{}) reflect.Value {
var v reflect.Value
if rv, isValue := t.(reflect.Value); isValue {
v = rv
} else {
v = reflect.ValueOf(t)
}
for v.Type().Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
// UnsafeCopy will copy data from src to dst while ignoring type information.
// Both types need to be of the same size and dst and src have to be pointers.
// UnsafeCopy will panic if these requirements are not met.
func UnsafeCopy(dst, src interface{}) {
dstValue := reflect.ValueOf(dst)
srcValue := reflect.ValueOf(src)
UnsafeCopyValue(dstValue, srcValue)
}
// UnsafeCopyValue will copy data from src to dst while ignoring type
// information. Both types need to be of the same size or this function will
// panic. Also both types must support dereferencing via reflect.Elem()
func UnsafeCopyValue(dstValue reflect.Value, srcValue reflect.Value) {
dstType := dstValue.Elem().Type()
srcType := srcValue.Type()
var srcPtr uintptr
if srcValue.Kind() != reflect.Ptr {
// If we don't get a pointer to our source data we need to forcefully
// retrieve it by accessing the interface pointer. This is ok as we
// only read from it.
iface := srcValue.Interface()
srcPtr = reflect.ValueOf(&iface).Elem().InterfaceData()[1] // Pointer to data
} else {
srcType = srcValue.Elem().Type()
srcPtr = srcValue.Pointer()
}
if dstType.Size() != srcType.Size() {
panic("Type size mismatch between " + dstType.String() + " and " + srcType.String())
}
dstAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: dstValue.Pointer(),
Len: int(dstType.Size()),
Cap: int(dstType.Size()),
}))
srcAsSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: srcPtr,
Len: int(srcType.Size()),
Cap: int(srcType.Size()),
}))
copy(dstAsSlice, srcAsSlice)
}
// SetMemberByName sets member name of the given pointer-to-struct to the data
// passed to this function. The member may be private, too.
func SetMemberByName(ptrToStruct interface{}, name string, data interface{}) {
structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct))
member := structVal.FieldByName(name)
SetValue(member, data)
}
// SetMemberByIndex sets member idx of the given pointer-to-struct to the data
// passed to this function. The member may be private, too.
func SetMemberByIndex(ptrToStruct interface{}, idx int, data interface{}) {
structVal := reflect.Indirect(reflect.ValueOf(ptrToStruct))
member := structVal.Field(idx)
SetValue(member, data)
}
// SetValue sets an addressable value to the data passed to this function.
// In contrast to golangs reflect package this will also work with private
// variables. Please note that this function may not support all types, yet.
func SetValue(member reflect.Value, data interface{}) {
if member.CanSet() {
member.Set(reflect.ValueOf(data).Convert(member.Type()))
return // ### return, easy way ###
}
if !member.CanAddr() {
panic("SetValue requires addressable member type")
}
ptrToMember := unsafe.Pointer(member.UnsafeAddr())
dataValue := reflect.ValueOf(data)
switch member.Kind() {
case reflect.Bool:
*(*bool)(ptrToMember) = dataValue.Bool()
case reflect.Uint:
*(*uint)(ptrToMember) = uint(dataValue.Uint())
case reflect.Uint8:
*(*uint8)(ptrToMember) = uint8(dataValue.Uint())
case reflect.Uint16:
*(*uint16)(ptrToMember) = uint16(dataValue.Uint())
case reflect.Uint32:
*(*uint32)(ptrToMember) = uint32(dataValue.Uint())
case reflect.Uint64:
*(*uint64)(ptrToMember) = dataValue.Uint()
case reflect.Int:
*(*int)(ptrToMember) = int(dataValue.Int())
case reflect.Int8:
*(*int8)(ptrToMember) = int8(dataValue.Int())
case reflect.Int16:
*(*int16)(ptrToMember) = int16(dataValue.Int())
case reflect.Int32:
*(*int32)(ptrToMember) = int32(dataValue.Int())
case reflect.Int64:
*(*int64)(ptrToMember) = dataValue.Int()
case reflect.Float32:
*(*float32)(ptrToMember) = float32(dataValue.Float())
case reflect.Float64:
*(*float64)(ptrToMember) = dataValue.Float()
case reflect.Complex64:
*(*complex64)(ptrToMember) = complex64(dataValue.Complex())
case reflect.Complex128:
*(*complex128)(ptrToMember) = dataValue.Complex()
case reflect.String:
*(*string)(ptrToMember) = dataValue.String()
case reflect.Map, reflect.Chan:
// Exploit the fact that "map" is actually "*runtime.hmap" and force
// overwrite that pointer in the passed struct.
// Same foes for "chan" which is actually "*runtime.hchan".
// Note: Assigning a map or channel to another variable does NOT copy
// the contents so copying the pointer follows go's standard behavior.
dataAsPtr := unsafe.Pointer(dataValue.Pointer())
*(**uintptr)(ptrToMember) = (*uintptr)(dataAsPtr)
case reflect.Interface:
// Interfaces are basically two pointers, see runtime.iface.
// We want to modify exactly that data, which is returned by
// the InterfaceData() method.
if dataValue.Kind() != reflect.Interface {
// A type reference was passed. In order to overwrite the memory
// Representation of an interface we need to generate it first.
// Reflect does not allow us to do that unless we use the
// InterfaceData method which exposes the internal representation
// of an interface.
interfaceData := reflect.ValueOf(&data).Elem().InterfaceData()
dataValue = reflect.ValueOf(interfaceData)
}
fallthrough
default:
// Complex types are assigned memcpy style.
// Note: This should not break the garbage collector although we cannot
// be 100% sure on this.
UnsafeCopyValue(member.Addr(), dataValue)
}
}