Repository: goplus/gop
Pull request #565 (2020-08-21 09:23:47)

support unexport field
fix bug: https://github.com/goplus/gop/issues/558


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index 5c9095a2..3f3c8ba0 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -241,6 +241,7 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit) func() {
 		return compileMapLit(ctx, v)
 	}
 	typ := toType(ctx, v.Type)
+
 	switch kind := typ.Kind(); kind {
 	case reflect.Slice, reflect.Array:
 		var typSlice reflect.Type
@@ -311,15 +312,15 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit) func() {
 				switch e := elt.(type) {
 				case *ast.KeyValueExpr:
 					fieldName := e.Key.(*ast.Ident).Name
-					ctx.out.Push(fieldName)
 					field, _ := typStruct.FieldByName(fieldName)
+					ctx.out.Push(field.Index[0])
 					typVal := field.Type
 					compileExpr(ctx, e.Value)()
 					checkType(typVal, ctx.infer.Pop(), ctx.out)
 				default:
 					// ast.Expr
 					field := typStruct.Field(i)
-					ctx.out.Push(field.Name)
+					ctx.out.Push(field.Index[0])
 					typVal := field.Type
 					compileExpr(ctx, elt)()
 					checkType(typVal, ctx.infer.Pop(), ctx.out)
@@ -1078,23 +1079,27 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compileMode
 	case *goValue:
 		_, t := countPtr(vx.t)
 		name := v.Sel.Name
-		if t.Kind() == reflect.Struct {
-			if sf, ok := t.FieldByName(name); ok {
-				checkType(sf.Type, in, ctx.out)
-				if ctx.fieldIndex == nil {
-					ctx.fieldStructType = vx.t
-				}
-				fieldIndex := append(ctx.fieldIndex, sf.Index...)
-				fieldStructType := ctx.fieldStructType
-				ctx.checkLoadAddr = true
-				if ctx.fieldExprX != nil {
-					ctx.fieldExprX()
-				} else {
-					exprX()
+		if t.PkgPath() != "" && ast.IsExported(name) || t.PkgPath() == "" {
+			if t.Kind() == reflect.Struct {
+				if sf, ok := t.FieldByName(name); ok {
+					checkType(sf.Type, in, ctx.out)
+					if ctx.fieldIndex == nil {
+						ctx.fieldStructType = vx.t
+					}
+					fieldIndex := append(ctx.fieldIndex, sf.Index...)
+					fieldStructType := ctx.fieldStructType
+					ctx.checkLoadAddr = true
+					if ctx.fieldExprX != nil {
+						ctx.fieldExprX()
+					} else {
+						exprX()
+					}
+					ctx.checkLoadAddr = false
+					ctx.out.StoreField(fieldStructType, fieldIndex)
 				}
-				ctx.checkLoadAddr = false
-				ctx.out.StoreField(fieldStructType, fieldIndex)
 			}
+		} else if t.PkgPath() != "" && !ast.IsExported(name) {
+			log.Panicf("%s.%s.%s undefined (cannot refer to unexported field or method %s)\n", t.PkgPath(), t.Name(), name, name)
 		}
 	default:
 		log.Panicln("compileSelectorExprLHS failed: unknown -", reflect.TypeOf(vx))
@@ -1155,37 +1160,39 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, allowAutoCall bool)
 		n, t := countPtr(vx.t)
 		autoCall := false
 		name := v.Sel.Name
-		if t.Kind() == reflect.Struct {
-			if sf, ok := t.FieldByName(name); ok {
-				ctx.infer.Ret(1, &goValue{t: sf.Type})
-				if ctx.fieldIndex == nil {
-					ctx.fieldExprX = exprX
-					ctx.fieldStructType = vx.t
-				}
-				ctx.fieldIndex = append(ctx.fieldIndex, sf.Index...)
-				fieldIndex := ctx.fieldIndex
-				fieldExprX := ctx.fieldExprX
-				fieldStructType := ctx.fieldStructType
-				return func() {
-					if fieldExprX != nil {
-						fieldExprX()
+		if t.PkgPath() != "" && ast.IsExported(name) || t.PkgPath() == "" {
+			if t.Kind() == reflect.Struct {
+				if sf, ok := t.FieldByName(name); ok {
+					ctx.infer.Ret(1, &goValue{t: sf.Type})
+					if ctx.fieldIndex == nil {
+						ctx.fieldExprX = exprX
+						ctx.fieldStructType = vx.t
 					}
-					if ctx.takeAddr || ctx.checkLoadAddr {
-						ctx.out.AddrField(fieldStructType, fieldIndex)
-					} else {
-						ctx.out.LoadField(fieldStructType, fieldIndex)
+					ctx.fieldIndex = append(ctx.fieldIndex, sf.Index...)
+					fieldIndex := ctx.fieldIndex
+					fieldExprX := ctx.fieldExprX
+					fieldStructType := ctx.fieldStructType
+					return func() {
+						if fieldExprX != nil {
+							fieldExprX()
+						}
+						if ctx.takeAddr || ctx.checkLoadAddr {
+							ctx.out.AddrField(fieldStructType, fieldIndex)
+						} else {
+							ctx.out.LoadField(fieldStructType, fieldIndex)
+						}
 					}
 				}
 			}
+			if fDecl, ok := ctx.findMethod(t, name); ok {
+				ctx.infer.Pop()
+				fn := newQlFunc(fDecl)
+				ctx.use(fDecl)
+				ctx.infer.Push(fn)
+				return func() {}
+			}
 		}
 
-		if fDecl, ok := ctx.findMethod(t, name); ok {
-			ctx.infer.Pop()
-			fn := newQlFunc(fDecl)
-			ctx.use(fDecl)
-			ctx.infer.Push(fn)
-			return func() {}
-		}
 		if _, ok := vx.t.MethodByName(name); !ok && isLower(name) {
 			name = strings.Title(name)
 			if _, ok = vx.t.MethodByName(name); ok {
diff --git a/cl/type_decl.go b/cl/type_decl.go
index c06b573b..87f95fb5 100644
--- a/cl/type_decl.go
+++ b/cl/type_decl.go
@@ -333,6 +333,12 @@ func buildField(ctx *blockCtx, field *ast.Field, anonymous bool, fieldName strin
 		Type:      toType(ctx, field.Type).(reflect.Type),
 		Anonymous: anonymous,
 	}
+	if fieldName != "" {
+		c := fieldName[0]
+		if 'a' <= c && c <= 'z' || c == '_' {
+			f.PkgPath = ctx.pkg.Name
+		}
+	}
 	if field.Tag != nil {
 		tag, _ := strconv.Unquote(field.Tag.Value)
 		f.Tag = reflect.StructTag(tag)
diff --git a/exec/bytecode/struct.go b/exec/bytecode/struct.go
index c3b32de0..875a0a66 100644
--- a/exec/bytecode/struct.go
+++ b/exec/bytecode/struct.go
@@ -158,7 +158,8 @@ func (p *Builder) Struct(typ reflect.Type, arity int) *Builder {
 }
 
 func execStruct(i Instr, p *Context) {
-	typStruct := getType(i&bitsOpCallFuncvOperand, p)
+	typ := getType(i&bitsOpCallFuncvOperand, p)
+	typStruct := toType(typ)
 	arity := int((i >> bitsOpCallFuncvShift) & bitsFuncvArityOperand)
 	if arity == bitsFuncvArityMax {
 		arity = p.Pop().(int) + bitsFuncvArityMax
@@ -177,8 +178,8 @@ func makeStruct(typStruct reflect.Type, arity int, p *Context) {
 	}
 	v := reflect.New(typStruct).Elem()
 	for i := 0; i < n; i += 2 {
-		fieldName := args[i].(string)
-		v.FieldByName(fieldName).Set(reflect.ValueOf(args[i+1]))
+		index := args[i].(int)
+		v.Field(index).Set(reflect.ValueOf(args[i+1]))
 	}
 	if ptr {
 		p.Ret(n, v.Addr().Interface())
diff --git a/exec/bytecode/var.go b/exec/bytecode/var.go
index 2155be39..300d505a 100644
--- a/exec/bytecode/var.go
+++ b/exec/bytecode/var.go
@@ -237,6 +237,7 @@ func (p *Builder) storeVar(addr tAddress) *Builder {
 // Var represents a variable.
 type Var struct {
 	typ       reflect.Type
+	actualTyp reflect.Type
 	name      string
 	nestDepth uint32
 	idx       uint32
@@ -244,7 +245,8 @@ type Var struct {
 
 // NewVar creates a variable instance.
 func NewVar(typ reflect.Type, name string) *Var {
-	return &Var{typ: typ, name: "Q" + name, idx: 0xffffffff}
+	totyp := toType(typ)
+	return &Var{typ: totyp, name: "Q" + name, idx: 0xffffffff, actualTyp: typ}
 }
 
 func (p *Var) isGlobal() bool {
@@ -253,7 +255,7 @@ func (p *Var) isGlobal() bool {
 
 // Type returns variable's type.
 func (p *Var) Type() reflect.Type {
-	return p.typ
+	return p.actualTyp
 }
 
 // Name returns variable's name.
@@ -308,6 +310,28 @@ func (p *varManager) addVars(vars ...exec.Var) {
 	}
 }
 
+func toType(typ reflect.Type) reflect.Type {
+	if typ.Kind() == reflect.Ptr {
+		temp := typ.Elem()
+		structType := toType(temp)
+		return reflect.PtrTo(structType)
+	}
+
+	if typ.Kind() == reflect.Struct && typ.Name() == "" {
+		var fields = make([]StructField, 0, typ.NumField())
+		for i := 0; i < typ.NumField(); i++ {
+			field := typ.Field(i)
+			fields = append(fields, StructField{
+				Type: toType(field.Type),
+				Name: "Q" + field.Name,
+			})
+		}
+
+		typ = Struct(fields).Type()
+	}
+	return typ
+}
+
 type blockCtx struct {
 	varManager
 	parent *varManager
diff --git a/exec/golang/expr.go b/exec/golang/expr.go
index 9c206c32..dea2fac5 100644
--- a/exec/golang/expr.go
+++ b/exec/golang/expr.go
@@ -688,7 +688,7 @@ func (p *Builder) Struct(typ reflect.Type, arity int) *Builder {
 	args := p.rhs.GetArgs(arity << 1)
 	for i := 0; i < arity; i++ {
 		elts[i] = &ast.KeyValueExpr{
-			Key:   toField(args[i<<1].(ast.Expr)),
+			Key:   toField(args[i<<1].(ast.Expr), typ),
 			Value: args[(i<<1)+1].(ast.Expr),
 		}
 	}
@@ -709,11 +709,14 @@ func (p *Builder) Struct(typ reflect.Type, arity int) *Builder {
 	return p
 }
 
-func toField(expr ast.Expr) *ast.Ident {
-	if ident, ok := expr.(*ast.Ident); ok {
-		return ident
+func toField(expr ast.Expr, typ reflect.Type) *ast.Ident {
+	if blit, ok := expr.(*ast.BasicLit); ok {
+		i, err := strconv.Atoi(blit.Value)
+		if err == nil {
+			field := typ.Field(i)
+			return Ident(field.Name)
+		}
 	}
-	lit := expr.(*ast.BasicLit)
-	field, _ := strconv.Unquote(lit.Value)
-	return Ident(field)
+	log.Panicln("toField expression must be arrayType")
+	return nil
 }
diff --git a/tutorial/25-struct/struct.gop b/tutorial/25-struct/struct.gop
index d15635b7..2a3470eb 100644
--- a/tutorial/25-struct/struct.gop
+++ b/tutorial/25-struct/struct.gop
@@ -10,10 +10,18 @@ b := &struct {
 	B string
 }{1, "Hello"}
 
+c := &struct {
+	a int
+	b string
+}{1, "Hello"}
+
 println(b)
 
-a.A, a.B = 2, "Hi"
+a.A, a.B = 1, "Hi"
 println(a)
 
-b.A, b.B = 2, "Hi"
+b.A, b.B = 2, "Hi2"
 println(b)
+
+c.a, c.b = 3, "Hi3"
+println(c)


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 9f55b4b3..2eb9355b 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -882,6 +882,37 @@ var testStructClauses = map[string]testData{
 				B string
 			}{A: 1,B: "Hello"})
 					`, "&{1 Hello}\n", false},
+	"struct_key_value_ptr_unexport_field": {`
+			println(&struct {
+				a int  ` + "`json:\"a\"`" + `
+				b string
+			}{a: 1,b: "Hello"})
+					`, "&{1 Hello}\n", false},
+	"struct_key_value_unexport_field": {`
+			println(struct {
+				a int  ` + "`json:\"a\"`" + `
+				b string
+			}{a: 1,b: "Hello"})
+					`, "{1 Hello}\n", false},
+	"struct_unexport_field": {`
+			println(struct {
+				a int
+				b string
+			}{1, "Hello"})	
+					`, "{1 Hello}\n", false},
+	"struct_ptr_unexport_field": {`
+			println(&struct {
+				a int
+				b string
+			}{1, "Hello"})	
+					`, "&{1 Hello}\n", false},
+	"struct_store_field_panic": {`
+				import "sync"
+
+				mu := sync.WaitGroup{}
+				
+				mu.noCopy = struct{}{}
+					`, "", true},
 }
 
 func TestStruct2(t *testing.T) {
diff --git a/cl/gopkg_test.go b/cl/gopkg_test.go
index d22cdf3b..43620465 100644
--- a/cl/gopkg_test.go
+++ b/cl/gopkg_test.go
@@ -504,17 +504,17 @@ import (
 	}
 }
 
-type tpoint struct {
+type Tpoint struct {
 	X int
 	Y int
 }
 
-type trect struct {
-	Min tpoint
-	Max tpoint
+type Trect struct {
+	Min Tpoint
+	Max Tpoint
 }
 
-type tfieldinfo struct {
+type Tfieldinfo struct {
 	V1  bool
 	V2  rune
 	V3  string
@@ -522,28 +522,28 @@ type tfieldinfo struct {
 	V5  []int
 	V6  []string
 	V7  map[int]string
-	V8  trect
-	V9  *trect
-	V10 []trect
-	V11 []*trect
-	V12 [2]trect
-	V13 [2]*trect
+	V8  Trect
+	V9  *Trect
+	V10 []Trect
+	V11 []*Trect
+	V12 [2]Trect
+	V13 [2]*Trect
 }
 
-func tNewRect(x1 int, y1 int, x2 int, y2 int) *trect {
-	return &trect{tpoint{x1, y1}, tpoint{x2, y2}}
+func tNewRect(x1 int, y1 int, x2 int, y2 int) *Trect {
+	return &Trect{Tpoint{x1, y1}, Tpoint{x2, y2}}
 }
 
-func tMakeRect(x1 int, y1 int, x2 int, y2 int) trect {
-	return trect{tpoint{x1, y1}, tpoint{x2, y2}}
+func tMakeRect(x1 int, y1 int, x2 int, y2 int) Trect {
+	return Trect{Tpoint{x1, y1}, Tpoint{x2, y2}}
 }
 
-func tNewPoint(x int, y int) *tpoint {
-	return &tpoint{x, y}
+func tNewPoint(x int, y int) *Tpoint {
+	return &Tpoint{x, y}
 }
 
-func tMakePoint(x int, y int) tpoint {
-	return tpoint{x, y}
+func tMakePoint(x int, y int) Tpoint {
+	return Tpoint{x, y}
 }
 
 func execTestNewPoint(_ int, p *exec.Context) {
@@ -587,12 +587,12 @@ func TestPkgField(t *testing.T) {
 	ar[6] = m
 	ar[7] = tMakeRect(10, 20, 100, 200)
 	ar[8] = tNewRect(10, 20, 100, 200)
-	ar[9] = []trect{tMakeRect(10, 20, 30, 40), tMakeRect(50, 60, 70, 80)}
-	ar[10] = []*trect{tNewRect(10, 20, 30, 40), tNewRect(50, 60, 70, 80)}
-	ar[11] = [2]trect{tMakeRect(10, 20, 30, 40), tMakeRect(50, 60, 70, 80)}
-	ar[12] = [2]*trect{tNewRect(10, 20, 30, 40), tNewRect(50, 60, 70, 80)}
+	ar[9] = []Trect{tMakeRect(10, 20, 30, 40), tMakeRect(50, 60, 70, 80)}
+	ar[10] = []*Trect{tNewRect(10, 20, 30, 40), tNewRect(50, 60, 70, 80)}
+	ar[11] = [2]Trect{tMakeRect(10, 20, 30, 40), tMakeRect(50, 60, 70, 80)}
+	ar[12] = [2]*Trect{tNewRect(10, 20, 30, 40), tNewRect(50, 60, 70, 80)}
 
-	info := &tfieldinfo{}
+	info := &Tfieldinfo{}
 	info.V7 = make(map[int]string)
 	v := reflect.ValueOf(info).Elem()
 	for i := 0; i < v.NumField(); i++ {
@@ -701,8 +701,8 @@ import (
 func TestPkgType(t *testing.T) {
 	var I = exec.NewGoPackage("pkg_test_type")
 	I.RegisterTypes(
-		I.Type("Rect", reflect.TypeOf((*trect)(nil)).Elem()),
-		I.Type("Point", reflect.TypeOf((*tpoint)(nil)).Elem()),
+		I.Type("Rect", reflect.TypeOf((*Trect)(nil)).Elem()),
+		I.Type("Point", reflect.TypeOf((*Tpoint)(nil)).Elem()),
 	)
 
 	var testSource = `
@@ -750,16 +750,16 @@ func TestPkgType(t *testing.T) {
 	if v := ctx.Get(-5); v != nil && !reflect.DeepEqual(v, tNewRect(5, 6, 7, 8)) {
 		t.Fatal("check rect", v)
 	}
-	if v := ctx.Get(-4); v != nil && !reflect.DeepEqual(v, tpoint{1, 2}) {
+	if v := ctx.Get(-4); v != nil && !reflect.DeepEqual(v, Tpoint{1, 2}) {
 		t.Fatal("check point", v)
 	}
-	if v := ctx.Get(-3); v != nil && !reflect.DeepEqual(v, tpoint{X: 3, Y: 4}) {
+	if v := ctx.Get(-3); v != nil && !reflect.DeepEqual(v, Tpoint{X: 3, Y: 4}) {
 		t.Fatal("check point", v)
 	}
-	if v := ctx.Get(-2); v != nil && !reflect.DeepEqual(v, &tpoint{5, 6}) {
+	if v := ctx.Get(-2); v != nil && !reflect.DeepEqual(v, &Tpoint{5, 6}) {
 		t.Fatal("check point", v)
 	}
-	if v := ctx.Get(-1); v != nil && !reflect.DeepEqual(v, &tpoint{X: 7, Y: 8}) {
+	if v := ctx.Get(-1); v != nil && !reflect.DeepEqual(v, &Tpoint{X: 7, Y: 8}) {
 		t.Fatal("check point", v)
 	}
 
@@ -768,9 +768,9 @@ func TestPkgType(t *testing.T) {
 func TestPkgTakeAddr(t *testing.T) {
 	var I = exec.NewGoPackage("pkg_test_takeaddr")
 	rc := tMakeRect(10, 20, 100, 200)
-	ar := [2]trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
-	slice := []trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
-	m := make(map[int]trect)
+	ar := [2]Trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
+	slice := []Trect{tMakeRect(1, 2, 10, 20), tMakeRect(10, 20, 100, 200)}
+	m := make(map[int]Trect)
 	m[1] = tMakeRect(1, 2, 10, 20)
 	m[2] = tMakeRect(10, 20, 100, 200)
 	I.RegisterVars(
diff --git a/exec/bytecode/struct_test.go b/exec/bytecode/struct_test.go
index 9c442584..8a90073f 100644
--- a/exec/bytecode/struct_test.go
+++ b/exec/bytecode/struct_test.go
@@ -41,9 +41,9 @@ func TestStruct(t *testing.T) {
 	expect(t,
 		func() {
 			b.DefineVar(v)
-			b.Push("Name")
+			b.Push(0)
 			b.Push("bar")
-			b.Push("Age")
+			b.Push(1)
 			b.Push(30)
 			b.Struct(reflect.TypeOf(p), 2)
 			b.StoreVar(v)
@@ -74,9 +74,9 @@ func TestStruct2(t *testing.T) {
 	expect(t,
 		func() {
 			b.DefineVar(v)
-			b.Push("Name")
+			b.Push(0)
 			b.Push("bar")
-			b.Push("Age")
+			b.Push(1)
 			b.Push(30)
 			b.Struct(reflect.TypeOf(p), 2)
 			b.StoreVar(v)
diff --git a/exec/golang/builder_test.go b/exec/golang/builder_test.go
index 07276d0c..17c90f53 100644
--- a/exec/golang/builder_test.go
+++ b/exec/golang/builder_test.go
@@ -366,9 +366,9 @@ func main() {
 	typ := reflect.StructOf(fields)
 
 	code := NewBuilder("main", nil, nil).Interface().
-		Push("Name").
+		Push(0).
 		Push("bar").
-		Push("Age").
+		Push(1).
 		Push(30).
 		Struct(typ, 2).
 		CallGoFuncv(println, 1, 1).
@@ -410,9 +410,9 @@ func main() {
 	typ := reflect.StructOf(fields)
 
 	code := NewBuilder("main", nil, nil).Interface().
-		Push("Name").
+		Push(0).
 		Push("bar").
-		Push("Age").
+		Push(1).
 		Push(30).
 		Struct(reflect.PtrTo(typ), 2).
 		CallGoFuncv(println, 1, 1).
@@ -463,9 +463,9 @@ func main() {
 		DefineVar(p).
 		DefineType(typ, "Person").
 		EndStmt(nil, &stmtState{rhsBase: 0}).
-		Push("Name").
+		Push(0).
 		Push("bar").
-		Push("Age").
+		Push(1).
 		Push(30).
 		Struct(typ, 2).
 		StoreVar(p).
diff --git a/exec/golang/testdata/26.struct/struct.gop b/exec/golang/testdata/26.struct/struct.gop
index 6111c45c..aa287f41 100644
--- a/exec/golang/testdata/26.struct/struct.gop
+++ b/exec/golang/testdata/26.struct/struct.gop
@@ -1,7 +1,13 @@
+type C struct {
+	D int
+	F string
+}
+
 a := struct {
 	A int
 	B string
-}{1, "Hello"}
+	C C
+}{1, "Hello", C{2, "World"}}
 
 println(a)
 
@@ -10,6 +16,11 @@ b := &struct {
 	B string
 }{1, "Hello"}
 
+c := &struct {
+	a int
+	b string
+}{1, "Hello"}
+
 println(b)
 
 a.A, a.B = 2, "Hi"
@@ -17,3 +28,6 @@ println(a)
 
 b.B = "Hi"
 println(b)
+
+c.a, c.b = 2, "Hi"
+println(c)