Repository: goplus/gop
Pull request #293 (2020-06-19 05:59:06)

pkg.var load and store
只是 pkg var 变量本身的读取和写入，不支持 field 操作.
目前的实现是在 blockCtx 中加入 checkLHS  以对 array 单独处理，可能存在问题，需要继续测试。


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index 7febe6c8..ddc4d535 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -243,6 +243,7 @@ type blockCtx struct {
 	syms      map[string]iSymbol
 	noExecCtx bool
 	checkFlag bool
+	inLHS     bool
 }
 
 // function block ctx
diff --git a/cl/ctx_check.go b/cl/ctx_check.go
index b09808b1..2b63f12a 100644
--- a/cl/ctx_check.go
+++ b/cl/ctx_check.go
@@ -340,6 +340,8 @@ func isNoExecCtxExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) bool {
 		return isNoExecCtxIdentLHS(ctx, v.Name, mode)
 	case *ast.IndexExpr:
 		return isNoExecCtxIndexExprLHS(ctx, v, mode)
+	case *ast.SelectorExpr:
+		return isNoExecCtxSelectorExprLHS(ctx, v, mode)
 	default:
 		log.Panicln("isNoExecCtxExprLHS failed: unknown -", reflect.TypeOf(v))
 	}
@@ -360,4 +362,11 @@ func isNoExecCtxIdentLHS(ctx *blockCtx, name string, mode compleMode) bool {
 	return true
 }
 
+func isNoExecCtxSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compleMode) bool {
+	if noExecCtx := isNoExecCtxExpr(ctx, v.X); !noExecCtx {
+		return false
+	}
+	return true
+}
+
 // -----------------------------------------------------------------------------
