Repository: goplus/gop
Pull request #542 (2020-08-11 17:12:47)

exec,cl : load & store field
https://github.com/goplus/gop/pull/485

codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/542?src=pr&el=h1) Report
> Merging [#542](https://codecov.io/gh/goplus/gop/pull/542?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/8a2e56ac12b859cf8abeb6874feadd5d0baeb0a7&el=desc) will **increase** coverage by `0.22%`.
> The diff coverage is `98.11%`.

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

```diff
@@            Coverage Diff             @@
##           master     #542      +/-   ##
==========================================
+ Coverage   89.65%   89.88%   +0.22%     
==========================================
  Files          40       40              
  Lines       10038    10190     +152     
==========================================
+ Hits         9000     9159     +159     
+ Misses        759      752       -7     
  Partials      279      279              
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/542?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [exec/bytecode/code.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb2RlLmdv) | `100.00% <ø> (ø)` | |
| [exec/bytecode/context.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb250ZXh0Lmdv) | `94.54% <ø> (ø)` | |
| [exec/bytecode/gopkg.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9nb3BrZy5nbw==) | `88.37% <96.42%> (+2.83%)` | :arrow_up: |
| [cl/expr.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-Y2wvZXhwci5nbw==) | `86.36% <97.43%> (+1.44%)` | :arrow_up: |
| [cl/context.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-Y2wvY29udGV4dC5nbw==) | `82.87% <100.00%> (+0.32%)` | :arrow_up: |
| [cl/stmt.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-Y2wvc3RtdC5nbw==) | `93.61% <100.00%> (+0.01%)` | :arrow_up: |
| [exec/bytecode/const.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9jb25zdC5nbw==) | `92.39% <100.00%> (+2.28%)` | :arrow_up: |
| [exec/bytecode/goinstr\_lstcompr.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9nb2luc3RyX2xzdGNvbXByLmdv) | `93.40% <100.00%> (ø)` | |
| [exec/bytecode/interface.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9ieXRlY29kZS9pbnRlcmZhY2UuZ28=) | `99.11% <100.00%> (+0.03%)` | :arrow_up: |
| [exec/golang/expr.go](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree#diff-ZXhlYy9nb2xhbmcvZXhwci5nbw==) | `94.24% <100.00%> (+0.59%)` | :arrow_up: |
| ... and [3 more](https://codecov.io/gh/goplus/gop/pull/542/diff?src=pr&el=tree-more) | |

------

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


Patch:
diff --git a/cl/context.go b/cl/context.go
index 442c751d..db38fd00 100644
--- a/cl/context.go
+++ b/cl/context.go
@@ -192,6 +192,15 @@ type blockCtx struct {
 	takeAddr       bool
 	checkFlag      bool
 	checkArrayAddr bool
+	fieldVar       interface{}
+	fieldIndex     []int
+	fieldExprX     func()
+}
+
+func (ctx *blockCtx) resetFieldVar(v interface{}) {
+	ctx.fieldVar = v
+	ctx.fieldIndex = nil
+	ctx.fieldExprX = nil
 }
 
 // function block ctx
diff --git a/cl/expr.go b/cl/expr.go
index 83d10430..37ee6f25 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -41,6 +41,7 @@ const (
 // -----------------------------------------------------------------------------
 
 func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compileMode) {
+	ctx.resetFieldVar(nil)
 	switch v := expr.(type) {
 	case *ast.Ident:
 		compileIdentLHS(ctx, v.Name, mode)
@@ -157,7 +158,9 @@ func compileIdent(ctx *blockCtx, name string) func() {
 		switch v := sym.(type) {
 		case *execVar:
 			ctx.infer.Push(&goValue{t: v.v.Type()})
+			ctx.resetFieldVar(v.v)
 			return func() {
+				ctx.resetFieldVar(nil)
 				if ctx.checkArrayAddr && v.v.Type().Kind() == reflect.Array {
 					ctx.out.AddrVar(v.v)
 				} else {
@@ -669,6 +672,7 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, ct callType) func() {
 
 func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr, ct callType) func() {
 	fn := ctx.infer.Pop()
+	ctx.resetFieldVar(nil)
 	switch vfn := fn.(type) {
 	case *qlFunc:
 		if ct == callExpr {
@@ -688,8 +692,9 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr, ct call
 			}
 		}
 	case *goFunc:
+		var ret iValue
 		if ct == callExpr {
-			ret := vfn.Results()
+			ret = vfn.Results()
 			ctx.infer.Push(ret)
 		}
 		return func() {
@@ -707,6 +712,13 @@ func compileCallExprCall(ctx *blockCtx, exprFun func(), v *ast.CallExpr, ct call
 			case exec.SymbolFuncv:
 				builder(ctx, ct).CallGoFuncv(exec.GoFuncvAddr(vfn.addr), nexpr, arity)
 			}
+			if ct == callExpr {
+				if ret.NumValues() > 0 {
+					ctx.resetFieldVar(ret.Value(0).Type())
+				} else {
+					ctx.resetFieldVar(nil)
+				}
+			}
 		}
 	case *goValue:
 		if vfn.t.Kind() != reflect.Func {
@@ -774,7 +786,7 @@ func compileIndexExprLHS(ctx *blockCtx, v *ast.IndexExpr, mode compileMode) {
 
 	if cons, ok := val.(*constVal); ok {
 		cons.bound(typElem, ctx.out)
-	} else if t := val.(iValue).Type(); t != typElem {
+	} else if t := val.(iValue).Type(); t != typElem && typElem.Kind() != reflect.Interface {
 		log.Panicf("compileIndexExprLHS: can't assign `%v`[i] = `%v`\n", typ, t)
 	}
 	exprIdx := compileExpr(ctx, v.Index)
@@ -884,13 +896,16 @@ func compileIndexExpr(ctx *blockCtx, v *ast.IndexExpr) func() { // x[i]
 		typElem = typ.Elem()
 	}
 	ctx.infer.Ret(1, &goValue{typElem})
+	ctx.resetFieldVar(nil)
 	return func() {
-		exprX()
 		switch kind {
 		case reflect.String, reflect.Slice, reflect.Array:
+			exprX()
 			n := compileIdx(ctx, v.Index, 1<<30, kind)
 			ctx.out.Index(n)
+			ctx.resetFieldVar(typElem)
 		case reflect.Map:
+			exprX()
 			typIdx := typ.Key()
 			compileExpr(ctx, v.Index)()
 			i := ctx.infer.Pop()
@@ -1006,7 +1021,12 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode
 		_, t := countPtr(vx.t)
 		name := v.Sel.Name
 		if sf, ok := t.FieldByName(name); ok {
-			log.Panicln("compileSelectorExprLHS todo: structField -", t, sf)
+			checkType(sf.Type, in, ctx.out)
+			if ctx.fieldVar == nil {
+				exprX()
+			}
+			ctx.out.StoreField(ctx.fieldVar, append(ctx.fieldIndex, sf.Index...))
+			ctx.resetFieldVar(nil)
 		}
 	default:
 		log.Panicln("compileSelectorExprLHS failed: unknown -", reflect.TypeOf(vx))
@@ -1038,7 +1058,8 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 			}
 			switch kind {
 			case exec.SymbolFunc, exec.SymbolFuncv:
-				ctx.infer.Ret(1, newGoFunc(addr, kind, 0, ctx))
+				fn := newGoFunc(addr, kind, 0, ctx)
+				ctx.infer.Ret(1, fn)
 				return func() {
 					log.Panicln("compileSelectorExpr: todo")
 				}
@@ -1046,7 +1067,9 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 				info := ctx.GetGoVarInfo(exec.GoVarAddr(addr))
 				vt := reflect.ValueOf(info.This)
 				ctx.infer.Ret(1, &goValue{t: vt.Elem().Type()})
+				ctx.resetFieldVar(exec.GoVarAddr(addr))
 				return func() {
+					ctx.resetFieldVar(nil)
 					if ctx.checkArrayAddr && vt.Elem().Kind() == reflect.Array {
 						ctx.out.AddrGoVar(exec.GoVarAddr(addr))
 					} else {
@@ -1064,7 +1087,24 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 		autoCall := false
 		name := v.Sel.Name
 		if sf, ok := t.FieldByName(name); ok {
-			log.Panicln("compileSelectorExpr todo: structField -", t, sf)
+			ctx.infer.Ret(1, &goValue{t: sf.Type})
+			if ctx.fieldIndex == nil {
+				ctx.fieldExprX = exprX
+			}
+			ctx.fieldIndex = append(ctx.fieldIndex, sf.Index...)
+			fieldIndex := ctx.fieldIndex
+			fieldExprX := ctx.fieldExprX
+			return func() {
+				if fieldExprX != nil {
+					fieldExprX()
+				}
+				if ctx.checkArrayAddr && sf.Type.Kind() == reflect.Array {
+					ctx.out.AddrField(ctx.fieldVar, fieldIndex)
+				} else {
+					ctx.out.LoadField(ctx.fieldVar, fieldIndex)
+				}
+				ctx.resetFieldVar(nil)
+			}
 		}
 		if _, ok := vx.t.MethodByName(name); !ok && isLower(name) {
 			name = strings.Title(name)
diff --git a/cl/stmt.go b/cl/stmt.go
index 1f83a98f..b6128b30 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -51,6 +51,7 @@ func compileBodyWith(ctx *blockCtx, body []ast.Stmt) {
 }
 
 func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
+	ctx.resetFieldVar(nil)
 	start := ctx.out.StartStmt(stmt)
 	switch v := stmt.(type) {
 	case *ast.ExprStmt:
diff --git a/exec.spec/interface.go b/exec.spec/interface.go
index d5caa9b5..359bca05 100644
--- a/exec.spec/interface.go
+++ b/exec.spec/interface.go
@@ -298,6 +298,15 @@ type Builder interface {
 	// AddrGoVar instr
 	AddrGoVar(addr GoVarAddr) Builder
 
+	// LoadField instr
+	LoadField(v interface{}, index []int) Builder
+
+	// StoreField instr
+	StoreField(v interface{}, index []int) Builder
+
+	// AddrField instr
+	AddrField(v interface{}, index []int) Builder
+
 	// AddrOp instr
 	AddrOp(kind Kind, op AddrOperator) Builder
 
diff --git a/exec/bytecode/code.go b/exec/bytecode/code.go
index 2e66080f..5b367372 100644
--- a/exec/bytecode/code.go
+++ b/exec/bytecode/code.go
@@ -124,7 +124,10 @@ const (
 	opWrapIfErr     = 43 // reserved(2) offset(24)
 	opDefer         = 44 // reserved(26)
 	opGo            = 45 // arity(26)
-	opStruct        = 46 // funvArity(10) type(16)
+	opLoadField     = 46 // op(26)
+	opStoreField    = 47 // op(26)
+	opAddrField     = 48 // op(26)
+	opStruct        = 49 // funvArity(10) type(16)
 )
 
 const (
@@ -212,6 +215,9 @@ var instrInfos = []InstrInfo{
 	opWrapIfErr:     {"wrapIfErr", "", "offset", 26},                      // reserved(2) offset(24)
 	opDefer:         {"defer", "", "", 0},                                 // reserved(26)
 	opGo:            {"go", "", "arity", 26},                              // arity(26)
+	opLoadField:     {"loadField", "", "", 26},                            // addr(26)
+	opStoreField:    {"storeField", "", "", 26},                           // addr(26)
+	opAddrField:     {"addrField", "", "", 26},                            // addr(26)
 	opStruct:        {"struct", "funvArity", "type", (10 << 8) | 16},      // funvArity(10) type(16)
 }
 
diff --git a/exec/bytecode/const.go b/exec/bytecode/const.go
index bd41d11e..d682e6ef 100644
--- a/exec/bytecode/const.go
+++ b/exec/bytecode/const.go
@@ -127,6 +127,8 @@ func (p *Builder) pushInstr(val interface{}) (i Instr) {
 		return iPushFalse
 	} else if kind == reflect.String || (kind >= reflect.Float32 && kind <= reflect.Complex128) {
 		// noop
+	} else if kind == reflect.Slice && v.Type().Elem().Kind() == reflect.Int {
+		// []int
 	} else if !isNilOrRatConst(kind, v) {
 		log.Panicln("Push failed: unsupported type:", reflect.TypeOf(val), "-", val)
 	}
diff --git a/exec/bytecode/context.go b/exec/bytecode/context.go
index c837b10e..d95a367e 100644
--- a/exec/bytecode/context.go
+++ b/exec/bytecode/context.go
@@ -239,6 +239,9 @@ var _execTable = [...]func(i Instr, p *Context){
 	opWrapIfErr:     execWrapIfErr,
 	opDefer:         execDefer,
 	opGo:            execGo,
+	opLoadField:     execLoadField,
+	opStoreField:    execStoreField,
+	opAddrField:     execAddrField,
 	opStruct:        execStruct,
 }
 
diff --git a/exec/bytecode/goinstr_lstcompr.go b/exec/bytecode/goinstr_lstcompr.go
index 2510db61..76cf53be 100644
--- a/exec/bytecode/goinstr_lstcompr.go
+++ b/exec/bytecode/goinstr_lstcompr.go
@@ -265,7 +265,7 @@ func execIndex(i Instr, p *Context) {
 	case indexOpGet: // sliceData $idx $getIndex
 		p.data[n-1] = v.Interface()
 	case indexOpSet: // value sliceData $idx $setIndex
-		v.Set(reflect.ValueOf(p.data[n-2]))
+		setValue(v, p.data[n-2])
 		p.PopN(2)
 	case indexOpAddr: // sliceData $idx $setIndex
 		p.data[n-1] = v.Addr().Interface()
diff --git a/exec/bytecode/gopkg.go b/exec/bytecode/gopkg.go
index b15d0874..539d663b 100644
--- a/exec/bytecode/gopkg.go
+++ b/exec/bytecode/gopkg.go
@@ -63,6 +63,39 @@ func execAddrGoVar(i Instr, p *Context) {
 	p.Push(govars[idx].Addr)
 }
 
+func execLoadField(i Instr, p *Context) {
+	index := p.Pop()
+	v := reflect.ValueOf(p.Pop())
+	v = reflect.Indirect(v)
+	p.Push(v.FieldByIndex(index.([]int)).Interface())
+}
+
+func execAddrField(i Instr, p *Context) {
+	index := p.Pop()
+	v := reflect.ValueOf(p.Pop())
+	v = toElem(v)
+	p.Push(v.FieldByIndex(index.([]int)).Addr().Interface())
+}
+
+func toElem(v reflect.Value) reflect.Value {
+	for v.Kind() == reflect.Ptr {
+		v = v.Elem()
+	}
+	return v
+}
+
+func execStoreField(i Instr, p *Context) {
+	index := p.Pop()
+	val := p.Pop()
+	value := p.Pop()
+	v := reflect.ValueOf(val)
+	v = toElem(v)
+	if !v.CanSet() {
+		log.Panicf("cannot assign to %v\n", v)
+	}
+	setValue(v.FieldByIndex(index.([]int)), value)
+}
+
 // -----------------------------------------------------------------------------
 
 // A ConstKind represents the specific kind of type that a Type represents.
@@ -393,4 +426,49 @@ func (p *Builder) AddrGoVar(addr GoVarAddr) *Builder {
 	return p
 }
 
+// LoadField instr
+func (p *Builder) LoadField(v interface{}, index []int) *Builder {
+	switch x := v.(type) {
+	case exec.GoVarAddr:
+		p.LoadGoVar(x)
+	case *Var:
+		p.LoadVar(x)
+	case reflect.Type:
+	}
+	p.Push(index)
+	i := (opLoadField << bitsOpShift)
+	p.code.data = append(p.code.data, uint32(i))
+	return p
+}
+
+// AddrField instr
+func (p *Builder) AddrField(v interface{}, index []int) *Builder {
+	switch x := v.(type) {
+	case exec.GoVarAddr:
+		p.AddrGoVar(x)
+	case *Var:
+		p.AddrVar(x)
+	case reflect.Type:
+	}
+	p.Push(index)
+	i := (opAddrField << bitsOpShift)
+	p.code.data = append(p.code.data, uint32(i))
+	return p
+}
+
+// StoreField instr
+func (p *Builder) StoreField(v interface{}, index []int) *Builder {
+	switch x := v.(type) {
+	case exec.GoVarAddr:
+		p.AddrGoVar(x)
+	case *Var:
+		p.AddrVar(x)
+	case reflect.Type:
+	}
+	p.Push(index)
+	i := (opStoreField << bitsOpShift)
+	p.code.data = append(p.code.data, uint32(i))
+	return p
+}
+
 // -----------------------------------------------------------------------------
diff --git a/exec/bytecode/interface.go b/exec/bytecode/interface.go
index d43333ba..cb3264bf 100644
--- a/exec/bytecode/interface.go
+++ b/exec/bytecode/interface.go
@@ -324,6 +324,24 @@ func (p *iBuilder) AddrGoVar(addr GoVarAddr) exec.Builder {
 	return p
 }
 
+// LoadField instr
+func (p *iBuilder) LoadField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).LoadField(v, index)
+	return p
+}
+
+// AddrField instr
+func (p *iBuilder) AddrField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).AddrField(v, index)
+	return p
+}
+
+// StoreField instr
+func (p *iBuilder) StoreField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).StoreField(v, index)
+	return p
+}
+
 // AddrOp instr
 func (p *iBuilder) AddrOp(kind exec.Kind, op exec.AddrOperator) exec.Builder {
 	((*Builder)(p)).AddrOp(kind, op)
diff --git a/exec/golang/expr.go b/exec/golang/expr.go
index aa2e8fcb..38cb5ccb 100644
--- a/exec/golang/expr.go
+++ b/exec/golang/expr.go
@@ -438,6 +438,61 @@ func (p *Builder) AddrGoVar(addr exec.GoVarAddr) *Builder {
 	return p
 }
 
+func (p *Builder) fieldExpr(v interface{}, index []int) ast.Expr {
+	var typ reflect.Type
+	expr := &ast.SelectorExpr{}
+	switch x := v.(type) {
+	case exec.GoVarAddr:
+		gvi := defaultImpl.GetGoVarInfo(x)
+		typ = reflect.TypeOf(gvi.This).Elem()
+		expr.X = p.GoSymIdent(gvi.Pkg.PkgPath(), gvi.Name)
+	case exec.Var:
+		typ = x.Type()
+		expr.X = Ident(x.Name())
+	case reflect.Type:
+		typ = x
+		expr.X = p.rhs.Pop().(ast.Expr)
+	}
+	if typ.Kind() == reflect.Ptr {
+		typ = typ.Elem()
+	}
+	for i := 0; i < len(index); i++ {
+		sf := typ.FieldByIndex(index[:i+1])
+		if sf.Anonymous {
+			continue
+		}
+		if expr.Sel != nil {
+			expr.X = &ast.SelectorExpr{expr.X, expr.Sel}
+		}
+		expr.Sel = Ident(sf.Name)
+	}
+	return expr
+}
+
+// LoadField instr
+func (p *Builder) LoadField(v interface{}, index []int) *Builder {
+	expr := p.fieldExpr(v, index)
+	p.rhs.Push(expr)
+	return p
+}
+
+// AddrField instr
+func (p *Builder) AddrField(v interface{}, index []int) *Builder {
+	expr := p.fieldExpr(v, index)
+	p.rhs.Push(&ast.UnaryExpr{
+		Op: token.AND,
+		X:  expr,
+	})
+	return p
+}
+
+// StoreField instr
+func (p *Builder) StoreField(v interface{}, index []int) *Builder {
+	expr := p.fieldExpr(v, index)
+	p.lhs.Push(expr)
+	return p
+}
+
 // Append instr
 func (p *Builder) Append(typ reflect.Type, arity int) *Builder {
 	p.rhs.Push(appendIdent)
diff --git a/exec/golang/interface.go b/exec/golang/interface.go
index bd919146..33120460 100644
--- a/exec/golang/interface.go
+++ b/exec/golang/interface.go
@@ -340,6 +340,24 @@ func (p *iBuilder) AddrGoVar(addr exec.GoVarAddr) exec.Builder {
 	return p
 }
 
+// LoadField instr
+func (p *iBuilder) LoadField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).LoadField(v, index)
+	return p
+}
+
+// AddrField instr
+func (p *iBuilder) AddrField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).AddrField(v, index)
+	return p
+}
+
+// StoreField instr
+func (p *iBuilder) StoreField(v interface{}, index []int) exec.Builder {
+	((*Builder)(p)).StoreField(v, index)
+	return p
+}
+
 // AddrOp instr
 func (p *iBuilder) AddrOp(kind exec.Kind, op exec.AddrOperator) exec.Builder {
 	((*Builder)(p)).AddrOp(kind, op)


Test patch:
diff --git a/cl/gopkg_test.go b/cl/gopkg_test.go
index 038ad3d1..d61a9d08 100644
--- a/cl/gopkg_test.go
+++ b/cl/gopkg_test.go
@@ -19,6 +19,7 @@ package cl
 import (
 	"fmt"
 	"math"
+	"os"
 	"reflect"
 	"strings"
 	"testing"
@@ -502,3 +503,153 @@ import (
 		}
 	}
 }
+
+type testFieldPoint struct {
+	X int
+	Y int
+}
+
+type testFieldInfo struct {
+	V1  bool
+	V2  rune
+	V3  string
+	V4  int
+	V5  []int
+	V6  []string
+	V7  map[int]string
+	V8  testFieldPoint
+	V9  *testFieldPoint
+	V10 []testFieldPoint
+	V11 []*testFieldPoint
+	V12 [2]testFieldPoint
+	V13 [2]*testFieldPoint
+}
+
+func testNewPoint(x int, y int) *testFieldPoint {
+	return &testFieldPoint{x, y}
+}
+
+func execTestNewPoint(_ int, p *exec.Context) {
+	args := p.GetArgs(2)
+	ret0 := testNewPoint(args[0].(int), args[1].(int))
+	p.Ret(2, ret0)
+}
+
+func TestPkgField(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_field")
+
+	m := make(map[int]string)
+	m[1] = "hello"
+	m[2] = "world"
+
+	var ar [13]interface{}
+	ar[0] = true
+	ar[1] = 'A'
+	ar[2] = "Info"
+	ar[3] = -100
+	ar[4] = []int{100, 200}
+	ar[5] = []string{"hello", "world"}
+	ar[6] = m
+	ar[7] = testFieldPoint{10, 20}
+	ar[8] = &testFieldPoint{-10, -20}
+	ar[9] = []testFieldPoint{{100, 200}, {300, 400}}
+	ar[10] = []*testFieldPoint{&testFieldPoint{100, 200}, &testFieldPoint{300, 400}}
+	ar[11] = [2]testFieldPoint{{100, 200}, {300, 400}}
+	ar[12] = [2]*testFieldPoint{&testFieldPoint{100, 200}, &testFieldPoint{300, 400}}
+
+	info := &testFieldInfo{}
+	info.V7 = make(map[int]string)
+	v := reflect.ValueOf(info).Elem()
+	for i := 0; i < v.NumField(); i++ {
+		v.Field(i).Set(reflect.ValueOf(ar[i]))
+	}
+
+	var out [13]interface{}
+	var sum int
+	I.RegisterVars(
+		I.Var("Info", &info),
+		I.Var("Out", &out),
+		I.Var("Sum", &sum),
+	)
+	I.RegisterFuncs(
+		I.Func("NewPoint", testNewPoint, execTestNewPoint),
+	)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_field"
+)
+
+`
+	const nField = len(ar)
+
+	// make set
+	for i := 0; i < nField; i++ {
+		testSource += fmt.Sprintf("pkg.Info.V%v = pkg.Info.V%v\n", i+1, i+1)
+	}
+
+	// make println
+	for i := 0; i < nField; i++ {
+		testSource += fmt.Sprintf("println(\"pkg.Info.V%v = \", pkg.Info.V%v)\n", i+1, i+1)
+	}
+
+	for i := 0; i < nField; i++ {
+		testSource += fmt.Sprintf("pkg.Out[%v] = pkg.Info.V%v\n", i, i+1)
+	}
+
+	testSource += `
+	pkg.Sum = 1+pkg.Info.V4+pkg.NewPoint(1000,200).X+pkg.Info.V8.X-pkg.Info.V9.X
+	pkg.Info.V5[0] = -100
+	println("pkg.Info.V5", pkg.Info.V5)
+	println("pkg.Info.V11[0]",pkg.Info.V11[0])
+	pkg.Info.V11[0] = nil
+	println("pkg.Info.V11",pkg.Info.V11)
+	pkg.Info.V11[1].X = -101
+	println("pkg.Info.V11[1].X",pkg.Info.V11[1].X)
+	pkg.Info.V13[0] = nil
+	println("pkg.Info.V13",pkg.Info.V13)
+	pkg.Info.V13[1].X = -102
+	println("pkg.Info.V13[1].X",pkg.Info.V13[1].X)
+	`
+
+	fsTestPkgVar := asttest.NewSingleFileFS("/foo", "bar.gop", testSource)
+	t.Log(testSource)
+
+	fset := token.NewFileSet()
+	pkgs, err := parser.ParseFSDir(fset, fsTestPkgVar, "/foo", nil, 0)
+	if err != nil || len(pkgs) != 1 {
+		t.Fatal("ParseFSDir failed:", err, len(pkgs))
+	}
+
+	bar := pkgs["main"]
+	b := exec.NewBuilder(nil)
+	_, _, err = newPackage(b, bar, fset)
+	if err != nil {
+		t.Fatal("Compile failed:", err)
+	}
+	code := b.Resolve()
+	code.Dump(os.Stdout)
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	for i := 0; i < nField; i++ {
+		if !reflect.DeepEqual(ar[i], out[i]) {
+			t.Fatal(i, ar[i], out[i])
+		}
+	}
+	if sum != 921 {
+		t.Fatal("binary expr check fail", sum)
+	}
+	if info.V5[0] != -100 {
+		t.Fatal("V5", info.V5)
+	}
+	if info.V11[0] != nil || info.V11[1].X != -101 {
+		t.Fatal("V11", info.V11)
+	}
+	if info.V13[0] != nil || info.V13[1].X != -102 {
+		t.Fatal("V13", info.V13)
+	}
+}
diff --git a/exec/bytecode/gopkg_test.go b/exec/bytecode/gopkg_test.go
index 154f66ae..ed9f8477 100644
--- a/exec/bytecode/gopkg_test.go
+++ b/exec/bytecode/gopkg_test.go
@@ -180,6 +180,135 @@ func TestGoVar(t *testing.T) {
 	}
 }
 
