forked from TrueCloudLab/neoneo-go
Optimisations and API changes for smart contracts (#67)
* support VM to pass method and arguments to a script. * added support for type assertions in smartcontracts. * added native vm support for print. * moved VM API packages to vm -> API * reverted the native Print opcode in favor of runtime.Log * added support for registering custom interop hooks in the VM. * Updated README * Updated compiler with @OPTIMIZE tags * Moved more tests to VM package. * optimized and refactored compiler and vm API * updated README with new smartcontract apis * bumped version
This commit is contained in:
parent
b2021c126e
commit
4bd5b2812e
29 changed files with 655 additions and 494 deletions
|
@ -118,19 +118,6 @@ func (c *codegen) emitStoreStructField(i int) {
|
|||
emitOpcode(c.prog, vm.Osetitem)
|
||||
}
|
||||
|
||||
func (c *codegen) emitSyscallReturn() {
|
||||
emitOpcode(c.prog, vm.Ojmp)
|
||||
emitOpcode(c.prog, vm.Opcode(0x03))
|
||||
emitOpcode(c.prog, vm.Opush0)
|
||||
|
||||
emitInt(c.prog, int64(0))
|
||||
|
||||
emitOpcode(c.prog, vm.Onop)
|
||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||
emitOpcode(c.prog, vm.Odrop)
|
||||
emitOpcode(c.prog, vm.Oret)
|
||||
}
|
||||
|
||||
// convertGlobals will traverse the AST and only convert global declarations.
|
||||
// If we call this in convertFuncDecl then it will load all global variables
|
||||
// into the scope of the function.
|
||||
|
@ -160,7 +147,7 @@ func (c *codegen) convertFuncDecl(file *ast.File, decl *ast.FuncDecl) {
|
|||
}
|
||||
|
||||
c.scope = f
|
||||
ast.Inspect(decl, c.scope.analyzeVoidCalls)
|
||||
ast.Inspect(decl, c.scope.analyzeVoidCalls) // @OPTIMIZE
|
||||
|
||||
// All globals copied into the scope of the function need to be added
|
||||
// to the stack size of the function.
|
||||
|
@ -193,17 +180,19 @@ func (c *codegen) convertFuncDecl(file *ast.File, decl *ast.FuncDecl) {
|
|||
l := c.scope.newLocal(name)
|
||||
c.emitStoreLocal(l)
|
||||
}
|
||||
|
||||
// If this function is a syscall we will manipulate the return value to 0.
|
||||
// All the syscalls are just signatures functions and bring no real return value.
|
||||
// The return values you will find in the smartcontract package is just for
|
||||
// satisfying the typechecker and the user experience.
|
||||
if isSyscall(f.name) {
|
||||
c.emitSyscallReturn()
|
||||
} else {
|
||||
// After loading the arguments we can convert the globals into the scope of the function.
|
||||
// Load in all the global variables in to the scope of the function.
|
||||
// This is not necessary for syscalls.
|
||||
if !isSyscall(f.name) {
|
||||
c.convertGlobals(file)
|
||||
ast.Walk(c, decl.Body)
|
||||
}
|
||||
|
||||
ast.Walk(c, decl.Body)
|
||||
|
||||
// If this function returs the void (no return stmt) we will cleanup its junk on the stack.
|
||||
if !hasReturnStmt(decl) {
|
||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||
emitOpcode(c.prog, vm.Odrop)
|
||||
emitOpcode(c.prog, vm.Oret)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,6 +257,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
|
||||
// @OPTIMIZE: We could skip these 3 instructions for each return statement.
|
||||
// To be backwards compatible we will put them them in.
|
||||
// See issue #65 (https://github.com/CityOfZion/neo-go/issues/65)
|
||||
l := c.newLabel()
|
||||
emitJmp(c.prog, vm.Ojmp, int16(l))
|
||||
c.setLabel(l)
|
||||
|
@ -276,9 +266,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
ast.Walk(c, n.Results[0])
|
||||
}
|
||||
|
||||
emitOpcode(c.prog, vm.Onop)
|
||||
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE
|
||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||
emitOpcode(c.prog, vm.Odrop)
|
||||
emitOpcode(c.prog, vm.Odrop) // Cleanup the stack.
|
||||
emitOpcode(c.prog, vm.Oret)
|
||||
return nil
|
||||
|
||||
|
@ -439,8 +429,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
}
|
||||
|
||||
// If we are not assigning this function to a variable we need to drop
|
||||
// the top stack item. It's not a void but you get the point \o/.
|
||||
if _, ok := c.scope.voidCalls[n]; ok && !isNoRetSyscall(f.name) {
|
||||
// (cleanup) the top stack item. It's not a void but you get the point \o/.
|
||||
if _, ok := c.scope.voidCalls[n]; ok {
|
||||
emitOpcode(c.prog, vm.Odrop)
|
||||
}
|
||||
return nil
|
||||
|
@ -518,6 +508,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
c.setLabel(fend)
|
||||
|
||||
return nil
|
||||
|
||||
// We dont really care about assertions for the core logic.
|
||||
// The only thing we need is to please the compiler type checking.
|
||||
// For this to work properly, we only need to walk the expression
|
||||
// not the assertion type.
|
||||
case *ast.TypeAssertExpr:
|
||||
ast.Walk(c, n.X)
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
@ -528,7 +526,7 @@ func (c *codegen) convertSyscall(name string) {
|
|||
log.Fatalf("unknown VM syscall api: %s", name)
|
||||
}
|
||||
emitSyscall(c.prog, api)
|
||||
emitOpcode(c.prog, vm.Onop)
|
||||
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE
|
||||
}
|
||||
|
||||
func (c *codegen) convertBuiltin(name string, expr *ast.CallExpr) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue