Repository: goplus/gop
Pull request #489 (2020-07-23 08:27:59)

exec: add AddrIndex
bytecode & golang: AddrIndex

```
&a[0]
```

codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=h1) Report
> Merging [#489](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/693b1694e7d97f056a034adc6407eaccff980d0a&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `88.57%`.

[![Impacted file tree graph](https://codecov.io/gh/goplus/gop/pull/489/graphs/tree.svg?width=650&height=150&src=pr&token=4qln9Wk3Pn)](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #489      +/-   ##
==========================================
- Coverage   88.92%   88.91%   -0.02%     
==========================================
  Files          38       39       +1     
  Lines        9605     9739     +134     
==========================================
+ Hits         8541     8659     +118     
- Misses        785      798      +13     
- Partials      279      282       +3     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [exec/bytecode/code.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb2RlLmdv) | `100.00% <ø> (ø)` | |
| [exec/golang/interface.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvaW50ZXJmYWNlLmdv) | `88.61% <57.14%> (-1.13%)` | :arrow_down: |
| [exec/bytecode/goinstr.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9nb2luc3RyLmdv) | `92.95% <80.00%> (-1.22%)` | :arrow_down: |
| [cl/stmt.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-Y2wvc3RtdC5nbw==) | `91.89% <85.50%> (-1.31%)` | :arrow_down: |
| [cl/ctx\_check.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-Y2wvY3R4X2NoZWNrLmdv) | `75.10% <100.00%> (+1.02%)` | :arrow_up: |
| [exec/bytecode/const.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb25zdC5nbw==) | `90.32% <100.00%> (+0.21%)` | :arrow_up: |
| [exec/bytecode/context.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb250ZXh0Lmdv) | `91.59% <100.00%> (+0.21%)` | :arrow_up: |
| [exec/bytecode/defer.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9kZWZlci5nbw==) | `100.00% <100.00%> (ø)` | |
| [exec/bytecode/interface.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9pbnRlcmZhY2UuZ28=) | `99.03% <100.00%> (+0.03%)` | :arrow_up: |
| [exec/golang/builder.go](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvYnVpbGRlci5nbw==) | `91.19% <100.00%> (+0.11%)` | :arrow_up: |
| ... and [5 more](https://codecov.io/gh/goplus/gop/pull/489/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=footer). Last update [28dff1c...8404be7](https://codecov.io/gh/goplus/gop/pull/489?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.md b/README.md
index ac387e55..30822f29 100644
--- a/README.md
+++ b/README.md
@@ -160,6 +160,7 @@ When we use `gop run` command, it doesn't call `go run` command. It generates by
 
 ```bash
 gop run         # Run a Go+ program
+gop repl        # Run Go+ in REPL/Console mode
 gop go [-test]  # Convert Go+ packages into Go packages. If -test specified, it tests related packages.
 gop fmt         # Format Go+ packages
 gop export      # Export Go packages for Go+ programs
diff --git a/cl/context.go b/cl/context.go
index 5c4e1191..98dbfdee 100644
--- a/cl/context.go
+++ b/cl/context.go
@@ -185,12 +185,12 @@ func checkLabel(fl *flowLabel) bool {
 type blockCtx struct {
 	*pkgCtx
 	*funcCtx
-	file      *fileCtx
-	parent    *blockCtx
-	syms      map[string]iSymbol
-	noExecCtx bool
-	checkFlag bool
-	inLHS     bool
+	file           *fileCtx
+	parent         *blockCtx
+	syms           map[string]iSymbol
+	noExecCtx      bool
+	checkFlag      bool
+	checkArrayAddr bool
 }
 
 // function block ctx
@@ -323,6 +323,18 @@ func (p *blockCtx) findFunc(name string) (addr *funcDecl, err error) {
 	return nil, ErrSymbolNotFunc
 }
 
+// getCtxVar finds a var in currentCtx only
+func (p *blockCtx) getCtxVar(name string) (addr iVar, err error) {
+	v, ok := p.syms[name]
+	if !ok {
+		return nil, ErrNotFound
+	}
+	if addr, ok = v.(iVar); ok {
+		return
+	}
+	return nil, ErrSymbolNotVariable
+}
+
 func (p *blockCtx) findVar(name string) (addr iVar, err error) {
 	v, ok := p.find(name)
 	if !ok {
diff --git a/cl/ctx_check.go b/cl/ctx_check.go
index c029f796..6e7120dd 100644
--- a/cl/ctx_check.go
+++ b/cl/ctx_check.go
@@ -345,7 +345,7 @@ func isNoExecCtxAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) bool {
 	return true
 }
 
-func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) bool {
+func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) bool {
 	switch v := expr.(type) {
 	case *ast.Ident:
 		return isNoExecCtxIdentLHS(ctx, v.Name, mode)
@@ -359,21 +359,21 @@ func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) bool {
 	return true
 }
 
-func isNoExecCtxIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compleMode) bool {
+func isNoExecCtxIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) bool {
 	if noExecCtx := isNoExecCtxExpr(ctx, v.X); !noExecCtx {
 		return false
 	}
 	return isNoExecCtxExpr(ctx, v.Index)
 }
 
-func isNoExecCtxIdentLHS(ctx *blockCtx, name string, mode compleMode) bool {
+func isNoExecCtxIdentLHS(ctx *blockCtx, name string, mode compileMode) bool {
 	if mode == lhsDefine && !ctx.exists(name) {
 		ctx.insertVar(name, exec.TyEmptyInterface, true)
 	}
 	return true
 }
 
-func isNoExecCtxSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compleMode) bool {
+func isNoExecCtxSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode) bool {
 	if noExecCtx := isNoExecCtxExpr(ctx, v.X); !noExecCtx {
 		return false
 	}
diff --git a/cl/expr.go b/cl/expr.go
index 8874e5a6..f982945b 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -19,7 +19,6 @@ package cl
 import (
 	"reflect"
 	"strings"
-	"syscall"
 
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/astutil"
@@ -32,17 +31,16 @@ import (
 
 // -----------------------------------------------------------------------------
 
-type compleMode = token.Token
+type compileMode = token.Token
 
 const (
-	lhsAssign compleMode = token.ASSIGN // leftHandSide = ...
-	lhsDefine compleMode = token.DEFINE // leftHandSide := ...
+	lhsAssign compileMode = token.ASSIGN // leftHandSide = ...
+	lhsDefine compileMode = token.DEFINE // leftHandSide := ...
 )
 
 // -----------------------------------------------------------------------------
 
-func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) {
-	ctx.inLHS = true
+func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) {
 	switch v := expr.(type) {
 	case *ast.Ident:
 		compileIdentLHS(ctx, v.Name, mode)
@@ -53,7 +51,6 @@ func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) {
 	default:
 		log.Panicln("compileExpr failed: unknown -", reflect.TypeOf(v))
 	}
-	ctx.inLHS = false
 }
 
 func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
@@ -63,7 +60,7 @@ func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
 	case *ast.BasicLit:
 		return compileBasicLit(ctx, v)
 	case *ast.CallExpr:
-		return compileCallExpr(ctx, v)
+		return compileCallExpr(ctx, v, false)
 	case *ast.BinaryExpr:
 		return compileBinaryExpr(ctx, v)
 	case *ast.UnaryExpr:
@@ -100,14 +97,20 @@ func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
 	}
 }
 
-func compileIdentLHS(ctx *blockCtx, name string, mode compleMode) {
+func compileIdentLHS(ctx *blockCtx, name string, mode compileMode) {
 	in := ctx.infer.Get(-1)
 	addr, err := ctx.findVar(name)
+	if mode == lhsDefine {
+		addr, err = ctx.getCtxVar(name)
+		if addr != nil {
+			log.Panicf("compileIdentLHS failed: %s redeclared in this block\n", name)
+		}
+	}
 	if err == nil {
 		if mode == lhsDefine && !addr.inCurrentCtx(ctx) {
 			log.Warn("requireVar: variable is shadowed -", name)
 		}
-	} else if mode == lhsAssign || err != syscall.ENOENT {
+	} else if mode == lhsAssign || err != ErrNotFound {
 		log.Panicln("compileIdentLHS failed:", err, "-", name)
 	} else {
 		typ := boundType(in.(iValue))
@@ -155,7 +158,7 @@ func compileIdent(ctx *blockCtx, name string) func() {
 		case *execVar:
 			ctx.infer.Push(&goValue{t: v.v.Type()})
 			return func() {
-				if ctx.inLHS && v.v.Type().Kind() == reflect.Array {
+				if ctx.checkArrayAddr && v.v.Type().Kind() == reflect.Array {
 					ctx.out.AddrVar(v.v)
 				} else {
 					ctx.out.LoadVar(v.v)
@@ -233,19 +236,14 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit) func() {
 	typ := toType(ctx, v.Type)
 	switch kind := typ.Kind(); kind {
 	case reflect.Slice, reflect.Array:
-		var typSlice, typRet reflect.Type
+		var typSlice reflect.Type
 		if t, ok := typ.(*unboundArrayType); ok {
 			n := toBoundArrayLen(ctx, v)
 			typSlice = reflect.ArrayOf(n, t.elem)
 		} else {
 			typSlice = typ.(reflect.Type)
 		}
-		if typSlice.Kind() == reflect.Array {
-			typRet = reflect.PtrTo(typSlice)
-		} else {
-			typRet = typSlice
-		}
-		ctx.infer.Push(&goValue{t: typRet})
+		ctx.infer.Push(&goValue{t: typSlice})
 		return func() {
 			var nLen int
 			if kind == reflect.Array {
@@ -618,7 +616,7 @@ var binaryOps = [...]exec.Operator{
 	token.LOR:     exec.OpLOr,
 }
 
-func compileCallExpr(ctx *blockCtx, v *ast.CallExpr) func() {
+func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	var exprFun func()
 	switch f := v.Fun.(type) {
 	case *ast.SelectorExpr:
@@ -626,15 +624,17 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr) func() {
 	default:
 		exprFun = compileExpr(ctx, f)
 	}
-	return compileCallExprCall(ctx, exprFun, v)
+	return compileCallExprCall(ctx, exprFun, v, isDefer)
 }
 
-func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func() {
+func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr, isDefer bool) func() {
 	fn := ctx.infer.Pop()
 	switch vfn := fn.(type) {
 	case *qlFunc:
-		ret := vfn.Results()
-		ctx.infer.Push(ret)
+		if !isDefer {
+			ret := vfn.Results()
+			ctx.infer.Push(ret)
+		}
 		return func() {
 			for _, arg := range v.Args {
 				compileExpr(ctx, arg)()
@@ -642,14 +642,16 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func()
 			arity := checkFuncCall(vfn.Proto(), 0, v, ctx)
 			fun := vfn.FuncInfo()
 			if fun.IsVariadic() {
-				ctx.out.CallFuncv(fun, len(v.Args), arity)
+				builder(ctx, isDefer).CallFuncv(fun, len(v.Args), arity)
 			} else {
-				ctx.out.CallFunc(fun, len(v.Args))
+				builder(ctx, isDefer).CallFunc(fun, len(v.Args))
 			}
 		}
 	case *goFunc:
-		ret := vfn.Results()
-		ctx.infer.Push(ret)
+		if !isDefer {
+			ret := vfn.Results()
+			ctx.infer.Push(ret)
+		}
 		return func() {
 			if vfn.isMethod != 0 {
 				compileExpr(ctx, v.Fun.(*ast.SelectorExpr).X)()
@@ -661,17 +663,19 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func()
 			arity := checkFuncCall(vfn.Proto(), vfn.isMethod, v, ctx)
 			switch vfn.kind {
 			case exec.SymbolFunc:
-				ctx.out.CallGoFunc(exec.GoFuncAddr(vfn.addr), nexpr)
+				builder(ctx, isDefer).CallGoFunc(exec.GoFuncAddr(vfn.addr), nexpr)
 			case exec.SymbolFuncv:
-				ctx.out.CallGoFuncv(exec.GoFuncvAddr(vfn.addr), nexpr, arity)
+				builder(ctx, isDefer).CallGoFuncv(exec.GoFuncvAddr(vfn.addr), nexpr, arity)
 			}
 		}
 	case *goValue:
 		if vfn.t.Kind() != reflect.Func {
 			log.Panicln("compileCallExpr failed: call a non function.")
 		}
-		ret := newFuncResults(vfn.t)
-		ctx.infer.Push(ret)
+		if !isDefer {
+			ret := newFuncResults(vfn.t)
+			ctx.infer.Push(ret)
+		}
 		return func() {
 			for _, arg := range v.Args {
 				compileExpr(ctx, arg)()
@@ -681,13 +685,16 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func()
 			if arity == -1 {
 				arity, ellipsis = len(v.Args), true
 			}
-			ctx.out.CallGoClosure(len(v.Args), arity, ellipsis)
+			builder(ctx, isDefer).CallGoClosure(len(v.Args), arity, ellipsis)
 		}
 	case *nonValue:
 		switch nv := vfn.v.(type) {
 		case goInstr:
-			return nv(ctx, v)
+			return nv(ctx, v, isDefer)
 		case reflect.Type:
+			if isDefer {
+				log.Panicln("defer requires function call, not conversion")
+			}
 			return compileTypeCast(nv, ctx, v)
 		}
 	}
@@ -695,21 +702,25 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func()
 	return nil
 }
 
-func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compleMode) {
+func builder(ctx *blockCtx, isDefer bool) (out exec.Builder) {
+	if out = ctx.out; isDefer {
+		out.Defer()
+	}
+	return
+}
+
+func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 	if mode == lhsDefine {
 		log.Panicln("compileIndexExprLHS: `:=` can't be used for index expression")
 	}
 	val := ctx.infer.Get(-1)
+
+	ctx.checkArrayAddr = true
 	compileExpr(ctx, v.X)()
+	ctx.checkArrayAddr = false
+
 	typ := ctx.infer.Get(-1).(iValue).Type()
 	typElem := typ.Elem()
-	if typ.Kind() == reflect.Ptr {
-		if typElem.Kind() != reflect.Array {
-			logPanic(ctx, v, `type %v does not support indexing`, typ)
-		}
-		typ = typElem
-		typElem = typElem.Elem()
-	}
 	if cons, ok := val.(*constVal); ok {
 		cons.bound(typElem, ctx.out)
 	} else if t := val.(iValue).Type(); t != typElem {
@@ -754,16 +765,14 @@ func compileSliceExpr(ctx *blockCtx, v *ast.SliceExpr) func() { // x[i:j:k]
 	exprX := compileExpr(ctx, v.X)
 	x := ctx.infer.Get(-1)
 	typ := x.(iValue).Type()
-	if kind = typ.Kind(); kind == reflect.Ptr {
-		typ = typ.Elem()
-		if kind = typ.Kind(); kind != reflect.Array {
-			logPanic(ctx, v, `cannot slice a (type *%v)`, typ)
-		}
+	if kind = typ.Kind(); kind == reflect.Array {
 		typ = reflect.SliceOf(typ.Elem())
 		ctx.infer.Ret(1, &goValue{typ})
 	}
 	return func() {
+		ctx.checkArrayAddr = true
 		exprX()
+		ctx.checkArrayAddr = false
 		i, j, k := exec.SliceDefaultIndex, exec.SliceDefaultIndex, exec.SliceDefaultIndex
 		if v.Low != nil {
 			i = compileIdx(ctx, v.Low, exec.SliceConstIndexLast, kind)
@@ -811,12 +820,7 @@ func compileIndexExpr(ctx *blockCtx, v *ast.IndexExpr) func() { // x[i]
 	exprX := compileExpr(ctx, v.X)
 	x := ctx.infer.Get(-1)
 	typ := x.(iValue).Type()
-	if kind = typ.Kind(); kind == reflect.Ptr {
-		typ = typ.Elem()
-		if kind = typ.Kind(); kind != reflect.Array {
-			logPanic(ctx, v, `type *%v does not support indexing`, typ)
-		}
-	}
+	kind = typ.Kind()
 	if kind == reflect.String {
 		typElem = exec.TyByte
 	} else {
@@ -910,7 +914,7 @@ func getFuncInfo(fun exec.FuncInfo) (name string, narg int) {
 	return "main", 0
 }
 
-func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compleMode) {
+func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode) {
 	if mode == lhsDefine {
 		log.Panicln("compileSelectorExprLHS: `:=` can't be used for index expression")
 	}
@@ -986,7 +990,7 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 				vt := reflect.ValueOf(info.This)
 				ctx.infer.Ret(1, &goValue{t: vt.Elem().Type()})
 				return func() {
-					if ctx.inLHS && vt.Elem().Kind() == reflect.Array {
+					if ctx.checkArrayAddr && vt.Elem().Kind() == reflect.Array {
 						ctx.out.AddrGoVar(exec.GoVarAddr(addr))
 					} else {
 						ctx.out.LoadGoVar(exec.GoVarAddr(addr))
@@ -1029,7 +1033,7 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 			call := &ast.CallExpr{Fun: &copy}
 			v.X = call
 			v.Sel = nil
-			return compileCallExprCall(ctx, nil, call)
+			return compileCallExprCall(ctx, nil, call, false)
 		}
 		return func() {
 			log.Panicln("compileSelectorExpr: todo")
diff --git a/cl/goinstr.go b/cl/goinstr.go
index a0053c80..6dfd3d55 100644
--- a/cl/goinstr.go
+++ b/cl/goinstr.go
@@ -26,7 +26,7 @@ import (
 
 // -----------------------------------------------------------------------------
 
-type goInstr = func(ctx *blockCtx, v *ast.CallExpr) func()
+type goInstr = func(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func()
 
 type goInstrInfo struct {
 	instr goInstr
@@ -65,7 +65,7 @@ func checkSliceType(args []interface{}, i int) (typ reflect.Type, ok bool) {
 */
 
 // func append(slice []Type, elems ...Type) []Type
-func igoAppend(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoAppend(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	if len(v.Args) < 2 {
 		log.Panicln("append: argument count not enough")
 	}
@@ -74,6 +74,9 @@ func igoAppend(ctx *blockCtx, v *ast.CallExpr) func() {
 	if sliceTy.Kind() != reflect.Slice {
 		log.Panicln("append: first argument not a slice")
 	}
+	if isDefer {
+		ctx.infer.PopN(1)
+	}
 	return func() {
 		sliceExpr()
 		n1 := len(v.Args) - 1
@@ -88,17 +91,17 @@ func igoAppend(ctx *blockCtx, v *ast.CallExpr) func() {
 			}
 			checkType(sliceTy, args[0], ctx.out)
 			ctx.infer.PopN(1)
-			ctx.out.Append(elem, -1)
+			builder(ctx, isDefer).Append(elem, -1)
 		} else {
 			checkElementType(elem, args, 0, n1, 1, ctx.out)
 			ctx.infer.PopN(n1)
-			ctx.out.Append(elem, n1+1)
+			builder(ctx, isDefer).Append(elem, n1+1)
 		}
 	}
 }
 
 // func copy(dst, src []Type) int
-func igoCopy(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoCopy(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	if len(v.Args) < 2 {
 		log.Panicln("not enough arguments in call to copy")
 	}
@@ -110,7 +113,9 @@ func igoCopy(ctx *blockCtx, v *ast.CallExpr) func() {
 	if dstTy.Kind() != reflect.Slice {
 		log.Panicln("arguments to copy must be slices; have ", dstTy.Kind())
 	}
-	ctx.infer.Ret(1, &goValue{exec.TyInt})
+	if !isDefer {
+		ctx.infer.Ret(1, &goValue{exec.TyInt})
+	}
 	return func() {
 		dstExpr()
 		compileExpr(ctx, v.Args[1])()
@@ -128,12 +133,12 @@ func igoCopy(ctx *blockCtx, v *ast.CallExpr) func() {
 			log.Panicln("second argument to copy should be slice or string; have", srcTy.Kind())
 		}
 		ctx.infer.Pop()
-		ctx.out.GoBuiltin(dstTy, exec.GobCopy)
+		builder(ctx, isDefer).GoBuiltin(dstTy, exec.GobCopy)
 	}
 }
 
 // func delete(m map[Type]Type1, key Type)
-func igoDelete(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoDelete(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	if len(v.Args) < 2 {
 		log.Panicln("missing second (key) argument to delete")
 	}
@@ -155,7 +160,7 @@ func igoDelete(ctx *blockCtx, v *ast.CallExpr) func() {
 		elem := mapType.Key()
 		checkType(elem, args[0], ctx.out)
 		ctx.infer.PopN(n1)
-		ctx.out.GoBuiltin(mapType, exec.GobDelete)
+		builder(ctx, isDefer).GoBuiltin(mapType, exec.GobDelete)
 	}
 }
 
@@ -171,12 +176,6 @@ func igoLenOrCap(ctx *blockCtx, v *ast.CallExpr, op exec.GoBuiltin) func() {
 	x := ctx.infer.Get(-1)
 	typ := x.(iValue).Type()
 	kind := typ.Kind()
-	if kind == reflect.Ptr {
-		typ = typ.Elem()
-		if kind = typ.Kind(); kind != reflect.Array {
-			logPanic(ctx, v, `invalid argument a (type *%v) for %v`, typ, op)
-		}
-	}
 	switch kind {
 	case reflect.Array:
 		n := typ.Len()
@@ -214,17 +213,26 @@ func igoLenOrCap(ctx *blockCtx, v *ast.CallExpr, op exec.GoBuiltin) func() {
 }
 
 // func len(v Type) int
-func igoLen(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoLen(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	return igoLenOrCap(ctx, v, exec.GobLen)
 }
 
 // func cap(v Type) int
-func igoCap(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoCap(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	return igoLenOrCap(ctx, v, exec.GobCap)
 }
 
 // func make(t Type, size ...IntegerType) Type
-func igoMake(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoMake(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	if len(v.Args) < 1 {
 		logPanic(ctx, v, `missing argument to make: %v`, ctx.code(v))
 	}
@@ -251,32 +259,44 @@ func igoMake(ctx *blockCtx, v *ast.CallExpr) func() {
 }
 
 // func new(Type) *Type
-func igoNew(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoNew(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	panic("todo")
 }
 
 // func complex(r, i FloatType) ComplexType
-func igoComplex(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoComplex(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	panic("todo")
 }
 
 // func real(c ComplexType) FloatType
-func igoReal(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoReal(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	panic("todo")
 }
 
 // func imag(c ComplexType) FloatType
-func igoImag(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoImag(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
+	if isDefer {
+		log.Panicln("defer discards result of", ctx.code(v))
+	}
 	panic("todo")
 }
 
 // func close(c chan<- Type)
-func igoClose(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoClose(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	panic("todo")
 }
 
 // func recover() interface{}
-func igoRecover(ctx *blockCtx, v *ast.CallExpr) func() {
+func igoRecover(ctx *blockCtx, v *ast.CallExpr, isDefer bool) func() {
 	panic("todo")
 }
 
diff --git a/cl/stmt.go b/cl/stmt.go
index 3015994e..f73afa16 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -37,6 +37,12 @@ func compileBlockStmtWithout(ctx *blockCtx, body *ast.BlockStmt) {
 	}
 }
 
+func compileNewBlock(ctx *blockCtx, block *ast.BlockStmt) {
+	ctx.out.DefineBlock()
+	compileBlockStmtWith(ctx, block)
+	ctx.out.EndBlock()
+}
+
 func compileBodyWith(ctx *blockCtx, body []ast.Stmt) {
 	ctxWith := newNormBlockCtx(ctx)
 	for _, stmt := range body {
@@ -62,7 +68,7 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
 	case *ast.ForStmt:
 		compileForStmt(ctx, v)
 	case *ast.BlockStmt:
-		compileBlockStmtWith(ctx, v)
+		compileNewBlock(ctx, v)
 	case *ast.ReturnStmt:
 		compileReturnStmt(ctx, v)
 	case *ast.IncDecStmt:
@@ -147,9 +153,11 @@ func toIdent(e ast.Expr) *ast.Ident {
 }
 
 func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
-	if v.Init != nil {
-		ctx = newNormBlockCtx(ctx)
-		compileStmt(ctx, v.Init)
+	if init := v.Init; init != nil {
+		v.Init = nil
+		block := &ast.BlockStmt{List: []ast.Stmt{init, v}}
+		compileNewBlock(ctx, block)
+		return
 	}
 	out := ctx.out
 	start := ctx.NewLabel("")
@@ -232,92 +240,7 @@ func compileLabeledStmt(ctx *blockCtx, v *ast.LabeledStmt) {
 }
 
 func compileDeferStmt(ctx *blockCtx, v *ast.DeferStmt) {
-	var instr exec.Reserved
-	out := ctx.out
-	start := ctx.NewLabel("")
-	end := ctx.NewLabel("")
-
-	var f func()
-	exprFun := compileExpr(ctx, v.Call.Fun)
-	fn := ctx.infer.Pop()
-	switch vfn := fn.(type) {
-	case *qlFunc:
-		ret := vfn.Results()
-		ctx.infer.Push(ret)
-
-		for _, arg := range v.Call.Args {
-			compileExpr(ctx, arg)()
-		}
-		instr = ctx.out.Reserve()
-		out.Label(start)
-		f = func() {
-			arity := checkFuncCall(vfn.Proto(), 0, v.Call, ctx)
-			fun := vfn.FuncInfo()
-			if fun.IsVariadic() {
-				ctx.out.CallFuncv(fun, len(v.Call.Args), arity)
-			} else {
-				ctx.out.CallFunc(fun, len(v.Call.Args))
-			}
-		}
-	case *goFunc:
-		ret := vfn.Results()
-		ctx.infer.Push(ret)
-
-		for _, arg := range v.Call.Args {
-			compileExpr(ctx, arg)()
-		}
-		instr = ctx.out.Reserve()
-		out.Label(start)
-		f = func() {
-			if vfn.isMethod != 0 {
-				compileExpr(ctx, v.Call.Fun.(*ast.SelectorExpr).X)()
-			}
-			nexpr := len(v.Call.Args) + vfn.isMethod
-			arity := checkFuncCall(vfn.Proto(), vfn.isMethod, v.Call, ctx)
-			switch vfn.kind {
-			case exec.SymbolFunc:
-				ctx.out.CallGoFunc(exec.GoFuncAddr(vfn.addr), nexpr)
-			case exec.SymbolFuncv:
-				ctx.out.CallGoFuncv(exec.GoFuncvAddr(vfn.addr), nexpr, arity)
-			}
-		}
-	case *goValue:
-		if vfn.t.Kind() != reflect.Func {
-			log.Panicln("compileCallExpr failed: call a non function.")
-		}
-		ret := newFuncResults(vfn.t)
-		ctx.infer.Push(ret)
-
-		for _, arg := range v.Call.Args {
-			compileExpr(ctx, arg)()
-		}
-		instr = ctx.out.Reserve()
-		out.Label(start)
-		f = func() {
-			exprFun()
-			arity, ellipsis := checkFuncCall(vfn.t, 0, v.Call, ctx), false
-			if arity == -1 {
-				arity, ellipsis = len(v.Call.Args), true
-			}
-			ctx.out.CallGoClosure(len(v.Call.Args), arity, ellipsis)
-		}
-	case *nonValue:
-		instr = ctx.out.Reserve()
-		out.Label(start)
-		// TODO compile args before compileCallExpr
-		switch nv := vfn.v.(type) {
-		case goInstr:
-			f = nv(ctx, v.Call)
-		case reflect.Type:
-			f = compileTypeCast(nv, ctx, v.Call)
-		}
-	}
-
-	f()
-	ctx.infer.Pop()
-
-	out.Label(end)
-	instr.Set(out, out.Defer(start, end))
+	compileCallExpr(ctx, v.Call, true)()
 }
 
 func compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {
@@ -477,7 +400,11 @@ func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
 	}
 	out.Label(label)
 	if hasElse {
-		compileStmt(ctxIf, v.Else)
+		if ve, ok := v.Else.(*ast.BlockStmt); ok {
+			compileBlockStmtWithout(ctx, ve)
+		} else {
+			compileStmt(ctxIf, v.Else)
+		}
 		out.Label(done)
 	}
 }
diff --git a/cmd/gop/main.go b/cmd/gop/main.go
index 515455fa..92a3a940 100644
--- a/cmd/gop/main.go
+++ b/cmd/gop/main.go
@@ -11,6 +11,7 @@ import (
 	"github.com/goplus/gop/cmd/internal/gengo"
 	"github.com/goplus/gop/cmd/internal/gopfmt"
 	"github.com/goplus/gop/cmd/internal/help"
+	"github.com/goplus/gop/cmd/internal/repl"
 	"github.com/goplus/gop/cmd/internal/run"
 
 	_ "github.com/goplus/gop/lib"
@@ -28,6 +29,7 @@ func init() {
 		gengo.Cmd,
 		gopfmt.Cmd,
 		export.Cmd,
+		repl.Cmd,
 	}
 }
 
@@ -40,7 +42,7 @@ func main() {
 
 	base.CmdName = args[0] // for error messages
 	if args[0] == "help" {
-		help.Help(os.Stdout, args[1:])
+		help.Help(os.Stderr, args[1:])
 		return
 	}
 
@@ -58,7 +60,7 @@ BigCmdLoop:
 					os.Exit(2)
 				}
 				if args[0] == "help" {
-					help.Help(os.Stdout, append(strings.Split(base.CmdName, " "), args[1:]...))
+					help.Help(os.Stderr, append(strings.Split(base.CmdName, " "), args[1:]...))
 					return
 				}
 				base.CmdName += " " + args[0]
diff --git a/cmd/igop/igop.go b/cmd/igop/igop.go
new file mode 100644
index 00000000..c81c3a30
--- /dev/null
+++ b/cmd/igop/igop.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"os"
+
+	"github.com/goplus/gop/cmd/internal/base"
+	"github.com/goplus/gop/cmd/internal/repl"
+
+	_ "github.com/goplus/gop/lib"
+)
+
+func main() {
+	base.Main(repl.Cmd, "igop", os.Args[1:])
+}
diff --git a/cmd/internal/base/base.go b/cmd/internal/base/base.go
index 4d03414c..d59eca63 100644
--- a/cmd/internal/base/base.go
+++ b/cmd/internal/base/base.go
@@ -21,6 +21,7 @@ package base
 import (
 	"flag"
 	"fmt"
+	"io"
 	"os"
 	"strings"
 )
@@ -77,10 +78,15 @@ func (c *Command) Name() string {
 }
 
 // Usage show the command usage.
-func (c *Command) Usage() {
-	fmt.Fprintf(os.Stderr, "%s\n\nUsage: %s\n", c.Short, c.UsageLine)
+func (c *Command) Usage(w io.Writer) {
+	fmt.Fprintf(w, "%s\n\nUsage: %s\n", c.Short, c.UsageLine)
+
+	// restore output of flag
+	defer c.Flag.SetOutput(c.Flag.Output())
+
+	c.Flag.SetOutput(w)
 	c.Flag.PrintDefaults()
-	fmt.Fprintln(os.Stderr)
+	fmt.Fprintln(w)
 	os.Exit(2)
 }
 
diff --git a/cmd/internal/export/export.go b/cmd/internal/export/export.go
index 7013d7ea..28725819 100644
--- a/cmd/internal/export/export.go
+++ b/cmd/internal/export/export.go
@@ -53,7 +53,7 @@ func init() {
 func runCmd(cmd *base.Command, args []string) {
 	flag.Parse(args)
 	if flag.NArg() < 1 {
-		cmd.Usage()
+		cmd.Usage(os.Stderr)
 		return
 	}
 	var libDir string
diff --git a/cmd/internal/export/gopkg/export.go b/cmd/internal/export/gopkg/export.go
index f6dda364..8228af7f 100644
--- a/cmd/internal/export/gopkg/export.go
+++ b/cmd/internal/export/gopkg/export.go
@@ -64,6 +64,18 @@ func exportConst(e *Exporter, o *types.Const) (err error) {
 	return e.ExportConst(o)
 }
 
+func cleanPkgVer(pkgPath string) string {
+	i := strings.Index(pkgPath, "@")
+	if i == -1 {
+		return pkgPath
+	}
+	j := strings.Index(pkgPath[i:], "/")
+	if j == -1 {
+		return pkgPath[:i]
+	}
+	return pkgPath[:i] + pkgPath[i:][j:]
+}
+
 func findLastVerPkg(pkgDirBase string, name string) (verName string) {
 	verName = name
 	fis, err := ioutil.ReadDir(pkgDirBase)
@@ -110,6 +122,33 @@ func getPkgKind(pkgPath string) int {
 	return pkgStandard
 }
 
+// LookupMod looup pkgPath srcDir from GOMOD root
+// github.com/qiniu/x/log
+// github.com/qiniu/x@v1.11.5/log
+func LookupMod(pkgPath string) (srcDir string, err error) {
+	parts := strings.Split(pkgPath, "/")
+	n := len(parts)
+	if n < 3 {
+		return "", ErrInvalidPkgPath
+	}
+	srcDir = filepath.Join(getModRoot(), parts[0], parts[1])
+	noVer := strings.Index(parts[2], "@") == -1
+	if noVer {
+		parts[2] = findLastVerPkg(srcDir, parts[2])
+	}
+	srcDir = filepath.Join(srcDir, parts[2])
+	if n > 3 {
+		srcDir += "/" + strings.Join(parts[3:], "/")
+	}
+	return srcDir, nil
+}
+
+// ImportSource import a Go package from pkgPath and srcDir
+func ImportSource(pkgPath string, srcDir string) (*types.Package, error) {
+	imp := importer.ForCompiler(token.NewFileSet(), "source", nil)
+	return imp.(types.ImporterFrom).ImportFrom(pkgPath, srcDir, 0)
+}
+
 // Import imports a Go package.
 func Import(pkgPath string) (*types.Package, error) {
 	var imp types.Importer
@@ -125,6 +164,8 @@ func Import(pkgPath string) (*types.Package, error) {
 		noVer := strings.Index(parts[2], "@") == -1
 		if noVer {
 			parts[2] = findLastVerPkg(srcDir, parts[2])
+		} else {
+			pkgPath = cleanPkgVer(pkgPath)
 		}
 		srcDir = filepath.Join(srcDir, parts[2])
 		if n > 3 {
@@ -153,7 +194,11 @@ var (
 
 func getModRoot() string {
 	gOnce.Do(func() {
-		gModRoot = os.Getenv("GOPATH") + "/pkg/mod"
+		gModRoot = os.Getenv("GOMODCACHE")
+		if gModRoot == "" {
+			paths := strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator))
+			gModRoot = paths[0] + "/pkg/mod"
+		}
 	})
 	return gModRoot
 }
diff --git a/cmd/internal/gengo/go.go b/cmd/internal/gengo/go.go
index 48dd1b91..0607002b 100644
--- a/cmd/internal/gengo/go.go
+++ b/cmd/internal/gengo/go.go
@@ -185,7 +185,7 @@ func init() {
 func runCmd(cmd *base.Command, args []string) {
 	flag.Parse(args)
 	if flag.NArg() < 1 {
-		cmd.Usage()
+		cmd.Usage(os.Stderr)
 		return
 	}
 	dir := flag.Arg(0)
diff --git a/cmd/internal/help/help.go b/cmd/internal/help/help.go
index 8daf3fa7..11e6a35f 100644
--- a/cmd/internal/help/help.go
+++ b/cmd/internal/help/help.go
@@ -41,9 +41,9 @@ Args:
 	}
 
 	if len(cmd.Commands) > 0 {
-		PrintUsage(os.Stdout, cmd)
+		PrintUsage(w, cmd)
 	} else {
-		cmd.Usage()
+		cmd.Usage(w)
 	}
 	// not exit 2: succeeded at 'gop help cmd'.
 	return
diff --git a/cmd/internal/repl/repl.go b/cmd/internal/repl/repl.go
new file mode 100644
index 00000000..8573f05d
--- /dev/null
+++ b/cmd/internal/repl/repl.go
@@ -0,0 +1,163 @@
+/*
+ Copyright 2020 The GoPlus Authors (goplus.org)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+// Package repl implements the ``gop repl'' command.
+package repl
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/goplus/gop/cl"
+	"github.com/goplus/gop/cmd/internal/base"
+	"github.com/goplus/gop/parser"
+	"github.com/goplus/gop/token"
+	"github.com/peterh/liner"
+
+	exec "github.com/goplus/gop/exec/bytecode"
+)
+
+func init() {
+	Cmd.Run = runCmd
+}
+
+// Cmd - gop repl
+var Cmd = &base.Command{
+	UsageLine: "repl",
+	Short:     "Play Go+ in console",
+}
+
+type repl struct {
+	src          string       // the whole source code from repl
+	preContext   exec.Context // store the context after exec
+	ip           int          // store the ip after exec
+	prompt       string       // the prompt type in console
+	continueMode bool         // switch to control the promot type
+	liner        *liner.State // liner instance
+}
+
+const (
+	continuePrompt string = "... "
+	standardPrompt string = ">>> "
+	welcome        string = "welcome to Go+ console!"
+)
+
+func runCmd(cmd *base.Command, args []string) {
+	fmt.Println(welcome)
+	liner := liner.NewLiner()
+	rInstance := repl{prompt: standardPrompt, liner: liner}
+	defer rInstance.liner.Close()
+
+	for {
+		l, err := rInstance.liner.Prompt(string(rInstance.prompt))
+		if err != nil {
+			if err == io.EOF {
+				fmt.Printf("\n")
+				break
+			}
+			fmt.Printf("Problem reading line: %v\n", err)
+			continue
+		}
+		rInstance.replOneLine(l)
+	}
+}
+
+// replOneLine handle one line
+func (r *repl) replOneLine(line string) {
+	if line != "" {
+		// mainly for scroll up
+		r.liner.AppendHistory(line)
+	}
+	if r.continueMode {
+		r.continueModeByLine(line)
+	}
+	if !r.continueMode {
+		r.run(line)
+	}
+}
+
+// continueModeByLine check if continue-mode should continue :)
+func (r *repl) continueModeByLine(line string) {
+	if line != "" {
+		r.src += line + "\n"
+		return
+	}
+	// input nothing means jump out continue mode
+	r.continueMode = false
+	r.prompt = standardPrompt
+}
+
+// run execute the input line
+func (r *repl) run(newLine string) (err error) {
+	src := r.src + newLine + "\n"
+	defer func() {
+		if err == nil {
+			r.src = src
+		}
+		if errR := recover(); errR != nil {
+			replErr(newLine, errR)
+			err = errors.New("panic err")
+			// TODO: Need a better way to log and show the stack when crash
+			// It is too long to print stack on terminal even only print part of the them; not friendly to user
+		}
+	}()
+	fset := token.NewFileSet()
+	pkgs, err := parser.Parse(fset, "", src, 0)
+	if err != nil {
+		// check if into continue mode
+		if strings.Contains(err.Error(), `expected ')', found 'EOF'`) ||
+			strings.Contains(err.Error(), "expected '}', found 'EOF'") {
+			r.prompt = continuePrompt
+			r.continueMode = true
+			err = nil
+			return
+		}
+		fmt.Println("ParseGopFiles err", err)
+		return
+	}
+	cl.CallBuiltinOp = exec.CallBuiltinOp
+
+	b := exec.NewBuilder(nil)
+
+	_, err = cl.NewPackage(b.Interface(), pkgs["main"], fset, cl.PkgActClMain)
+	if err != nil {
+		if err == cl.ErrMainFuncNotFound {
+			err = nil
+			return
+		}
+		fmt.Printf("NewPackage err %+v\n", err)
+		return
+	}
+	code := b.Resolve()
+	ctx := exec.NewContext(code)
+	if r.ip != 0 {
+		// if it is not the first time, restore pre var
+		r.preContext.CloneSetVarScope(ctx)
+	}
+	currentIP := ctx.Exec(r.ip, code.Len())
+	r.preContext = *ctx
+	// "currentip - 1" is the index of `return`
+	// next time it will replace by new code from newLine
+	r.ip = currentIP - 1
+	return
+}
+
+func replErr(line string, err interface{}) {
+	fmt.Println("code run fail : ", line)
+	fmt.Println("repl err: ", err)
+}
diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go
index baca27d9..cbb4ff9b 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -21,6 +21,7 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/cmd/internal/base"
 	"github.com/goplus/gop/parser"
@@ -53,7 +54,7 @@ func init() {
 func runCmd(cmd *base.Command, args []string) {
 	flag.Parse(args)
 	if flag.NArg() < 1 {
-		cmd.Usage()
+		cmd.Usage(os.Stderr)
 	}
 
 	log.SetFlags(log.Ldefault &^ log.LstdFlags)
@@ -72,9 +73,14 @@ func runCmd(cmd *base.Command, args []string) {
 	if err != nil {
 		log.Fatalln("input arg check failed:", err)
 	}
-	pkgs, err := parser.ParseGopFiles(fset, target, isDir, 0)
+	var pkgs map[string]*ast.Package
+	if isDir {
+		pkgs, err = parser.ParseDir(fset, target, nil, 0)
+	} else {
+		pkgs, err = parser.Parse(fset, target, nil, 0)
+	}
 	if err != nil {
-		log.Fatalln("ParseGopFiles failed:", err)
+		log.Fatalln("parser.Parse failed:", err)
 	}
 	cl.CallBuiltinOp = exec.CallBuiltinOp
 
@@ -88,8 +94,7 @@ func runCmd(cmd *base.Command, args []string) {
 		code.Dump(os.Stdout)
 		return
 	}
-	ctx := exec.NewContext(code)
-	ctx.Exec(0, code.Len())
+	exec.NewContext(code).Run()
 	if *flagProf {
 		exec.ProfileReport()
 	}
diff --git a/exec.spec/interface.go b/exec.spec/interface.go
index 83293713..ac26f0b9 100644
--- a/exec.spec/interface.go
+++ b/exec.spec/interface.go
@@ -43,11 +43,6 @@ type Label interface {
 	Name() string
 }
 
-type Instr interface {
-	// The val of instr
-	Val() interface{}
-}
-
 // Reserved represents a reserved instruction position.
 type Reserved int
 
@@ -59,14 +54,10 @@ func (p Reserved) Push(b Builder, val interface{}) {
 	b.ReservedAsPush(p, val)
 }
 
-func (p Reserved) Set(b Builder, instr Instr) {
-	b.ReservedAsInstr(p, instr)
-}
-
-// BreakAsReturn
+// BreakAsReturn - todo
 const BreakAsReturn = -2
 
-// ContinueAsReturn
+// ContinueAsReturn - todo
 const ContinueAsReturn = -3
 
 // ForPhrase represents a for range phrase.
@@ -259,6 +250,15 @@ type Builder interface {
 	// CallGoFuncv instr
 	CallGoFuncv(fun GoFuncvAddr, nexpr, arity int) Builder
 
+	// Append instr
+	Append(typ reflect.Type, arity int) Builder
+
+	// GoBuiltin instr
+	GoBuiltin(typ reflect.Type, op GoBuiltin) Builder
+
+	// Defer instr
+	Defer() Builder
+
 	// DefineFunc instr
 	DefineFunc(fun FuncInfo) Builder
 
@@ -301,9 +301,6 @@ type Builder interface {
 	// AddrOp instr
 	AddrOp(kind Kind, op AddrOperator) Builder
 
-	// Append instr
-	Append(typ reflect.Type, arity int) Builder
-
 	// MakeArray instr
 	MakeArray(typ reflect.Type, arity int) Builder
 
@@ -322,6 +319,9 @@ type Builder interface {
 	// Index instr
 	Index(idx int) Builder
 
+	// AddrIndex instr
+	AddrIndex(idx int) Builder
+
 	// SetIndex instr
 	SetIndex(idx int) Builder
 
@@ -334,9 +334,6 @@ type Builder interface {
 	// TypeCast instr
 	TypeCast(from, to reflect.Type) Builder
 
-	// GoBuiltin instr
-	GoBuiltin(typ reflect.Type, op GoBuiltin) Builder
-
 	// Zero instr
 	Zero(typ reflect.Type) Builder
 
@@ -352,17 +349,17 @@ type Builder interface {
 	// ReservedAsPush sets Reserved as Push(v)
 	ReservedAsPush(r Reserved, v interface{})
 
-	// ReservedAsInstr set Reserved as instr
-	ReservedAsInstr(r Reserved, instr Instr)
-
 	// GetPackage returns the Go+ package that the Builder works for.
 	GetPackage() Package
 
 	// Resolve resolves all unresolved labels/functions/consts/etc.
 	Resolve() Code
 
-	// Defer instr
-	Defer(start, end Label) Instr
+	// DefineBlock instr
+	DefineBlock() Builder
+
+	// EndBlock instr
+	EndBlock() Builder
 }
 
 // Package represents a Go+ package.
diff --git a/exec.spec/stack.go b/exec.spec/stack.go
index 1f394107..bf709eb8 100644
--- a/exec.spec/stack.go
+++ b/exec.spec/stack.go
@@ -18,6 +18,8 @@ package exec
 
 // -----------------------------------------------------------------------------
 
+const defaultStkSize = 64
+
 // A Stack represents a FILO container.
 type Stack struct {
 	data []interface{}
@@ -25,12 +27,12 @@ type Stack struct {
 
 // NewStack creates a Stack instance.
 func NewStack() (p *Stack) {
-	return &Stack{data: make([]interface{}, 0, 64)}
+	return &Stack{data: make([]interface{}, 0, defaultStkSize)}
 }
 
 // Init initializes this Stack object.
 func (p *Stack) Init() {
-	p.data = make([]interface{}, 0, 64)
+	p.data = make([]interface{}, 0, defaultStkSize)
 }
 
 // Get returns the value at specified index.
diff --git a/exec/bytecode/code.go b/exec/bytecode/code.go
index 3a6e705b..b88278f8 100644
--- a/exec/bytecode/code.go
+++ b/exec/bytecode/code.go
@@ -77,7 +77,7 @@ const (
 	opPushUint      = 4  // intKind(3) intVal(23)
 	opPushValSpec   = 5  // valSpec(26) - false=0, true=1
 	opPushConstR    = 6  // idx(26)
-	opIndex         = 7  // set(1) idx(25)
+	opIndex         = 7  // set(3) idx(23)
 	opMake          = 8  // funvArity(10) type(16)
 	opAppend        = 9  // arity(26)
 	opBuiltinOp     = 10 // reserved(16) kind(5) builtinOp(5)
@@ -114,7 +114,7 @@ const (
 	opGoBuiltin     = 41 // op(26)
 	opErrWrap       = 42 // idx(26)
 	opWrapIfErr     = 43 // reserved(2) offset(24)
-	opDeferOp       = 44 // reserved(2) offset(24)
+	opDefer         = 44
 )
 
 const (
@@ -123,9 +123,10 @@ const (
 	iPushTrue       = (opPushValSpec << bitsOpShift) | 1
 	iPushNil        = (opPushValSpec << bitsOpShift) | 2
 	iPushUnresolved = (opInvalid << bitsOpShift)
-	iReturn         = (opReturn << bitsOpShift) | (0xffffffff & bitsOperand)
-	iBreak          = (opReturn << bitsOpShift) | (0xfffffffe & bitsOperand)
-	iContinue       = (opReturn << bitsOpShift) | (0xfffffffd & bitsOperand)
+
+	iReturn   = (opReturn << bitsOpShift) | (0xffffffff & bitsOperand)
+	iBreak    = (opReturn << bitsOpShift) | (0xfffffffe & bitsOperand)
+	iContinue = (opReturn << bitsOpShift) | (0xfffffffd & bitsOperand)
 )
 
 const (
@@ -162,7 +163,7 @@ var instrInfos = []InstrInfo{
 	opPushUint:      {"pushUint", "intKind", "intVal", (3 << 8) | 23},     // intKind(3) intVal(23)
 	opPushValSpec:   {"pushValSpec", "", "valSpec", 26},                   // valSpec(26) - false=0, true=1
 	opPushConstR:    {"pushConstR", "", "idx", 26},                        // idx(26)
-	opIndex:         {"index", "set", "idx", (1 << 8) | 25},               // set(1) idx(25)
+	opIndex:         {"index", "set", "idx", (3 << 8) | 23},               // set(3) idx(23)
 	opMake:          {"make", "funvArity", "type", (10 << 8) | 16},        // funvArity(10) type(16)
 	opAppend:        {"append", "", "arity", 26},                          // arity(26)
 	opBuiltinOp:     {"builtinOp", "kind", "op", (21 << 8) | 5},           // reserved(16) kind(5) builtinOp(5)
@@ -199,7 +200,7 @@ var instrInfos = []InstrInfo{
 	opGoBuiltin:     {"goBuiltin", "", "op", 26},                          // op(26)
 	opErrWrap:       {"errWrap", "", "idx", 26},                           // idx(26)
 	opWrapIfErr:     {"wrapIfErr", "", "offset", 26},                      // reserved(2) offset(24)
-	opDeferOp:       {"opDeferOp", "", "offset", 26},                      // reserved(2) offset(24)
+	opDefer:         {"defer", "", "", 0},
 }
 
 // -----------------------------------------------------------------------------
@@ -230,8 +231,13 @@ func (p *Code) Len() int {
 
 // Dump dumps code.
 func (p *Code) Dump(w io.Writer) {
+	DumpCodeBlock(w, p.data...)
+}
+
+// DumpCodeBlock dumps a code block.
+func DumpCodeBlock(w io.Writer, data ...Instr) {
 	b := bufio.NewWriter(w)
-	for _, i := range p.data {
+	for _, i := range data {
 		v, p1, p2 := DecodeInstr(i)
 		b.WriteString(v.Name)
 		b.WriteByte(' ')
diff --git a/exec/bytecode/const.go b/exec/bytecode/const.go
index eea9c5ae..bd41d11e 100644
--- a/exec/bytecode/const.go
+++ b/exec/bytecode/const.go
@@ -19,7 +19,6 @@ package bytecode
 import (
 	"reflect"
 
-	"github.com/goplus/gop/exec.spec"
 	"github.com/qiniu/x/log"
 )
 
@@ -157,11 +156,6 @@ func (p *Builder) ReservedAsPush(r Reserved, val interface{}) {
 	p.code.data[r] = p.pushInstr(val)
 }
 
-// ReservedAsInstr sets Reserved as Instr
-func (p *Builder) ReservedAsInstr(r Reserved, instr exec.Instr) {
-	p.code.data[r] = instr.Val().(Instr)
-}
-
 // Pop instr
 func (p *Builder) Pop(n int) *Builder {
 	i := (opPop << bitsOpShift) | uint32(n)
diff --git a/exec/bytecode/context.go b/exec/bytecode/context.go
index a714c2e6..fad405fd 100644
--- a/exec/bytecode/context.go
+++ b/exec/bytecode/context.go
@@ -25,73 +25,6 @@ import (
 
 // -----------------------------------------------------------------------------
 
-const defaultStkSize = 64
-
-// A Stack represents a FILO container.
-type Stack struct {
-	data []interface{}
-}
-
-// NewStack creates a Stack instance.
-func NewStack() (p *Stack) {
-	return &Stack{data: make([]interface{}, 0, defaultStkSize)}
-}
-
-// Init initializes this Stack object.
-func (p *Stack) Init() {
-	p.data = make([]interface{}, 0, defaultStkSize)
-}
-
-// Get returns the value at specified index.
-func (p *Stack) Get(idx int) interface{} {
-	return p.data[len(p.data)+idx]
-}
-
-// Set returns the value at specified index.
-func (p *Stack) Set(idx int, v interface{}) {
-	p.data[len(p.data)+idx] = v
-}
-
-// GetArgs returns all arguments of a function.
-func (p *Stack) GetArgs(arity int) []interface{} {
-	return p.data[len(p.data)-arity:]
-}
-
-// Ret pops n values from this stack, and then pushes results.
-func (p *Stack) Ret(arity int, results ...interface{}) {
-	p.data = append(p.data[:len(p.data)-arity], results...)
-}
-
-// Push pushes a value into this stack.
-func (p *Stack) Push(v interface{}) {
-	p.data = append(p.data, v)
-}
-
-// PopN pops n elements.
-func (p *Stack) PopN(n int) {
-	p.data = p.data[:len(p.data)-n]
-}
-
-// Pop pops a value from this stack.
-func (p *Stack) Pop() interface{} {
-	n := len(p.data)
-	v := p.data[n-1]
-	p.data = p.data[:n-1]
-	return v
-}
-
-// Len returns count of stack elements.
-func (p *Stack) Len() int {
-	return len(p.data)
-}
-
-// SetLen sets count of stack elements.
-func (p *Stack) SetLen(base int) {
-	p.data = p.data[:base]
-}
-
-// -----------------------------------------------------------------------------
-
 type varScope struct {
 	vars   varsContext
 	parent *varScope
@@ -124,6 +57,16 @@ func NewContext(in exec.Code) *Context {
 	return p
 }
 
+// CloneSetVarScope clone already set varScope to new context
+func (ctx *Context) CloneSetVarScope(new *Context) {
+	if !ctx.vars.IsValid() {
+		return
+	}
+	for i := 0; i < ctx.vars.NumField(); i++ {
+		new.varScope.setVar(uint32(i), ctx.varScope.getVar(uint32(i)))
+	}
+}
+
 type savedScopeCtx struct {
 	base int
 	ip   int
@@ -158,8 +101,14 @@ func (ctx *Context) getScope(local bool) *varScope {
 	return scope
 }
 
+// Run executes the code.
+func (ctx *Context) Run() {
+	defer ctx.execDefers()
+	ctx.Exec(0, ctx.code.Len())
+}
+
 // Exec executes a code block from ip to ipEnd.
-func (ctx *Context) Exec(ip, ipEnd int) {
+func (ctx *Context) Exec(ip, ipEnd int) (currentIP int) {
 	const allowProfile = true
 	var lastInstr Instr
 	var start time.Time
@@ -185,8 +134,6 @@ func (ctx *Context) Exec(ip, ipEnd int) {
 			}
 		case opBuiltinOp:
 			execBuiltinOp(i, ctx)
-		case opDeferOp:
-			execDeferOp(i, ctx)
 		case opCallFunc:
 			fun := ctx.code.funs[i&bitsOperand]
 			fun.exec(ctx, ctx.getScope(fun.nestDepth > 1))
@@ -207,7 +154,7 @@ func (ctx *Context) Exec(ip, ipEnd int) {
 		case opCallGoFuncv:
 			execGoFuncv(i, ctx)
 		case opReturn:
-			ctx.ip = int(i)
+			currentIP = ctx.ip
 			if i == iBreak || i == iContinue || i == iReturn {
 				ctx.ip = int(i)
 			} else {
@@ -225,17 +172,19 @@ func (ctx *Context) Exec(ip, ipEnd int) {
 		}
 	}
 finished:
-	execDefers(ctx)
 	if allowProfile && doProfile {
 		if lastInstr != 0 {
 			instrProfile(lastInstr, time.Since(start))
 		}
 	}
+	return
 }
 
 var _execTable = [...]func(i Instr, p *Context){
 	opCallGoFunc:    execGoFunc,
 	opCallGoFuncv:   execGoFuncv,
+	opCallFunc:      execFunc,
+	opCallFuncv:     execFuncv,
 	opPushInt:       execPushInt,
 	opPushUint:      execPushUint,
 	opPushValSpec:   execPushValSpec,
@@ -274,7 +223,7 @@ var _execTable = [...]func(i Instr, p *Context){
 	opGoBuiltin:     execGoBuiltin,
 	opErrWrap:       execErrWrap,
 	opWrapIfErr:     execWrapIfErr,
-	opDeferOp:       execDeferOp,
+	opDefer:         execDefer,
 }
 
 var execTable []func(i Instr, p *Context)
diff --git a/exec/bytecode/defer.go b/exec/bytecode/defer.go
index d2e027a7..73664884 100644
--- a/exec/bytecode/defer.go
+++ b/exec/bytecode/defer.go
@@ -14,65 +14,43 @@
  limitations under the License.
 */
 
-// Package bytecode implements a bytecode backend for the Go+ language.
 package bytecode
 
-import (
-	"github.com/goplus/gop/exec.spec"
-)
+// -----------------------------------------------------------------------------
 
 type theDefer struct {
-	next *theDefer
-	Stack
-	start int
-	end   int
+	next  *theDefer
+	state varScope // block state
+	n     int      // stack len
+	i     Instr
 }
 
-const (
-	bitsOpDefer        = bitsOp + 2
-	bitsOpDeferOperand = bitsOpDefer - 1
-)
-
-func execDefers(ctx *Context) {
+func (ctx *Context) execDefers() {
 	d := ctx.defers
-	if d == nil {
-		return
-	}
-
 	ctx.defers = nil
-	for {
-		ctx.Stack = d.Stack
-		ctx.Exec(d.start, d.end)
+	for d != nil {
+		ctx.data = ctx.data[:d.n]
+		ctx.varScope = d.state
+		execTable[d.i>>bitsOpShift](d.i, ctx)
 		d = d.next
-		if d == nil {
-			break
-		}
 	}
 }
 
-func execDeferOp(i Instr, p *Context) {
-	end := i & bitsOpDeferOperand << bitsOpDefer >> bitsOpDefer
-	def := &theDefer{
-		Stack: p.Stack,
-		next:  p.defers,
-		start: p.ip,
+func execDefer(i Instr, ctx *Context) {
+	i = ctx.code.data[ctx.ip]
+	ctx.ip++
+	ctx.defers = &theDefer{
+		next:  ctx.defers,
+		state: ctx.varScope,
+		n:     len(ctx.data),
+		i:     i,
 	}
-
-	p.ip += int(end)
-	def.end = p.ip
-	p.defers = def
 }
 
-func (p *Builder) Defer(start, end *Label) exec.Instr {
-	return &iDefer{start: p.labels[start], end: p.labels[end]}
+// Defer instr
+func (p *Builder) Defer() *Builder {
+	p.code.data = append(p.code.data, opDefer<<bitsOpShift)
+	return p
 }
 
-type iDefer struct {
-	start int
-	end   int
-}
-
-func (c *iDefer) Val() interface{} {
-	instr := opDeferOp<<bitsOpShift | uint32(c.end-c.start)&bitsOpDeferOperand
-	return Instr(instr)
-}
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/func.go b/exec/bytecode/func.go
index 971524e4..73163de3 100644
--- a/exec/bytecode/func.go
+++ b/exec/bytecode/func.go
@@ -274,13 +274,32 @@ func (p *FuncInfo) Type() reflect.Type {
 	return p.t
 }
 
+func (p *FuncInfo) execFunc(ctx *Context) {
+	oldDefers := ctx.defers
+	ctx.defers = nil
+	defer func() {
+		ctx.execDefers()
+		ctx.defers = oldDefers
+	}()
+	ctx.Exec(p.funEntry, p.funEnd)
+	if ctx.ip == ipReturnN { // TODO: optimize
+		if ctx.defers != nil {
+			ctx.ip = ipInvalid
+			rets := ctx.GetArgs(p.numOut)
+			for i, v := range rets {
+				ctx.setVar(uint32(i), v)
+			}
+		} else {
+			n := len(ctx.data)
+			ctx.data = append(ctx.data[:ctx.base-len(p.in)], ctx.data[n-p.numOut:]...)
+		}
+	}
+}
+
 func (p *FuncInfo) exec(ctx *Context, parent *varScope) {
 	old := ctx.switchScope(parent, &p.varManager)
-	ctx.Exec(p.funEntry, p.funEnd)
-	if ctx.ip == ipReturnN {
-		n := len(ctx.data)
-		ctx.data = append(ctx.data[:ctx.base-len(p.in)], ctx.data[n-p.numOut:]...)
-	} else {
+	p.execFunc(ctx)
+	if ctx.ip != ipReturnN {
 		ctx.data = ctx.data[:ctx.base-len(p.in)]
 		n := uint32(p.numOut)
 		for i := uint32(0); i < n; i++ {
diff --git a/exec/bytecode/goinstr.go b/exec/bytecode/goinstr.go
index f93b67bc..4623f2d0 100644
--- a/exec/bytecode/goinstr.go
+++ b/exec/bytecode/goinstr.go
@@ -157,17 +157,24 @@ func execMakeArray(i Instr, p *Context) {
 func makeArray(typSlice reflect.Type, arity int, p *Context) {
 	args := p.GetArgs(arity)
 	var ret, set reflect.Value
-	if typSlice.Kind() == reflect.Slice {
+	kind := typSlice.Kind()
+	if kind == reflect.Slice {
 		ret = reflect.MakeSlice(typSlice, arity, arity)
 		set = ret
-	} else {
+	} else if kind == reflect.Array {
 		ret = reflect.New(typSlice)
 		set = ret.Elem()
+	} else {
+		log.Panic("makeArray bad type:", typSlice)
 	}
 	for i, arg := range args {
 		set.Index(i).Set(getElementOf(arg, typSlice))
 	}
-	p.Ret(arity, ret.Interface())
+	if kind == reflect.Slice {
+		p.Ret(arity, ret.Interface())
+	} else {
+		p.Ret(arity, ret.Elem().Interface())
+	}
 }
 
 func execAppend(i Instr, p *Context) {
@@ -242,8 +249,8 @@ func execTypeCast(i Instr, p *Context) {
 }
 
 func execIndex(i Instr, p *Context) {
-	idx := int(i & setIndexOperand)
-	if idx == setIndexOperand {
+	idx := int(i & opIndexOperand)
+	if idx == opIndexOperand {
 		idx = p.Pop().(int)
 	}
 	n := len(p.data)
@@ -251,6 +258,8 @@ func execIndex(i Instr, p *Context) {
 	if (i & setIndexFlag) != 0 { // value sliceData $idx $setIndex
 		v.Set(reflect.ValueOf(p.data[n-2]))
 		p.PopN(2)
+	} else if (i & addrIndexFlag) != 0 { // sliceData $idx $setIndex
+		p.data[n-1] = v.Addr().Interface()
 	} else { // sliceData $idx $setIndex
 		p.data[n-1] = v.Interface()
 	}
@@ -473,30 +482,42 @@ func (p *Builder) SetMapIndex() *Builder {
 
 // Index instr
 func (p *Builder) Index(idx int) *Builder {
-	if idx >= setIndexOperand {
+	if idx >= opIndexOperand {
 		p.Push(idx)
 		idx = -1
 	}
-	i := (opIndex << bitsOpShift) | uint32(idx&setIndexOperand)
+	i := (opIndex << bitsOpShift) | uint32(idx&opIndexOperand)
 	p.code.data = append(p.code.data, i)
 	return p
 }
 
 // SetIndex instr
 func (p *Builder) SetIndex(idx int) *Builder {
-	if idx >= setIndexOperand {
+	if idx >= opIndexOperand {
+		p.Push(idx)
+		idx = -1
+	}
+	i := (opIndex<<bitsOpShift | setIndexFlag) | uint32(idx&opIndexOperand)
+	p.code.data = append(p.code.data, i)
+	return p
+}
+
+// AddrIndex instr
+func (p *Builder) AddrIndex(idx int) *Builder {
+	if idx >= opIndexOperand {
 		p.Push(idx)
 		idx = -1
 	}
-	i := (opIndex<<bitsOpShift | setIndexFlag) | uint32(idx&setIndexOperand)
+	i := (opIndex<<bitsOpShift | addrIndexFlag) | uint32(idx&opIndexOperand)
 	p.code.data = append(p.code.data, i)
 	return p
 }
 
 const (
-	setIndexFlag    = (1 << 25)
-	setIndexOperand = setIndexFlag - 1
-	sliceIndexMask  = (1 << 13) - 1
+	setIndexFlag   = 1 << 23
+	addrIndexFlag  = 1 << 25
+	opIndexOperand = (1 << 23) - 1
+	sliceIndexMask = (1 << 13) - 1
 	// SliceConstIndexLast - slice const index max
 	SliceConstIndexLast = exec.SliceConstIndexLast
 	// SliceDefaultIndex - unspecified index
diff --git a/exec/bytecode/interface.go b/exec/bytecode/interface.go
index ba7f5542..27a80588 100644
--- a/exec/bytecode/interface.go
+++ b/exec/bytecode/interface.go
@@ -128,8 +128,9 @@ func (p *iBuilder) Default() exec.Builder {
 }
 
 // Default instr
-func (p *iBuilder) Defer(start, end exec.Label) exec.Instr {
-	return ((*Builder)(p)).Defer(start.(*Label), end.(*Label))
+func (p *iBuilder) Defer() exec.Builder {
+	((*Builder)(p)).Defer()
+	return p
 }
 
 // WrapIfErr instr
@@ -366,6 +367,12 @@ func (p *iBuilder) Index(idx int) exec.Builder {
 	return p
 }
 
+// AddrIndex instr
+func (p *iBuilder) AddrIndex(idx int) exec.Builder {
+	((*Builder)(p)).AddrIndex(idx)
+	return p
+}
+
 // SetIndex instr
 func (p *iBuilder) SetIndex(idx int) exec.Builder {
 	((*Builder)(p)).SetIndex(idx)
@@ -422,11 +429,6 @@ func (p *iBuilder) ReservedAsPush(r exec.Reserved, v interface{}) {
 	((*Builder)(p)).ReservedAsPush(r, v)
 }
 
-// ReservedAsInstr sets Reserved as Instr
-func (p *iBuilder) ReservedAsInstr(r exec.Reserved, instr exec.Instr) {
-	((*Builder)(p)).ReservedAsInstr(r, instr)
-}
-
 // GetPackage returns the Go+ package that the Builder works for.
 func (p *iBuilder) GetPackage() exec.Package {
 	return NewPackage(p.code)
@@ -437,4 +439,14 @@ func (p *iBuilder) Resolve() exec.Code {
 	return ((*Builder)(p)).Resolve()
 }
 
+// DefineBlock
+func (p *iBuilder) DefineBlock() exec.Builder {
+	return p
+}
+
+// EndBlock
+func (p *iBuilder) EndBlock() exec.Builder {
+	return p
+}
+
 // -----------------------------------------------------------------------------
diff --git a/exec/bytecode/operator.go b/exec/bytecode/operator.go
index 301f791a..d7adc12d 100644
--- a/exec/bytecode/operator.go
+++ b/exec/bytecode/operator.go
@@ -216,13 +216,13 @@ func (p *Code) builtinOp(kind Kind, op Operator) error {
 }
 
 // BuiltinOp instr
-func (ctx *Builder) BuiltinOp(kind Kind, op Operator) *Builder {
+func (p *Builder) BuiltinOp(kind Kind, op Operator) *Builder {
 	log.Debug("BuiltinOp:", kind, op)
-	err := ctx.code.builtinOp(kind, op)
+	err := p.code.builtinOp(kind, op)
 	if err != nil {
 		panic(err)
 	}
-	return ctx
+	return p
 }
 
 // CallBuiltinOp calls BuiltinOp
diff --git a/exec/bytecode/stack.go b/exec/bytecode/stack.go
new file mode 100644
index 00000000..d56cbfa0
--- /dev/null
+++ b/exec/bytecode/stack.go
@@ -0,0 +1,86 @@
+/*
+ Copyright 2020 The GoPlus Authors (goplus.org)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package bytecode
+
+// -----------------------------------------------------------------------------
+
+const defaultStkSize = 64
+
+// A Stack represents a FILO container.
+type Stack struct {
+	data []interface{}
+}
+
+// NewStack creates a Stack instance.
+func NewStack() (p *Stack) {
+	return &Stack{data: make([]interface{}, 0, defaultStkSize)}
+}
+
+// Init initializes this Stack object.
+func (p *Stack) Init() {
+	p.data = make([]interface{}, 0, defaultStkSize)
+}
+
+// Get returns the value at specified index.
+func (p *Stack) Get(idx int) interface{} {
+	return p.data[len(p.data)+idx]
+}
+
+// Set returns the value at specified index.
+func (p *Stack) Set(idx int, v interface{}) {
+	p.data[len(p.data)+idx] = v
+}
+
+// GetArgs returns all arguments of a function.
+func (p *Stack) GetArgs(arity int) []interface{} {
+	return p.data[len(p.data)-arity:]
+}
+
+// Ret pops n values from this stack, and then pushes results.
+func (p *Stack) Ret(arity int, results ...interface{}) {
+	p.data = append(p.data[:len(p.data)-arity], results...)
+}
+
+// Push pushes a value into this stack.
+func (p *Stack) Push(v interface{}) {
+	p.data = append(p.data, v)
+}
+
+// PopN pops n elements.
+func (p *Stack) PopN(n int) {
+	p.data = p.data[:len(p.data)-n]
+}
+
+// Pop pops a value from this stack.
+func (p *Stack) Pop() interface{} {
+	n := len(p.data)
+	v := p.data[n-1]
+	p.data = p.data[:n-1]
+	return v
+}
+
+// Len returns count of stack elements.
+func (p *Stack) Len() int {
+	return len(p.data)
+}
+
+// SetLen sets count of stack elements.
+func (p *Stack) SetLen(base int) {
+	p.data = p.data[:base]
+}
+
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/var.go b/exec/bytecode/var.go
index ed7fd5cc..2155be39 100644
--- a/exec/bytecode/var.go
+++ b/exec/bytecode/var.go
@@ -157,17 +157,17 @@ func getParentCtx(p *Context, idx tAddress) *varScope {
 }
 
 // GetVar func.
-func (p *Context) GetVar(x *Var) interface{} {
+func (ctx *Context) GetVar(x *Var) interface{} {
 	if x.isGlobal() {
-		return p.getVar(x.idx)
+		return ctx.getVar(x.idx)
 	}
 	panic("variable not defined, or not a global variable")
 }
 
 // SetVar func.
-func (p *Context) SetVar(x *Var, v interface{}) {
+func (ctx *Context) SetVar(x *Var, v interface{}) {
 	if x.isGlobal() {
-		p.setVar(x.idx, v)
+		ctx.setVar(x.idx, v)
 		return
 	}
 	panic("variable not defined, or not a global variable")
@@ -319,8 +319,8 @@ func newBlockCtx(nestDepth uint32, parent *varManager) *blockCtx {
 
 // -----------------------------------------------------------------------------
 
-func (p *Context) getNestDepth() (nestDepth uint32) {
-	vs := &p.varScope
+func (ctx *Context) getNestDepth() (nestDepth uint32) {
+	vs := &ctx.varScope
 	for {
 		if vs = vs.parent; vs == nil {
 			return
diff --git a/exec/golang/builder.go b/exec/golang/builder.go
index e08ba492..5434485b 100644
--- a/exec/golang/builder.go
+++ b/exec/golang/builder.go
@@ -96,6 +96,7 @@ type Builder struct {
 	comprehens  func() // current comprehension
 	identBase   int    // auo-increasement ident index
 	*scopeCtx          // current block scope
+	inDefer     bool   // in defer statement currently
 }
 
 // NewBuilder creates a new Code Builder instance.
@@ -322,11 +323,6 @@ func (p *Builder) ReservedAsPush(r exec.Reserved, v interface{}) {
 	p.reserveds[r].Expr = Const(p, v)
 }
 
-// ReservedAsInstr sets Reserved as Instr
-func (p *Builder) ReservedAsInstr(r exec.Reserved, v exec.Instr) {
-	log.Panicln("todo")
-}
-
 // Pop instr
 func (p *Builder) Pop(n int) *Builder {
 	log.Panicln("todo")
diff --git a/exec/golang/expr.go b/exec/golang/expr.go
index 3e807190..5322ba87 100644
--- a/exec/golang/expr.go
+++ b/exec/golang/expr.go
@@ -366,7 +366,14 @@ func (p *Builder) Call(narg int, ellipsis bool, args ...ast.Expr) *Builder {
 	if ellipsis {
 		expr.Ellipsis++
 	}
-	p.rhs.Push(expr)
+	if p.inDefer {
+		p.inDefer = false
+		p.rhs.Push(&ast.DeferStmt{
+			Call: expr,
+		})
+	} else {
+		p.rhs.Push(expr)
+	}
 	return p
 }
 
@@ -455,9 +462,6 @@ func (p *Builder) MakeArray(typ reflect.Type, arity int) *Builder {
 		Type: typExpr,
 		Elts: elts,
 	}
-	if typ.Kind() == reflect.Array {
-		xExpr = &ast.UnaryExpr{Op: token.AND, X: xExpr}
-	}
 	p.rhs.Ret(arity, xExpr)
 	return p
 }
@@ -498,6 +502,15 @@ func (p *Builder) Index(idx int) *Builder {
 	return p
 }
 
+// AddrIndex instr
+func (p *Builder) AddrIndex(idx int) *Builder {
+	p.rhs.Push(&ast.UnaryExpr{
+		Op: token.AND,
+		X:  IndexWith(p, idx),
+	})
+	return p
+}
+
 // SetIndex instr
 func (p *Builder) SetIndex(idx int) *Builder {
 	p.lhs.Push(IndexWith(p, idx))
diff --git a/exec/golang/interface.go b/exec/golang/interface.go
index 38891c7d..389e719b 100644
--- a/exec/golang/interface.go
+++ b/exec/golang/interface.go
@@ -167,8 +167,9 @@ func (p *iBuilder) ForPhrase(f exec.ForPhrase, key, val exec.Var, hasExecCtx ...
 }
 
 // Defer instr
-func (p *iBuilder) Defer(start, end exec.Label) exec.Instr {
-	return ((*Builder)(p)).Defer(end.(*Label), end.(*Label))
+func (p *iBuilder) Defer() exec.Builder {
+	((*Builder)(p)).Defer()
+	return p
 }
 
 func toVar(v exec.Var) *Var {
@@ -381,6 +382,12 @@ func (p *iBuilder) Index(idx int) exec.Builder {
 	return p
 }
 
+// AddrIndex instr
+func (p *iBuilder) AddrIndex(idx int) exec.Builder {
+	((*Builder)(p)).AddrIndex(idx)
+	return p
+}
+
 // SetIndex instr
 func (p *iBuilder) SetIndex(idx int) exec.Builder {
 	((*Builder)(p)).SetIndex(idx)
@@ -438,11 +445,6 @@ func (p *iBuilder) ReservedAsPush(r exec.Reserved, v interface{}) {
 	((*Builder)(p)).ReservedAsPush(r, v)
 }
 
-// ReservedAsInstr sets Reserved as Instr
-func (p *iBuilder) ReservedAsInstr(r exec.Reserved, instr exec.Instr) {
-	((*Builder)(p)).ReservedAsInstr(r, instr)
-}
-
 // GetPackage returns the Go+ package that the Builder works for.
 func (p *iBuilder) GetPackage() exec.Package {
 	return defaultImpl
@@ -453,4 +455,16 @@ func (p *iBuilder) Resolve() exec.Code {
 	return ((*Builder)(p)).Resolve()
 }
 
+// DefineBlock
+func (p *iBuilder) DefineBlock() exec.Builder {
+	((*Builder)(p)).DefineBlock()
+	return p
+}
+
+// EndBlock
+func (p *iBuilder) EndBlock() exec.Builder {
+	((*Builder)(p)).EndBlock()
+	return p
+}
+
 // -----------------------------------------------------------------------------
diff --git a/exec/golang/stmt.go b/exec/golang/stmt.go
index 539253ae..55e34f43 100644
--- a/exec/golang/stmt.go
+++ b/exec/golang/stmt.go
@@ -393,8 +393,50 @@ func (p *Builder) EndComprehension(c *Comprehension) *Builder {
 	return p
 }
 
-func (p *Builder) Defer(start, end *Label) exec.Instr {
-	panic("The method defer under the builder of golang is not yet supported")
+// Defer instr
+func (p *Builder) Defer() *Builder {
+	p.inDefer = true
+	return p
+}
+
+// DefineBlock starts a new block.
+func (p *Builder) DefineBlock() *Builder {
+	p.scopeCtx = &scopeCtx{parentCtx: p.scopeCtx}
+	p.initStmts()
+	return p
+}
+
+// EndBlock ends a block.
+func (p *Builder) EndBlock() *Builder {
+	p.endBlockStmt(0)
+	blockStmt := getBlockStmts(p)
+	if p.parentCtx == nil {
+		p.rhs.Push(blockStmt)
+	} else {
+		p.parentCtx.stmts = append(p.parentCtx.stmts, blockStmt)
+		p.scopeCtx = p.parentCtx
+	}
+	return p
+}
+
+// getBlockStmts will check whether the first stmt in block stmts is labeledStmt.
+// if true ,the whole block should be labeled
+func getBlockStmts(p *Builder) ast.Stmt {
+	body := &ast.BlockStmt{List: p.getStmts(p)}
+	if len(body.List) > 0 {
+		for i := 0; i < len(body.List); i++ {
+			if _, ok := body.List[i].(*ast.DeclStmt); ok {
+				continue
+			}
+			if v, ok := body.List[i].(*ast.LabeledStmt); ok {
+				body.List[i] = v.Stmt
+				v.Stmt = body
+				return v
+			}
+			break
+		}
+	}
+	return body
 }
 
 // ----------------------------------------------------------------------------
diff --git a/exec/golang/var.go b/exec/golang/var.go
index b71c486a..7dc0c940 100644
--- a/exec/golang/var.go
+++ b/exec/golang/var.go
@@ -69,8 +69,9 @@ func (p *Var) setScope(where *scopeCtx) {
 // -----------------------------------------------------------------------------
 
 type scopeCtx struct {
-	vlist []exec.Var
-	stmts []ast.Stmt
+	parentCtx *scopeCtx
+	vlist     []exec.Var
+	stmts     []ast.Stmt
 }
 
 func (p *scopeCtx) addVar(vars ...exec.Var) {
diff --git a/go.mod b/go.mod
index 40cae4fd..6b1deeb9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,5 +3,6 @@ module github.com/goplus/gop
 go 1.14
 
 require (
+	github.com/peterh/liner v1.2.0
 	github.com/qiniu/x v1.11.5
 )
diff --git a/go.sum b/go.sum
index bebef3bc..72a2c58b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,6 @@
+github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg=
+github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
 github.com/qiniu/x v1.11.5 h1:TYr5cl4g2yoHAZeDK4MTjKF6CMoG+IHlCDvvM5qym6U=
 github.com/qiniu/x v1.11.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
diff --git a/parser/parse.go b/parser/parse.go
index 1c91abd6..99e58f24 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -56,26 +56,16 @@ func (p localFS) Join(elem ...string) string {
 
 var local FileSystem = localFS{}
 
-// ParseGopFiles parses the Go+ source files under directory or single Go+ source file.
-// The target specifies the directory or single Go+ source file.
-//
-// The ParseGopFiles should return the map of packages to run Go+ script, even the target is single file.
-//
-// If the file or directory couldn't be read, a nil map and the respective error are
-// returned.
-// If the target is directory and a parse error occurred, a non-nil but incomplete map and the first error encountered are returned.
-func ParseGopFiles(fset *token.FileSet, target string, isDir bool, mode Mode) (pkgs map[string]*ast.Package, err error) {
-	if !isDir {
-		file, err := ParseFile(fset, target, nil, mode)
-		if err != nil {
-			return pkgs, err
-		}
-		pkgs = make(map[string]*ast.Package)
-		pkg := astFileToPkg(file, target)
-		pkgs[file.Name.Name] = pkg
-		return pkgs, nil
+// Parse parses a single Go+ source file. The target specifies the Go+ source file.
+// If the file couldn't be read, a nil map and the respective error are returned.
+func Parse(fset *token.FileSet, target string, src interface{}, mode Mode) (pkgs map[string]*ast.Package, err error) {
+	file, err := ParseFile(fset, target, src, mode)
+	if err != nil {
+		return
 	}
-	return ParseDir(fset, target, nil, mode)
+	pkgs = make(map[string]*ast.Package)
+	pkgs[file.Name.Name] = astFileToPkg(file, target)
+	return
 }
 
 // astFileToPkg translate ast.File to ast.Package
diff --git a/tutorial/22-Defer/defer.gop b/tutorial/22-Defer/defer.gop
deleted file mode 100644
index ee323868..00000000
--- a/tutorial/22-Defer/defer.gop
+++ /dev/null
@@ -1,18 +0,0 @@
-func test() {
-	defer func() {
-		println("Hello, test defer!")
-	}()
-	println("Hello, test!")
-}
-
-defer func() {
-	println("Hello, defer1!")
-}()
-defer func() {
-	println("Hello, defer2!")
-}()
-defer func() {
-	println("Hello, defer3!")
-}()
-println("Hello, world!")
-test()
diff --git a/tutorial/23-Defer/defer.gop b/tutorial/23-Defer/defer.gop
new file mode 100644
index 00000000..7ea750ff
--- /dev/null
+++ b/tutorial/23-Defer/defer.gop
@@ -0,0 +1,31 @@
+func f() (x int) {
+	defer func() {
+		x = 3
+	}()
+	return 1
+}
+
+func g() (x int) {
+	defer func() {
+		x = 3
+	}()
+	x = 1
+	return
+}
+
+func h() (x int) {
+	for i <- [3, 2, 1] {
+		v := i
+		defer func() {
+			x = v
+		}()
+	}
+	return
+}
+
+println("f-x:", f())
+println("g-x:", g())
+println("h-x:", h())
+
+defer println(println("Hello, defer"))
+println("Hello, Go+")


Test patch:
diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go
index 8380ead9..6f2516c5 100644
--- a/cl/cltest/cltest.go
+++ b/cl/cltest/cltest.go
@@ -48,9 +48,7 @@ func testFrom(t *testing.T, pkgDir, sel, exclude string) {
 		t.Fatal("Compile failed:", err)
 	}
 	code := b.Resolve()
-
-	ctx := exec.NewContext(code)
-	ctx.Exec(0, code.Len())
+	exec.NewContext(code).Run()
 }
 
 // FromTestdata - run test cases from a directory
diff --git a/cl/cltest/testing.go b/cl/cltest/testing.go
index 5e0b9cb5..1f2610b7 100644
--- a/cl/cltest/testing.go
+++ b/cl/cltest/testing.go
@@ -1,6 +1,8 @@
 package cltest
 
 import (
+	"fmt"
+	"os"
 	"testing"
 
 	"github.com/goplus/gop/ast/asttest"
@@ -36,9 +38,10 @@ func Expect(t *testing.T, script string, expected string, panicMsg ...interface{
 		}
 		cl.Debug(pkg)
 		code := b.Resolve()
-		ctx := exec.NewContext(code)
-		ctx.Exec(0, code.Len())
-	}).Expect(expected).Panic(panicMsg...)
+		code.Dump(os.Stderr)
+		fmt.Fprintln(os.Stderr)
+		exec.NewContext(code).Run()
+	}).Panic(panicMsg...).Expect(expected)
 }
 
 // -----------------------------------------------------------------------------
@@ -62,7 +65,7 @@ func Call(t *testing.T, script string, idx ...int) *ts.TestCase {
 		cl.Debug(pkg)
 		code := b.Resolve()
 		ctx := exec.NewContext(code)
-		ctx.Exec(0, code.Len())
+		ctx.Run()
 		return ctx.Get(append(idx, -1)[0])
 	})
 }
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 8d71ad66..90f9f664 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -205,15 +205,22 @@ func TestArray(t *testing.T) {
 		y := [...]float64{1, 2.3, 3.6}
 		println("y:", y)
 		`,
-		"x: &[2.3 3.6 0 0]\ny: &[1 2.3 3.6]\n",
+		"x: [2.3 3.6 0 0]\ny: [1 2.3 3.6]\n",
 	)
 	cltest.Expect(t, `
 		x := [...]float64{1, 3: 3.4, 5}
 		x[1] = 217
 		println("x:", x, "x[1]:", x[1])
 		`,
-		"x: &[1 217 0 3.4 5] x[1]: 217\n",
+		"x: [1 217 0 3.4 5] x[1]: 217\n",
 	)
+	cltest.Expect(t, `
+		x := [...]float64{1, 2.3, 3, 4}
+		x[2] = 3.1
+		println("x[1:]:", x[1:])
+		println(len(x))
+	`,
+		"x[1:]: [2.3 3.1 4]\n4\n")
 }
 
 func TestMap(t *testing.T) {
diff --git a/cl/goinstr_test.go b/cl/goinstr_test.go
new file mode 100644
index 00000000..1f987614
--- /dev/null
+++ b/cl/goinstr_test.go
@@ -0,0 +1,66 @@
+package cl
+
+import (
+	"testing"
+
+	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/token"
+	"github.com/qiniu/x/ts"
+
+	exec "github.com/goplus/gop/exec/bytecode"
+)
+
+// -----------------------------------------------------------------------------
+
+var instrDeferDiscardsResult = map[string]goInstrInfo{
+	"len":     {igoLen},
+	"cap":     {igoCap},
+	"make":    {igoMake},
+	"new":     {igoNew},
+	"complex": {igoComplex},
+	"real":    {igoReal},
+	"imag":    {igoImag},
+}
+
+func TestDeferFileNotFound(t *testing.T) {
+	b := exec.NewBuilder(nil)
+	pkg := &ast.Package{
+		Files: map[string]*ast.File{},
+	}
+	pkgCtx := newPkgCtx(b.Interface(), pkg, token.NewFileSet())
+	ctx := newGblBlockCtx(pkgCtx)
+	ts.New(t).Call(func() {
+		fn := &ast.Ident{Name: "len"}
+		expr := &ast.CallExpr{Fun: fn}
+		igoLen(ctx, expr, true)
+	}).Panic(
+		"pkgCtx.getCodeInfo failed: file not found - \n",
+	)
+}
+
+func TestDeferDiscardsResult(t *testing.T) {
+	for k, v := range instrDeferDiscardsResult {
+		b := exec.NewBuilder(nil)
+		file := &ast.File{
+			Code: []byte(k + "()\n"),
+		}
+		pkg := &ast.Package{
+			Files: map[string]*ast.File{
+				"bar.gop": file,
+			},
+		}
+		fset := token.NewFileSet()
+		fset.AddFile("bar.gop", 1, 100)
+		pkgCtx := newPkgCtx(b.Interface(), pkg, fset)
+		ctx := newGblBlockCtx(pkgCtx)
+		ts.New(t).Call(func() {
+			fn := &ast.Ident{NamePos: 1, Name: k}
+			expr := &ast.CallExpr{Fun: fn, Rparen: token.Pos(len(k) + 2)}
+			v.instr(ctx, expr, true)
+		}).Panic(
+			"defer discards result of " + k + "()\n",
+		)
+	}
+}
+
+// -----------------------------------------------------------------------------
diff --git a/cl/stmt_test.go b/cl/stmt_test.go
index afdc9468..44749468 100644
--- a/cl/stmt_test.go
+++ b/cl/stmt_test.go
@@ -1158,16 +1158,6 @@ func TestMapForPhraseWithBranch(t *testing.T) {
 // -----------------------------------------------------------------------------
 
 var testDeferClauses = map[string]testData{
-	"func_in_defer_func": {clause: `
-		defer println(println("hello world"))
-		println("test defer")
-		`, want: "hello world\ntest defer\n12 <nil>\n"},
-	"func_in_defer_for": {clause: `
-		for i:=0;i<10;i++ {
-			defer println(i)
-		}
-		println("test defer")
-		`, want: "test defer\n9\n8\n7\n6\n5\n4\n3\n2\n1\n0\n"},
 	"multi_defer": {clause: `
 	func test() {
 		defer println("Hello, test defer!")
@@ -1185,7 +1175,7 @@ var testDeferClauses = map[string]testData{
 		defer println("Hello, test defer!")
 		println("Hello, test!",i)
 	}
-	
+
 	defer println("Hello, defer1!")
 	defer println("Hello, defer2!")
 	defer println("Hello, defer3!")
@@ -1238,45 +1228,15 @@ var testDeferClauses = map[string]testData{
 		defer myprint("hello %s\n", "defer")
 		myprint("hello %s\n", "world")
 		`, want: "this is test print: hello world\nthis is test print: hello defer\n"},
-	"unnamed_func4": {clause: `
-	import "strings"
-
-	f2 := func(s string, s2 string) {
-		defer func() {
-			println(s)
-			println(s2)
-		}()
-		s = strings.Replace(s, "?", "!", -1)
-		s2 = strings.Replace(s2, "?", "!", -1)
-	}
-	s := "hello world???"
-	s2 := "hello world????"
-	defer f2(s, s2)
-	
-	println(s)
-	println(s2)
-		`, want: "hello world???\nhello world????\nhello world!!!\nhello world!!!!\n"},
-	"unnamed_func5": {clause: `
-	import (
-		"fmt"
-	)
-	
-	defer func(format string, a ...interface{}) {
-		format = format
-		fmt.Printf(format, a...)
-	}("hello %s\n", "defer")
-	
-	printf("hello %s\n", "world")
-		`, want: "hello world\nhello defer\n"},
 	"unnamed_func6": {clause: `
 	import (
 		"fmt"
 	)
-	
+
 	myprint := func() {
 		fmt.Printf("hello defer\n")
 	}
-	
+
 	defer myprint()
 	printf("hello %s\n", "world")
 		`, want: "hello world\nhello defer\n"},
@@ -1292,16 +1252,162 @@ var testDeferClauses = map[string]testData{
 	defer fmt.Println("hello defer")
 	myprint()
 		`, want: "hello world\nhello defer\n"},
-	"nonvalue": {clause: `
-	a := [1, 2, 3]
-	b := [4, 5, 6]
-	defer func() {
-		println(b)
-	}()
-	defer copy(b, a)
-		`, want: "[1 2 3]\n"},
 }
 
 func TestDeferStmt(t *testing.T) {
 	testScripts(t, "TestDeferStmt", testDeferClauses)
 }
+
+func TestDeferCopy(t *testing.T) {
+	cltest.Expect(t, `
+		a := [1, 2, 3]
+		b := [4, 5, 6]
+		defer func() {
+			println(b)
+		}()
+		defer copy(b, a)
+		`,
+		"[1 2 3]\n",
+	)
+}
+
+func TestDefer1(t *testing.T) {
+	cltest.Expect(t, `
+		defer println(println("hello world"))
+		println("test defer")
+		`,
+		"hello world\ntest defer\n12 <nil>\n",
+	)
+}
+
+func TestDeferInFunc(t *testing.T) {
+	cltest.Expect(t, `
+		func f() {
+			defer println(println("hello world"))
+			println("test defer")
+		}
+		defer println("hello main")
+		f()
+		f()
+		`,
+		"hello world\ntest defer\n12 <nil>\n"+
+			"hello world\ntest defer\n12 <nil>\n"+
+			"hello main\n",
+	)
+}
+
+func TestDeferInClosure(t *testing.T) {
+	cltest.Expect(t, `
+		defer func() {
+			defer println(println("hello world"))
+			println("test defer")
+		}()
+		`,
+		"hello world\ntest defer\n12 <nil>\n",
+	)
+}
+
+func TestDeferInFor(t *testing.T) {
+	cltest.Expect(t, `
+		for i:=0;i<10;i++ {
+			defer println(i)
+		}
+		println("test defer")
+		`,
+		"test defer\n9\n8\n7\n6\n5\n4\n3\n2\n1\n0\n",
+	)
+}
+
+func TestDefer4(t *testing.T) {
+	cltest.Expect(t, `
+		import "strings"
+
+		f2 := func(s string, s2 string) {
+			defer func() {
+				println(s)
+				println(s2)
+			}()
+			s = strings.Replace(s, "?", "!", -1)
+			s2 = strings.Replace(s2, "?", "!", -1)
+		}
+		s := "hello world???"
+		s2 := "hello world????"
+		defer f2(s, s2)
+
+		println(s)
+		println(s2)
+		`,
+		"hello world???\nhello world????\nhello world!!!\nhello world!!!!\n",
+	)
+}
+
+func TestDefer5(t *testing.T) {
+	cltest.Expect(t, `
+		import "fmt"
+
+		defer func(format string, a ...interface{}) {
+			format = format
+			fmt.Printf(format, a...)
+		}("hello %s\n", "defer")
+
+		printf("hello %s\n", "world")
+		`,
+		"hello world\nhello defer\n",
+	)
+}
+
+func TestDefer6(t *testing.T) {
+	cltest.Expect(t, `
+		func f() (x int) {
+			defer func() {
+				x = 3
+			}()
+			return 1
+		}
+		println(f())
+		`,
+		"3\n",
+	)
+}
+
+func TestDefer7(t *testing.T) {
+	cltest.Expect(t, `
+		func h() (x int) {
+			for i <- [3, 2, 1] {
+				v := i
+				defer func() {
+					x = v
+				}()
+			}
+			return
+		}
+		println(h())
+		`,
+		"3\n",
+	)
+}
+
+// -----------------------------------------------------------------------------
+
+var testVarScopeClauses = map[string]testData{
+	"variable_redefinition_#issue304": {`
+						a := []float64{1, 2, 3.4}
+						println(a)
+						
+						a := []float64{1, 2, 3.4}
+						println(a)
+					`, "", true},
+	"variable_scope_bug_#issue303": {`
+						i:=0
+						{
+						    i:=0
+						    i++
+						    println("inner is",i)
+						}
+						println("outer is",i)
+					`, "inner is 1\nouter is 0\n", false},
+}
+
+func TestVarScopeStmt(t *testing.T) {
+	testScripts(t, "TestVarScopeStmt", testVarScopeClauses)
+}
diff --git a/cmd/internal/export/gopkg/exporter_test.go b/cmd/internal/export/gopkg/exporter_test.go
index f3d5bd8d..7f23caa5 100644
--- a/cmd/internal/export/gopkg/exporter_test.go
+++ b/cmd/internal/export/gopkg/exporter_test.go
@@ -8,6 +8,7 @@ import (
 	"go/types"
 	"io/ioutil"
 	"os/exec"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -179,6 +180,61 @@ func TestImport(t *testing.T) {
 	}
 }
 
+func TestLooupMod(t *testing.T) {
+	dir, err := LookupMod("github.com/qiniu/x/log")
+	if err != nil {
+		t.Fatal("LookupMod failed:", err)
+	}
+	i := strings.Index(dir, "github.com")
+	dir1, err := LookupMod(filepath.ToSlash(dir[i:]))
+	if err != nil {
+		t.Fatal("LookupMod failed:", err)
+	}
+	if dir != dir1 {
+		t.Fatal("LookupMod failed:", dir, dir1)
+	}
+}
+
+func TestExportVersion(t *testing.T) {
+	pkgPath := "github.com/qiniu/x/log"
+	dir, err := LookupMod(pkgPath)
+	if err != nil {
+		t.Fatal("LookupMod failed:", err)
+	}
+	pkgPath = filepath.ToSlash(dir[len(getModRoot())+1:])
+	err = Export(pkgPath, ioutil.Discard)
+	if err != nil {
+		t.Fatal("TestExport failed:", err)
+	}
+}
+
+func TestCleanVersion(t *testing.T) {
+	pkg := cleanPkgVer("github.com/qiniu/x@v1.11.5/log")
+	if pkg != "github.com/qiniu/x/log" {
+		t.Fatal("cleanPkgVer failed:", pkg)
+	}
+	pkg = cleanPkgVer("github.com/qiniu/x@v1.11.5")
+	if pkg != "github.com/qiniu/x" {
+		t.Fatal("cleanPkgVer failed:", pkg)
+	}
+}
+
+func TestImportSource(t *testing.T) {
+	pkgPath := "github.com/qiniu/x/log"
+	dir, err := LookupMod(pkgPath)
+	if err != nil {
+		t.Fatal("LookupMod failed:", err)
+	}
+	pkg, err := ImportSource(pkgPath, dir)
+	if err != nil {
+		t.Fatal("ImportSource failed:", err)
+	}
+	err = ExportPackage(pkg, ioutil.Discard)
+	if err != nil {
+		t.Fatal("ExportPackage failed:", err)
+	}
+}
+
 func TestBadImport(t *testing.T) {
 	_, err := Import("go/badtypes")
 	if err == nil {
diff --git a/exec/bytecode/code_test.go b/exec/bytecode/code_test.go
index c79d7739..da23fc7c 100644
--- a/exec/bytecode/code_test.go
+++ b/exec/bytecode/code_test.go
@@ -17,8 +17,11 @@
 package bytecode
 
 import (
+	"testing"
+
 	"github.com/goplus/gop/exec.spec"
 	"github.com/qiniu/x/log"
+	"github.com/qiniu/x/ts"
 )
 
 var (
@@ -108,3 +111,11 @@ func checkPop(ctx *Context) interface{} {
 }
 
 // -----------------------------------------------------------------------------
+
+func expect(t *testing.T, f func(), expected string, panicMsg ...interface{}) {
+	e := ts.StartExpecting(t, ts.CapStdout)
+	defer e.Close()
+	e.Call(f).Panic(panicMsg...).Expect(expected)
+}
+
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/defer_test.go b/exec/bytecode/defer_test.go
index 303e5511..d18eb8d0 100644
--- a/exec/bytecode/defer_test.go
+++ b/exec/bytecode/defer_test.go
@@ -16,45 +16,33 @@
 package bytecode
 
 import (
+	"os"
 	"testing"
 )
 
-func TestDefer1(t *testing.T) {
-	b := newBuilder()
-	off := b.Reserve()
-	b.ReservedAsInstr(off, &iDefer{start: 1, end: 3})
-	code := b.Resolve()
-
-	ctx := NewContext(code)
+// -----------------------------------------------------------------------------
 
-	if i := ctx.code.data[0] >> bitsOpShift; i != opDeferOp {
-		t.Fatal("opDeferOp != opDeferOp, ret =", i)
+func TestDefer(t *testing.T) {
+	println, ok := I.FindFuncv("Println")
+	if !ok {
+		t.Fatal("FindFuncv failed: Println")
 	}
-}
-
-func TestDefer2(t *testing.T) {
 	b := newBuilder()
-	l1 := NewLabel("")
-	l2 := NewLabel("")
-	off := b.Reserve()
-	b.Label(l1)
-	b.Push(1)
-	b.Push(2)
-	b.Push(3)
-	b.Label(l2)
-	b.ReservedAsInstr(off, b.Defer(l1, l2))
-	code := b.Resolve()
-	ctx := NewContext(code)
-	ctx.Exec(0, code.Len())
-	var i = 3
-	for {
-		if ctx.Len() < 1 {
-			break
-		}
-		v := ctx.Pop()
-		if v != i {
-			t.Fatalf("v != %d, ret = %v", i, v)
-		}
-		i--
-	}
+	expect(t,
+		func() {
+			b.Push("Hello, defer")
+			b.CallGoFuncv(println, 1, 1)
+			b.Defer().CallGoFuncv(println, 1, 2)
+			b.Push("Hello, Go+")
+			b.CallGoFuncv(println, 1, 1)
+			code := b.Resolve()
+			code.(*Code).Dump(os.Stderr)
+			ctx := NewContext(code)
+			ctx.Exec(0, code.Len())
+			ctx.execDefers()
+		},
+		"Hello, defer\nHello, Go+\n13 <nil>\n",
+	)
 }
+
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/goinstr_test.go b/exec/bytecode/goinstr_test.go
index 52400c16..66a465ea 100644
--- a/exec/bytecode/goinstr_test.go
+++ b/exec/bytecode/goinstr_test.go
@@ -53,7 +53,7 @@ func TestLargeArray(t *testing.T) {
 
 	ctx := NewContext(code)
 	ctx.Exec(0, code.Len())
-	if v := checkPop(ctx); !reflect.DeepEqual(v, &ret) {
+	if v := checkPop(ctx); !reflect.DeepEqual(v, ret) {
 		t.Fatal("32 times(1024) mkslice != `32` times(1024) slice, ret =", v)
 	}
 }
@@ -357,6 +357,53 @@ func TestIndex2(t *testing.T) {
 	}
 }
 
+func TestAddrIndex(t *testing.T) {
+	a := NewVar(reflect.SliceOf(TyFloat64), "")
+	code := newBuilder().
+		DefineVar(a).
+		Push(0.7).
+		Push(3.2).
+		Push(1.2).
+		Push(2.4).
+		MakeArray(reflect.SliceOf(TyFloat64), 3).
+		StoreVar(a).
+		LoadVar(a).
+		Push(2).
+		SetIndex(-1).
+		LoadVar(a).
+		AddrIndex(2).
+		AddrOp(Float64, OpAddrVal).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != 0.7 {
+		t.Fatal("[3.2, 1.2, 0.7], ret:", v)
+	}
+}
+
+func TestAddrLargeIndex(t *testing.T) {
+	a := NewVar(reflect.SliceOf(TyFloat64), "")
+	code := newBuilder().
+		DefineVar(a).
+		Push(opIndexOperand+1).
+		Make(reflect.SliceOf(TyFloat64), 1).
+		StoreVar(a).
+		Push(1.7).
+		LoadVar(a).
+		SetIndex(opIndexOperand).
+		LoadVar(a).
+		AddrIndex(opIndexOperand).
+		AddrOp(Float64, OpAddrVal).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	if v := checkPop(ctx); v != 1.7 {
+		t.Fatal("v != 1.7, ret:", v)
+	}
+}
+
 func TestSetIndex(t *testing.T) {
 	a := NewVar(reflect.SliceOf(TyFloat64), "")
 	code := newBuilder().
@@ -384,14 +431,14 @@ func TestSetLargeIndex(t *testing.T) {
 	a := NewVar(reflect.SliceOf(TyFloat64), "")
 	code := newBuilder().
 		DefineVar(a).
-		Push(setIndexOperand+1).
+		Push(opIndexOperand+1).
 		Make(reflect.SliceOf(TyFloat64), 1).
 		StoreVar(a).
 		Push(1.7).
 		LoadVar(a).
-		SetIndex(setIndexOperand).
+		SetIndex(opIndexOperand).
 		LoadVar(a).
-		Index(setIndexOperand).
+		Index(opIndexOperand).
 		Resolve()
 
 	ctx := NewContext(code)
diff --git a/exec/bytecode/gopkg_test.go b/exec/bytecode/gopkg_test.go
index fe85ae1a..154f66ae 100644
--- a/exec/bytecode/gopkg_test.go
+++ b/exec/bytecode/gopkg_test.go
@@ -43,6 +43,12 @@ func execStrcat(arity int, p *Context) {
 	p.Ret(2, ret)
 }
 
+func execPrintln(arity int, p *Context) {
+	args := p.GetArgs(arity)
+	n, err := fmt.Println(args...)
+	p.Ret(arity, n, err)
+}
+
 func execSprint(arity int, p *Context) {
 	args := p.GetArgs(arity)
 	s := fmt.Sprint(args...)
@@ -69,6 +75,7 @@ func init() {
 		I.Funcv("Sprint", fmt.Sprint, execSprint),
 		I.Funcv("Sprintf", fmt.Sprintf, execSprintf),
 		I.Funcv("Errorf", fmt.Errorf, execErrorf),
+		I.Funcv("Println", fmt.Println, execPrintln),
 	)
 	I.RegisterFuncs(
 		I.Func("strcat", Strcat, execStrcat),
diff --git a/exec/bytecode/stack_test.go b/exec/bytecode/stack_test.go
new file mode 100644
index 00000000..728c1b43
--- /dev/null
+++ b/exec/bytecode/stack_test.go
@@ -0,0 +1,39 @@
+/*
+ Copyright 2020 The GoPlus Authors (goplus.org)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package bytecode
+
+import (
+	"testing"
+
+	"github.com/qiniu/x/ts"
+)
+
+// -----------------------------------------------------------------------------
+
+func TestStack(test *testing.T) {
+	t := ts.New(test)
+	stk := NewStack()
+	stk.Push(1)
+	stk.Push(2)
+	stk.Push(3)
+	stk.Set(-1, 4)
+	t.Call(stk.Get, -1).Equal(4)
+	stk.SetLen(2)
+	t.Call(stk.Get, -1).Equal(2)
+}
+
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/var_test.go b/exec/bytecode/var_test.go
index edfdee7a..7ab82041 100644
--- a/exec/bytecode/var_test.go
+++ b/exec/bytecode/var_test.go
@@ -135,4 +135,51 @@ func TestAddrVar(t *testing.T) {
 	}
 }
 
+func TestContext_CloneSetVarScope(t *testing.T) {
+	sprint, ok := I.FindFuncv("Sprint")
+	strcat, ok2 := I.FindFunc("strcat")
+	if !ok || !ok2 {
+		t.Fatal("FindFunc failed: Sprintf/strcat")
+	}
+
+	x := defaultImpl.NewVar(TyString, "x").(*Var)
+	y := NewVar(TyString, "y")
+	b := newBuilder()
+	code := b.
+		DefineVar(x, y).
+		Push(5).
+		Push("32").
+		CallGoFuncv(sprint, 2, 2).
+		StoreVar(x). // x = sprint(5, "32")
+		Push("78").
+		LoadVar(x).
+		CallGoFunc(strcat, 2).
+		StoreVar(y). // y = strcat("78", x)
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	z := defaultImpl.NewVar(TyString, "z").(*Var)
+	h := NewVar(TyString, "h")
+	b1 := newBuilder()
+	code = b1.
+		DefineVar(z, h).
+		Push(5).
+		Push("32").
+		CallGoFuncv(sprint, 2, 2).
+		StoreVar(z).
+		Push("78").
+		LoadVar(z).
+		CallGoFunc(strcat, 2).
+		StoreVar(h).
+		Resolve()
+
+	ctxNew := NewContext(code)
+	ctx.CloneSetVarScope(ctxNew)
+	if ctxNew.GetVar(y) != ctx.GetVar(y) {
+		t.Fatal("clone set var scop err")
+	}
+}
+
 // -----------------------------------------------------------------------------
diff --git a/exec/golang/builder_test.go b/exec/golang/builder_test.go
index 3dda5dde..e5cf3e66 100644
--- a/exec/golang/builder_test.go
+++ b/exec/golang/builder_test.go
@@ -70,6 +70,50 @@ func main() {
 	}
 }
 
+func TestIndex(t *testing.T) {
+	codeExp := `package main
+
+import fmt "fmt"
+
+var a []float64
+
+func main() {
+	a = []float64{3.2, 1.2, 2.4}
+	a[1] = 1.6
+	fmt.Println(a[0], &a[1])
+}
+`
+	println, _ := I.FindFuncv("println")
+	a := NewVar(reflect.SliceOf(exec.TyFloat64), "a")
+	code := NewBuilder("main", nil, nil).Interface().
+		DefineVar(a).
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(3.2).
+		Push(1.2).
+		Push(2.4).
+		MakeArray(reflect.SliceOf(exec.TyFloat64), 3).
+		StoreVar(a).
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(1.6).
+		LoadVar(a).
+		Push(1).
+		SetIndex(-1).
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		LoadVar(a).
+		Index(0).
+		LoadVar(a).
+		AddrIndex(1).
+		CallGoFuncv(println, 2, 2).
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Resolve()
+
+	codeGen := code.(*Code).String()
+	if codeGen != codeExp {
+		fmt.Println(codeGen)
+		t.Fatal("TestBasic failed: codeGen != codeExp")
+	}
+}
+
 func TestGoVar(t *testing.T) {
 	codeExp := `package main
 
@@ -101,7 +145,7 @@ func main() {
 		t.Fatal("FindVar failed:", y)
 	}
 
-	code := NewBuilder("main", nil, nil).
+	code := NewBuilder("main", nil, nil).Interface().
 		Push(5).
 		Push(6).
 		StoreGoVar(y).
@@ -118,7 +162,7 @@ func main() {
 		EndStmt(nil, &stmtState{rhsBase: 0}).
 		Resolve()
 
-	codeGen := code.String()
+	codeGen := code.(*Code).String()
 	if codeGen != codeExp {
 		fmt.Println(codeGen)
 		t.Fatal("TestGoVar failed: codeGen != codeExp")
@@ -136,27 +180,4 @@ func TestReserved(t *testing.T) {
 	}
 }
 
-func TestReserved2(t *testing.T) {
-	code := NewBuilder("main", nil, nil)
-	defer func() {
-		err := recover()
-		if !reflect.DeepEqual(err, "The method defer under the builder of golang is not yet supported") {
-			t.Fatal("TestReserved failed: Defer is not yet supported now")
-		}
-	}()
-	var start = NewLabel("")
-	var end = NewLabel("")
-	code.Interface().Defer(start, end)
-}
-
-func TestReserved3(t *testing.T) {
-	code := NewBuilder("main", nil, nil)
-	off := code.Reserve()
-	defer func() {
-		err := recover()
-		if !reflect.DeepEqual(err, "todo\n") {
-			t.Fatal("TestReserved failed: ReservedAsInstr is todo now")
-		}
-	}()
-	code.Interface().ReservedAsInstr(off, nil)
-}
+// -----------------------------------------------------------------------------
diff --git a/exec/golang/testdata/01.const/bar.gop b/exec/golang/testdata/01.const/bar.gop
deleted file mode 100644
index c46de522..00000000
--- a/exec/golang/testdata/01.const/bar.gop
+++ /dev/null
@@ -1,1 +0,0 @@
-println("Hello "+"qiniu:", 123, 4.5, 7i)
diff --git a/exec/golang/testdata/01.var-const/var_const.gop b/exec/golang/testdata/01.var-const/var_const.gop
new file mode 100644
index 00000000..d9be9099
--- /dev/null
+++ b/exec/golang/testdata/01.var-const/var_const.gop
@@ -0,0 +1,26 @@
+println("Hello "+"qiniu:", 123, 4.5, 7i)
+
+i := 0
+{
+	i := 0
+	i++
+	println("inner is", i)
+}
+println("outer is", i)
+println("start")
+{
+	i := 0
+	{
+		j := 2
+		{
+			c := 3
+			println(i, j, c)
+		}
+		j++
+		i++
+		println(i, j)
+	}
+	i++
+	println(i)
+}
+println("end")
diff --git a/exec/golang/testdata/24.defer/defer.gop b/exec/golang/testdata/24.defer/defer.gop
new file mode 100644
index 00000000..7ea750ff
--- /dev/null
+++ b/exec/golang/testdata/24.defer/defer.gop
@@ -0,0 +1,31 @@
+func f() (x int) {
+	defer func() {
+		x = 3
+	}()
+	return 1
+}
+
+func g() (x int) {
+	defer func() {
+		x = 3
+	}()
+	x = 1
+	return
+}
+
+func h() (x int) {
+	for i <- [3, 2, 1] {
+		v := i
+		defer func() {
+			x = v
+		}()
+	}
+	return
+}
+
+println("f-x:", f())
+println("g-x:", g())
+println("h-x:", h())
+
+defer println(println("Hello, defer"))
+println("Hello, Go+")
diff --git a/parser/parse_test.go b/parser/parse_test.go
index c3a5e6b5..7ac9e6d7 100644
--- a/parser/parse_test.go
+++ b/parser/parse_test.go
@@ -17,8 +17,10 @@
 package parser
 
 import (
+	"bytes"
 	"fmt"
 	"reflect"
+	"strings"
 	"testing"
 
 	"github.com/goplus/gop/ast"
@@ -90,7 +92,7 @@ func TestParseNoPackageAndGlobalCode(t *testing.T) {
 
 // -----------------------------------------------------------------------------
 
-var fsTest3 = asttest.NewSingleFileFS("/foo", "bar.gop", `package bar; import "io"
+const script3 = `package bar; import "io"
 func New() (*Bar, error) {
 	return nil, io.EOF
 }
@@ -98,16 +100,17 @@ func New() (*Bar, error) {
 bar, err := New()
 if err != nil {
 	log.Println(err)
-}`)
+}`
 
-func TestParseGopFiles(t *testing.T) {
+func TestParse(t *testing.T) {
 	fset := token.NewFileSet()
-	local = fsTest3
+	local = asttest.NewSingleFileFS("/foo", "bar.gop", script3)
 	defer func() {
 		local = localFS{}
 	}()
-	// test parse directory
-	pkgs, err := ParseGopFiles(fset, "/foo", true, 0)
+
+	// test parse file
+	pkgs, err := Parse(fset, "/foo/bar.gop", nil, 0)
 	if err != nil {
 		t.Error("parse err!", err)
 		return
@@ -125,19 +128,21 @@ func TestParseGopFiles(t *testing.T) {
 	if !checkFSTest(file, []string{`"io"`}, []string{"init", "New"}) {
 		t.Error("file check file")
 	}
+}
 
-	// test parse file
-	pkgs, err = ParseGopFiles(fset, "/foo/bar.gop", false, 0)
+func doTestParse(t *testing.T, src interface{}) {
+	fset := token.NewFileSet()
+	pkgs, err := Parse(fset, "/foo/bar.gop", src, 0)
 	if err != nil {
 		t.Error("parse err!", err)
 		return
 	}
-	pkg, ok = pkgs["bar"]
+	pkg, ok := pkgs["bar"]
 	if ok != true {
 		t.Error("pkg not found")
 		return
 	}
-	file, ok = pkg.Files["/foo/bar.gop"]
+	file, ok := pkg.Files["/foo/bar.gop"]
 	if ok != true {
 		t.Error("file not found")
 		return
@@ -147,6 +152,12 @@ func TestParseGopFiles(t *testing.T) {
 	}
 }
 
+func TestParse2(t *testing.T) {
+	doTestParse(t, script3)
+	doTestParse(t, bytes.NewBufferString(script3))
+	doTestParse(t, strings.NewReader(script3))
+}
+
 func checkFSTest(file *ast.File, targetImport []string, targetFuncDecl []string) (pass bool) {
 	var foundImport, foundFunc int
 	for _, decl := range file.Decls {