1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-31 08:47:03 +00:00

ok make it cleaner, it's working well and blazing fast but I have to do a lot cleaning and commenting and docs as well before push it to master --- hope at christmas day, also thinking some internal ideas - the whole code is not ready to be readen by a third person yet.

Former-commit-id: 0b3fb2841d5032ff47bdca42a6f4ccfeb789ce3c
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-12-19 23:40:42 +02:00
parent 4261b5784a
commit c15763c556
11 changed files with 343 additions and 313 deletions

View File

@@ -85,22 +85,15 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v
}
}
// s.Length = n
s.Length = len(s.inputs)
s.Valid = s.Length > 0
for i, in := range s.inputs {
bindmethodTyp := "Static"
if in.Object.BindType == Dynamic {
bindmethodTyp = "Dynamic"
}
bindmethodTyp := bindTypeString(in.Object.BindType)
typIn := typ.In(in.InputIndex)
// remember: on methods that are part of a struct (i.e controller)
// the input index = 1 is the begggining instead of the 0,
// because the 0 is the controller receiver pointer of the method.
s.trace += fmt.Sprintf("[%d] %s binding: '%s' for input position: %d and type: '%s'\n", i+1, bindmethodTyp, in.Object.Type.String(), in.InputIndex, typIn.String())
}

View File

