forked from TrueCloudLab/neoneo-go
6baed7a010
Previously this declarations were ignored which resulted in runtime errors, because VM's nil is an element of primitive type and can't be converted to an array.
235 lines
5.1 KiB
Go
235 lines
5.1 KiB
Go
package compiler
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/constant"
|
|
"go/types"
|
|
|
|
"golang.org/x/tools/go/loader"
|
|
)
|
|
|
|
var (
|
|
// Go language builtin functions and custom builtin utility functions.
|
|
builtinFuncs = []string{
|
|
"len", "append", "SHA256",
|
|
"SHA1", "Hash256", "Hash160",
|
|
"VerifySignature", "AppCall",
|
|
"FromAddress", "Equals",
|
|
"panic",
|
|
}
|
|
)
|
|
|
|
// typeAndValueForField returns a zero initialized typeAndValue for the given type.Var.
|
|
func typeAndValueForField(fld *types.Var) (types.TypeAndValue, error) {
|
|
switch t := fld.Type().(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.Int:
|
|
return types.TypeAndValue{
|
|
Type: t,
|
|
Value: constant.MakeInt64(0),
|
|
}, nil
|
|
case types.String:
|
|
return types.TypeAndValue{
|
|
Type: t,
|
|
Value: constant.MakeString(""),
|
|
}, nil
|
|
case types.Bool, types.UntypedBool:
|
|
return types.TypeAndValue{
|
|
Type: t,
|
|
Value: constant.MakeBool(false),
|
|
}, nil
|
|
default:
|
|
return types.TypeAndValue{}, fmt.Errorf("could not initialize struct field %s to zero, type: %s", fld.Name(), t)
|
|
}
|
|
}
|
|
return types.TypeAndValue{}, nil
|
|
}
|
|
|
|
// countGlobals counts the global variables in the program to add
|
|
// them with the stack size of the function.
|
|
func countGlobals(f ast.Node) (i int64) {
|
|
ast.Inspect(f, func(node ast.Node) bool {
|
|
switch node.(type) {
|
|
// Skip all function declarations.
|
|
case *ast.FuncDecl:
|
|
return false
|
|
// After skipping all funcDecls we are sure that each value spec
|
|
// is a global declared variable or constant.
|
|
case *ast.ValueSpec:
|
|
i++
|
|
}
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
// isIdentBool looks if the given ident is a boolean.
|
|
func isIdentBool(ident *ast.Ident) bool {
|
|
return ident.Name == "true" || ident.Name == "false"
|
|
}
|
|
|
|
// isExprNil looks if the given expression is a `nil`.
|
|
func isExprNil(e ast.Expr) bool {
|
|
v, ok := e.(*ast.Ident)
|
|
return ok && v.Name == "nil"
|
|
}
|
|
|
|
// makeBoolFromIdent creates a bool type from an *ast.Ident.
|
|
func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) (types.TypeAndValue, error) {
|
|
var b bool
|
|
switch ident.Name {
|
|
case "true":
|
|
b = true
|
|
case "false":
|
|
b = false
|
|
default:
|
|
return types.TypeAndValue{}, fmt.Errorf("givent identifier cannot be converted to a boolean => %s", ident.Name)
|
|
}
|
|
return types.TypeAndValue{
|
|
Type: tinfo.ObjectOf(ident).Type(),
|
|
Value: constant.MakeBool(b),
|
|
}, nil
|
|
}
|
|
|
|
// resolveEntryPoint returns the function declaration of the entrypoint and the corresponding file.
|
|
func resolveEntryPoint(entry string, pkg *loader.PackageInfo) (*ast.FuncDecl, *ast.File) {
|
|
var (
|
|
main *ast.FuncDecl
|
|
file *ast.File
|
|
)
|
|
for _, f := range pkg.Files {
|
|
ast.Inspect(f, func(n ast.Node) bool {
|
|
switch t := n.(type) {
|
|
case *ast.FuncDecl:
|
|
if t.Name.Name == entry {
|
|
main = t
|
|
file = f
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
return main, file
|
|
}
|
|
|
|
// indexOfStruct returns the index of the given field inside that struct.
|
|
// If the struct does not contain that field it will return -1.
|
|
func indexOfStruct(strct *types.Struct, fldName string) int {
|
|
for i := 0; i < strct.NumFields(); i++ {
|
|
if strct.Field(i).Name() == fldName {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
type funcUsage map[string]bool
|
|
|
|
func (f funcUsage) funcUsed(name string) bool {
|
|
_, ok := f[name]
|
|
return ok
|
|
}
|
|
|
|
// hasReturnStmt looks if the given FuncDecl has a return statement.
|
|
func hasReturnStmt(decl ast.Node) (b bool) {
|
|
ast.Inspect(decl, func(node ast.Node) bool {
|
|
if _, ok := node.(*ast.ReturnStmt); ok {
|
|
b = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
func analyzeFuncUsage(pkgs map[*types.Package]*loader.PackageInfo) funcUsage {
|
|
usage := funcUsage{}
|
|
|
|
for _, pkg := range pkgs {
|
|
for _, f := range pkg.Files {
|
|
ast.Inspect(f, func(node ast.Node) bool {
|
|
switch n := node.(type) {
|
|
case *ast.CallExpr:
|
|
switch t := n.Fun.(type) {
|
|
case *ast.Ident:
|
|
usage[t.Name] = true
|
|
case *ast.SelectorExpr:
|
|
usage[t.Sel.Name] = true
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
return usage
|
|
}
|
|
|
|
func isBuiltin(expr ast.Expr) bool {
|
|
var name string
|
|
|
|
switch t := expr.(type) {
|
|
case *ast.Ident:
|
|
name = t.Name
|
|
case *ast.SelectorExpr:
|
|
name = t.Sel.Name
|
|
default:
|
|
return false
|
|
}
|
|
|
|
for _, n := range builtinFuncs {
|
|
if name == n {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *codegen) isCompoundArrayType(t ast.Expr) bool {
|
|
switch s := t.(type) {
|
|
case *ast.ArrayType:
|
|
return true
|
|
case *ast.Ident:
|
|
arr, ok := c.typeInfo.Types[s].Type.Underlying().(*types.Slice)
|
|
return ok && !isByte(arr.Elem())
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isByte(t types.Type) bool {
|
|
e, ok := t.(*types.Basic)
|
|
return ok && e.Kind() == types.Byte
|
|
}
|
|
|
|
func isByteArray(lit *ast.CompositeLit, tInfo *types.Info) bool {
|
|
if len(lit.Elts) == 0 {
|
|
if typ, ok := lit.Type.(*ast.ArrayType); ok {
|
|
if name, ok := typ.Elt.(*ast.Ident); ok {
|
|
return name.Name == "byte" || name.Name == "uint8"
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
typ := tInfo.Types[lit.Elts[0]].Type.Underlying()
|
|
return isByte(typ)
|
|
}
|
|
|
|
func isSyscall(fun *funcScope) bool {
|
|
if fun.selector == nil {
|
|
return false
|
|
}
|
|
_, ok := syscalls[fun.selector.Name][fun.name]
|
|
return ok
|
|
}
|
|
|
|
func isByteArrayType(t types.Type) bool {
|
|
return t.String() == "[]byte"
|
|
}
|
|
|
|
func isStringType(t types.Type) bool {
|
|
return t.String() == "string"
|
|
}
|