github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/caller/caller.go (about)

     1  // Copyright 2021 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  // Package caller provides utilities to inspect the caller of a function.
     6  package caller
     7  
     8  import (
     9  	"fmt"
    10  	"path"
    11  	"runtime"
    12  
    13  	"go.chromium.org/tast/core/internal/packages"
    14  )
    15  
    16  // isRedirect returns true if curFN and nextFN are the same function after
    17  // normalization, meaning curFN is a redirect to nextFN.
    18  func isRedirect(curFN, nextFN string) bool {
    19  	return packages.Normalize(curFN) == packages.Normalize(nextFN)
    20  }
    21  
    22  // FuncWithIgnore is Get with custom ignore function. If ignore returns true,
    23  // the function is ignored on counting the number of skips.
    24  // This method is exported for unit testing.
    25  func FuncWithIgnore(skip int, ignore func(curFN, nextFN string) bool) (*runtime.Func, uintptr) {
    26  	var callStack []string
    27  	skipCount := 0
    28  	for {
    29  		pc, _, _, ok := runtime.Caller(len(callStack))
    30  		if !ok {
    31  			panic("Could not decide the caller")
    32  		}
    33  		f := runtime.FuncForPC(pc)
    34  		callStack = append(callStack, f.Name())
    35  
    36  		if n := len(callStack); n >= 2 && ignore(callStack[n-1], callStack[n-2]) {
    37  			// Ignore the function on counting the number of skips.
    38  			continue
    39  		}
    40  		if skipCount == skip {
    41  			return f, pc
    42  		}
    43  		skipCount++
    44  	}
    45  }
    46  
    47  // Get is the implementation of the same-name public function.
    48  func Get(skip int) string {
    49  	f, _ := FuncWithIgnore(skip+1, isRedirect)
    50  	return f.Name()
    51  }
    52  
    53  // Func is similar to Get but returns *runtime.Func and program counter of
    54  // the caller.
    55  func Func(skip int) (*runtime.Func, uintptr) {
    56  	return FuncWithIgnore(skip+1, isRedirect)
    57  }
    58  
    59  // Check is the implementation of the same-name public function.
    60  // Check uses packages.Same to compare two packages.
    61  func Check(skip int, pkgs []string) {
    62  	caller := Get(skip + 1)
    63  
    64  	callerPkg, _ := packages.SplitFuncName(caller)
    65  	for _, pkg := range pkgs {
    66  		if packages.Same(callerPkg, pkg) {
    67  			return
    68  		}
    69  	}
    70  	callee := Get(skip)
    71  	panic(fmt.Sprintf(
    72  		"%s is not allowed to call %s; check the list in %s",
    73  		caller, path.Base(callee), path.Base(callee)))
    74  }