Repository: goplus/gop
Pull request #503 (2020-07-27 04:17:12)

tutorial/23-Defer

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

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

```diff
@@            Coverage Diff             @@
##           master     #503      +/-   ##
==========================================
- Coverage   89.07%   88.90%   -0.17%     
==========================================
  Files          39       39              
  Lines        9791     9763      -28     
==========================================
- Hits         8721     8680      -41     
- Misses        787      797      +10     
- Partials      283      286       +3     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/503?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [exec/bytecode/const.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb25zdC5nbw==) | `90.10% <ø> (-0.22%)` | :arrow_down: |
| [exec/golang/builder.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvYnVpbGRlci5nbw==) | `91.08% <ø> (-0.12%)` | :arrow_down: |
| [cl/goinstr.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-Y2wvZ29pbnN0ci5nbw==) | `70.52% <35.29%> (-6.36%)` | :arrow_down: |
| [exec/bytecode/func.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9mdW5jLmdv) | `90.58% <73.68%> (-1.53%)` | :arrow_down: |
| [cl/expr.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-Y2wvZXhwci5nbw==) | `84.04% <92.30%> (-0.08%)` | :arrow_down: |
| [cl/stmt.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-Y2wvc3RtdC5nbw==) | `93.44% <100.00%> (+1.37%)` | :arrow_up: |
| [exec/bytecode/code.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb2RlLmdv) | `100.00% <100.00%> (ø)` | |
| [exec/bytecode/context.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb250ZXh0Lmdv) | `90.40% <100.00%> (ø)` | |
| [exec/bytecode/defer.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9kZWZlci5nbw==) | `100.00% <100.00%> (ø)` | |
| [exec/bytecode/interface.go](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9pbnRlcmZhY2UuZ28=) | `99.03% <100.00%> (-0.01%)` | :arrow_down: |
| ... and [3 more](https://codecov.io/gh/goplus/gop/pull/503/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/503?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/503?src=pr&el=footer). Last update [7c49512...6e30307](https://codecov.io/gh/goplus/gop/pull/503?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index 3f849120..f982945b 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -60,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:
@@ -616,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:
@@ -624,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)()
@@ -640,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)()
@@ -659,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)()
@@ -679,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)
 		}
 	}
@@ -693,6 +702,13 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr) func()
 	return nil
 }
 
+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")
@@ -1017,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 24901ab6..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)
 	}
 }
 
@@ -208,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))
 	}
@@ -245,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 4a2c112d..f73afa16 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -240,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) {
diff --git a/cmd/igop/igop.go b/cmd/igop/igop.go
index 83753f30..c81c3a30 100644
--- a/cmd/igop/igop.go
+++ b/cmd/igop/igop.go
@@ -10,5 +10,5 @@ import (
 )
 
 func main() {
-	base.Main(repl.Cmd, "repl", os.Args[1:])
+	base.Main(repl.Cmd, "igop", os.Args[1:])
 }
diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go
index b9f27e6b..cbb4ff9b 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -94,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 f26485ef..fc7edad6 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
 
@@ -334,9 +331,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,18 +346,12 @@ 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
 
diff --git a/exec/bytecode/code.go b/exec/bytecode/code.go
index 9e5bbc44..fe9377af 100644
--- a/exec/bytecode/code.go
+++ b/exec/bytecode/code.go
@@ -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 (
@@ -200,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},
 }
 
 // -----------------------------------------------------------------------------
@@ -231,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 4c65665e..f89ec6f1 100644
--- a/exec/bytecode/context.go
+++ b/exec/bytecode/context.go
@@ -168,6 +168,12 @@ 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) (currentIP int) {
 	const allowProfile = true
@@ -195,8 +201,6 @@ func (ctx *Context) Exec(ip, ipEnd int) (currentIP 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))
@@ -235,7 +239,6 @@ func (ctx *Context) Exec(ip, ipEnd int) (currentIP int) {
 		}
 	}
 finished:
-	execDefers(ctx)
 	if allowProfile && doProfile {
 		if lastInstr != 0 {
 			instrProfile(lastInstr, time.Since(start))
@@ -247,6 +250,8 @@ finished:
 var _execTable = [...]func(i Instr, p *Context){
 	opCallGoFunc:    execGoFunc,
 	opCallGoFuncv:   execGoFuncv,
+	opCallFunc:      execFunc,
+	opCallFuncv:     execFuncv,
 	opPushInt:       execPushInt,
 	opPushUint:      execPushUint,
 	opPushValSpec:   execPushValSpec,
@@ -285,7 +290,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 bd133793..73664884 100644
--- a/exec/bytecode/defer.go
+++ b/exec/bytecode/defer.go
@@ -16,63 +16,41 @@
 
 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
 }
 
 // Defer instr
-func (p *Builder) Defer(start, end *Label) exec.Instr {
-	return &iDefer{start: p.labels[start], end: p.labels[end]}
+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/interface.go b/exec/bytecode/interface.go
index 66252c4b..4eebf93e 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
@@ -422,11 +423,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)
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 8bc9533e..53047798 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
 }
 
diff --git a/exec/golang/interface.go b/exec/golang/interface.go
index 7353f261..6396389f 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 {
@@ -438,11 +439,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
diff --git a/exec/golang/stmt.go b/exec/golang/stmt.go
index b774cfde..55e34f43 100644
--- a/exec/golang/stmt.go
+++ b/exec/golang/stmt.go
@@ -393,18 +393,20 @@ 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
+// DefineBlock starts a new block.
 func (p *Builder) DefineBlock() *Builder {
 	p.scopeCtx = &scopeCtx{parentCtx: p.scopeCtx}
 	p.initStmts()
 	return p
 }
 
-//  EndBlock ends a  block
+// EndBlock ends a block.
 func (p *Builder) EndBlock() *Builder {
 	p.endBlockStmt(0)
 	blockStmt := getBlockStmts(p)
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/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 34dbadde..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,20 +1252,141 @@ 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{
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..9afae7ac 100644
--- a/exec/bytecode/defer_test.go
+++ b/exec/bytecode/defer_test.go
@@ -16,45 +16,29 @@
 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/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/golang/builder_test.go b/exec/golang/builder_test.go
index 3dda5dde..032d6e81 100644
--- a/exec/golang/builder_test.go
+++ b/exec/golang/builder_test.go
@@ -136,27 +136,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/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+")