327 lines
9.2 KiB
Go
327 lines
9.2 KiB
Go
// Copyright (c) 2021 Uber Technologies, Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package dig
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math/rand"
|
|
"reflect"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// A ScopeOption modifies the default behavior of Scope; currently,
|
|
// there are no implementations.
|
|
type ScopeOption interface {
|
|
noScopeOption() //yet
|
|
}
|
|
|
|
// Scope is a scoped DAG of types and their dependencies.
|
|
// A Scope may also have one or more child Scopes that inherit
|
|
// from it.
|
|
type Scope struct {
|
|
// This implements containerStore interface.
|
|
|
|
// Name of the Scope
|
|
name string
|
|
// Mapping from key to all the constructor node that can provide a value for that
|
|
// key.
|
|
providers map[key][]*constructorNode
|
|
|
|
// Mapping from key to the decorator that decorates a value for that key.
|
|
decorators map[key]*decoratorNode
|
|
|
|
// constructorNodes provided directly to this Scope. i.e. it does not include
|
|
// any nodes that were provided to the parent Scope this inherited from.
|
|
nodes []*constructorNode
|
|
|
|
// Values that generated via decorators in the Scope.
|
|
decoratedValues map[key]reflect.Value
|
|
|
|
// Values that generated directly in the Scope.
|
|
values map[key]reflect.Value
|
|
|
|
// Values groups that generated directly in the Scope.
|
|
groups map[key][]reflect.Value
|
|
|
|
// Values groups that generated via decoraters in the Scope.
|
|
decoratedGroups map[key]reflect.Value
|
|
|
|
// Source of randomness.
|
|
rand *rand.Rand
|
|
|
|
// Flag indicating whether the graph has been checked for cycles.
|
|
isVerifiedAcyclic bool
|
|
|
|
// Defer acyclic check on provide until Invoke.
|
|
deferAcyclicVerification bool
|
|
|
|
// Recover from panics in user-provided code and wrap in an exported error type.
|
|
recoverFromPanics bool
|
|
|
|
// invokerFn calls a function with arguments provided to Provide or Invoke.
|
|
invokerFn invokerFn
|
|
|
|
// graph of this Scope. Note that this holds the dependency graph of all the
|
|
// nodes that affect this Scope, not just the ones provided directly to this Scope.
|
|
gh *graphHolder
|
|
|
|
// Parent of this Scope.
|
|
parentScope *Scope
|
|
|
|
// All the child scopes of this Scope.
|
|
childScopes []*Scope
|
|
}
|
|
|
|
func newScope() *Scope {
|
|
s := &Scope{
|
|
providers: make(map[key][]*constructorNode),
|
|
decorators: make(map[key]*decoratorNode),
|
|
values: make(map[key]reflect.Value),
|
|
decoratedValues: make(map[key]reflect.Value),
|
|
groups: make(map[key][]reflect.Value),
|
|
decoratedGroups: make(map[key]reflect.Value),
|
|
invokerFn: defaultInvoker,
|
|
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
}
|
|
s.gh = newGraphHolder(s)
|
|
return s
|
|
}
|
|
|
|
// Scope creates a new Scope with the given name and options from current Scope.
|
|
// Any constructors that the current Scope knows about, as well as any modifications
|
|
// made to it in the future will be propagated to the child scope.
|
|
// However, no modifications made to the child scope being created will be propagated
|
|
// to the parent Scope.
|
|
func (s *Scope) Scope(name string, opts ...ScopeOption) *Scope {
|
|
child := newScope()
|
|
child.name = name
|
|
child.parentScope = s
|
|
child.invokerFn = s.invokerFn
|
|
child.deferAcyclicVerification = s.deferAcyclicVerification
|
|
child.recoverFromPanics = s.recoverFromPanics
|
|
|
|
// child copies the parent's graph nodes.
|
|
for _, node := range s.gh.nodes {
|
|
child.gh.nodes = append(child.gh.nodes, node)
|
|
if ctrNode, ok := node.Wrapped.(*constructorNode); ok {
|
|
ctrNode.CopyOrder(s, child)
|
|
}
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt.noScopeOption()
|
|
}
|
|
|
|
s.childScopes = append(s.childScopes, child)
|
|
return child
|
|
}
|
|
|
|
// ancestors returns a list of scopes of ancestors of this scope up to the
|
|
// root. The scope at at index 0 is this scope itself.
|
|
func (s *Scope) ancestors() []*Scope {
|
|
var scopes []*Scope
|
|
for s := s; s != nil; s = s.parentScope {
|
|
scopes = append(scopes, s)
|
|
}
|
|
return scopes
|
|
}
|
|
|
|
func (s *Scope) appendSubscopes(dest []*Scope) []*Scope {
|
|
dest = append(dest, s)
|
|
for _, cs := range s.childScopes {
|
|
dest = cs.appendSubscopes(dest)
|
|
}
|
|
return dest
|
|
}
|
|
|
|
func (s *Scope) storesToRoot() []containerStore {
|
|
scopes := s.ancestors()
|
|
stores := make([]containerStore, len(scopes))
|
|
for i, s := range scopes {
|
|
stores[i] = s
|
|
}
|
|
return stores
|
|
}
|
|
|
|
func (s *Scope) knownTypes() []reflect.Type {
|
|
typeSet := make(map[reflect.Type]struct{}, len(s.providers))
|
|
for k := range s.providers {
|
|
typeSet[k.t] = struct{}{}
|
|
}
|
|
|
|
types := make([]reflect.Type, 0, len(typeSet))
|
|
for t := range typeSet {
|
|
types = append(types, t)
|
|
}
|
|
sort.Sort(byTypeName(types))
|
|
return types
|
|
}
|
|
|
|
func (s *Scope) getValue(name string, t reflect.Type) (v reflect.Value, ok bool) {
|
|
v, ok = s.values[key{name: name, t: t}]
|
|
return
|
|
}
|
|
|
|
func (s *Scope) getDecoratedValue(name string, t reflect.Type) (v reflect.Value, ok bool) {
|
|
v, ok = s.decoratedValues[key{name: name, t: t}]
|
|
return
|
|
}
|
|
|
|
func (s *Scope) setValue(name string, t reflect.Type, v reflect.Value) {
|
|
s.values[key{name: name, t: t}] = v
|
|
}
|
|
|
|
func (s *Scope) setDecoratedValue(name string, t reflect.Type, v reflect.Value) {
|
|
s.decoratedValues[key{name: name, t: t}] = v
|
|
}
|
|
|
|
func (s *Scope) getValueGroup(name string, t reflect.Type) []reflect.Value {
|
|
items := s.groups[key{group: name, t: t}]
|
|
// shuffle the list so users don't rely on the ordering of grouped values
|
|
return shuffledCopy(s.rand, items)
|
|
}
|
|
|
|
func (s *Scope) getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool) {
|
|
items, ok := s.decoratedGroups[key{group: name, t: t}]
|
|
return items, ok
|
|
}
|
|
|
|
func (s *Scope) submitGroupedValue(name string, t reflect.Type, v reflect.Value) {
|
|
k := key{group: name, t: t}
|
|
s.groups[k] = append(s.groups[k], v)
|
|
}
|
|
|
|
func (s *Scope) submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value) {
|
|
k := key{group: name, t: t}
|
|
s.decoratedGroups[k] = v
|
|
}
|
|
|
|
func (s *Scope) getValueProviders(name string, t reflect.Type) []provider {
|
|
return s.getProviders(key{name: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getGroupProviders(name string, t reflect.Type) []provider {
|
|
return s.getProviders(key{group: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getValueDecorator(name string, t reflect.Type) (decorator, bool) {
|
|
return s.getDecorators(key{name: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getGroupDecorator(name string, t reflect.Type) (decorator, bool) {
|
|
return s.getDecorators(key{group: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getDecorators(k key) (decorator, bool) {
|
|
d, found := s.decorators[k]
|
|
return d, found
|
|
}
|
|
|
|
func (s *Scope) getProviders(k key) []provider {
|
|
nodes := s.providers[k]
|
|
providers := make([]provider, len(nodes))
|
|
for i, n := range nodes {
|
|
providers[i] = n
|
|
}
|
|
return providers
|
|
}
|
|
|
|
func (s *Scope) getAllGroupProviders(name string, t reflect.Type) []provider {
|
|
return s.getAllProviders(key{group: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getAllValueProviders(name string, t reflect.Type) []provider {
|
|
return s.getAllProviders(key{name: name, t: t})
|
|
}
|
|
|
|
func (s *Scope) getAllProviders(k key) []provider {
|
|
allScopes := s.ancestors()
|
|
var providers []provider
|
|
for _, scope := range allScopes {
|
|
providers = append(providers, scope.getProviders(k)...)
|
|
}
|
|
return providers
|
|
}
|
|
|
|
func (s *Scope) invoker() invokerFn {
|
|
return s.invokerFn
|
|
}
|
|
|
|
// adds a new graphNode to this Scope and all of its descendent
|
|
// scope.
|
|
func (s *Scope) newGraphNode(wrapped interface{}, orders map[*Scope]int) {
|
|
orders[s] = s.gh.NewNode(wrapped)
|
|
for _, cs := range s.childScopes {
|
|
cs.newGraphNode(wrapped, orders)
|
|
}
|
|
}
|
|
|
|
func (s *Scope) cycleDetectedError(cycle []int) error {
|
|
var path []cycleErrPathEntry
|
|
for _, n := range cycle {
|
|
if n, ok := s.gh.Lookup(n).(*constructorNode); ok {
|
|
path = append(path, cycleErrPathEntry{
|
|
Key: key{
|
|
t: n.CType(),
|
|
},
|
|
Func: n.Location(),
|
|
})
|
|
}
|
|
}
|
|
return errCycleDetected{Path: path, scope: s}
|
|
}
|
|
|
|
// Returns the root Scope that can be reached from this Scope.
|
|
func (s *Scope) rootScope() *Scope {
|
|
curr := s
|
|
for curr.parentScope != nil {
|
|
curr = curr.parentScope
|
|
}
|
|
return curr
|
|
}
|
|
|
|
// String representation of the entire Scope
|
|
func (s *Scope) String() string {
|
|
b := &bytes.Buffer{}
|
|
fmt.Fprintln(b, "nodes: {")
|
|
for k, vs := range s.providers {
|
|
for _, v := range vs {
|
|
fmt.Fprintln(b, "\t", k, "->", v)
|
|
}
|
|
}
|
|
fmt.Fprintln(b, "}")
|
|
|
|
fmt.Fprintln(b, "values: {")
|
|
for k, v := range s.values {
|
|
fmt.Fprintln(b, "\t", k, "=>", v)
|
|
}
|
|
for k, vs := range s.groups {
|
|
for _, v := range vs {
|
|
fmt.Fprintln(b, "\t", k, "=>", v)
|
|
}
|
|
}
|
|
fmt.Fprintln(b, "}")
|
|
|
|
return b.String()
|
|
}
|