+type testPoint struct {
+	X int
+	Y int
+}
+
+type testInfo struct {
+	Info string
+}
+
+type testRect struct {
+	testInfo
+	Pt1 testPoint
+	Pt2 *testPoint
+}
+
+var g_Rect *testRect
+
+func init() {
+	g_Rect = &testRect{}
+	g_Rect.Pt2 = &testPoint{}
+}
+
+func getTestRect() *testRect {
+	return g_Rect
+}
+
+func execTestRect(_ int, p *Context) {
+	p.Ret(0, getTestRect())
+}
+
+func TestGoField(t *testing.T) {
+	sprint, ok := I.FindFuncv("Sprint")
+
+	pkg := NewGoPackage("pkg_field")
+
+	rc := testRect{}
+	rc.Info = "Info"
+	rc.Pt1 = testPoint{10, 20}
+	rc.Pt2 = &testPoint{30, 40}
+
+	rc2 := testRect{}
+	rc2.Pt2 = &testPoint{}
+
+	pkg.RegisterVars(
+		pkg.Var("Rect", &rc),
+		pkg.Var("Rect2", &rc2),
+	)
+	pkg.RegisterFuncs(
+		pkg.Func("GetRect", getTestRect, execTestRect),
+	)
+
+	x, ok := pkg.FindVar("Rect")
+	if !ok {
+		t.Fatal("FindVar failed:", x)
+	}
+	x2, ok := pkg.FindVar("Rect2")
+	if !ok {
+		t.Fatal("FindVar failed:", x2)
+	}
+	fnTestRect, ok := pkg.FindFunc("GetRect")
+	if !ok {
+		t.Fatal("FindFunc failed:", fnTestRect)
+	}
+
+	it := reflect.TypeOf(rc)
+	it2 := reflect.TypeOf(g_Rect)
+
+	y := NewVar(it, "y")
+	b := newBuilder()
+
+	code := b.
+		DefineVar(y). // y
+		LoadGoVar(x2).
+		StoreVar(y). // y = pkg_field.Rect2
+		Push("hello").
+		StoreField(x, []int{0, 0}). // pkg_field.Rect.Info = "hello"
+		Push(-1).
+		StoreField(x, []int{1, 0}). // pkg_field.Rect.Pt1.X = -1
+		Push(-2).
+		StoreField(x, []int{2, 1}). // pkg_field.Rect.Pt2.Y = -2
+		Push("world").
+		StoreField(y, []int{0, 0}). // y.Info = "world"
+		Push(-10).
+		StoreField(y, []int{1, 0}). // y.Pt1.X = -10
+		Push(-20).
+		StoreField(y, []int{2, 1}). // y.Pt2.Y = -20
+		Push("next").
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{0, 0}). // pkg_field.GetRect().Info = "next"
+		Push(101).
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{1, 0}). // pkg_field.GetRect().Pt1.X = 101
+		Push(102).
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{2, 1}). // pkg_field.GetRect().Pt2.Y = 102
+		LoadVar(y).
+		StoreGoVar(x2). // pkg_field.Rect2 = y
+		LoadField(x, []int{0, 0}).
+		LoadField(x, []int{2, 1}).
+		LoadField(y, []int{0, 0}).
+		LoadField(y, []int{2, 1}).
+		CallGoFunc(fnTestRect, 0).
+		LoadField(it2, []int{0, 0}).
+		CallGoFunc(fnTestRect, 0).
+		LoadField(it2, []int{2, 1}).
+		AddrField(x, []int{1}).
+		AddrField(y, []int{1}).
+		CallGoFunc(fnTestRect, 0).
+		AddrField(it2, []int{1}).
+		CallGoFuncv(sprint, 9, 9). // print(pkg_field.Info,pkg_field.Pt2.Y,y.Info,y.Pt2.Y,pkg_field.GetRect().Info,pkg_field.GetRect().Pt2.Y)
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	if rc.Info != "hello" || rc.Pt1.X != -1 || rc.Pt2.Y != -2 {
+		t.Fatal("Rect", rc)
+	}
+	if rc2.Info != "world" || rc2.Pt1.X != -10 || rc2.Pt2.Y != -20 {
+		t.Fatal("Rect2", rc2)
+	}
+	if g_Rect.Info != "next" || g_Rect.Pt1.X != 101 || g_Rect.Pt2.Y != 102 {
+		t.Fatal("g_Rect", g_Rect)
+	}
+	if v := ctx.Get(-1); v != "hello-2world-20next102 &{-1 20} &{-10 0} &{101 0}" {
+		t.Fatal("LoadField", v)
+	}
+}
+
 func TestSprint(t *testing.T) {
 	sprint, ok := I.FindFuncv("Sprint")
 	if !ok {
diff --git a/exec/golang/builder_test.go b/exec/golang/builder_test.go
index 2d488725..157a14fb 100644
--- a/exec/golang/builder_test.go
+++ b/exec/golang/builder_test.go
@@ -169,6 +169,162 @@ func main() {
 	}
 }
 
+type testInfo struct {
+	Info string
+}
+
+type testPoint struct {
+	X int
+	Y int
+}
+
+type testRect struct {
+	testInfo
+	Pt1 testPoint
+	Pt2 *testPoint
+}
+
+var g_Rect *testRect
+
+func init() {
+	g_Rect = &testRect{}
+	g_Rect.Pt2 = &testPoint{}
+}
+
+func getTestRect() *testRect {
+	return g_Rect
+}
+
+func execTestRect(_ int, p *qexec.Context) {
+	p.Ret(0, getTestRect())
+}
+
+func TestGoField(t *testing.T) {
+	codeExp := `package main
+
+import (
+	fmt "fmt"
+	pkg_field "pkg_field"
+)
+
+var y golang.testRect
+
+func main() {
+	y = pkg_field.Rect2
+	pkg_field.Rect.Info = "hello"
+	pkg_field.Rect.Pt1.X = -1
+	pkg_field.Rect.Pt2.Y = -2
+	y.Info = "world"
+	y.Pt1.X = -10
+	y.Pt2.Y = -20
+	pkg_field.GetRect().Info = "next"
+	pkg_field.GetRect().Pt1.X = 101
+	pkg_field.GetRect().Pt2.Y = 102
+	pkg_field.Rect2 = y
+	fmt.Println(pkg_field.Rect.Info, pkg_field.Rect.Pt2.Y, y.Info, y.Pt2.Y, pkg_field.GetRect().Info, pkg_field.GetRect().Pt2.Y, &pkg_field.Rect.Pt1, &y.Pt1, &pkg_field.GetRect().Pt1)
+}
+`
+	println, _ := I.FindFuncv("println")
+
+	pkg := qexec.NewGoPackage("pkg_field")
+
+	rc := testRect{}
+	rc.Info = "Info"
+	rc.Pt1 = testPoint{10, 20}
+	rc.Pt2 = &testPoint{30, 40}
+
+	rc2 := testRect{}
+	rc2.Pt2 = &testPoint{}
+
+	pkg.RegisterVars(
+		pkg.Var("Rect", &rc),
+		pkg.Var("Rect2", &rc2),
+	)
+	pkg.RegisterFuncs(
+		pkg.Func("GetRect", getTestRect, execTestRect),
+	)
+
+	x, ok := pkg.FindVar("Rect")
+	if !ok {
+		t.Fatal("FindVar failed:", x)
+	}
+	x2, ok := pkg.FindVar("Rect2")
+	if !ok {
+		t.Fatal("FindVar failed:", x2)
+	}
+	fnTestRect, ok := pkg.FindFunc("GetRect")
+	if !ok {
+		t.Fatal("FindFunc failed:", fnTestRect)
+	}
+
+	it := reflect.TypeOf(rc)
+	it2 := reflect.TypeOf(g_Rect)
+
+	y := NewVar(it, "y")
+
+	b := NewBuilder("main", nil, nil)
+
+	code := b.Interface().
+		DefineVar(y). // y
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		LoadGoVar(x2).
+		StoreVar(y). // y = pkg_field.Rect2
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push("hello").
+		StoreField(x, []int{0, 0}). // pkg_field.Rect.Info = "hello"
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(-1).
+		StoreField(x, []int{1, 0}). // pkg_field.Rect.Pt1.X = -1
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(-2).
+		StoreField(x, []int{2, 1}). // pkg_field.Rect.Pt2.Y = -2
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push("world").
+		StoreField(y, []int{0, 0}). // y.Info = "world"
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(-10).
+		StoreField(y, []int{1, 0}). // y.Pt1.X = -10
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(-20).
+		StoreField(y, []int{2, 1}). // y.Pt2.Y = -20
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push("next").
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{0, 0}). // pkg_field.GetRect().Info = "next"
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(101).
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{1, 0}). // pkg_field.GetRect().Pt1.X = 101
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Push(102).
+		CallGoFunc(fnTestRect, 0).
+		StoreField(it2, []int{2, 1}). // pkg_field.GetRect().Pt2.Y = 102
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		LoadVar(y).
+		StoreGoVar(x2). // pkg_field.Rect2 = y
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		LoadField(x, []int{0, 0}).
+		LoadField(x, []int{2, 1}).
+		LoadField(y, []int{0, 0}).
+		LoadField(y, []int{2, 1}).
+		CallGoFunc(fnTestRect, 0).
+		LoadField(it2, []int{0, 0}).
+		CallGoFunc(fnTestRect, 0).
+		LoadField(it2, []int{2, 1}).
+		AddrField(x, []int{1}).
+		AddrField(y, []int{1}).
+		CallGoFunc(fnTestRect, 0).
+		AddrField(it2, []int{1}).
+		CallGoFuncv(println, 9, 9). // println(pkg_field.Rect.Info, pkg_field.Rect.Pt2.Y, y.Info, y.Pt2.Y, pkg_field.GetRect().Info, pkg_field.GetRect().Pt2.Y, &pkg_field.Rect.Pt1, &y.Pt1, &pkg_field.GetRect().Pt1)
+		EndStmt(nil, &stmtState{rhsBase: 0}).
+		Resolve()
+
+	if v := code.(*Code); v.String() != codeExp {
+		fmt.Println(v.String())
+		log.Fatal("TestGoVar failed: codeGen != codeExp")
+	}
+}
+
 // -----------------------------------------------------------------------------
 
 func TestStruct(t *testing.T) {