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:
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
134
mvc/di/struct.go
134
mvc/di/struct.go
@@ -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)}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user