Skip to content

Commit

Permalink
Add Condense() and SaveTo() (#35)
Browse files Browse the repository at this point in the history
`SaveTo()` added.  Intermediate values can now be requested and written through pointers.

`Collection.Condense()` added -- a collection can now be turned into a single injector.
This can be done recursively.  Combined with `SaveTo`, multiple copies of the same type
can be generated and preserved separately.

The `Reflective` type has been joined by new siblings: `ReflectiveInvoker`, `ReflectiveArgs`, and `ReflectiveWrapper`
  • Loading branch information
muir authored May 21, 2022
1 parent bf7d347 commit f09ca2b
Show file tree
Hide file tree
Showing 22 changed files with 1,385 additions and 336 deletions.
16 changes: 16 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,28 @@ type Collection struct {
contents []*provider
}

var _ Provider = &Collection{}

// Provider is an individual injector (function, constant, or
// wrapper). Functions that take injectors, take interface{}.
// Functions that return invjectors return Provider so that
// methods can be attached.
type Provider interface {
thing
String() string

// For single providers, DownFlows includes all inputs and
// all outputs. For collections, Downflows only includes
// the net inputs and net outputs.
DownFlows() (inputs []reflect.Type, outputs []reflect.Type)

// For single providers, Upflows includes all consumes and
// all returns. For collections, Upflows only includes
// the net consumes and returns.
//
// Providers that return TerminalError are a special case and count as
// producting error.
UpFlows() (consume []reflect.Type, produce []reflect.Type)
}

// Sequence creates a Collection of providers. Each collection must
Expand Down
89 changes: 54 additions & 35 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider,

// Add debugging provider
{
d := newProvider(func() *Debugging { return nil }, -1, "Debugging")
d.cacheable = true
d.mustCache = true
d, err = characterizeFunc(d, charContext{inputsAreStatic: true})
//nolint:govet // err is shadowing, who cares?
d, err := makeDebuggingProvider()
if err != nil {
return fmt.Errorf("internal error #29: problem with debugging injectors: %w", err)
return err
}
d.isSynthetic = true
debuggingProvider = &d
funcs = append(funcs, d)
}
Expand Down Expand Up @@ -314,24 +311,29 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider,

debugln("SET INIT FUNC")
if real {
reflect.ValueOf(initF.fn).Elem().Set(
reflect.MakeFunc(reflect.ValueOf(initF.fn).Type().Elem(),
func(inputs []reflect.Value) []reflect.Value {
debugln("INSIDE INIT")
// if initDone panic, return error, or ignore?
initOnce.Do(func() {
outMap(baseValues, inputs)
debugln("RUN STATIC CHAIN")
_ = runStaticChain()
})
dumpValueArray(baseValues, "base values before init return", downVmap)
out := inMap(baseValues)
debugln("DONE INIT")
dumpValueArray(out, "init return", nil)
dumpF("init", initF)

return out
}))
initImp := func(inputs []reflect.Value) []reflect.Value {
debugln("INSIDE INIT")
// if initDone panic, return error, or ignore?
initOnce.Do(func() {
outMap(baseValues, inputs)
debugln("RUN STATIC CHAIN")
_ = runStaticChain()
})
dumpValueArray(baseValues, "base values before init return", downVmap)
out := inMap(baseValues)
debugln("DONE INIT")
dumpValueArray(out, "init return", nil)
dumpF("init", initF)

return out
}
if ri, ok := initF.fn.(ReflectiveInvoker); ok {
ri.Set(initImp)
} else {
reflect.ValueOf(initF.fn).Elem().Set(
reflect.MakeFunc(reflect.ValueOf(initF.fn).Type().Elem(),
initImp))
}
}
debugln("SET INIT FUNC - DONE")

Expand All @@ -357,17 +359,22 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider,

debugln("SET INVOKE FUNC")
if real {
reflect.ValueOf(invokeF.fn).Elem().Set(
reflect.MakeFunc(reflect.ValueOf(invokeF.fn).Type().Elem(),
func(inputs []reflect.Value) []reflect.Value {
initFunc()
values := baseValues.Copy()
dumpValueArray(values, "invoke - before input copy", downVmap)
outMap(values, inputs)
dumpValueArray(values, "invoke - after input copy", downVmap)
f(values)
return inMap(values)
}))
invokeImpl := func(inputs []reflect.Value) []reflect.Value {
initFunc()
values := baseValues.Copy()
dumpValueArray(values, "invoke - before input copy", downVmap)
outMap(values, inputs)
dumpValueArray(values, "invoke - after input copy", downVmap)
f(values)
return inMap(values)
}
if ri, ok := invokeF.fn.(ReflectiveInvoker); ok {
ri.Set(invokeImpl)
} else {
reflect.ValueOf(invokeF.fn).Elem().Set(
reflect.MakeFunc(reflect.ValueOf(invokeF.fn).Type().Elem(),
invokeImpl))
}
}
debugln("SET INVOKE FUNC - DONE")
}
Expand Down Expand Up @@ -396,3 +403,15 @@ func addToVmap(fm *provider, param flowType, vMap map[typeCode]int, rMap map[typ
}
}
}

