Skip to content

Commit

Permalink
Node reference id bug fix (#25)
Browse files Browse the repository at this point in the history
1. As we were maintaining global cache of processed references. It was
colliding with objects across files, hence the nodes which were
processed for one file's ast, was getting just referenced with id from
other file. Made changes to intialise the cache before every file is
being processed.
2. Updated respective test cases.
  • Loading branch information
pandurangpatil authored Jun 28, 2023
1 parent d944c7c commit 9961077
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 96 deletions.
58 changes: 32 additions & 26 deletions goastgen/libgoastgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ func ParseAstFromSource(filename string, src any) (string, error) {
log.Print(err)
return "", err
}
result := serilizeToMap(parsedAst, fset)
// We maintain the cache of processed object pointers mapped to their respective node_id
var nodeAddressMap = make(map[uintptr]interface{})
// Last node id reference
lastNodeId := 1
result := serilizeToMap(parsedAst, fset, &lastNodeId, nodeAddressMap)
return serilizeToJsonStr(result)
}

Expand All @@ -57,7 +61,11 @@ func ParseAstFromDir(dir string) (string, error) {
log.Print(err)
return "", err
}
result := serilizeToMap(parsedAst, fset)
// We maintain the cache of processed object pointers mapped to their respective node_id
var nodeAddressMap = make(map[uintptr]interface{})
// Last node id reference
lastNodeId := 1
result := serilizeToMap(parsedAst, fset, &lastNodeId, nodeAddressMap)
return serilizeToJsonStr(result)
}

Expand All @@ -81,7 +89,11 @@ func ParseAstFromFile(file string) (string, error) {
log.Print(err)
return "", err
}
result := serilizeToMap(parsedAst, fset)
// We maintain the cache of processed object pointers mapped to their respective node_id
var nodeAddressMap = make(map[uintptr]interface{})
// Last node id reference
lastNodeId := 1
result := serilizeToMap(parsedAst, fset, &lastNodeId, nodeAddressMap)
return serilizeToJsonStr(result)
}

