Skip to content

Commit

Permalink
Redesign of union implementation. Fixes #533, #574, #580 (#611)
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin8105 authored and elliotchance committed Feb 21, 2018
1 parent a406d16 commit 087a013
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 275 deletions.
48 changes: 47 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
"testing"
Expand Down Expand Up @@ -167,7 +168,52 @@ func TestIntegrationScripts(t *testing.T) {
goProgramStderr = strings.Replace(goProgramStderr, currentDir+"/", "", -1)

if cProgramStderr != goProgramStderr {
t.Fatalf("Expected %s\nGot: %s", cProgramStderr, goProgramStderr)
// Add addition debug information for lines like:
// build/tests/cast/main_test.go:195:1: expected '}', found 'type'
buildPrefix := "build/tests/"
var output string
lines := strings.Split(goProgramStderr, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, buildPrefix) {
continue
}
index := strings.Index(line, ":")
if index < 0 {
continue
}
filename := "./" + line[0:index]
output += "+========================+\n"
output += fmt.Sprintf("File : %s\n\n", filename)
if len(line) <= index+1 {
continue
}
line = line[index+1:]
index = strings.Index(line, ":")
if index < 0 {
continue
}
linePosition, err := strconv.Atoi(line[:index])
if err != nil {
err = nil
continue
}
content, err := ioutil.ReadFile(filename)
if err != nil {
err = nil
continue
}
fileLines := strings.Split(string(content), "\n")
start := linePosition - 20
if start < 0 {
start = 0
}
for i := start; i < linePosition+5 && i < len(fileLines); i++ {
output += fmt.Sprintf("Line : %3d : %s\n", i, fileLines[i])
}
}
t.Fatalf("Expected %s\nGot: %s\nParts of code:\n%s",
cProgramStderr, goProgramStderr, output)
}

// Check stdout
Expand Down
43 changes: 43 additions & 0 deletions program/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package program

import (
"fmt"
"strings"

"github.com/elliotchance/c2go/ast"
)
Expand Down Expand Up @@ -53,3 +54,45 @@ func NewStruct(n *ast.RecordDecl) *Struct {
Fields: fields,
}
}

// IsUnion - return true if the cType is 'union' or
// typedef of union
func (p *Program) IsUnion(cType string) bool {
if strings.HasPrefix(cType, "union ") {
return true
}
if _, ok := p.Unions[cType]; ok {
return true
}
if _, ok := p.Unions["union "+cType]; ok {
return true
}
if _, ok := p.GetBaseTypeOfTypedef("union " + cType); ok {
return true
}
if t, ok := p.GetBaseTypeOfTypedef(cType); ok {
if t == cType {
panic(fmt.Errorf("Cannot be same name: %s", t))
}
if strings.HasPrefix(t, "struct ") {
return false
}
if t == "" {
panic(fmt.Errorf("Type cannot be empty"))
}
return p.IsUnion(t)
}
return false
}

// GetBaseTypeOfTypedef - return typedef type
func (p *Program) GetBaseTypeOfTypedef(cTypedef string) (
cBase string, ok bool) {

cBase, ok = p.TypedefType[cTypedef]
if cBase == "" && ok {
panic(fmt.Errorf("Type cannot be empty"))
}

return
}
45 changes: 44 additions & 1 deletion tests/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ void struct_with_rec_fuction()
is_eq(j_function(NULL, 4.0),-1);
}

struct FinFinS {
double d;
int (*f)(int(*)(int));
};

int FinF1(int a)
{
return a+1;
}

int FinF2(int (*f)(int))
{
int g = 45;
return f(g);
}

void func_in_func_in_struct()
{
diag("function in function in struct");
struct FinFinS ffs;
ffs.f = FinF2;
int res = ffs.f(FinF1) ;
is_eq(res , 46);
};

struct info {
struct deep_info{
int a, b, c;
Expand Down Expand Up @@ -135,9 +160,23 @@ void struct_in_struct_with_star()
is_eq(in.star_con->sa,45);
}

union STRS{
double d;
struct {
double d;
} T;
};

void struct_inside_union()
{
union STRS s;
s.T.d = 10.0;
is_true(s.d != 0);
}

int main()
{
plan(59);
plan(61);

struct programming variable;
char *s = "Programming in Software Development.";
Expand Down Expand Up @@ -386,5 +425,9 @@ int main()

struct_in_struct_with_star();

func_in_func_in_struct();

struct_inside_union();

done_testing();
}
80 changes: 73 additions & 7 deletions tests/union.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

union programming
{
float constant;
int constant;
char *pointer;
};

Expand All @@ -17,18 +17,18 @@ union programming init_var()
variable.pointer = s;
is_streq(variable.pointer, "Programming in Software Development.");

variable.constant = 1.23;
is_eq(variable.constant, 1.23);
variable.constant = 123;
is_eq(variable.constant, 123);

return variable;
}

