Repository: goplus/gop
Pull request #426 (2020-07-09 03:25:50)

Builtin copy
#415 


Patch:
diff --git a/cl/goinstr.go b/cl/goinstr.go
index 9730695b..6262b467 100644
--- a/cl/goinstr.go
+++ b/cl/goinstr.go
@@ -99,7 +99,37 @@ func igoAppend(ctx *blockCtx, v *ast.CallExpr) func() {
 
 // func copy(dst, src []Type) int
 func igoCopy(ctx *blockCtx, v *ast.CallExpr) func() {
-	panic("todo")
+	if len(v.Args) < 2 {
+		log.Panicln("not enough arguments in call to copy")
+	}
+	if len(v.Args) > 2 {
+		log.Panicln("too many arguments in call to copy")
+	}
+	dstExpr := compileExpr(ctx, v.Args[0])
+	dstTy := ctx.infer.Get(-1).(iValue).Type()
+	if dstTy.Kind() != reflect.Slice {
+		log.Panicln("arguments to copy must be slices; have ", dstTy.Kind())
+	}
+	ctx.infer.Ret(1, &goValue{exec.TyInt})
+	return func() {
+		dstExpr()
+		compileExpr(ctx, v.Args[1])()
+		srcTy := ctx.infer.Get(-1).(iValue).Type()
+		switch srcTy.Kind() {
+		case reflect.Slice:
+			if srcTy.Elem().Kind() != dstTy.Elem().Kind() {
+				log.Panicf("arguments to copy have different element types: %s(%s) %s(%s)", dstTy.Kind(), dstTy.Elem().Kind(), srcTy.Kind(), srcTy.Elem().Kind())
+			}
+		case reflect.String:
+			if dstTy.Elem().Kind() != reflect.Uint8 {
+				log.Panicln("arguments to copy have different element types:", dstTy.Kind(), srcTy.Kind())
+			}
+		default:
+			log.Panicln("second argument to copy should be slice or string; have", srcTy.Kind())
+		}
+		ctx.infer.Pop()
+		ctx.out.GoBuiltin(nil, exec.GobCopy)
+	}
 }
 
 // func delete(m map[Type]Type1, key Type)
diff --git a/exec/bytecode/code.go b/exec/bytecode/code.go
index a994f19f..5ddadc14 100644
--- a/exec/bytecode/code.go
+++ b/exec/bytecode/code.go
@@ -110,7 +110,7 @@ const (
 	opTypeCast      = 37 // type(26)
 	opSlice         = 38 // i(13) j(13)
 	opSlice3        = 39 // i(13) j(13)
-	opMapIndex      = 40 // reserved(25) set(1)
+	opMapIndex      = 40 // reserved(24) set(2)
 	opGoBuiltin     = 41 // op(26)
 	opErrWrap       = 42 // idx(26)
 	opWrapIfErr     = 43 // reserved(2) offset(24)
@@ -192,7 +192,7 @@ var instrInfos = []InstrInfo{
 	opTypeCast:      {"typeCast", "", "type", 26},                         // type(26)
 	opSlice:         {"slice", "i", "j", (13 << 8) | 13},                  // i(13) j(13)
 	opSlice3:        {"slice3", "i", "j", (13 << 8) | 13},                 // i(13) j(13)
-	opMapIndex:      {"mapIndex", "", "set", 26},                          // reserved(25) set(1)
+	opMapIndex:      {"mapIndex", "", "set", 26},                          // reserved(24) set(2)
 	opGoBuiltin:     {"goBuiltin", "", "op", 26},                          // op(26)
 	opErrWrap:       {"errWrap", "", "idx", 26},                           // idx(26)
 	opWrapIfErr:     {"wrapIfErr", "", "offset", 26},                      // reserved(2) offset(24)
diff --git a/exec/bytecode/interface.go b/exec/bytecode/interface.go
index e1486e71..9b3aeec6 100644
--- a/exec/bytecode/interface.go
+++ b/exec/bytecode/interface.go
@@ -355,7 +355,7 @@ func (p *iBuilder) SetMapIndex() exec.Builder {
 	return p
 }
 
-// SetMapIndex instr
+// Delete instr
 func (p *iBuilder) Delete() exec.Builder {
 	((*Builder)(p)).Delete()
 	return p
diff --git a/exec/bytecode/var.go b/exec/bytecode/var.go
index 488aa966..60e2f13c 100644
--- a/exec/bytecode/var.go
+++ b/exec/bytecode/var.go
@@ -129,6 +129,11 @@ func execGoBuiltin(i Instr, p *Context) {
 	case GobCap:
 		v := reflect.ValueOf(p.data[n-1])
 		p.data[n-1] = reflect.Indirect(v).Cap()
+	case GobCopy:
+		n := len(p.data)
+		src := reflect.ValueOf(p.data[n-1])
+		dst := reflect.ValueOf(p.data[n-2])
+		p.Ret(2, reflect.Copy(dst, src))
 	default:
 		log.Panicln("execGoBuiltin: todo -", i)
 	}
diff --git a/exec/golang/interface.go b/exec/golang/interface.go
index 21fb6127..62dc179c 100644
--- a/exec/golang/interface.go
+++ b/exec/golang/interface.go
@@ -370,7 +370,7 @@ func (p *iBuilder) SetMapIndex() exec.Builder {
 	return p
 }
 
-// SetMapIndex instr
+// Delete instr
 func (p *iBuilder) Delete() exec.Builder {
 	((*Builder)(p)).Delete()
 	return p


Test patch:
diff --git a/cl/expr_test.go b/cl/expr_test.go
index 3c549dc6..9ae05f13 100644
--- a/cl/expr_test.go
+++ b/cl/expr_test.go
@@ -1264,3 +1264,75 @@ func TestDelete(t *testing.T) {
 }
 
 // -----------------------------------------------------------------------------
+
+var testCopyClauses = map[string]testData{
+	"copy_int": {`
+					a:=[1,2,3]
+					b:=[4,5,6]
+					n:=copy(b,a)
+					println(n)
+					println(b)
+					`, []string{"3", "[1 2 3]"}},
+	"copy_string": {`
+					a:=["hello"]
+					b:=["hi"]
+					n:=copy(b,a)
+					println(n)
+					println(b)
+					`, []string{"1", "[hello]"}},
+	"copy_byte_string": {`
+					a:=[byte(65),byte(66),byte(67)]
+					println(string(a))
+					n:=copy(a,"abc")
+					println(n)
+					println(a)
+					println(string(a))
+					`, []string{"ABC", "3", "[97 98 99]", "abc"}},
+	"copy_first_not_slice_panic": {`
+					a:=1
+					b:=[1,2,3]
+					copy(a,b)
+					println(a)
+					`, []string{"_panic"}},
+	"copy_second_not_slice_panic": {`
+					a:=1
+					b:=[1,2,3]
+					copy(b,a)
+					println(b)
+					`, []string{"_panic"}},
+	"copy_one_args_panic": {`
+					a:=[1,2,3]
+					copy(a)
+					println(a)
+					`, []string{"_panic"}},
+	"copy_multi_args_panic": {`
+					a:=[1,2,3]
+					copy(a,a,a)
+					println(a)
+					`, []string{"_panic"}},
+	"copy_string_panic": {`
+					a:=[65,66,67]
+					copy(a,"abc")
+					println(a)
+					`, []string{"_panic"}},
+	"copy_different_type_panic": {`
+					a:=[65,66,67]
+					b:=[1.2,1.5,1.7]
+					copy(b,a)
+					copy(b,a)
+					println(b)
+					`, []string{"_panic"}},
+	"copy_with_operation": {`
+					a:=[65,66,67]
+					b:=[1]
+					println(copy(a,b)+copy(b,a)==2)
+					`, []string{"true"}},
+}
+
+func TestCopy(t *testing.T) {
+	for name, clause := range testCopyClauses {
+		testSingleStmt(name, t, asttest.NewSingleFileFS("/foo", "bar.gop", clause.clause), clause.wants)
+	}
+}
+
+// -----------------------------------------------------------------------------
diff --git a/exec/bytecode/goinstr_test.go b/exec/bytecode/goinstr_test.go
index ca0a79c3..c96c6870 100644
--- a/exec/bytecode/goinstr_test.go
+++ b/exec/bytecode/goinstr_test.go
@@ -637,3 +637,55 @@ func TestDelete(t *testing.T) {
 		t.Fatal("expected: {`Go+`: 2}, ret =", v)
 	}
 }
+
+func TestCopy(t *testing.T) {
+	a := NewVar(reflect.SliceOf(TyInt), "")
+	b := NewVar(reflect.SliceOf(TyInt), "")
+	code := newBuilder().
+		DefineVar(a).
+		DefineVar(b).
+		Push(1).
+		Push(2).
+		Push(3).
+		MakeArray(reflect.SliceOf(TyInt), 3).
+		StoreVar(a).
+		Push(11).
+		MakeArray(reflect.SliceOf(TyInt), 1).
+		StoreVar(b).
+		LoadVar(b).
+		LoadVar(a).
+		GoBuiltin(nil, GobCopy).
+		LoadVar(b).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	arr := ctx.Pop().([]int)
+	n := ctx.Pop().(int)
+	if n != 1 || reflect.DeepEqual(arr, []int{11, 2, 3}) {
+		t.Fatal("copy failed")
+	}
+}
+
+func TestCopy2(t *testing.T) {
+	a := NewVar(reflect.SliceOf(TyByte), "")
+	code := newBuilder().
+		DefineVar(a).
+		Push(byte(96)).
+		Push(byte(97)).
+		MakeArray(reflect.SliceOf(TyByte), 2).
+		StoreVar(a).
+		LoadVar(a).
+		Push("hello").
+		GoBuiltin(nil, GobCopy).
+		LoadVar(a).
+		Resolve()
+
+	ctx := NewContext(code)
+	ctx.Exec(0, code.Len())
+	arr := ctx.Pop().([]byte)
+	n := ctx.Pop().(int)
+	if n != 2 || string(arr) != "he" {
+		t.Fatal("copy failed")
+	}
+}
diff --git a/exec/golang/testdata/04.slicelit/slicelit.gop b/exec/golang/testdata/04.slicelit/slicelit.gop
index 7e5c7651..36e3fc03 100644
--- a/exec/golang/testdata/04.slicelit/slicelit.gop
+++ b/exec/golang/testdata/04.slicelit/slicelit.gop
@@ -12,3 +12,8 @@ println([5+6i])         // []complex128
 println(["xsw", 3])     // []interface{}
 
 println("empty slice:", []) // []interface{}
+
+a := [1, 2, 3]
+b := [4, 5]
+println(copy(a, b))
+println("slice [4 5 3]", a)
diff --git a/exec/golang/testdata/23.stdout/stdout.gop b/exec/golang/testdata/23.stdout/stdout.gop
index 587bd9f0..1fc122ff 100644
--- a/exec/golang/testdata/23.stdout/stdout.gop
+++ b/exec/golang/testdata/23.stdout/stdout.gop
@@ -1,3 +1,4 @@
+/*TODO
 import (
 	"log"
 	"os"
@@ -7,3 +8,4 @@ log.Println("hello", true)
 
 logger := log.New(os.Stdout, "logger: ", log.Lshortfile)
 logger.Println("foo", "bar", false, 666)
+*/