Skip to content

Commit 5415090

Browse files
authored
feat: Support for ClassInstanceExpr (#142)
* added support for querying ClassInstanceExpr * output now supports different data types 🚢 * now output supports printing SELECT objct * fix test 🐛 * added testcase for util func
1 parent e56422a commit 5415090

File tree

10 files changed

+258
-15
lines changed

10 files changed

+258
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM ClassInstanceExpr AS cie
2+
WHERE cie.getClassInstanceExpr().GetClassName() == "Intent"
3+
SELECT cie.getClassInstanceExpr().GetArgs(), cie.getClassInstanceExpr().GetNumArgs(), cie.getClassInstanceExpr().GetArg(0)

sourcecode-parser/cmd/query.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,17 @@ func processQuery(input string, codeGraph *graph.CodeGraph, output string) (stri
154154
verticalLine := "|"
155155
yellowCode := color.New(color.FgYellow).SprintFunc()
156156
greenCode := color.New(color.FgGreen).SprintFunc()
157-
for _, entity := range entities {
157+
for i, entity := range entities {
158158
for _, entityObject := range entity {
159159
header := fmt.Sprintf("\tFile: %s, Line: %s \n", greenCode(entityObject.File), greenCode(entityObject.LineNumber))
160+
// add formatted output to result
161+
output := "\tResult: "
162+
for _, outputObject := range formattedOutput[i] {
163+
output += graph.FormatType(outputObject)
164+
output += " "
165+
output += verticalLine + " "
166+
}
167+
header += output + "\n"
160168
result += header
161169
result += "\n"
162170
codeSnippetArray := strings.Split(entityObject.CodeSnippet, "\n")

sourcecode-parser/cmd/query_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestExecuteCLIQuery(t *testing.T) {
2929
query: "FROM method_declaration AS md WHERE md.getName() == \"onCreateOptionsMenu\" SELECT md.getName()",
3030
output: "",
3131
stdin: false,
32-
expectedOutput: "File: ../../test-src/android/app/src/main/java/com/ivb/udacity/movieListActivity.java, Line: 96 \n\n\t\t 96 | @Override\n\t\t 97 | public boolean onCreateOptionsMenu(Menu menu) {\n\t\t 98 | MenuInflater inflater = getMenuInflater();\n\t\t 99 | inflater.inflate(R.menu.main, menu);\n\t\t 100 | return true;\n\t\t 101 | }",
32+
expectedOutput: "File: ../../test-src/android/app/src/main/java/com/ivb/udacity/movieListActivity.java, Line: 96 \n\tResult: onCreateOptionsMenu | \n\n\t\t 96 | @Override\n\t\t 97 | public boolean onCreateOptionsMenu(Menu menu) {\n\t\t 98 | MenuInflater inflater = getMenuInflater();\n\t\t 99 | inflater.inflate(R.menu.main, menu);\n\t\t 100 | return true;\n\t\t 101 | }",
3333
expectedError: "",
3434
},
3535
{
@@ -79,7 +79,7 @@ func TestProcessQuery(t *testing.T) {
7979
name: "Basic query",
8080
input: "FROM method_declaration AS md WHERE md.getName() == \"testFunc\" SELECT md.getName()",
8181
output: "",
82-
expectedResult: "\tFile: test.java, Line: 5 \n\n\t\t 5 | public void testFunc() {}\n\n",
82+
expectedResult: "\tFile: test.java, Line: 5 \n\tResult: testFunc | \n\n\t\t 5 | public void testFunc() {}\n\n",
8383
expectedError: "",
8484
},
8585
{

sourcecode-parser/graph/construct.go

+48
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log"
77
"os"
88
"path/filepath"
9+
"strconv"
910
"strings"
1011
"sync"
1112
"time"
@@ -43,6 +44,7 @@ type Node struct {
4344
Annotation []string
4445
JavaDoc *model.Javadoc
4546
BinaryExpr *model.BinaryExpr
47+
ClassInstanceExpr *model.ClassInstanceExpr
4648
}
4749

4850
type Edge struct {
@@ -715,6 +717,52 @@ func buildGraphFromAST(node *sitter.Node, sourceCode []byte, graph *CodeGraph, c
715717
isJavaSourceFile: isJavaSourceFile,
716718
}
717719
graph.AddNode(variableNode)
720+
case "object_creation_expression":
721+
className := ""
722+
classInstanceExpression := model.ClassInstanceExpr{
723+
ClassName: "",
724+
Args: []*model.Expr{},
725+
}
726+
for i := 0; i < int(node.ChildCount()); i++ {
727+
child := node.Child(i)
728+
if child.Type() == "type_identifier" || child.Type() == "scoped_type_identifier" {
729+
className = child.Content(sourceCode)
730+
classInstanceExpression.ClassName = className
731+
}
732+
if child.Type() == "argument_list" {
733+
classInstanceExpression.Args = []*model.Expr{}
734+
for j := 0; j < int(child.ChildCount()); j++ {
735+
argType := child.Child(j).Type()
736+
argumentStopWords := map[string]bool{
737+
"(": true,
738+
")": true,
739+
"{": true,
740+
"}": true,
741+
"[": true,
742+
"]": true,
743+
",": true,
744+
}
745+
if !argumentStopWords[argType] {
746+
argument := &model.Expr{}
747+
argument.Type = child.Child(j).Type()
748+
argument.NodeString = child.Child(j).Content(sourceCode)
749+
classInstanceExpression.Args = append(classInstanceExpression.Args, argument)
750+
}
751+
}
752+
}
753+
}
754+
755+
objectNode := &Node{
756+
ID: GenerateMethodID(className, []string{strconv.Itoa(int(node.StartPoint().Row + 1))}, file),
757+
Type: "ClassInstanceExpr",
758+
Name: className,
759+
CodeSnippet: node.Content(sourceCode),
760+
LineNumber: node.StartPoint().Row + 1,
761+
File: file,
762+
isJavaSourceFile: isJavaSourceFile,
763+
ClassInstanceExpr: &classInstanceExpression,
764+
}
765+
graph.AddNode(objectNode)
718766
}
719767

720768
// Recursively process child nodes

sourcecode-parser/graph/construct_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,22 @@ func TestBuildGraphFromAST(t *testing.T) {
782782
expectedTypes: []string{"class_declaration", "method_declaration", "block_comment"},
783783
unexpectedTypes: []string{"variable_declaration", "binary_expression"},
784784
},
785+
// add testcase for object creation expression
786+
{
787+
name: "Class with object creation expression",
788+
sourceCode: `
789+
public class ObjectCreationClass {
790+
public static void main(String[] args) {
791+
ObjectCreationClass obj = new ObjectCreationClass();
792+
Socket socket = new Socket("www.google.com", 80);
793+
}
794+
}
795+
`,
796+
expectedNodes: 6,
797+
expectedEdges: 0,
798+
expectedTypes: []string{"class_declaration", "method_declaration", "ClassInstanceExpr"},
799+
unexpectedTypes: []string{"binary_expression"},
800+
},
785801
}
786802

787803
for _, tt := range tests {

sourcecode-parser/graph/query.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ func (env *Env) GetRightOperand() string {
111111
return env.Node.BinaryExpr.RightOperand.NodeString
112112
}
113113

114-
func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]string) {
114+
func (env *Env) GetClassInstanceExpr() *model.ClassInstanceExpr {
115+
return env.Node.ClassInstanceExpr
116+
}
117+
118+
func (env *Env) GetClassInstanceExprName() string {
119+
return env.Node.ClassInstanceExpr.ClassName
120+
}
121+
122+
func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) {
115123
result := make([][]*Node, 0)
116124

117125
// log query select list alone
@@ -131,10 +139,10 @@ func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, outpu
131139
return nodes, output
132140
}
133141

134-
func generateOutput(nodeSet [][]*Node, query parser.Query) [][]string {
135-
results := make([][]string, 0, len(nodeSet))
142+
func generateOutput(nodeSet [][]*Node, query parser.Query) [][]interface{} {
143+
results := make([][]interface{}, 0, len(nodeSet))
136144
for _, nodeSet := range nodeSet {
137-
var result []string
145+
var result []interface{}
138146
for _, outputFormat := range query.SelectOutput {
139147
switch outputFormat.Type {
140148
case "string":
@@ -156,7 +164,7 @@ func generateOutput(nodeSet [][]*Node, query parser.Query) [][]string {
156164
return results
157165
}
158166

159-
func evaluateExpression(node []*Node, expression string, query parser.Query) (string, error) {
167+
func evaluateExpression(node []*Node, expression string, query parser.Query) (interface{}, error) {
160168
env := generateProxyEnvForSet(node, query)
161169

162170
program, err := expr.Compile(expression, expr.Env(env))
@@ -169,7 +177,7 @@ func evaluateExpression(node []*Node, expression string, query parser.Query) (st
169177
fmt.Println("Error evaluating expression: ", err)
170178
return "", err
171179
}
172-
return output.(string), nil
180+
return output, nil
173181
}
174182

175183
func generateCartesianProduct(graph *CodeGraph, selectList []parser.SelectList, conditions []string) [][]*Node {
@@ -193,7 +201,8 @@ func generateCartesianProduct(graph *CodeGraph, selectList []parser.SelectList,
193201
}
194202
}
195203
} else {
196-
for _, node := range graph.Nodes {
204+
filteredNodes := graph.FindNodesByType(selectList[0].Entity)
205+
for _, node := range filteredNodes {
197206
query := parser.Query{Expression: condition, SelectList: selectList}
198207
if FilterEntities([]*Node{node}, query) {
199208
typeIndex[node.Type] = appendUnique(typeIndex[node.Type], node)
@@ -280,6 +289,7 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
280289
orBitwiseExpression := "or_bitwise_expression"
281290
unsignedRightShiftExpression := "unsigned_right_shift_expression"
282291
xorBitwsieExpression := "xor_bitwise_expression"
292+
classInstanceExpression := "ClassInstanceExpr"
283293

284294
// print query select list
285295
for _, entity := range query.SelectList {
@@ -326,6 +336,8 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
326336
unsignedRightShiftExpression = entity.Alias
327337
case "xor_bitwise_expression":
328338
xorBitwsieExpression = entity.Alias
339+
case "ClassInstanceExpr":
340+
classInstanceExpression = entity.Alias
329341
}
330342
}
331343
env := map[string]interface{}{
@@ -450,6 +462,12 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
450462
"getOperator": "^",
451463
"toString": proxyenv.ToString,
452464
},
465+
classInstanceExpression: map[string]interface{}{
466+
"getName": proxyenv.GetName,
467+
"getDoc": proxyenv.GetDoc,
468+
"toString": proxyenv.ToString,
469+
"getClassInstanceExpr": proxyenv.GetClassInstanceExpr,
470+
},
453471
}
454472
return env
455473
}

sourcecode-parser/graph/util.go

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graph
33
import (
44
"crypto/sha256"
55
"encoding/hex"
6+
"encoding/json"
67
"fmt"
78
)
89

@@ -26,3 +27,20 @@ func appendUnique(slice []*Node, node *Node) []*Node {
2627
}
2728
return append(slice, node)
2829
}
30+
31+
func FormatType(v interface{}) string {
32+
switch val := v.(type) {
33+
case string:
34+
return val
35+
case int, int64:
36+
return fmt.Sprintf("%d", val)
37+
case float32, float64:
38+
return fmt.Sprintf("%.2f", val)
39+
case []interface{}:
40+
//nolint:all
41+
jsonBytes, _ := json.Marshal(val)
42+
return string(jsonBytes)
43+
default:
44+
return fmt.Sprintf("%v", val)
45+
}
46+
}

sourcecode-parser/graph/util_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,71 @@ func isValidHexString(s string) bool {
113113
_, err := hex.DecodeString(s)
114114
return err == nil
115115
}
116+
117+
func TestFormatType(t *testing.T) {
118+
tests := []struct {
119+
name string
120+
input interface{}
121+
want string
122+
}{
123+
{
124+
name: "String input",
125+
input: "test string",
126+
want: "test string",
127+
},
128+
{
129+
name: "Integer input",
130+
input: 42,
131+
want: "42",
132+
},
133+
{
134+
name: "Int64 input",
135+
input: int64(9223372036854775807),
136+
want: "9223372036854775807",
137+
},
138+
{
139+
name: "Float32 input",
140+
input: float32(3.14),
141+
want: "3.14",
142+
},
143+
{
144+
name: "Float64 input",
145+
input: 2.71828,
146+
want: "2.72",
147+
},
148+
{
149+
name: "Slice of integers",
150+
input: []interface{}{1, 2, 3},
151+
want: "[1,2,3]",
152+
},
153+
{
154+
name: "Slice of mixed types",
155+
input: []interface{}{"a", 1, true},
156+
want: `["a",1,true]`,
157+
},
158+
{
159+
name: "Boolean input",
160+
input: true,
161+
want: "true",
162+
},
163+
{
164+
name: "Nil input",
165+
input: nil,
166+
want: "<nil>",
167+
},
168+
{
169+
name: "Struct input",
170+
input: struct{ Name string }{"John"},
171+
want: "{John}",
172+
},
173+
}
174+
175+
for _, tt := range tests {
176+
t.Run(tt.name, func(t *testing.T) {
177+
got := FormatType(tt.input)
178+
if got != tt.want {
179+
t.Errorf("FormatType() = %v, want %v", got, tt.want)
180+
}
181+
})
182+
}
183+
}

sourcecode-parser/model/expr.go

+33-2
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ func (e *ExprParent) GetNumChildExpr() int64 {
2222

2323
type Expr struct {
2424
ExprParent
25-
kind int
25+
Kind int
2626
Node sitter.Node
2727
NodeString string
28+
Type string
29+
}
30+
31+
func (e *Expr) String() string {
32+
return fmt.Sprintf("Expr(%s)", e.NodeString)
2833
}
2934

3035
func (e *Expr) GetAChildExpr() *Expr {
@@ -43,7 +48,7 @@ func (e *Expr) GetBoolValue() {
4348
}
4449

4550
func (e *Expr) GetKind() int {
46-
return e.kind
51+
return e.Kind
4752
}
4853

4954
type BinaryExpr struct {
@@ -278,3 +283,29 @@ type XorBitwiseExpr struct {
278283
func (e *XorBitwiseExpr) GetOp() string {
279284
return e.op
280285
}
286+
287+
type ClassInstanceExpr struct {
288+
Expr
289+
ClassName string
290+
Args []*Expr
291+
}
292+
293+
func (e *ClassInstanceExpr) GetClassName() string {
294+
return e.ClassName
295+
}
296+
297+
func (e *ClassInstanceExpr) GetArgs() []*Expr {
298+
return e.Args
299+
}
300+
301+
func (e *ClassInstanceExpr) GetArg(i int) *Expr {
302+
return e.Args[i]
303+
}
304+
305+
func (e *ClassInstanceExpr) GetNumArgs() int {
306+
return len(e.Args)
307+
}
308+
309+
func (e *ClassInstanceExpr) String() string {
310+
return fmt.Sprintf("ClassInstanceExpr(%s, %v)", e.ClassName, e.Args)
311+
}

0 commit comments

Comments
 (0)