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:
Anthony De Meulemeester 2018-04-10 11:45:31 +02:00 committed by GitHub
parent b2021c126e
commit 4bd5b2812e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 655 additions and 494 deletions

View file

@ -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) {