func makeDebuggingProvider() (*provider, error) {
d := newProvider(func() *Debugging { return nil }, -1, "Debugging")
d.cacheable = true
d.mustCache = true
d, err := characterizeFunc(d, charContext{inputsAreStatic: true})
if err != nil {
return nil, fmt.Errorf("internal error #29: problem with debugging injectors: %w", err)
}
d.isSynthetic = true
return d, nil
}
76 changes: 62 additions & 14 deletions characterize.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type charContext struct {
inputsAreStatic bool
}

type flowMapType [lastFlowType][]typeCode
type flowMapType [lastFlowType]typeCodes

type characterization struct {
name string
Expand Down Expand Up @@ -161,6 +161,9 @@ var (
)

var noAnonymousFuncs = predicate("has an untyped functional argument", func(a testArgs) bool {
if _, ok := a.fm.fn.(ReflectiveWrapper); ok {
return false
}
return !hasAnonymousFuncs(typesIn(a.t), false) &&
!hasAnonymousFuncs(typesOut(a.t), false)
})
Expand All @@ -171,13 +174,30 @@ var noAnonymousExceptFirstInput = predicate("has extra untyped functional argume
})

var hasInner = predicate("does not have an Inner function (untyped functional argument in the 1st position)", func(a testArgs) bool {
t := a.t
return t.Kind() == reflect.Func && t.NumIn() > 0 && t.In(0).Kind() == reflect.Func
return isWrapper(a.t, a.fm.fn)
})

func isWrapper(t reflectType, fn interface{}) bool {
if _, ok := fn.(ReflectiveWrapper); ok {
return true
}
return t.Kind() == reflect.Func && t.NumIn() > 0 && t.In(0).Kind() == reflect.Func
}

var isFuncPointer = predicate("is not a pointer to a function", func(a testArgs) bool {
t := a.t
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func
switch a.fm.fn.(type) {
case ReflectiveInvoker:
return true
case Reflective, ReflectiveArgs:
return false
default:
t := a.t
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func
}
})

var isNotFuncPointer = predicate("is a pointer to a function", func(a testArgs) bool {
return !isFuncPointer.test(a)
})

