Repository: Konstantin8105/c4go
Pull request #391 (2019-04-04 20:42:18)

Example: tinn project

Konstantin8105: Close #390
codecov[bot]: # [Codecov](https://codecov.io/gh/Konstantin8105/c4go/pull/391?src=pr&el=h1) Report
> Merging [#391](https://codecov.io/gh/Konstantin8105/c4go/pull/391?src=pr&el=desc) into [master](https://codecov.io/gh/Konstantin8105/c4go/commit/091426564e9688834b169562c66ba9c210fe385a?src=pr&el=desc) will **increase** coverage by `0.04%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/Konstantin8105/c4go/pull/391/graphs/tree.svg?width=650&token=esH2rHXlZ2&height=150&src=pr)](https://codecov.io/gh/Konstantin8105/c4go/pull/391?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #391      +/-   ##
==========================================
+ Coverage   85.47%   85.51%   +0.04%     
==========================================
  Files         188      188              
  Lines       11005    11037      +32     
==========================================
+ Hits         9406     9438      +32     
  Misses       1140     1140              
  Partials      459      459
```


| [Impacted Files](https://codecov.io/gh/Konstantin8105/c4go/pull/391?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [transpiler/bind.go](https://codecov.io/gh/Konstantin8105/c4go/pull/391/diff?src=pr&el=tree#diff-dHJhbnNwaWxlci9iaW5kLmdv) | `71.75% <100%> (+4.37%)` | :arrow_up: |
| [noarch/stdlib.go](https://codecov.io/gh/Konstantin8105/c4go/pull/391/diff?src=pr&el=tree#diff-bm9hcmNoL3N0ZGxpYi5nbw==) | `91.11% <100%> (+0.15%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Konstantin8105/c4go/pull/391?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/Konstantin8105/c4go/pull/391?src=pr&el=footer). Last update [0914265...166b9f3](https://codecov.io/gh/Konstantin8105/c4go/pull/391?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/noarch/stdlib.go b/noarch/stdlib.go
index e41eca5..69681b5 100644
--- a/noarch/stdlib.go
+++ b/noarch/stdlib.go
@@ -517,7 +517,11 @@ func AtexitRun() {
 }
 
 func Int32() int32 {
-	return int32(rand.Int())
+	r := int32(rand.Int())
+	if r < 0 {
+		return Int32()
+	}
+	return r
 }
 
 func Exit(e int32) {
diff --git a/transpiler/bind.go b/transpiler/bind.go
index 1ea1970..b42aee3 100644
--- a/transpiler/bind.go
+++ b/transpiler/bind.go
@@ -79,11 +79,14 @@ func generateBinding(p *program.Program) (bindHeader, bindCode string) {
 	return
 }
 
+func getBindArgName(pos int) string {
+	return fmt.Sprintf("arg%d", pos)
+}
+
 func getBindFunction(p *program.Program, d program.DefinitionFunction) (code string, err error) {
 	var f goast.FuncDecl
 	f.Name = goast.NewIdent(d.Name)
 
-	prefix := "arg"
 	// arguments types
 	var ft goast.FuncType
 	var fl goast.FieldList
@@ -103,7 +106,7 @@ func getBindFunction(p *program.Program, d program.DefinitionFunction) (code str
 	for i := range argResolvedType {
 		resolveType := argResolvedType[i]
 		fl.List = append(fl.List, &goast.Field{
-			Names: []*goast.Ident{goast.NewIdent(fmt.Sprintf("%s%d", prefix, i))},
+			Names: []*goast.Ident{goast.NewIdent(getBindArgName(i))},
 			Type:  goast.NewIdent(resolveType),
 		})
 	}
@@ -128,7 +131,7 @@ func getBindFunction(p *program.Program, d program.DefinitionFunction) (code str
 	var arg []goast.Expr
 	for i := range argResolvedType {
 		// convert from Go type to Cgo type
-		cgoExpr, err := ResolveCgoType(p, argResolvedType[i], goast.NewIdent(fmt.Sprintf("%s%d", prefix, i)))
+		cgoExpr, err := ResolveCgoType(p, argResolvedType[i], goast.NewIdent(getBindArgName(i)))
 		if err != nil {
 			return "", fmt.Errorf("cannot resolve cgo type for function `%s`: %v", d.Name, err)
 		}
@@ -138,7 +141,9 @@ func getBindFunction(p *program.Program, d program.DefinitionFunction) (code str
 
 	f.Body = &goast.BlockStmt{}
 
-	stmts := bindFromCtoGo(p, d.ReturnType, returnResolvedType, util.NewCallExpr(fmt.Sprintf("C.%s", d.Name), arg...))
+	stmts := prepareIfForNilArgs(argResolvedType, returnResolvedType)
+	f.Body.List = append(f.Body.List, stmts...)
+	stmts = bindFromCtoGo(p, d.ReturnType, returnResolvedType, util.NewCallExpr(fmt.Sprintf("C.%s", d.Name), arg...))
 	f.Body.List = append(f.Body.List, stmts...)
 
 	// add comment for function
@@ -365,3 +370,54 @@ func bindFromCtoGo(p *program.Program, cType string, goType string, expr goast.E
 
 	return
 }
+
+// add if`s for nil cases
+//
+// strtok - add c-binding for implemention function
+// func strtok(arg0 []byte, arg1 []byte) []byte {
+//	if arg0 == nil {
+//		return []byte{}
+//	}
+//	if arg1 == nil {
+//		return []byte{}
+//	}
+//	return (.....)
+//}
+//
+func prepareIfForNilArgs(argType []string, returnType string) (stmts []goast.Stmt) {
+	var ret goast.Stmt
+	switch {
+	case strings.Contains(returnType, "[]"):
+		ret = &goast.ReturnStmt{
+			Results: []goast.Expr{
+				goast.NewIdent(returnType + "{}"),
+			},
+		}
+
+	default:
+		return
+	}
+
+	for i := range argType {
+		// for slices : []byte, []int, ...
+		// 	if arg... == nil{
+		//		return []...{}
+		//	}
+		if strings.Contains(argType[i], "[]") {
+			stmts = append(stmts, &goast.IfStmt{
+				Cond: &goast.BinaryExpr{
+					X:  goast.NewIdent(getBindArgName(i)),
+					Op: token.EQL,
+					Y:  goast.NewIdent("nil"),
+				},
+				Body: &goast.BlockStmt{
+					List: []goast.Stmt{
+						ret,
+					},
+				},
+			})
+			continue
+		}
+	}
+	return
+}


Test patch:
diff --git a/other_test.go b/other_test.go
index 62286b0..28df43d 100644
--- a/other_test.go
+++ b/other_test.go
@@ -78,10 +78,6 @@ func TestBookSources(t *testing.T) {
 		gitSource      string
 		ignoreFileList []string
 	}{
-		{
-			prefix:    "Tinn",
-			gitSource: "https://github.com/glouw/tinn.git",
-		},
 		{
 			prefix:    "brainfuck",
 			gitSource: "https://github.com/kgabis/brainfuck-c.git",
@@ -485,6 +481,7 @@ func downloadFile(filepath string, url string) error {
 	// Create the file
 	out, err := os.Create(filepath)
 	if err != nil {
+		err = fmt.Errorf("cannot create %v", err)
 		return err
 	}
 	defer out.Close()
@@ -666,3 +663,85 @@ func TestKiloEditor(t *testing.T) {
 			goFile, err, cmdErr.String())
 	}
 }
+
+func TestTinn(t *testing.T) {
+
+	prefix := "Tinn"
+	gitSource := "https://github.com/glouw/tinn.git"
+
+	fileList, err := getFileList(prefix, gitSource)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	goFile := fileList[0] + ".go"
+	args := DefaultProgramArgs()
+	args.inputFiles = fileList
+	args.outputFile = goFile
+	args.ast = false
+	args.verbose = false
+
+	if err := Start(args); err != nil {
+		t.Fatalf("Cannot transpile `%v`: %v", args, err)
+	}
+
+	// warning is not acceptable
+	dat, err := ioutil.ReadFile(goFile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if bytes.Contains(dat, []byte(program.WarningMessage)) {
+		t.Fatalf("find warning message")
+	}
+
+	// calculate amount unsafe operations
+	unsafeLimit := 15
+	uintptrLimit := 15
+	if count := bytes.Count(dat, []byte("unsafe.Pointer")); count > unsafeLimit {
+		t.Fatalf("too much unsafe operations: %d", count)
+	} else {
+		t.Logf("amount unsafe operations: %d", count)
+	}
+	if count := bytes.Count(dat, []byte("uintptr")); count > uintptrLimit {
+		t.Fatalf("too much uintptr operations: %d", count)
+	} else {
+		t.Logf("amount uintptr operations: %d", count)
+	}
+
+	cmd := exec.Command("go", "build",
+		"-o", goFile+".app",
+		"-gcflags", "-e",
+		goFile)
+	cmdOutput := &bytes.Buffer{}
+	cmdErr := &bytes.Buffer{}
+	cmd.Stdout = cmdOutput
+	cmd.Stderr = cmdErr
+	err = cmd.Run()
+	if err != nil {
+		t.Fatalf("Go build test `%v` : err = %v\n%v",
+			goFile, err, cmdErr.String())
+	}
+
+	// wget http://archive.ics.uci.edu/ml/machine-learning-databases/semeion/semeion.data
+	index := strings.LastIndex(fileList[0], "/")
+	filepath := fileList[0][:index+1]
+	os.Chdir(filepath)
+	if err := downloadFile(filepath+"semeion.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/semeion/semeion.data"); err != nil {
+		t.Fatalf("%v", err)
+	}
+
+	cmd = exec.Command("./Tinn.c.go.app")
+	cmdOutput = &bytes.Buffer{}
+	cmdErr = &bytes.Buffer{}
+	cmd.Stdout = cmdOutput
+	cmd.Stderr = cmdErr
+	err = cmd.Run()
+	if err != nil {
+		t.Fatalf("Go run `%v` : err = %v\n%v\n%v",
+			goFile, err, cmdOutput.String(), cmdErr.String())
+	}
+	t.Logf("%s", cmdOutput.String())
+
+	// TODO : add result compare
+}