2018-02-19 09:24:28 +00:00
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
2023-08-17 09:39:06 +00:00
|
|
|
"fmt"
|
2018-02-19 09:24:28 +00:00
|
|
|
"go/ast"
|
2020-06-08 11:16:41 +00:00
|
|
|
"go/types"
|
2018-02-19 09:24:28 +00:00
|
|
|
)
|
|
|
|
|
2018-02-25 12:26:56 +00:00
|
|
|
// A funcScope represents the scope within the function context.
|
2022-04-20 18:30:09 +00:00
|
|
|
// It holds all the local variables along with the initialized struct positions.
|
2018-02-19 09:24:28 +00:00
|
|
|
type funcScope struct {
|
2019-10-22 14:56:03 +00:00
|
|
|
// Identifier of the function.
|
2018-02-19 09:24:28 +00:00
|
|
|
name string
|
|
|
|
|
2018-08-22 07:51:35 +00:00
|
|
|
// Selector of the function if there is any. Only functions imported
|
|
|
|
// from other packages should have a selector.
|
2022-07-11 16:28:15 +00:00
|
|
|
selector ast.Expr
|
2018-08-22 07:51:35 +00:00
|
|
|
|
2018-02-25 12:26:56 +00:00
|
|
|
// The declaration of the function in the AST. Nil if this scope is not a function.
|
2018-02-19 09:24:28 +00:00
|
|
|
decl *ast.FuncDecl
|
|
|
|
|
2020-06-08 11:16:41 +00:00
|
|
|
// Package where the function is defined.
|
|
|
|
pkg *types.Package
|
|
|
|
|
2021-02-04 12:41:00 +00:00
|
|
|
file *ast.File
|
|
|
|
|
2018-02-25 12:26:56 +00:00
|
|
|
// Program label of the scope
|
2020-02-21 08:50:03 +00:00
|
|
|
label uint16
|
2018-02-19 09:24:28 +00:00
|
|
|
|
2020-03-31 12:56:10 +00:00
|
|
|
// Range of opcodes corresponding to the function.
|
|
|
|
rng DebugRange
|
2020-04-02 13:36:11 +00:00
|
|
|
// Variables together with it's type in neo-vm.
|
|
|
|
variables []string
|
2020-03-31 12:56:10 +00:00
|
|
|
|
2020-08-20 05:34:14 +00:00
|
|
|
// deferStack is a stack containing encountered `defer` statements.
|
|
|
|
deferStack []deferInfo
|
|
|
|
|
2018-02-25 12:26:56 +00:00
|
|
|
// Local variables
|
2020-06-29 13:45:27 +00:00
|
|
|
vars varScope
|
2018-02-19 09:24:28 +00:00
|
|
|
|
2018-02-24 09:06:48 +00:00
|
|
|
// voidCalls are basically functions that return their value
|
|
|
|
// into nothing. The stack has their return value but there
|
|
|
|
// is nothing that consumes it. We need to keep track of
|
|
|
|
// these functions so we can cleanup (drop) the returned
|
|
|
|
// value from the stack. We also need to add every voidCall
|
|
|
|
// return value to the stack size.
|
|
|
|
voidCalls map[*ast.CallExpr]bool
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// Local variable counter.
|
2018-02-19 09:24:28 +00:00
|
|
|
i int
|
|
|
|
}
|
|
|
|
|
2020-08-20 05:34:14 +00:00
|
|
|
type deferInfo struct {
|
2020-08-21 12:37:46 +00:00
|
|
|
catchLabel uint16
|
2020-08-20 05:34:14 +00:00
|
|
|
finallyLabel uint16
|
|
|
|
expr *ast.CallExpr
|
2022-02-03 10:20:40 +00:00
|
|
|
localIndex int
|
2020-08-20 05:34:14 +00:00
|
|
|
}
|
|
|
|
|
2021-04-28 08:20:41 +00:00
|
|
|
const (
|
|
|
|
finallyVarName = "<finally>"
|
|
|
|
exceptionVarName = "<exception>"
|
|
|
|
)
|
|
|
|
|
2020-07-29 14:20:00 +00:00
|
|
|
func (c *codegen) newFuncScope(decl *ast.FuncDecl, label uint16) *funcScope {
|
2020-05-06 15:10:20 +00:00
|
|
|
var name string
|
|
|
|
if decl.Name != nil {
|
|
|
|
name = decl.Name.Name
|
|
|
|
}
|
2018-02-19 09:24:28 +00:00
|
|
|
return &funcScope{
|
2020-05-06 15:10:20 +00:00
|
|
|
name: name,
|
2018-02-24 09:06:48 +00:00
|
|
|
decl: decl,
|
|
|
|
label: label,
|
2021-12-02 14:44:53 +00:00
|
|
|
pkg: c.currPkg.Types,
|
2020-06-29 13:45:27 +00:00
|
|
|
vars: newVarScope(),
|
2018-02-24 09:06:48 +00:00
|
|
|
voidCalls: map[*ast.CallExpr]bool{},
|
2020-04-02 13:36:11 +00:00
|
|
|
variables: []string{},
|
2018-02-24 09:06:48 +00:00
|
|
|
i: -1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 14:20:00 +00:00
|
|
|
func (c *codegen) getFuncNameFromDecl(pkgPath string, decl *ast.FuncDecl) string {
|
|
|
|
name := decl.Name.Name
|
|
|
|
if decl.Recv != nil {
|
2020-09-02 19:39:20 +00:00
|
|
|
switch t := decl.Recv.List[0].Type.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
name = t.Name + "." + name
|
|
|
|
case *ast.StarExpr:
|
2023-08-17 09:39:06 +00:00
|
|
|
switch t.X.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
name = t.X.(*ast.Ident).Name + "." + name
|
|
|
|
case *ast.IndexExpr:
|
|
|
|
// Generic func declaration receiver: func (x *Pointer[T]) Load() *T
|
|
|
|
name = t.X.(*ast.IndexExpr).X.(*ast.Ident).Name + "." + name
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("unexpected function `%s` receiver type: %T", name, t.X))
|
|
|
|
}
|
|
|
|
case *ast.IndexExpr:
|
|
|
|
switch t.X.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
// Generic func declaration receiver: func (x Pointer[T]) Load() *T
|
|
|
|
name = t.X.(*ast.Ident).Name + "." + name
|
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("unexpected function `%s` receiver type: %T", name, t.X))
|
|
|
|
}
|
2020-09-02 19:39:20 +00:00
|
|
|
}
|
2020-07-29 14:20:00 +00:00
|
|
|
}
|
|
|
|
return c.getIdentName(pkgPath, name)
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// analyzeVoidCalls checks for functions that are not assigned
|
2018-02-24 09:06:48 +00:00
|
|
|
// and therefore we need to cleanup the return value from the stack.
|
|
|
|
func (c *funcScope) analyzeVoidCalls(node ast.Node) bool {
|
2020-10-19 07:43:45 +00:00
|
|
|
est, ok := node.(*ast.ExprStmt)
|
|
|
|
if ok {
|
|
|
|
ce, ok := est.X.(*ast.CallExpr)
|
2020-10-12 16:00:07 +00:00
|
|
|
if ok {
|
2020-10-19 07:43:45 +00:00
|
|
|
c.voidCalls[ce] = true
|
2020-09-24 17:29:52 +00:00
|
|
|
}
|
2018-02-19 09:24:28 +00:00
|
|
|
}
|
2018-02-24 09:06:48 +00:00
|
|
|
return true
|
2018-02-19 09:24:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 08:54:35 +00:00
|
|
|
func (c *funcScope) countArgs() int {
|
|
|
|
n := c.decl.Type.Params.NumFields()
|
2018-02-19 09:24:28 +00:00
|
|
|
if c.decl.Recv != nil {
|
2020-05-07 08:54:35 +00:00
|
|
|
n += c.decl.Recv.NumFields()
|
2018-02-19 09:24:28 +00:00
|
|
|
}
|
2020-05-07 08:54:35 +00:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// newVariable creates a new local variable or argument in the scope of the function.
|
|
|
|
func (c *funcScope) newVariable(t varType, name string) int {
|
2020-06-29 13:45:27 +00:00
|
|
|
return c.vars.newVariable(t, name)
|
2018-02-19 09:24:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 08:54:35 +00:00
|
|
|
// newLocal creates a new local variable into the scope of the function.
|
|
|
|
func (c *funcScope) newLocal(name string) int {
|
|
|
|
return c.newVariable(varLocal, name)
|
2018-02-19 09:24:28 +00:00
|
|
|
}
|