Expand Down Expand Up @@ -167,7 +179,7 @@ Parameters:
Returns:
It returns and object of map[string]interface{} by converting any 'Struct' type value field to map
*/
func processMap(object interface{}, fset *token.FileSet) interface{} {
func processMap(object interface{}, fset *token.FileSet, lastNodeId *int, nodeAddressMap map[uintptr]interface{}) interface{} {
value := reflect.ValueOf(object)
objMap := make(map[string]interface{})
for _, key := range value.MapKeys() {
Expand All @@ -193,7 +205,7 @@ func processMap(object interface{}, fset *token.FileSet) interface{} {
case reflect.String, reflect.Int, reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
objMap[key.String()] = objValue.Interface()
case reflect.Struct:
objMap[key.String()] = processStruct(objValue.Interface(), ptrValue, fset)
objMap[key.String()] = processStruct(objValue.Interface(), ptrValue, fset, lastNodeId, nodeAddressMap)
default:
log.SetPrefix("[WARNING]")
log.Println(getLogPrefix(), objValue.Kind(), "- not handled")
Expand All @@ -214,7 +226,7 @@ func processMap(object interface{}, fset *token.FileSet) interface{} {
Returns:
It will return []map[string]interface{}
*/
func processArrayOrSlice(object interface{}, fset *token.FileSet) interface{} {
func processArrayOrSlice(object interface{}, fset *token.FileSet, lastNodeId *int, nodeAddressMap map[uintptr]interface{}) interface{} {
value := reflect.ValueOf(object)
var nodeList []interface{}
for j := 0; j < value.Len(); j++ {
Expand All @@ -234,9 +246,9 @@ func processArrayOrSlice(object interface{}, fset *token.FileSet) interface{} {
case reflect.String, reflect.Int, reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
nodeList = append(nodeList, arrayElementValue.Interface())
case reflect.Struct:
nodeList = append(nodeList, processStruct(arrayElementValue.Interface(), ptrValue, fset))
nodeList = append(nodeList, processStruct(arrayElementValue.Interface(), ptrValue, fset, lastNodeId, nodeAddressMap))
case reflect.Map:
nodeList = append(nodeList, processMap(arrayElementValue.Interface(), fset))
nodeList = append(nodeList, processMap(arrayElementValue.Interface(), fset, lastNodeId, nodeAddressMap))
case reflect.Pointer:
// In case the node is pointer, it will check if given Value contains valid pointer address.
if arrayElementValue.Elem().IsValid() {
Expand All @@ -245,9 +257,9 @@ func processArrayOrSlice(object interface{}, fset *token.FileSet) interface{} {
case reflect.String, reflect.Int, reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
nodeList = append(nodeList, arrayElementValue.Elem().Interface())
case reflect.Struct:
nodeList = append(nodeList, processStruct(arrayElementValue.Elem().Interface(), ptrValue, fset))
nodeList = append(nodeList, processStruct(arrayElementValue.Elem().Interface(), ptrValue, fset, lastNodeId, nodeAddressMap))
case reflect.Map:
nodeList = append(nodeList, processMap(arrayElementValue.Elem().Interface(), fset))
nodeList = append(nodeList, processMap(arrayElementValue.Elem().Interface(), fset, lastNodeId, nodeAddressMap))
default:
log.SetPrefix("[WARNING]")
log.Println(getLogPrefix(), arrayElementValuePtrKind, "- not handled for array pointer element")
Expand All @@ -261,12 +273,6 @@ func processArrayOrSlice(object interface{}, fset *token.FileSet) interface{} {
return nodeList
}

// We maintain the cache of processed object pointers mapped to their respective node_id
var nodeAddressMap = make(map[uintptr]interface{})

// Last node id reference
var lastNodeId int = 1

/*
This will process object of 'struct' type and convert it into document / map[string]interface{}.
It will process each field of this object, if it contains further child objects, arrays or maps.
Expand All @@ -284,7 +290,7 @@ var lastNodeId int = 1
It will return object of map[string]interface{} by converting all the child fields recursively into map
*/
func processStruct(node interface{}, objPtrValue reflect.Value, fset *token.FileSet) interface{} {
func processStruct(node interface{}, objPtrValue reflect.Value, fset *token.FileSet, lastNodeId *int, nodeAddressMap map[uintptr]interface{}) interface{} {
objectMap := make(map[string]interface{})
elementType := reflect.TypeOf(node)
elementValueObj := reflect.ValueOf(node)
Expand Down Expand Up @@ -326,8 +332,8 @@ func processStruct(node interface{}, objPtrValue reflect.Value, fset *token.File
}
}

objectMap["node_id"] = lastNodeId
lastNodeId++
objectMap["node_id"] = *lastNodeId
*lastNodeId++
objectMap["node_type"] = elementValueObj.Type().String()

if process {
Expand Down Expand Up @@ -364,11 +370,11 @@ func processStruct(node interface{}, objPtrValue reflect.Value, fset *token.File
case reflect.String, reflect.Int, reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
objectMap[field.Name] = value.Interface()
case reflect.Struct:
objectMap[field.Name] = processStruct(value.Interface(), ptrValue, fset)
objectMap[field.Name] = processStruct(value.Interface(), ptrValue, fset, lastNodeId, nodeAddressMap)
case reflect.Map:
objectMap[field.Name] = processMap(value.Interface(), fset)
objectMap[field.Name] = processMap(value.Interface(), fset, lastNodeId, nodeAddressMap)
case reflect.Array, reflect.Slice:
objectMap[field.Name] = processArrayOrSlice(value.Interface(), fset)
objectMap[field.Name] = processArrayOrSlice(value.Interface(), fset, lastNodeId, nodeAddressMap)
default:
log.SetPrefix("[WARNING]")
log.Println(getLogPrefix(), field.Name, "- of Kind ->", fieldKind, "- not handled")
Expand All @@ -395,7 +401,7 @@ func processStruct(node interface{}, objPtrValue reflect.Value, fset *token.File
possible return value types could be primitive type, map (map[string]interface{}) or slice ([]interface{})
*/
func serilizeToMap(node interface{}, fset *token.FileSet) interface{} {
func serilizeToMap(node interface{}, fset *token.FileSet, lastNodeId *int, nodeAddressMap map[uintptr]interface{}) interface{} {
var elementType reflect.Type
var elementValue reflect.Value
var ptrValue reflect.Value
Expand All @@ -420,11 +426,11 @@ func serilizeToMap(node interface{}, fset *token.FileSet) interface{} {
case reflect.String, reflect.Int, reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return elementValue.Interface()
case reflect.Struct:
return processStruct(elementValue.Interface(), ptrValue, fset)
return processStruct(elementValue.Interface(), ptrValue, fset, lastNodeId, nodeAddressMap)
case reflect.Map:
return processMap(elementValue.Interface(), fset)
return processMap(elementValue.Interface(), fset, lastNodeId, nodeAddressMap)
case reflect.Array, reflect.Slice:
return processArrayOrSlice(elementValue.Interface(), fset)
return processArrayOrSlice(elementValue.Interface(), fset, lastNodeId, nodeAddressMap)
default:
log.SetPrefix("[WARNING]")
log.Println(getLogPrefix(), elementType.Kind(), " - not handled")
Expand Down
55 changes: 38 additions & 17 deletions goastgen/libgoastgen_array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,49 @@ import (
)

func TestArrayWithnillPointerCheck(t *testing.T) {
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

var nilStr *string
var nilObj *Phone
var nilMap *map[string]Phone
arrayWithnil := [4]interface{}{"valid string", nilStr, nilObj, nilMap}
result := processArrayOrSlice(arrayWithnil, nil)
result := processArrayOrSlice(arrayWithnil, nil, &lastNodeId, nodeAddressMap)
expectedResult := []interface{}{"valid string"}

assert.Equal(t, expectedResult, result, "It should process valid values of the array successfully")
}

func TestSimpleInterfaceWithArray(t *testing.T) {
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

arrayType := [2]interface{}{"first", "second"}
result := processArrayOrSlice(arrayType, nil)
result := processArrayOrSlice(arrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := []interface{}{"first", "second"}
assert.Equal(t, expectedResult, result, "Array of interface containing string pointers should match with expected results")
}

func TestSimpleInterfaceWithArrayOfPointersType(t *testing.T) {
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

first := "first"
second := "second"
arrayType := [2]interface{}{&first, &second}
result := processArrayOrSlice(arrayType, nil)
result := processArrayOrSlice(arrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := []interface{}{"first", "second"}
assert.Equal(t, expectedResult, result, "Array of interface containing string pointers should match with expected results")
}

func TestObjectInterfaceWithArrayOfPointers(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

phone1 := Phone{PhoneNo: "1234567890", Type: "Home"}
phone2 := Phone{PhoneNo: "0987654321", Type: "Office"}
arrayType := [2]interface{}{&phone1, &phone2}
result := processArrayOrSlice(arrayType, nil)
result := processArrayOrSlice(arrayType, nil, &lastNodeId, nodeAddressMap)
firstPhoneItem := make(map[string]interface{})
firstPhoneItem["PhoneNo"] = "1234567890"
firstPhoneItem["Type"] = "Home"
Expand All @@ -53,11 +64,13 @@ func TestObjectInterfaceWithArrayOfPointers(t *testing.T) {
}

func TestSliceObjctPtrType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

phone1 := Phone{PhoneNo: "1234567890", Type: "Home"}
phone2 := Phone{PhoneNo: "0987654321", Type: "Office"}
objArrayType := SliceObjPtrType{Id: 20, PhoneList: []*Phone{&phone1, &phone2}}
result := serilizeToMap(objArrayType, nil)
result := serilizeToMap(objArrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 20
expectedResult["node_type"] = "goastgen.SliceObjPtrType"
Expand All @@ -78,12 +91,14 @@ func TestSliceObjctPtrType(t *testing.T) {
}

func TestArrayPtrType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

firstStr := "First"
secondStr := "Second"
thirdStr := "Third"
arrayType := ArrayPtrType{Id: 10, NameList: [3]*string{&firstStr, &secondStr, &thirdStr}}
result := serilizeToMap(arrayType, nil)
result := serilizeToMap(arrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 10
expectedResult["node_type"] = "goastgen.ArrayPtrType"
Expand All @@ -94,9 +109,11 @@ func TestArrayPtrType(t *testing.T) {
}

func TestObjectSliceType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

objArrayType := ObjectSliceType{Id: 20, PhoneList: []Phone{{PhoneNo: "1234567890", Type: "Home"}, {PhoneNo: "0987654321", Type: "Office"}}}
result := serilizeToMap(objArrayType, nil)
result := serilizeToMap(objArrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 20
expectedResult["node_type"] = "goastgen.ObjectSliceType"
Expand All @@ -117,9 +134,11 @@ func TestObjectSliceType(t *testing.T) {
}

func TestArrayType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

arrayType := ArrayType{Id: 10, NameList: [3]string{"First", "Second", "Third"}}
result := serilizeToMap(arrayType, nil)
result := serilizeToMap(arrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 10
expectedResult["NameList"] = []interface{}{"First", "Second", "Third"}
Expand All @@ -129,9 +148,10 @@ func TestArrayType(t *testing.T) {
}

func TestSliceType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})
arrayType := SliceType{Id: 10, NameList: []string{"First", "Second"}}
result := serilizeToMap(arrayType, nil)
result := serilizeToMap(arrayType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 10
expectedResult["NameList"] = []interface{}{"First", "Second"}
Expand All @@ -141,13 +161,14 @@ func TestSliceType(t *testing.T) {
}

func TestSimpleArrayType(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

phone1 := Phone{PhoneNo: "1234567890", Type: "Home"}
phone2 := Phone{PhoneNo: "0987654321", Type: "Office"}
simplePtrStr := "Simple PTR String"
arrayType := []interface{}{&phone1, phone2, "Simple String", 90, &simplePtrStr}
result := serilizeToMap(arrayType, nil)
result := serilizeToMap(arrayType, nil, &lastNodeId, nodeAddressMap)

firstPhone := make(map[string]interface{})
firstPhone["PhoneNo"] = "1234567890"
Expand Down
6 changes: 4 additions & 2 deletions goastgen/libgoastgen_ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ type RecursivePtrType struct {
}

func TestRecursivePointerCheck(t *testing.T) {
lastNodeId = 1
lastNodeId := 1
var nodeAddressMap = make(map[uintptr]interface{})

recursivePtrType := RecursivePtrType{Id: 10, Name: "Gajraj"}
recursivePtrType.NodePtr = &recursivePtrType
result := serilizeToMap(&recursivePtrType, nil)
result := serilizeToMap(&recursivePtrType, nil, &lastNodeId, nodeAddressMap)
expectedResult := make(map[string]interface{})
expectedResult["Id"] = 10
expectedResult["Name"] = "Gajraj"
Expand Down
Loading

0 comments on commit 9961077

Please sign in to comment.