var invokeRegistry = typeRegistry{
Expand All @@ -191,8 +211,13 @@ var invokeRegistry = typeRegistry{
mutate: func(a testArgs) {
a.fm.group = invokeGroup
a.fm.class = initFunc
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t.Elem()))
a.fm.flows[bypassParams] = toTypeCodes(typesOut(a.t.Elem()))
if _, ok := a.fm.fn.(ReflectiveInvoker); ok {
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t))
a.fm.flows[bypassParams] = toTypeCodes(typesOut(a.t))
} else {
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t.Elem()))
a.fm.flows[bypassParams] = toTypeCodes(typesOut(a.t.Elem()))
}
a.fm.required = true
a.fm.isSynthetic = true
},
Expand All @@ -207,8 +232,13 @@ var invokeRegistry = typeRegistry{
mutate: func(a testArgs) {
a.fm.group = invokeGroup
a.fm.class = invokeFunc
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t.Elem()))
a.fm.flows[receviedParams] = toTypeCodes(typesOut(a.t.Elem()))
if _, ok := a.fm.fn.(ReflectiveInvoker); ok {
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t))
a.fm.flows[receviedParams] = toTypeCodes(typesOut(a.t))
} else {
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t.Elem()))
a.fm.flows[receviedParams] = toTypeCodes(typesOut(a.t.Elem()))
}
a.fm.required = true
a.fm.isSynthetic = true
},
Expand Down Expand Up @@ -246,6 +276,7 @@ var handlerRegistry = typeRegistry{
notMarkedNoCache,
mustNotMemoize,
notMarkedReorder,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -268,6 +299,7 @@ var handlerRegistry = typeRegistry{
notMarkedNoCache,
mustNotMemoize,
notMarkedReorder,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -292,6 +324,7 @@ var handlerRegistry = typeRegistry{
possibleMapKey,
notMarkedSingleton,
notMarkedReorder,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -318,6 +351,7 @@ var handlerRegistry = typeRegistry{
possibleMapKey,
notMarkedSingleton,
notMarkedReorder,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -342,6 +376,7 @@ var handlerRegistry = typeRegistry{
notMarkedNoCache,
notMarkedSingleton,
notMarkedReorder,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -362,6 +397,7 @@ var handlerRegistry = typeRegistry{
markedMemoized,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = runGroup
Expand All @@ -383,6 +419,7 @@ var handlerRegistry = typeRegistry{
mustNotMemoize,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = runGroup
Expand All @@ -405,6 +442,7 @@ var handlerRegistry = typeRegistry{
mustNotMemoize,
notMarkedNoCache,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = staticGroup
Expand All @@ -423,6 +461,7 @@ var handlerRegistry = typeRegistry{
markedMemoized,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = runGroup
Expand All @@ -442,6 +481,7 @@ var handlerRegistry = typeRegistry{
mustNotMemoize,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = runGroup
Expand All @@ -461,16 +501,23 @@ var handlerRegistry = typeRegistry{
mustNotMemoize,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
in := typesIn(a.t)
in[0] = reflect.TypeOf(noTypeExampleValue)
a.fm.group = runGroup
a.fm.class = wrapperFunc
a.fm.flows[inputParams] = toTypeCodes(in)
a.fm.flows[outputParams] = toTypeCodes(typesIn(a.t.In(0)))
a.fm.flows[returnParams] = toTypeCodes(typesOut(a.t))
a.fm.flows[receviedParams] = toTypeCodes(typesOut(a.t.In(0)))
var inner reflectType
if w, ok := a.fm.fn.(ReflectiveWrapper); ok {
inner = wrappedReflective{w.Inner()}
} else {
inner = a.t.In(0)
}
a.fm.flows[outputParams] = toTypeCodes(typesIn(inner))
a.fm.flows[receviedParams] = toTypeCodes(typesOut(inner))
},
},

Expand All @@ -483,6 +530,7 @@ var handlerRegistry = typeRegistry{
mustNotMemoize,
unstaticOkay,
notMarkedSingleton,
isNotFuncPointer,
},
mutate: func(a testArgs) {
a.fm.group = finalGroup
Expand All @@ -498,10 +546,10 @@ var handlerRegistry = typeRegistry{
func (reg typeRegistry) characterizeFuncDetails(fm *provider, cc charContext) (*provider, error) {
var rejectReasons []string
var a testArgs
if r, ok := fm.fn.(Reflective); ok {
if r, ok := fm.fn.(ReflectiveArgs); ok {
a = testArgs{
fm: fm.copy(),
t: reflectiveWrapper{r},
t: wrappedReflective{r},
isNil: false,
cc: cc,
}
Expand Down Expand Up @@ -533,7 +581,7 @@ Match:
}
a.fm.upRmap = make(map[typeCode]typeCode)
a.fm.downRmap = make(map[typeCode]typeCode)
a.fm.flows = [lastFlowType][]typeCode{}
a.fm.flows = [lastFlowType]typeCodes{}
match.mutate(a)
return a.fm, nil
}
Expand Down
2 changes: 1 addition & 1 deletion characterize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type doesJ struct {
func (dj *doesJ) I() int { return dj.j * 3 }

func params() flowMapType {
return [lastFlowType][]typeCode{}
return [lastFlowType]typeCodes{}
}

func (flows flowMapType) returns(f ...typeCode) flowMapType {
Expand Down
Loading

0 comments on commit f09ca2b

Please sign in to comment.