go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luciexe/build/reflect.go (about)

     1  // Copyright 2020 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package build
    16  
    17  import (
    18  	"context"
    19  	"reflect"
    20  
    21  	"google.golang.org/protobuf/proto"
    22  	"google.golang.org/protobuf/reflect/protoreflect"
    23  )
    24  
    25  var (
    26  	ctxType          = reflect.TypeOf((*context.Context)(nil)).Elem()
    27  	protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
    28  	errorType        = reflect.TypeOf((*error)(nil)).Elem()
    29  )
    30  
    31  func cmpArgsProtoT(perr error, expected []reflect.Type, actual func(int) reflect.Type) (ret reflect.Type) {
    32  	for i, ex := range expected {
    33  		cur := actual(i)
    34  		if ex == protoMessageType {
    35  			if !cur.Implements(ex) {
    36  				panic(perr)
    37  			}
    38  			ret = cur
    39  		} else if cur != ex {
    40  			panic(perr)
    41  		}
    42  	}
    43  	return
    44  }
    45  
    46  func derefFnPtr(perr error, fnptr any, in, out []reflect.Type) (fn reflect.Value, msgT protoreflect.Message) {
    47  	val := reflect.ValueOf(fnptr)
    48  	if val.Kind() != reflect.Ptr {
    49  		panic(perr)
    50  	}
    51  
    52  	fn = val.Elem()
    53  	var protoType reflect.Type
    54  
    55  	fnT := fn.Type()
    56  	if fnT.Kind() != reflect.Func {
    57  		panic(perr)
    58  	}
    59  
    60  	if fnT.NumIn() != len(in) {
    61  		panic(perr)
    62  	}
    63  	if pT := cmpArgsProtoT(perr, in, fnT.In); pT != nil {
    64  		protoType = pT
    65  	}
    66  
    67  	if fnT.NumOut() != len(out) {
    68  		panic(perr)
    69  	}
    70  	if pT := cmpArgsProtoT(perr, out, fnT.Out); pT != nil {
    71  		protoType = pT
    72  	}
    73  
    74  	return fn, reflect.New(protoType.Elem()).Interface().(proto.Message).ProtoReflect()
    75  }