diff --git a/cl/expr.go b/cl/expr.go
index 3b5dbe22..b93532c9 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -41,14 +41,18 @@ const (
 // -----------------------------------------------------------------------------
 
 func compileExprLHS(ctx *blockCtx, expr ast.Expr, mode compleMode) {
+	ctx.inLHS = true
 	switch v := expr.(type) {
 	case *ast.Ident:
 		compileIdentLHS(ctx, v.Name, mode)
 	case *ast.IndexExpr:
 		compileIndexExprLHS(ctx, v, mode)
+	case *ast.SelectorExpr:
+		compileSelectorExprLHS(ctx, v, mode)
 	default:
 		log.Panicln("compileExpr failed: unknown -", reflect.TypeOf(v))
 	}
+	ctx.inLHS = false
 }
 
 func compileExpr(ctx *blockCtx, expr ast.Expr) func() {
@@ -150,7 +154,11 @@ func compileIdent(ctx *blockCtx, name string) func() {
 		case *execVar:
 			ctx.infer.Push(&goValue{t: v.v.Type()})
 			return func() {
-				ctx.out.LoadVar(v.v)
+				if ctx.inLHS && v.v.Type().Kind() == reflect.Array {
+					ctx.out.AddrVar(v.v)
+				} else {
+					ctx.out.LoadVar(v.v)
+				}
 			}
 		case *stackVar:
 			ctx.infer.Push(&goValue{t: v.typ})
@@ -887,6 +895,49 @@ func getFuncInfo(fun exec.FuncInfo) (name string, narg int) {
 	return "main", 0
 }
 
+func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr, mode compleMode) {
+	if mode == lhsDefine {
+		log.Panicln("compileSelectorExprLHS: `:=` can't be used for index expression")
+	}
+	in := ctx.infer.Get(-1)
+	exprX := compileExpr(ctx, v.X)
+	x := ctx.infer.Get(-1)
+	ctx.infer.PopN(2)
+	switch vx := x.(type) {
+	case *nonValue:
+		switch nv := vx.v.(type) {
+		case exec.GoPackage:
+			if c, ok := nv.FindConst(v.Sel.Name); ok {
+				log.Panicln("cannot assign to ", c.Pkg.PkgPath()+"."+c.Name)
+			}
+			addr, kind, ok := nv.Find(v.Sel.Name)
+			if !ok {
+				log.Panicln("compileSelectorExprLHS: not found -", nv.PkgPath(), v.Sel.Name)
+			}
+			switch kind {
+			case exec.SymbolVar:
+				info := ctx.GetGoVarInfo(exec.GoVarAddr(addr))
+				t := reflect.TypeOf(info.This).Elem()
+				checkType(t, in, ctx.out)
+				ctx.out.StoreGoVar(exec.GoVarAddr(addr))
+			default:
+				log.Panicln("compileSelectorExprLHS: unknown GoPackage symbol kind -", kind)
+			}
+		default:
+			log.Panicln("compileSelectorExprLHS: unknown nonValue -", reflect.TypeOf(nv))
+		}
+	case *goValue:
+		_, t := countPtr(vx.t)
+		name := v.Sel.Name
+		if sf, ok := t.FieldByName(name); ok {
+			log.Panicln("compileSelectorExprLHS todo: structField -", t, sf)
+		}
+	default:
+		log.Panicln("compileSelectorExprLHS failed: unknown -", reflect.TypeOf(vx))
+	}
+	_ = exprX
+}
+
 func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr) func() {
 	exprX := compileExpr(ctx, v.X)
 	x := ctx.infer.Get(-1)
@@ -911,6 +962,17 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr) func() {
 				return func() {
 					log.Panicln("compileSelectorExpr: todo")
 				}
+			case exec.SymbolVar:
+				info := ctx.GetGoVarInfo(exec.GoVarAddr(addr))
+				vt := reflect.ValueOf(info.This)
+				ctx.infer.Ret(1, &goValue{t: vt.Elem().Type()})
+				return func() {
+					if ctx.inLHS && vt.Elem().Kind() == reflect.Array {
+						ctx.out.AddrGoVar(exec.GoVarAddr(addr))
+					} else {
+						ctx.out.LoadGoVar(exec.GoVarAddr(addr))
+					}
+				}
 			default:
 				log.Panicln("compileSelectorExpr: unknown GoPackage symbol kind -", kind)
 			}


Test patch:
diff --git a/cl/gopkg_test.go b/cl/gopkg_test.go
index bd9e0752..54e65ed1 100644
--- a/cl/gopkg_test.go
+++ b/cl/gopkg_test.go
@@ -37,7 +37,7 @@ type testConstInfo struct {
 }
 
 func TestPkgConst(t *testing.T) {
-	var I = exec.NewGoPackage("pkg")
+	var I = exec.NewGoPackage("pkg_test_const")
 	infos := []testConstInfo{
 		{"True", reflect.Bool, true},
 		{"False", reflect.Bool, false},
@@ -64,7 +64,7 @@ func TestPkgConst(t *testing.T) {
 	testSource = `package main
 
 import (
-	"pkg"
+	pkg "pkg_test_const"
 )
 
 `
@@ -116,3 +116,389 @@ import (
 		}
 	}
 }
+
+type testLoadGoVarInfo struct {
+	Name string
+	Addr interface{}
+}
+
+func TestPkgLoadGoVar(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_var_load")
+	v1 := true
+	v2 := false
+	v3 := rune('A')
+	v4 := "Info"
+	v5 := "信息"
+	v6 := -100
+	v7 := uint32(100)
+	v8 := []int{100, 200}
+	v9 := []string{"01", "02"}
+	v10 := make(map[int]string)
+	v10[1] = "01"
+	v10[2] = "02"
+	infos := []testLoadGoVarInfo{
+		{"True", &v1},
+		{"False", &v2},
+		{"A", &v3},
+		{"String1", &v4},
+		{"String2", &v5},
+		{"Int1", &v6},
+		{"Int2", &v7},
+		{"Ar1", &v8},
+		{"Ar2", &v9},
+		{"M1", &v10},
+	}
+
+	var vars []exec.GoVarInfo
+	for _, info := range infos {
+		vars = append(vars, I.Var(info.Name, info.Addr))
+	}
+	I.RegisterVars(vars...)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_var_load"
+)
+
+`
+
+	// make println
+	for _, info := range infos {
+		testSource += fmt.Sprintf("println(pkg.%v)\n", info.Name)
+	}
+	// make ret
+	var retList []string
+	// var retList2 []string
+	var name string
+	for i, info := range infos {
+		name = fmt.Sprintf("id%v := pkg.%v", i, info.Name)
+		retList = append(retList, name)
+	}
+
+	testSource += strings.Join(retList, "\n")
+
+	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()
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	n := len(infos)
+	for i, info := range infos {
+		if v := ctx.Get(i - n); reflect.DeepEqual(v, reflect.ValueOf(info.Addr).Elem().Interface()) {
+			t.Fatal(info.Name, v, reflect.ValueOf(info.Addr).Elem().Interface())
+		}
+	}
+}
+
+type testStoreGoVarInfo struct {
+	Name  string
+	Addr  interface{}
+	Store interface{}
+	Gop   string
+}
+
+func TestPkgStoreGoVar(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_store_var")
+	v1 := true
+	v2 := false
+	v3 := 'A'
+	v4 := "Info"
+	v5 := "信息"
+	v6 := -100
+	v7 := uint32(100)
+	v8 := []int{100, 200}
+	v9 := []string{"01", "02"}
+	v10 := make(map[int]string)
+	v10[1] = "01"
+	v10[2] = "02"
+	v10_1 := make(map[int]string)
+	v10_1[1] = "02"
+	v10_1[3] = "03"
+	infos := []testStoreGoVarInfo{
+		{"True", &v1, false, "false"},
+		{"False", &v2, true, "true"},
+		{"A", &v3, 'B', "'B'"},
+		{"String1", &v4, "Info2", `"Info2"`},
+		{"String2", &v5, "Inf3", `"Inf3"`},
+		{"Int1", &v6, 100, "100"},
+		{"Int2", &v7, uint32(200), "200"},
+		{"Ar1", &v8, []int{200, 300}, "[200,300]"},
+		{"Ar2", &v9, []string{"03", "04"}, `["03","04"]`},
+		{"M1", &v10, v10_1, `{1:"02",3:"03"}`},
+	}
+
+	var vars []exec.GoVarInfo
+	for _, info := range infos {
+		vars = append(vars, I.Var(info.Name, info.Addr))
+	}
+	I.RegisterVars(vars...)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_store_var"
+)
+
+`
+
+	for _, info := range infos {
+		testSource += fmt.Sprintf("pkg.%v = %v\n", info.Name, info.Gop)
+	}
+
+	// make println
+	for _, info := range infos {
+		testSource += fmt.Sprintf("println(pkg.%v)\n", info.Name)
+	}
+
+	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()
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	for _, info := range infos {
+		v := reflect.ValueOf(info.Addr).Elem().Interface()
+		if !reflect.DeepEqual(v, info.Store) {
+			t.Fatalf("%v, %v(%T), %v(%T)\n", info.Name, v, v, info.Store, info.Store)
+		}
+	}
+}
+
+func TestPkgGoVarMap(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_var_map")
+	m1 := make(map[int]string)
+	m1[0] = "hello"
+	m1[1] = "world"
+	m1_1 := make(map[int]string)
+	m1_1[0] = "001"
+	m1_1[1] = "002"
+
+	m2 := make(map[string]int)
+	m2["0"] = 100
+	m2["1"] = 200
+	m2_1 := make(map[string]int)
+	m2_1["0"] = -200
+	m2_1["1"] = -100
+	infos := []testStoreGoVarInfo{
+		{"M1", &m1, m1_1, `pkg.M1[0],pkg.M1[1]="001","002"`},
+		{"M2", &m2, m2_1, `pkg.M2["0"],pkg.M2["1"]=-200,-100`},
+	}
+
+	var vars []exec.GoVarInfo
+	for _, info := range infos {
+		vars = append(vars, I.Var(info.Name, info.Addr))
+	}
+	I.RegisterVars(vars...)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_var_map"
+)
+
+`
+
+	for _, info := range infos {
+		testSource += fmt.Sprintf("%v\n", info.Gop)
+	}
+
+	// make println
+	for _, info := range infos {
+		testSource += fmt.Sprintf("println(pkg.%v)\n", info.Name)
+	}
+
+	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()
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	for _, info := range infos {
+		v := reflect.ValueOf(info.Addr).Elem().Interface()
+		if !reflect.DeepEqual(v, info.Store) {
+			t.Fatalf("%v, %v(%T), %v(%T)\n", info.Name, v, v, info.Store, info.Store)
+		}
+	}
+}
+
+func TestPkgGoVarSlice(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_var_slice")
+
+	var s1 []string
+	s1 = append(s1, "hello")
+	s1 = append(s1, "world")
+	var s1_1 []string
+	s1_1 = append(s1_1, "001")
+	s1_1 = append(s1_1, "002")
+
+	infos := []testStoreGoVarInfo{
+		{"S1", &s1, s1_1, `pkg.S1[0],pkg.S1[1]="001","002"`},
+	}
+
+	var vars []exec.GoVarInfo
+	for _, info := range infos {
+		vars = append(vars, I.Var(info.Name, info.Addr))
+	}
+	I.RegisterVars(vars...)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_var_slice"
+)
+
+`
+
+	for _, info := range infos {
+		testSource += fmt.Sprintf("%v\n", info.Gop)
+	}
+
+	// make println
+	for _, info := range infos {
+		testSource += fmt.Sprintf("println(pkg.%v)\n", info.Name)
+	}
+
+	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()
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	for _, info := range infos {
+		v := reflect.ValueOf(info.Addr).Elem().Interface()
+		if !reflect.DeepEqual(v, info.Store) {
+			t.Fatalf("%v, %v(%T), %v(%T)\n", info.Name, v, v, info.Store, info.Store)
+		}
+	}
+}
+
+func TestPkgGoVarArray(t *testing.T) {
+	var I = exec.NewGoPackage("pkg_test_var_array")
+	var ar1 [2]string
+	ar1[0] = "hello"
+	ar1[1] = "world"
+	var ar1_1 [2]string
+	ar1_1[0] = "001"
+	ar1_1[1] = "002"
+
+	infos := []testStoreGoVarInfo{
+		{"A1", &ar1, ar1_1, `pkg.A1[0],pkg.A1[1]="001","002"`},
+	}
+
+	var vars []exec.GoVarInfo
+	for _, info := range infos {
+		vars = append(vars, I.Var(info.Name, info.Addr))
+	}
+	I.RegisterVars(vars...)
+
+	var testSource string
+	testSource = `package main
+
+import (
+	pkg "pkg_test_var_array"
+)
+
+	A0 := pkg.A1
+	A0[0], A0[1] = "003", "004"
+	println(A0)
+
+`
+
+	for _, info := range infos {
+		testSource += fmt.Sprintf("%v\n", info.Gop)
+	}
+
+	// make println
+	for _, info := range infos {
+		testSource += fmt.Sprintf("println(pkg.%v)\n", info.Name)
+	}
+
+	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()
+
+	ctx := exec.NewContext(code)
+	ctx.Exec(0, code.Len())
+
+	for _, info := range infos {
+		v := reflect.ValueOf(info.Addr).Elem().Interface()
+		if !reflect.DeepEqual(v, info.Store) {
+			t.Fatalf("%v, %v(%T), %v(%T)\n", info.Name, v, v, info.Store, info.Store)
+		}
+	}
+}