-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathMethodResolver.java
More file actions
235 lines (180 loc) · 7.5 KB
/
MethodResolver.java
File metadata and controls
235 lines (180 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package qimpp;
import xtc.tree.*;
import java.util.*;
public class MethodResolver {
private static InheritanceTreeManager inheritanceTree;
private static String callType;
private static GNode callingClassDeclaration;
/**
* @param methodName the unmangled method name, printers should mangle
* names by argument types
* @param classDeclaration the Type node of the calling class
* @param argumentTypes a Node containing the Types of the arguments
* @param inheritanceTree the data structure describing the inheritance
* relationships between translated classes
* @return a GNode the mangled method name, and the return Type node
*/
public static GNode resolve (String methodName, GNode classType, GNode argTypes, InheritanceTreeManager inheritanceTree, String callType, GNode callingClassDeclaration ) {
MethodResolver.callType = callType;
//TODO: Implement overloading. For now we just return the first method with the right name
MethodResolver.inheritanceTree = inheritanceTree;
String className = Disambiguator.getDotDelimitedName(classType.getGeneric(0));
GNode classDeclaration = inheritanceTree.getClassDeclarationNode(className);
MethodResolver.callingClassDeclaration = callingClassDeclaration;
ArrayList<GNode> nameMatches = findNameMatches(methodName, classDeclaration);
ArrayList<GNode> argLengthMatches = findArgLengthMatches(nameMatches, argTypes.size());
ArrayList<GNode> argCastMatches = findArgCastMatches(argLengthMatches, argTypes);
GNode calledMethod = getMostSpecific(argCastMatches);
methodName = Type.getCppMangledMethodName(calledMethod);
return GNode.create("CallInfo", methodName, calledMethod.getGeneric(1), calledMethod);
}
/**
* Get the best match
*/
private static GNode getMostSpecific(ArrayList<GNode> possibleMatches){
// Bubble up, because I'm lazy
// This will work without complaint if the result is actually ambiguous,
// we are assuming the Java program actually works.
for (int i = 0; i < possibleMatches.size() - 1; i++){
GNode formalParameters = possibleMatches.get(i).getGeneric(2);
GNode sourceTypes = GNode.create("ArgTypes");
for ( Object o : formalParameters ){
sourceTypes.add(((GNode)o).getGeneric(1));
}
formalParameters = possibleMatches.get(i+1).getGeneric(2);
GNode targetTypes = GNode.create("ArgTypes");
for (Object o : formalParameters ) {
targetTypes.add(((GNode) o).getGeneric(1));
}
if (isCastable(sourceTypes, targetTypes)){
possibleMatches.set(i + 1, possibleMatches.get(i));
}
}
return possibleMatches.get(possibleMatches.size() - 1);
}
/**
* Check if one type is upcastable to another
*/
private static boolean isCastable(GNode sourceArgTypes, GNode targetArgTypes){
for (int i = 0; i < targetArgTypes.size(); i++){
// They must both be PrimitiveType or both QualifiedIdentifier
GNode targetType = targetArgTypes.getGeneric(i);
GNode sourceType = sourceArgTypes.getGeneric(i);
if (!targetType.getGeneric(0).getName().equals(sourceType.getGeneric(0).getName())){
return false;
}
if (targetType.getGeneric(0).getName().equals("QualifiedIdentifier")){
if (!isClassCastable(sourceType, targetType)){
return false;
}
}
// Primitive type
else {
if (!isPrimitiveTypeCastable(sourceType, targetType)){
return false;
}
}
}
return true;
}
/**
* Determine if one class type is castable to another
*/
private static boolean isClassCastable(GNode sourceType, GNode targetType){
String sourceName = Disambiguator.getDotDelimitedName(sourceType.getGeneric(0));
String targetName = Disambiguator.getDotDelimitedName(targetType.getGeneric(0));
if (sourceName.equals(targetName)) return true;
GNode sourceClassTreeNode = inheritanceTree.getClassTreeNode(sourceName);
GNode targetClassTreeNode = inheritanceTree.getClassTreeNode(targetName);
while (!sourceName.equals("java.lang.Object")){
sourceClassTreeNode = (GNode)sourceClassTreeNode.getProperty(InheritanceTreeManager.PARENT_CLASS);
sourceName = (String)((GNode)sourceClassTreeNode.getProperty(InheritanceTreeManager.CLASS_DECLARATION)).get(0);
if (sourceName.equals(targetName)) return true;
}
return false;
}
/**
* Determine if an primitive argument of one type can be casted to another
*/
private static boolean isPrimitiveTypeCastable(GNode sourceType, GNode targetType){
String sourceName = sourceType.getGeneric(0).getString(0);
String targetName = targetType.getGeneric(0).getString(0);
return Type.canWiden(sourceName, targetName);
}
/**
* Get all the methods with matching length of arguments
*/
private static ArrayList<GNode> findArgLengthMatches(ArrayList<GNode> nameMatches, int numArgs){
ArrayList<GNode> lengthMatches = new ArrayList<GNode>();
for (GNode n : nameMatches){
GNode args = n.getGeneric(2);
if (args.size() == numArgs){
lengthMatches.add(n);
}
}
return lengthMatches;
}
/**
* Get all the methods whose arguments can be cast to from the given argument types
*/
private static ArrayList<GNode> findArgCastMatches(ArrayList<GNode> argLengthMatches, GNode argTypes){
ArrayList<GNode> argCastMatches = new ArrayList<GNode>();
for ( Object o : argLengthMatches ){
GNode formalParameters = ((GNode)o).getGeneric(2);
GNode targetArgTypes = GNode.create("ArgTypes");
for ( Object p : formalParameters ){
targetArgTypes.add(((GNode)p).getGeneric(1));
}
if (isCastable(argTypes, targetArgTypes)){
argCastMatches.add((GNode)o);
}
}
return argCastMatches;
}
private static ArrayList<GNode> findNameMatches(String methodName,
GNode classDeclaration) {
GNode methodContainer = classDeclaration.getGeneric(4);
ArrayList<GNode> matches = new ArrayList<GNode>();
//
//
//
for (int i = 0; i < methodContainer.size(); i++){
GNode method = methodContainer.getGeneric(i);
if (method.getName().equals("InheritedMethodContainer")){
method = method.getGeneric(0);
}
if (method.getString(0).equals(methodName)) {
if (method.getProperty("static") != null){
if (callType == Constants.CALL_DYNAMIC)
continue;
}
if (method.getProperty("private") != null){
if (callingClassDeclaration != classDeclaration){
continue;
}
}
/*
if (method.getProperty("public") == null){
// If a method is not public, it is automatically protected, and the packages have to match
String[] callingPackage = callingClassDeclaration.getString(0).split("\\.");
String[] callerPackage = classDeclaration.getString(0).split("\\.");
boolean packageMatch = true;
for (int j = 0; j < (callerPackage.length - 1); j++){
if (callingPackage.length <= j || !callingPackage[j].equals(callerPackage[j])){
packageMatch = false;
break;
}
}
if (packageMatch){
matches.add(method);
}
}
else{
matches.add(method);
}*/
matches.add(method);
}
}
return matches;
}
}