I'm trying to obtain the MethodDeclaration referred by a MethodReference, using this code:
public void visitMethodReference(MethodReference x) {
ProgramFactory pf = x.getFactory();
SourceInfo si = pf.getServiceConfiguration().getSourceInfo();
Method m = si.getMethod(x);
MethodDeclaration mDecl = si.getMethodDeclaration(m);
...
}
In some cases, si.getMethod(x) returns null and in others, si.getMethodDeclaration(m) throws a cast exception recoder.java.declaration.MethodDeclaration cannot be cast to recoder.java.CompilationUnit.
How is the way to do this?
Thanks in advance
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
getMethodDeclaration(Method m) returns "null" in case that Method m is not a MethodDeclaration, i.e., if it is not present as source code (could be bytecode methods or implicitly defined methods, like those created by a compiler for enum types, and default constructors also count in here).
How si.getMethod(x) would return null or the described class cast exception would occur I cannot figure out. Could you provide a full example of the source code that triggers the bug, along with a source code example of the program being analyzed?
/Tobias
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The goal of my program is to provide java to resolve ambiguous method calls at running time. For example, at this input program:
public class Main {
public static void main(String[] args) {
Main tmp;
tmp = new Main();
Object[] tmp2;
tmp2 = new Integer[2];
// compile error
tmp.operation(tmp2);
tmp2 = new Float[2];
// compile error
tmp.operation(tmp2);
}
public void operation(Integer[] values){
System.out.println("MultimethodsToJava.operation(Integer[])");
}
public void operation(Float[] values){
System.out.println("MultimethodsToJava.operation(Float[])");
}
}
it has to generate something like that:
public class Main {
public static void main(String[] args) {
Main tmp;
tmp = new Main();
Object[] tmp2;
tmp2 = new Integer[2];
// compile error
tmp.operation_1_dispatcher(tmp2);
tmp2 = new Float[2];
// compile error
tmp.operation_1_dispatcher(tmp2);
}
public void operation(Integer[] values) {
System.out.println("MultimethodsToJava.operation(Integer[])");
}
public void operation(Float[] values) {
System.out.println("MultimethodsToJava.operation(Float[])");
}
public void operation_1_dispatcher(Object[] a) {
Class aClass = a.getClass();
if ((aClass == Integer[].class)) {
operation((Integer[])a);
}
else if ((aClass == Float[].class)) {
operation((Float[])a);
}
}
}
Reviewing recoder sources I notice that I have to use getMethods(MethodReference) instead of getMethod(MethodReference), because the call is ambiguous. Although I've still having the same problems. At now, my code is this:
public void visitMethodReference(MethodReference x) {
ProgramFactory pf = x.getFactory();
SourceInfo si = pf.getServiceConfiguration().getSourceInfo();
// Gets compatible methods list
List<Method> methods = si.getMethods(x);
Iterator<Method> it = methods.iterator();
while (it.hasNext()) {
MethodDeclaration mDecl = si.getMethodDeclaration(it.next());
// Prevents byte code or implicit methods
if (mDecl != null) {
ClassDeclaration cDecl = (ClassDeclaration) mDecl.getASTParent();
Class cDef = tt.get(cDecl.getFullName());
MethodFamily mFamily = cDef.getMethodFamily(mDecl);
// We have to check that it's the real method family
if (mFamily.isMethodCompatible(mDecl)) {
// If method family has a dispatcher, changes the method reference
if (mFamily.needADispatcher()) {
MethodDeclaration dispatcher = mFamily.getDispatcherMethod();
Transformation.doReplace(x, pf.createMethodReference(pf.createIdentifier(dispatcher.getName()), x.getArguments()));
}
}
}
}
visitChilds(x);
}
Debugging the code I've noticed that the method references that are throwing the exception are those ones that are inside the dispatcher method implementation (for example the getClass() method reference). So, I suppose that the problems could be two:
The way that I'm using to add the dispatcher method declaration to the class is wrong. The code I'm using for that is this:
public void visitClassDeclaration(ClassDeclaration x) {
...
ASTList<MemberDeclaration> members = x.getMembers();
if(members == null){
members = new ASTArrayList<MemberDeclaration>();
x.setMembers(members);
}
members.add((MemberDeclaration)jd);
...
}
where jd is the generated MethodDeclaration.
Is needed an update of the model or something like that after the MethodDeclaration is added. I'm not sure of that.
Any suggestions? Sorry for the long post.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The parent of a MethodDeclaration is not necessarily a ClassDeclaration. Might be an interface/enum declaration as well (maybe stay general and use TypeDeclaration!?)
x.getArguments() is a part of the AST. By giving it as an argument to pf.createMethodReference(), it will be moved in the AST - however, some links to parent/child nodes may be inconsistens afterwards; thus, the AST is likely to be broken. Using x.getArguments().deepClone(); should solve this problem. HOWEVER, since you would then have a copy of x.getArguments() that will be part of the syntax tree later on, but still traverse the original x.getArguments(), any changes made in the child nodes of the method reference would not be applied to this copy. In order to solve this problem (generally a good idea when doing AST transformations), you should perform changes while ascending the AST, i.e., only after returning from child nodes should you perform changes in the node itself.
And here comes another (advanced) issue: getMethods(MethodReference) forces a model update. For bigger programs, that can take a while. Thus, it'd be a good idea to first collect all the places where you want to perform transformations, and first then, in a second step, perform the actual transformations without calling methods that trigger a model update. Note again, they must be performed in the right order (children first). Methods that trigger model updates are most methods that do not just query the abstract syntax tree.
If the above thought don't help fix your problem, please let me know, I'll then take a closer look
Best regards,
Tobias
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks for all your tips. I've used all of them but the error stills happening. Although, as you said that getMethods() could be slow for big programs, I will use another way to do the same. I only need to work a bit more :).
Thanks, one more time for your help.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've taken another, closer look. There is indeed a problem:
Simply adding the MethodDeclaration to the list is not sufficient; you also need to update the parent link (x.makeParentLinkValid();) and report the change to the ChangeHistory (getChangeHistory.attached(jd);)
Alternatively, use convenience methods:
recoder.kit.transformation.doAttach(jd, x, 0); // takes care in case getMemberDeclaration() would be "null", and also sets the parent link of "jd" to point to "x"
getChangeHistory.attached(jd);
A model update is done automatically once it's required by a query method.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
I'm trying to obtain the MethodDeclaration referred by a MethodReference, using this code:
public void visitMethodReference(MethodReference x) {
ProgramFactory pf = x.getFactory();
SourceInfo si = pf.getServiceConfiguration().getSourceInfo();
Method m = si.getMethod(x);
MethodDeclaration mDecl = si.getMethodDeclaration(m);
...
}
In some cases, si.getMethod(x) returns null and in others, si.getMethodDeclaration(m) throws a cast exception recoder.java.declaration.MethodDeclaration cannot be cast to recoder.java.CompilationUnit.
How is the way to do this?
Thanks in advance
Hej,
getMethodDeclaration(Method m) returns "null" in case that Method m is not a MethodDeclaration, i.e., if it is not present as source code (could be bytecode methods or implicitly defined methods, like those created by a compiler for enum types, and default constructors also count in here).
How si.getMethod(x) would return null or the described class cast exception would occur I cannot figure out. Could you provide a full example of the source code that triggers the bug, along with a source code example of the program being analyzed?
/Tobias
Hi and thanks for your fast response.
The goal of my program is to provide java to resolve ambiguous method calls at running time. For example, at this input program:
public class Main {
}
it has to generate something like that:
public class Main {
}
Reviewing recoder sources I notice that I have to use getMethods(MethodReference) instead of getMethod(MethodReference), because the call is ambiguous. Although I've still having the same problems. At now, my code is this:
public void visitMethodReference(MethodReference x) {
Debugging the code I've noticed that the method references that are throwing the exception are those ones that are inside the dispatcher method implementation (for example the getClass() method reference). So, I suppose that the problems could be two:
public void visitClassDeclaration(ClassDeclaration x) {
...
...
where jd is the generated MethodDeclaration.
Any suggestions? Sorry for the long post.
Some quick thoughts:
...
pf.createMethodReference(pf.createIdentifier(dispatcher.getName()), x.getArguments()));
...
visitChilds(x);
x.getArguments() is a part of the AST. By giving it as an argument to pf.createMethodReference(), it will be moved in the AST - however, some links to parent/child nodes may be inconsistens afterwards; thus, the AST is likely to be broken. Using x.getArguments().deepClone(); should solve this problem. HOWEVER, since you would then have a copy of x.getArguments() that will be part of the syntax tree later on, but still traverse the original x.getArguments(), any changes made in the child nodes of the method reference would not be applied to this copy. In order to solve this problem (generally a good idea when doing AST transformations), you should perform changes while ascending the AST, i.e., only after returning from child nodes should you perform changes in the node itself.
And here comes another (advanced) issue: getMethods(MethodReference) forces a model update. For bigger programs, that can take a while. Thus, it'd be a good idea to first collect all the places where you want to perform transformations, and first then, in a second step, perform the actual transformations without calling methods that trigger a model update. Note again, they must be performed in the right order (children first). Methods that trigger model updates are most methods that do not just query the abstract syntax tree.
If the above thought don't help fix your problem, please let me know, I'll then take a closer look
Best regards,
Tobias
Hi,
Thanks for all your tips. I've used all of them but the error stills happening. Although, as you said that getMethods() could be slow for big programs, I will use another way to do the same. I only need to work a bit more :).
Thanks, one more time for your help.
Hej,
I've taken another, closer look. There is indeed a problem:
Simply adding the MethodDeclaration to the list is not sufficient; you also need to update the parent link (x.makeParentLinkValid();) and report the change to the ChangeHistory (getChangeHistory.attached(jd);)
Alternatively, use convenience methods:
recoder.kit.transformation.doAttach(jd, x, 0); // takes care in case getMemberDeclaration() would be "null", and also sets the parent link of "jd" to point to "x"
getChangeHistory.attached(jd);
A model update is done automatically once it's required by a query method.