36533f7c3f
Fixes for new geth version
407 lines
11 KiB
Go
407 lines
11 KiB
Go
// Copyright (c) 2019 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 (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"go.uber.org/dig/internal/dot"
|
|
)
|
|
|
|
// The result interface represents a result produced by a constructor.
|
|
//
|
|
// The following implementations exist:
|
|
// resultList All values returned by the constructor.
|
|
// resultSingle A single value produced by a constructor.
|
|
// resultObject dig.Out struct where each field in the struct can be
|
|
// another result.
|
|
// resultGrouped A value produced by a constructor that is part of a value
|
|
// group.
|
|
type result interface {
|
|
// Extracts the values for this result from the provided value and
|
|
// stores them into the provided containerWriter.
|
|
//
|
|
// This MAY panic if the result does not consume a single value.
|
|
Extract(containerWriter, reflect.Value)
|
|
|
|
// DotResult returns a slice of dot.Result(s).
|
|
DotResult() []*dot.Result
|
|
}
|
|
|
|
var (
|
|
_ result = resultSingle{}
|
|
_ result = resultObject{}
|
|
_ result = resultList{}
|
|
_ result = resultGrouped{}
|
|
)
|
|
|
|
type resultOptions struct {
|
|
// If set, this is the name of the associated result value.
|
|
//
|
|
// For Result Objects, name:".." tags on fields override this.
|
|
Name string
|
|
Group string
|
|
}
|
|
|
|
// newResult builds a result from the given type.
|
|
func newResult(t reflect.Type, opts resultOptions) (result, error) {
|
|
switch {
|
|
case IsIn(t) || (t.Kind() == reflect.Ptr && IsIn(t.Elem())) || embedsType(t, _inPtrType):
|
|
return nil, fmt.Errorf("cannot provide parameter objects: %v embeds a dig.In", t)
|
|
case isError(t):
|
|
return nil, fmt.Errorf("cannot return an error here, return it from the constructor instead")
|
|
case IsOut(t):
|
|
return newResultObject(t, opts)
|
|
case embedsType(t, _outPtrType):
|
|
return nil, fmt.Errorf(
|
|
"cannot build a result object by embedding *dig.Out, embed dig.Out instead: "+
|
|
"%v embeds *dig.Out", t)
|
|
case t.Kind() == reflect.Ptr && IsOut(t.Elem()):
|
|
return nil, fmt.Errorf(
|
|
"cannot return a pointer to a result object, use a value instead: "+
|
|
"%v is a pointer to a struct that embeds dig.Out", t)
|
|
case len(opts.Group) > 0:
|
|
return resultGrouped{Type: t, Group: opts.Group}, nil
|
|
default:
|
|
return resultSingle{Type: t, Name: opts.Name}, nil
|
|
}
|
|
}
|
|
|
|
// resultVisitor visits every result in a result tree, allowing tracking state
|
|
// at each level.
|
|
type resultVisitor interface {
|
|
// Visit is called on the result being visited.
|
|
//
|
|
// If Visit returns a non-nil resultVisitor, that resultVisitor visits all
|
|
// the child results of this result.
|
|
Visit(result) resultVisitor
|
|
|
|
// AnnotateWithField is called on each field of a resultObject after
|
|
// visiting it but before walking its descendants.
|
|
//
|
|
// The same resultVisitor is used for all fields: the one returned upon
|
|
// visiting the resultObject.
|
|
//
|
|
// For each visited field, if AnnotateWithField returns a non-nil
|
|
// resultVisitor, it will be used to walk the result of that field.
|
|
AnnotateWithField(resultObjectField) resultVisitor
|
|
|
|
// AnnotateWithPosition is called with the index of each result of a
|
|
// resultList after vising it but before walking its descendants.
|
|
//
|
|
// The same resultVisitor is used for all results: the one returned upon
|
|
// visiting the resultList.
|
|
//
|
|
// For each position, if AnnotateWithPosition returns a non-nil
|
|
// resultVisitor, it will be used to walk the result at that index.
|
|
AnnotateWithPosition(idx int) resultVisitor
|
|
}
|
|
|
|
// walkResult walks the result tree for the given result with the provided
|
|
// visitor.
|
|
//
|
|
// resultVisitor.Visit will be called on the provided result and if a non-nil
|
|
// resultVisitor is received, it will be used to walk its descendants. If a
|
|
// resultObject or resultList was visited, AnnotateWithField and
|
|
// AnnotateWithPosition respectively will be called before visiting the
|
|
// descendants of that resultObject/resultList.
|
|
//
|
|
// This is very similar to how go/ast.Walk works.
|
|
func walkResult(r result, v resultVisitor) {
|
|
v = v.Visit(r)
|
|
if v == nil {
|
|
return
|
|
}
|
|
|
|
switch res := r.(type) {
|
|
case resultSingle, resultGrouped:
|
|
// No sub-results
|
|
case resultObject:
|
|
w := v
|
|
for _, f := range res.Fields {
|
|
if v := w.AnnotateWithField(f); v != nil {
|
|
walkResult(f.Result, v)
|
|
}
|
|
}
|
|
case resultList:
|
|
w := v
|
|
for i, r := range res.Results {
|
|
if v := w.AnnotateWithPosition(i); v != nil {
|
|
walkResult(r, v)
|
|
}
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf(
|
|
"It looks like you have found a bug in dig. "+
|
|
"Please file an issue at https://github.com/uber-go/dig/issues/ "+
|
|
"and provide the following message: "+
|
|
"received unknown result type %T", res))
|
|
}
|
|
}
|
|
|
|
// resultList holds all values returned by the constructor as results.
|
|
type resultList struct {
|
|
ctype reflect.Type
|
|
|
|
Results []result
|
|
|
|
// For each item at index i returned by the constructor, resultIndexes[i]
|
|
// is the index in .Results for the corresponding result object.
|
|
// resultIndexes[i] is -1 for errors returned by constructors.
|
|
resultIndexes []int
|
|
}
|
|
|
|
func (rl resultList) DotResult() []*dot.Result {
|
|
var types []*dot.Result
|
|
for _, result := range rl.Results {
|
|
types = append(types, result.DotResult()...)
|
|
}
|
|
return types
|
|
}
|
|
|
|
func newResultList(ctype reflect.Type, opts resultOptions) (resultList, error) {
|
|
rl := resultList{
|
|
ctype: ctype,
|
|
Results: make([]result, 0, ctype.NumOut()),
|
|
resultIndexes: make([]int, ctype.NumOut()),
|
|
}
|
|
|
|
resultIdx := 0
|
|
for i := 0; i < ctype.NumOut(); i++ {
|
|
t := ctype.Out(i)
|
|
if isError(t) {
|
|
rl.resultIndexes[i] = -1
|
|
continue
|
|
}
|
|
|
|
r, err := newResult(t, opts)
|
|
if err != nil {
|
|
return rl, errWrapf(err, "bad result %d", i+1)
|
|
}
|
|
|
|
rl.Results = append(rl.Results, r)
|
|
rl.resultIndexes[i] = resultIdx
|
|
resultIdx++
|
|
}
|
|
|
|
return rl, nil
|
|
}
|
|
|
|
func (resultList) Extract(containerWriter, reflect.Value) {
|
|
panic("It looks like you have found a bug in dig. " +
|
|
"Please file an issue at https://github.com/uber-go/dig/issues/ " +
|
|
"and provide the following message: " +
|
|
"resultList.Extract() must never be called")
|
|
}
|
|
|
|
func (rl resultList) ExtractList(cw containerWriter, values []reflect.Value) error {
|
|
for i, v := range values {
|
|
if resultIdx := rl.resultIndexes[i]; resultIdx >= 0 {
|
|
rl.Results[resultIdx].Extract(cw, v)
|
|
continue
|
|
}
|
|
|
|
if err, _ := v.Interface().(error); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// resultSingle is an explicit value produced by a constructor, optionally
|
|
// with a name.
|
|
//
|
|
// This object will be added to the graph as-is.
|
|
type resultSingle struct {
|
|
Name string
|
|
Type reflect.Type
|
|
}
|
|
|
|
func (rs resultSingle) DotResult() []*dot.Result {
|
|
return []*dot.Result{
|
|
{
|
|
Node: &dot.Node{
|
|
Type: rs.Type,
|
|
Name: rs.Name,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (rs resultSingle) Extract(cw containerWriter, v reflect.Value) {
|
|
cw.setValue(rs.Name, rs.Type, v)
|
|
}
|
|
|
|
// resultObject is a dig.Out struct where each field is another result.
|
|
//
|
|
// This object is not added to the graph. Its fields are interpreted as
|
|
// results and added to the graph if needed.
|
|
type resultObject struct {
|
|
Type reflect.Type
|
|
Fields []resultObjectField
|
|
}
|
|
|
|
func (ro resultObject) DotResult() []*dot.Result {
|
|
var types []*dot.Result
|
|
for _, field := range ro.Fields {
|
|
types = append(types, field.DotResult()...)
|
|
}
|
|
return types
|
|
}
|
|
|
|
func newResultObject(t reflect.Type, opts resultOptions) (resultObject, error) {
|
|
ro := resultObject{Type: t}
|
|
if len(opts.Name) > 0 {
|
|
return ro, fmt.Errorf(
|
|
"cannot specify a name for result objects: %v embeds dig.Out", t)
|
|
}
|
|
|
|
if len(opts.Group) > 0 {
|
|
return ro, fmt.Errorf(
|
|
"cannot specify a group for result objects: %v embeds dig.Out", t)
|
|
}
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
if f.Type == _outType {
|
|
// Skip over the dig.Out embed.
|
|
continue
|
|
}
|
|
|
|
rof, err := newResultObjectField(i, f, opts)
|
|
if err != nil {
|
|
return ro, errWrapf(err, "bad field %q of %v", f.Name, t)
|
|
}
|
|
|
|
ro.Fields = append(ro.Fields, rof)
|
|
}
|
|
return ro, nil
|
|
}
|
|
|
|
func (ro resultObject) Extract(cw containerWriter, v reflect.Value) {
|
|
for _, f := range ro.Fields {
|
|
f.Result.Extract(cw, v.Field(f.FieldIndex))
|
|
}
|
|
}
|
|
|
|
// resultObjectField is a single field inside a dig.Out struct.
|
|
type resultObjectField struct {
|
|
// Name of the field in the struct.
|
|
FieldName string
|
|
|
|
// Index of the field in the struct.
|
|
//
|
|
// We need to track this separately because not all fields of the struct
|
|
// map to results.
|
|
FieldIndex int
|
|
|
|
// Result produced by this field.
|
|
Result result
|
|
}
|
|
|
|
func (rof resultObjectField) DotResult() []*dot.Result {
|
|
return rof.Result.DotResult()
|
|
}
|
|
|
|
// newResultObjectField(i, f, opts) builds a resultObjectField from the field
|
|
// f at index i.
|
|
func newResultObjectField(idx int, f reflect.StructField, opts resultOptions) (resultObjectField, error) {
|
|
rof := resultObjectField{
|
|
FieldName: f.Name,
|
|
FieldIndex: idx,
|
|
}
|
|
|
|
var r result
|
|
switch {
|
|
case f.PkgPath != "":
|
|
return rof, fmt.Errorf(
|
|
"unexported fields not allowed in dig.Out, did you mean to export %q (%v)?", f.Name, f.Type)
|
|
|
|
case f.Tag.Get(_groupTag) != "":
|
|
var err error
|
|
r, err = newResultGrouped(f)
|
|
if err != nil {
|
|
return rof, err
|
|
}
|
|
|
|
default:
|
|
var err error
|
|
if name := f.Tag.Get(_nameTag); len(name) > 0 {
|
|
// can modify in-place because options are passed-by-value.
|
|
opts.Name = name
|
|
}
|
|
r, err = newResult(f.Type, opts)
|
|
if err != nil {
|
|
return rof, err
|
|
}
|
|
}
|
|
|
|
rof.Result = r
|
|
return rof, nil
|
|
}
|
|
|
|
// resultGrouped is a value produced by a constructor that is part of a result
|
|
// group.
|
|
//
|
|
// These will be produced as fields of a dig.Out struct.
|
|
type resultGrouped struct {
|
|
// Name of the group as specified in the `group:".."` tag.
|
|
Group string
|
|
|
|
// Type of value produced.
|
|
Type reflect.Type
|
|
}
|
|
|
|
func (rt resultGrouped) DotResult() []*dot.Result {
|
|
return []*dot.Result{
|
|
{
|
|
Node: &dot.Node{
|
|
Type: rt.Type,
|
|
Group: rt.Group,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// newResultGrouped(f) builds a new resultGrouped from the provided field.
|
|
func newResultGrouped(f reflect.StructField) (resultGrouped, error) {
|
|
rg := resultGrouped{Group: f.Tag.Get(_groupTag), Type: f.Type}
|
|
|
|
name := f.Tag.Get(_nameTag)
|
|
optional, _ := isFieldOptional(f)
|
|
switch {
|
|
case name != "":
|
|
return rg, fmt.Errorf(
|
|
"cannot use named values with value groups: name:%q provided with group:%q", name, rg.Group)
|
|
case optional:
|
|
return rg, errors.New("value groups cannot be optional")
|
|
}
|
|
|
|
return rg, nil
|
|
}
|
|
|
|
func (rt resultGrouped) Extract(cw containerWriter, v reflect.Value) {
|
|
cw.submitGroupedValue(rt.Group, rt.Type, v)
|
|
}
|