packer-cn/vendor/honnef.co/go/tools/ir/exits.go

272 lines
7.0 KiB
Go
Raw Normal View History

Drop the iso_checksum_type & iso_checksum_url fields (#8437) * Drop the iso_checksum_type & iso_checksum_url fields In favor of simply using iso_checksum that will know what to do. * fix after master merge * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * remove checksum lowercasing tests * Update builder_test.go * Update builder_test.go * better docs * Update builder_test.go * even better docs * Update config.go * Update builder_test.go * Update step_create_vmx_test.go * make generate * better docs * fix imports * up tests * Update _ISOConfig-required.html.md * Update builder_test.go * don't use sha1.Sum("none") as a caching path * Update builder_test.go * better docs * Update iso_config_test.go remove ISOChecksumType/ISOChecksumURL references * Update step_download_test.go * add iso_checksum_url and iso_checksum_type fixers + tests * add concrete examples of checksum values * add examples of checksumming from local file * update go-getter dep * up deps * use new go-getter version * up ESX5Driver.VerifyChecksum: use go-getter's checksumming * ISOConfig.Prepare: get checksum there in case we need it as a string in ESX5Driver.VerifyChecksum * Update iso_config.go * get go-getter from v2 branch * Update driver_esx5.go add more comments * Update driver_esx5.go * show better error message when the checksum is invalid * Update builder_test.go put in a valid checksum to fix tests, checksum is md5("packer") * Update builder_test.go test invalid and valid checksum * more test updating * fix default md5 string to be a valid md5 * TestChecksumFileNameMixedCaseBug: use 'file:' prefix for file checksumming * Update iso_config_test.go * Update iso_config_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update CHANGELOG.md * Update CHANGELOG.md * Update go.mod * Update go.mod * Update CHANGELOG.md
2020-05-28 05:02:09 -04:00
package ir
import (
"go/types"
)
func (b *builder) buildExits(fn *Function) {
if obj := fn.Object(); obj != nil {
switch obj.Pkg().Path() {
case "runtime":
switch obj.Name() {
case "exit":
fn.WillExit = true
return
case "throw":
fn.WillExit = true
return
case "Goexit":
fn.WillUnwind = true
return
}
case "github.com/sirupsen/logrus":
switch obj.(*types.Func).FullName() {
case "(*github.com/sirupsen/logrus.Logger).Exit":
// Technically, this method does not unconditionally exit
// the process. It dynamically calls a function stored in
// the logger. If the function is nil, it defaults to
// os.Exit.
//
// The main intent of this method is to terminate the
// process, and that's what the vast majority of people
// will use it for. We'll happily accept some false
// negatives to avoid a lot of false positives.
fn.WillExit = true
return
case "(*github.com/sirupsen/logrus.Logger).Panic",
"(*github.com/sirupsen/logrus.Logger).Panicf",
"(*github.com/sirupsen/logrus.Logger).Panicln":
// These methods will always panic, but that's not
// statically known from the code alone, because they
// take a detour through the generic Log methods.
fn.WillUnwind = true
return
case "(*github.com/sirupsen/logrus.Entry).Panicf",
"(*github.com/sirupsen/logrus.Entry).Panicln":
// Entry.Panic has an explicit panic, but Panicf and
// Panicln do not, relying fully on the generic Log
// method.
fn.WillUnwind = true
return
case "(*github.com/sirupsen/logrus.Logger).Log",
"(*github.com/sirupsen/logrus.Logger).Logf",
"(*github.com/sirupsen/logrus.Logger).Logln":
// TODO(dh): we cannot handle these case. Whether they
// exit or unwind depends on the level, which is set
// via the first argument. We don't currently support
// call-site-specific exit information.
}
}
}
buildDomTree(fn)
isRecoverCall := func(instr Instruction) bool {
if instr, ok := instr.(*Call); ok {
if builtin, ok := instr.Call.Value.(*Builtin); ok {
if builtin.Name() == "recover" {
return true
}
}
}
return false
}
// All panics branch to the exit block, which means that if every
// possible path through the function panics, then all
// predecessors of the exit block must panic.
willPanic := true
for _, pred := range fn.Exit.Preds {
if _, ok := pred.Control().(*Panic); !ok {
willPanic = false
}
}
if willPanic {
recovers := false
recoverLoop:
for _, u := range fn.Blocks {
for _, instr := range u.Instrs {
if instr, ok := instr.(*Defer); ok {
call := instr.Call.StaticCallee()
if call == nil {
// not a static call, so we can't be sure the
// deferred call isn't calling recover
recovers = true
break recoverLoop
}
if len(call.Blocks) == 0 {
// external function, we don't know what's
// happening inside it
//
// TODO(dh): this includes functions from
// imported packages, due to how go/analysis
// works. We could introduce another fact,
// like we've done for exiting and unwinding,
// but it doesn't seem worth it. Virtually all
// uses of recover will be in closures.
recovers = true
break recoverLoop
}
for _, y := range call.Blocks {
for _, instr2 := range y.Instrs {
if isRecoverCall(instr2) {
recovers = true
break recoverLoop
}
}
}
}
}
}
if !recovers {
fn.WillUnwind = true
return
}
}
// TODO(dh): don't check that any specific call dominates the exit
// block. instead, check that all calls combined cover every
// possible path through the function.
exits := NewBlockSet(len(fn.Blocks))
unwinds := NewBlockSet(len(fn.Blocks))
for _, u := range fn.Blocks {
for _, instr := range u.Instrs {
if instr, ok := instr.(CallInstruction); ok {
switch instr.(type) {
case *Defer, *Call:
default:
continue
}
if instr.Common().IsInvoke() {
// give up
return
}
var call *Function
switch instr.Common().Value.(type) {
case *Function, *MakeClosure:
call = instr.Common().StaticCallee()
case *Builtin:
// the only builtins that affect control flow are
// panic and recover, and we've already handled
// those
continue
default:
// dynamic dispatch
return
}
// buildFunction is idempotent. if we're part of a
// (mutually) recursive call chain, then buildFunction
// will immediately return, and fn.WillExit will be false.
if call.Package() == fn.Package() {
b.buildFunction(call)
}
dom := u.Dominates(fn.Exit)
if call.WillExit {
if dom {
fn.WillExit = true
return
}
exits.Add(u)
} else if call.WillUnwind {
if dom {
fn.WillUnwind = true
return
}
unwinds.Add(u)
}
}
}
}
// depth-first search trying to find a path to the exit block that
// doesn't cross any of the blacklisted blocks
seen := NewBlockSet(len(fn.Blocks))
var findPath func(root *BasicBlock, bl *BlockSet) bool
findPath = func(root *BasicBlock, bl *BlockSet) bool {
if root == fn.Exit {
return true
}
if seen.Has(root) {
return false
}
if bl.Has(root) {
return false
}
seen.Add(root)
for _, succ := range root.Succs {
if findPath(succ, bl) {
return true
}
}
return false
}
if exits.Num() > 0 {
if !findPath(fn.Blocks[0], exits) {
fn.WillExit = true
return
}
}
if unwinds.Num() > 0 {
seen.Clear()
if !findPath(fn.Blocks[0], unwinds) {
fn.WillUnwind = true
return
}
}
}
func (b *builder) addUnreachables(fn *Function) {
for _, bb := range fn.Blocks {
for i, instr := range bb.Instrs {
if instr, ok := instr.(*Call); ok {
var call *Function
switch v := instr.Common().Value.(type) {
case *Function:
call = v
case *MakeClosure:
call = v.Fn.(*Function)
}
if call == nil {
continue
}
if call.Package() == fn.Package() {
// make sure we have information on all functions in this package
b.buildFunction(call)
}
if call.WillExit {
// This call will cause the process to terminate.
// Remove remaining instructions in the block and
// replace any control flow with Unreachable.
for _, succ := range bb.Succs {
succ.removePred(bb)
}
bb.Succs = bb.Succs[:0]
bb.Instrs = bb.Instrs[:i+1]
bb.emit(new(Unreachable), instr.Source())
addEdge(bb, fn.Exit)
break
} else if call.WillUnwind {
// This call will cause the goroutine to terminate
// and defers to run (i.e. a panic or
// runtime.Goexit). Remove remaining instructions
// in the block and replace any control flow with
// an unconditional jump to the exit block.
for _, succ := range bb.Succs {
succ.removePred(bb)
}
bb.Succs = bb.Succs[:0]
bb.Instrs = bb.Instrs[:i+1]
bb.emit(new(Jump), instr.Source())
addEdge(bb, fn.Exit)
break
}
}
}
}
}