@@ -12,6 +12,15 @@ const (
Dynamic // dynamic value, depends on some input arguments from the caller.
)
func bindTypeString(typ BindType) string {
switch typ {
case Dynamic:
return "Dynamic"
default:
return "Static"
}
}
type BindObject struct {
Type reflect.Type // the Type of 'Value' or the type of the returned 'ReturnValue' .
Value reflect.Value

View File

@@ -66,6 +66,13 @@ func ValueOf(o interface{}) reflect.Value {
return reflect.ValueOf(o)
}
func ValuesOf(valuesAsInterface []interface{}) (values []reflect.Value) {
for _, v := range valuesAsInterface {
values = append(values, ValueOf(v))
}
return
}
func IndirectType(typ reflect.Type) reflect.Type {
switch typ.Kind() {
case reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
@@ -115,9 +122,10 @@ func structFieldIgnored(f reflect.StructField) bool {
}
type field struct {
Type reflect.Type
Index []int // the index of the field, slice if it's part of a embedded struct
Name string // the actual name
Type reflect.Type
Name string // the actual name.
Index []int // the index of the field, slice if it's part of a embedded struct
CanSet bool // is true if it's exported.
// this could be empty, but in our cases it's not,
// it's filled with the bind object (as service which means as static value)
@@ -127,11 +135,11 @@ type field struct {
// NumFields returns the total number of fields, and the embedded, even if the embedded struct is not exported,
// it will check for its exported fields.
func NumFields(elemTyp reflect.Type) int {
return len(lookupFields(elemTyp, nil))
func NumFields(elemTyp reflect.Type, skipUnexported bool) int {
return len(lookupFields(elemTyp, skipUnexported, nil))
}
func lookupFields(elemTyp reflect.Type, parentIndex []int) (fields []field) {
func lookupFields(elemTyp reflect.Type, skipUnexported bool, parentIndex []int) (fields []field) {
if elemTyp.Kind() != reflect.Struct {
return
}
@@ -141,14 +149,15 @@ func lookupFields(elemTyp reflect.Type, parentIndex []int) (fields []field) {
if IndirectType(f.Type).Kind() == reflect.Struct &&
!structFieldIgnored(f) {
fields = append(fields, lookupFields(f.Type, append(parentIndex, i))...)
fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...)
continue
}
// skip unexported fields here,
// after the check for embedded structs, these can be binded if their
// fields are exported.
if f.PkgPath != "" {
isExported := f.PkgPath == ""
if skipUnexported && !isExported {
continue
}
@@ -158,9 +167,10 @@ func lookupFields(elemTyp reflect.Type, parentIndex []int) (fields []field) {
}
field := field{
Type: f.Type,
Name: f.Name,
Index: index,
Type: f.Type,
Name: f.Name,
Index: index,
CanSet: isExported,
}
fields = append(fields, field)
@@ -172,12 +182,13 @@ func lookupFields(elemTyp reflect.Type, parentIndex []int) (fields []field) {
// LookupNonZeroFieldsValues lookup for filled fields based on the "v" struct value instance.
// It returns a slice of reflect.Value (same type as `Values`) that can be binded,
// like the end-developer's custom values.
func LookupNonZeroFieldsValues(v reflect.Value) (bindValues []reflect.Value) {
func LookupNonZeroFieldsValues(v reflect.Value, skipUnexported bool) (bindValues []reflect.Value) {
elem := IndirectValue(v)
fields := lookupFields(IndirectType(v.Type()), nil)
for _, f := range fields {
fields := lookupFields(IndirectType(v.Type()), skipUnexported, nil)
if fieldVal := elem.FieldByIndex(f.Index); f.Type.Kind() == reflect.Ptr && !IsZero(fieldVal) {
for _, f := range fields {
if fieldVal := elem.FieldByIndex(f.Index); /*f.Type.Kind() == reflect.Ptr &&*/
!IsZero(fieldVal) {
bindValues = append(bindValues, fieldVal)
}
}

View File

@@ -5,6 +5,13 @@ import (
"reflect"
)
type State uint8
const (
Stateless State = iota
Singleton
)
type (
targetStructField struct {
Object *BindObject
@@ -12,22 +19,38 @@ type (
}
StructInjector struct {
elemType reflect.Type
initRef reflect.Value
initRefAsSlice []reflect.Value // useful when the struct is passed on a func as input args via reflection.
elemType reflect.Type
//
fields []*targetStructField
Valid bool // is true when contains fields and it's a valid target struct.
trace string // for debug info.
// is true when contains bindable fields and it's a valid target struct,
// it maybe 0 but struct may contain unexported fields or exported but no bindable (Stateless)
// see `setState`.
HasFields bool
CanInject bool // if any bindable fields when the state is NOT singleton.
State State
}
)
func (s *StructInjector) countBindType(typ BindType) (n int) {
for _, f := range s.fields {
if f.Object.BindType == typ {
n++
}
}
return
}
func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *StructInjector {
s := &StructInjector{
elemType: IndirectType(v.Type()),
initRef: v,
initRefAsSlice: []reflect.Value{v},
elemType: IndirectType(v.Type()),
}
fields := lookupFields(s.elemType, nil)
fields := lookupFields(s.elemType, true, nil)
for _, f := range fields {
if hijack != nil {
if b, ok := hijack(f.Type); ok && b != nil {
s.fields = append(s.fields, &targetStructField{
@@ -55,28 +78,75 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
})
break
}
}
}
s.Valid = len(s.fields) > 0
if s.Valid {
for i, f := range s.fields {
bindmethodTyp := "Static"
if f.Object.BindType == Dynamic {
bindmethodTyp = "Dynamic"
}
elemField := s.elemType.FieldByIndex(f.FieldIndex)
s.trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n", i+1, bindmethodTyp, f.Object.Type.String(), elemField.Name, elemField.Type.String())
}
}
s.HasFields = len(s.fields) > 0
// set the overall state of this injector.
s.setState()
s.fillStruct()
return s
}
func (s *StructInjector) String() string {
return s.trace
// set the state, once.
func (s *StructInjector) setState() {
// note for zero length of struct's fields:
// if struct doesn't contain any field
// so both of the below variables will be 0,
// so it's a singleton.
// At the other hand the `s.HasFields` maybe false
// but the struct may contain UNEXPORTED fields or non-bindable fields (request-scoped on both cases)
// so a new controller/struct at the caller side should be initialized on each request,
// we should not depend on the `HasFields` for singleton or no, this is the reason I
// added the `.State` now.
staticBindingsFieldsLength := s.countBindType(Static)
structFieldsLength := NumFields(s.elemType, false)
// println("staticBindingsFieldsLength: ", staticBindingsFieldsLength)
// println("structFieldsLength: ", structFieldsLength)
// if the number of static values binded is equal to the
// total struct's fields(including unexported fields this time) then set as singleton.
if staticBindingsFieldsLength == structFieldsLength {
s.State = Singleton
return
}
s.CanInject = s.State == Stateless && s.HasFields
// the default is `Stateless`, which means that a new instance should be created
// on each inject action by the caller.
}
// fill the static bindings values once.
func (s *StructInjector) fillStruct() {
if !s.HasFields {
return
}
// if field is Static then set it to the value that passed by the caller,
// so will have the static bindings already and we can just use that value instead
// of creating new instance.
destElem := IndirectValue(s.initRef)
for _, f := range s.fields {
// if field is Static then set it to the value that passed by the caller,
// so will have the static bindings already and we can just use that value instead
// of creating new instance.
if s.State == Singleton && f.Object.BindType == Static {
destElem.FieldByIndex(f.FieldIndex).Set(f.Object.Value)
}
}
}
func (s *StructInjector) String() (trace string) {
for i, f := range s.fields {
elemField := s.elemType.FieldByIndex(f.FieldIndex)
trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n",
i+1, bindTypeString(f.Object.BindType), f.Object.Type.String(),
elemField.Name, elemField.Type.String())
}
return
}
func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
@@ -91,25 +161,21 @@ func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
for _, f := range s.fields {
f.Object.Assign(ctx, func(v reflect.Value) {
// fmt.Printf("%s for %s at index: %d\n", destElem.Type().String(), f.Object.Type.String(), f.FieldIndex)
destElem.FieldByIndex(f.FieldIndex).Set(v)
})
}
}
func (s *StructInjector) InjectElemStaticOnly(destElem reflect.Value) (n int) {
for _, f := range s.fields {
if f.Object.BindType != Static {
continue
}
destElem.FieldByIndex(f.FieldIndex).Set(f.Object.Value)
n++
func (s *StructInjector) New() reflect.Value {
if s.State == Singleton {
return s.initRef
}
return
return reflect.New(s.elemType)
}
func (s *StructInjector) New(ctx ...reflect.Value) reflect.Value {
dest := reflect.New(s.elemType)
s.InjectElem(dest, ctx...)
return dest
func (s *StructInjector) NewAsSlice() []reflect.Value {
if s.State == Singleton {
return s.initRefAsSlice
}
return []reflect.Value{reflect.New(s.elemType)}
}

View File

@@ -8,6 +8,8 @@ type ValuesReadOnly interface {
Has(value interface{}) bool
// Len returns the length of the values.
Len() int
// Clone returns a copy of the current values.
Clone() Values
}
type Values []reflect.Value
@@ -27,21 +29,29 @@ func (bv Values) Clone() Values {
return NewValues()
}
// CloneWithFieldsOf will return a copy of the current values
// plus the "v" struct's fields that are filled(non-zero) by the caller.
func (bv Values) CloneWithFieldsOf(s interface{}) Values {
values := bv.Clone()
// add the manual filled fields to the dependencies.
filledFieldValues := LookupNonZeroFieldsValues(ValueOf(s), true)
values = append(values, filledFieldValues...)
return values
}
func (bv Values) Len() int {
return len(bv)
}
// Add binds values to this controller, if you want to share
// binding values between controllers use the Engine's `Bind` function instead.
// Add adds values as dependencies, if the struct's fields
// or the function's input arguments needs them, they will be defined as
// bindings (at build-time) and they will be used (at serve-time).
func (bv *Values) Add(values ...interface{}) {
for _, val := range values {
bv.AddValue(reflect.ValueOf(val))
}
bv.AddValues(ValuesOf(values)...)
}
// AddValue same as `Add` but accepts reflect.Value
// instead.
func (bv *Values) AddValue(values ...reflect.Value) {
func (bv *Values) AddValues(values ...reflect.Value) {
for _, v := range values {
if !goodVal(v) {
continue
@@ -115,6 +125,6 @@ func (bv *Values) addIfNotExists(v reflect.Value) bool {
return false
}
bv.AddValue(v)
bv.Add(v)
return true
}