void pass_by_ref(union programming *addr)
{
char *s = "Show string member.";
float v = 1.23+4.56;
int v = 123+456;

addr->constant += 4.56;
addr->constant += 456;
is_eq(addr->constant, v);

addr->pointer = s;
Expand All @@ -39,7 +39,7 @@ void var_by_val(union programming value)
{
value.constant++;

is_eq(value.constant, 2.23);
is_eq(value.constant, 124);
}

struct SHA3 {
Expand Down Expand Up @@ -186,9 +186,72 @@ void union_func_pointers()
is_eq(u.f2(21), 22);
}

union array_union
{
float a[2];
float b[2];
};

void union_array()
{
union array_union arr;
arr.a[0] = 12;
arr.b[1] = 14;
is_eq( arr.a[0] , 12);
is_eq( arr.a[1] , 14);
is_eq( arr.b[0] , 12);
is_eq( arr.b[1] , 14);
}

typedef int ii;
typedef struct SHA SHA;
struct SHA{
union {
ii s[25];
unsigned char x[100];
} u;
unsigned uuu;
};

void union_arr_in_str()
{
SHA sha;
sha.uuu = 15;
is_eq(sha.uuu,15);
for (int i = 0 ; i< 25;i++)
sha.u.s[0] = 0;
is_eq(sha.u.s[0],0);
is_true(sha.u.x[0] == 0);
for (int i=0;i<6;i++){
sha.u.s[i] = (ii)(4);
sha.u.s[i] = (ii)(42) + sha.u.s[i];
}
is_eq(sha.u.s[5],46);
is_true(sha.u.x[0] != 0);
}


union un_struct{
struct {
short a;
short b;
} str;
long l;
};

void union_with_struct()
{
union un_struct u;
u.str.a = 12;
u.str.b = 45;
is_eq(u.str.a, 12);
is_eq(u.str.b, 45);
is_true( u.l > 0 );
}

int main()
{
plan(34);
plan(46);

union programming variable;

Expand All @@ -201,6 +264,9 @@ int main()
union_inside_struct2();
union_pointers();
union_func_pointers();
union_array();
union_arr_in_str();
union_with_struct();

done_testing();
}
68 changes: 0 additions & 68 deletions transpiler/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,6 @@ func transpileBinaryOperatorComma(n *ast.BinaryOperator, p *program.Program) (
return right[len(right)-1], preStmts, nil
}

func findUnion(node ast.Node) (unionNode ast.Node, haveMemberExprWithUnion bool) {
switch chi := node.(type) {
case *ast.MemberExpr:
if strings.HasPrefix(chi.Type, "union ") {
haveMemberExprWithUnion = true
unionNode = node
return
}
return findUnion(node.Children()[0])
case *ast.DeclRefExpr:
if strings.HasPrefix(chi.Type, "union ") {
haveMemberExprWithUnion = true
unionNode = node
return
}
}
return nil, false
}

func transpileBinaryOperator(n *ast.BinaryOperator, p *program.Program, exprIsStmt bool) (
expr goast.Expr, eType string, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) {
defer func() {
Expand Down Expand Up @@ -363,55 +344,6 @@ func transpileBinaryOperator(n *ast.BinaryOperator, p *program.Program, exprIsSt
right = util.NewNil()
}

unionNode, haveMemberExprWithUnion := findUnion(n.Children()[0])

// Construct code for assigning value to an union field
if memberExpr, ok := n.Children()[0].(*ast.MemberExpr); ok {
ref := memberExpr.GetDeclRefExpr()
if ref != nil {
union := p.GetStruct(ref.Type)
if union == nil {
union = p.GetStruct("union " + ref.Type)
}
if union != nil && union.IsUnion {
attrType, err := types.ResolveType(p, ref.Type)
if err != nil {
p.AddMessage(p.GenerateWarningMessage(err, memberExpr))
}

funcName := getFunctionNameForUnionSetter(ref.Name, attrType, memberExpr.Name)
resExpr := util.NewCallExpr(funcName, right)
resType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

return resExpr, resType, preStmts, postStmts, nil
}
}
// union inside struct
if un, ok := memberExpr.Children()[0].(*ast.MemberExpr); ok {
un.Type = types.GenerateCorrectType(un.Type)
un.Type2 = types.GenerateCorrectType(un.Type2)
union := p.GetStruct(un.Type)
if union != nil && union.IsUnion {
if str, ok := un.Children()[0].(*ast.DeclRefExpr); ok {
funcName := getFunctionNameForUnionSetter("", memberExpr.Type, memberExpr.Name)
funcName = str.Name + "." + un.Name + funcName
resExpr := &goast.CallExpr{
Fun: goast.NewIdent(funcName),
Args: []goast.Expr{right},
}
resType := types.ResolveTypeForBinaryOperator(p, n.Operator, leftType, rightType)

return resExpr, resType, preStmts, postStmts, nil
}
}
}

// struct inside union
if haveMemberExprWithUnion {
p.AddMessage(p.GenerateWarningMessage(
fmt.Errorf("Binary operation with union not support. AST node with union : %v", unionNode), n))
}
}
}
}

Expand Down
Loading

0 comments on commit 087a013

Please sign in to comment.