mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 04:53:42 +00:00
b8db79f6a6
This is important because once we move to compiling packages independently, SimpleDCE can't work anymore. Instead we'll have to compile all parts of a package and cache that for later reuse.
152 lines
3.7 KiB
Go
152 lines
3.7 KiB
Go
package ir
|
|
|
|
import (
|
|
"go/types"
|
|
|
|
"github.com/tinygo-org/tinygo/loader"
|
|
"golang.org/x/tools/go/ssa"
|
|
)
|
|
|
|
// This file provides a wrapper around go/ssa values and adds extra
|
|
// functionality to them.
|
|
|
|
// View on all functions, types, and globals in a program, with analysis
|
|
// results.
|
|
type Program struct {
|
|
Program *ssa.Program
|
|
LoaderProgram *loader.Program
|
|
mainPkg *ssa.Package
|
|
mainPath string
|
|
}
|
|
|
|
// Create and initialize a new *Program from a *ssa.Program.
|
|
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
|
program := lprogram.LoadSSA()
|
|
program.Build()
|
|
|
|
// Find the main package, which is a bit difficult when running a .go file
|
|
// directly.
|
|
mainPkg := program.ImportedPackage(mainPath)
|
|
if mainPkg == nil {
|
|
for _, pkgInfo := range program.AllPackages() {
|
|
if pkgInfo.Pkg.Name() == "main" {
|
|
if mainPkg != nil {
|
|
panic("more than one main package found")
|
|
}
|
|
mainPkg = pkgInfo
|
|
}
|
|
}
|
|
}
|
|
if mainPkg == nil {
|
|
panic("could not find main package")
|
|
}
|
|
|
|
return &Program{
|
|
Program: program,
|
|
LoaderProgram: lprogram,
|
|
mainPkg: mainPkg,
|
|
mainPath: mainPath,
|
|
}
|
|
}
|
|
|
|
// Packages returns a list of all packages, sorted by import order.
|
|
func (p *Program) Packages() []*ssa.Package {
|
|
packageList := []*ssa.Package{}
|
|
packageSet := map[string]struct{}{}
|
|
worklist := []string{"runtime", p.mainPath}
|
|
for len(worklist) != 0 {
|
|
pkgPath := worklist[0]
|
|
var pkg *ssa.Package
|
|
if pkgPath == p.mainPath {
|
|
pkg = p.mainPkg // necessary for compiling individual .go files
|
|
} else {
|
|
pkg = p.Program.ImportedPackage(pkgPath)
|
|
}
|
|
if pkg == nil {
|
|
// Non-SSA package (e.g. cgo).
|
|
packageSet[pkgPath] = struct{}{}
|
|
worklist = worklist[1:]
|
|
continue
|
|
}
|
|
if _, ok := packageSet[pkgPath]; ok {
|
|
// Package already in the final package list.
|
|
worklist = worklist[1:]
|
|
continue
|
|
}
|
|
|
|
unsatisfiedImports := make([]string, 0)
|
|
imports := pkg.Pkg.Imports()
|
|
for _, pkg := range imports {
|
|
if _, ok := packageSet[pkg.Path()]; ok {
|
|
continue
|
|
}
|
|
unsatisfiedImports = append(unsatisfiedImports, pkg.Path())
|
|
}
|
|
if len(unsatisfiedImports) == 0 {
|
|
// All dependencies of this package are satisfied, so add this
|
|
// package to the list.
|
|
packageList = append(packageList, pkg)
|
|
packageSet[pkgPath] = struct{}{}
|
|
worklist = worklist[1:]
|
|
} else {
|
|
// Prepend all dependencies to the worklist and reconsider this
|
|
// package (by not removing it from the worklist). At that point, it
|
|
// must be possible to add it to packageList.
|
|
worklist = append(unsatisfiedImports, worklist...)
|
|
}
|
|
}
|
|
|
|
return packageList
|
|
}
|
|
|
|
func (p *Program) MainPkg() *ssa.Package {
|
|
return p.mainPkg
|
|
}
|
|
|
|
// MethodSignature creates a readable version of a method signature (including
|
|
// the function name, excluding the receiver name). This string is used
|
|
// internally to match interfaces and to call the correct method on an
|
|
// interface. Examples:
|
|
//
|
|
// String() string
|
|
// Read([]byte) (int, error)
|
|
func MethodSignature(method *types.Func) string {
|
|
return method.Name() + signature(method.Type().(*types.Signature))
|
|
}
|
|
|
|
// Make a readable version of a function (pointer) signature.
|
|
// Examples:
|
|
//
|
|
// () string
|
|
// (string, int) (int, error)
|
|
func signature(sig *types.Signature) string {
|
|
s := ""
|
|
if sig.Params().Len() == 0 {
|
|
s += "()"
|
|
} else {
|
|
s += "("
|
|
for i := 0; i < sig.Params().Len(); i++ {
|
|
if i > 0 {
|
|
s += ", "
|
|
}
|
|
s += sig.Params().At(i).Type().String()
|
|
}
|
|
s += ")"
|
|
}
|
|
if sig.Results().Len() == 0 {
|
|
// keep as-is
|
|
} else if sig.Results().Len() == 1 {
|
|
s += " " + sig.Results().At(0).Type().String()
|
|
} else {
|
|
s += " ("
|
|
for i := 0; i < sig.Results().Len(); i++ {
|
|
if i > 0 {
|
|
s += ", "
|
|
}
|
|
s += sig.Results().At(i).Type().String()
|
|
}
|
|
s += ")"
|
|
}
|
|
return s
|
|
}
|