Modifying line numbers in Javassist - java

So I have been using Javassist a bit lately, and I have run into a question I haven't been able to find an answer to. The insertAt method of CtMethod allows you to insert code at a specific line number, but does it overwrite that line or keep it, and how do I make it do the opposite of what it does by default? I have an application which modifies source just before runtime with Javassist, based on 'hooks' in an XML file. I want to make it so that a line can be overridden, or a line can be placed above the line instead of overriding it. Obviously there are hackish ways to do that, but I'd rather use a proper way.

The easy part
The method insertAt(int lineNumber, String src) present in CtMethod object allows injecting the code written in src before the code that was in the given line.
For instance, take the following (simple) example program:
public class TestSubject {
public static void main(String[] args) {
TestSubject testSubject = new TestSubject();
testSubject.print();
}
private void print() {
System.out.println("One"); // line 9
System.out.println("Two"); // line 10
System.out.println("Three"); // line 11
}
}
By simply coding (keep in mind that method variable must be the CtMethod representation of print method):
// notice that I said line 10, which is where the sysout of "two" is
method.insertAt(10, true, "System.out.println(\"one and an half\");");
Will inject a new sysout instruction in the class. The output of the new class will be:
one
one and an half
two
three
The hard part
Javassist does not provide an easy way to remove a line of code, so if you really want to replace it you'll have no choice than hack your way through.
How to do it? Well, let me introduce you to your new friend (if you don't know it yet), the CodeAttribute object.
The CodeAttribute object is responsible for holding the bytecode that represents the method flow besides that code attribute also has another attribute called LineNumberAttribute which helps you map the line numbers into the bytecode array. So summing up this object has everything you need!
The idea in the following example is quite simple. Relate the bytes in bytecode array with the line that should be removed and substitute the bytes by a no operation code.
Once again, method is the CtMethod representation of method print
// let's erase the sysout "Two"
int lineNumberToReplace = 10;
// Access the code attribute
CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();
// Access the LineNumberAttribute
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute) codeAttribute.getAttribute(LineNumberAttribute.tag);
// Index in bytecode array where the instruction starts
int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);
// Index in the bytecode array where the following instruction starts
int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);
System.out.println("Modifying from " + startPc + " to " + endPc);
// Let's now get the bytecode array
byte[] code = codeAttribute.getCode();
for (int i = startPc; i < endPc; i++) {
// change byte to a no operation code
code[i] = CodeAttribute.NOP;
}
Running this modification in the original TestSubject class, would result in an injected class with the following output:
one
three
Summing Up
When you have the need to add a line and still keeping the existing one, you just need to use the example given in the easy part if you want to replace the line, you have to first remove the existing line using the example given in the hard part and then inject the new line using the 1st example.
Also keep in mind that in the examples I assumed you were already comfortable with the basics of javassist showing you only the juicy parts, instead of the all deal. That's why, for instance, in the examples there is no ctClass.writeFile... you still need to do it, I just left it out because I do expect you should know you have to do it.
If you need any extra help in the code examples, just ask. I'll be glad to help.

Related

How does it work when I have two methods inside .set() method?

I am new to java, and I need help understanding what the code is trying to do. I am interested on the last line( sd.setId(sh.getGrade().getSchoolId());). I know it is setting using setId in sd object, but then I am bit confused what the rest of the line(sh.getGrade().getSchoolId()) is trying to do. Does getSchoolId() method called first and then sh.getGrade() and set everything in sd? How do I read a code when there are multiple dot(.) operators in a single line of code?
while (oneIter.hasNext()) {
ShoolHistory sh= (ShoolHistory) oneIter.next();
ScoolDetailId sd = new ScoolDetailId();
sd.setId(sh.getGrade().getSchoolId());
For something like this it would be easiest to just split each command open into several lines. Then your result will be:
while (oneIter.hasNext()) {
ShoolHistory sh = (ShoolHistory) oneIter.next();
ScoolDetailId sd = new ScoolDetailId();
Grade grade = sh.getGrade(); // I'm just assuming some types here and for the id
Integer id = grade.getSchoolId(); // I like btw the usage of all possible variations of writing "school"
sd.setId(id);
}
So, if you have a line with multiple dot-operators, you start just reading left to right as you would normally do. Then, if it is like here used as arguments for some methods, you go from inside to outside.
I'm assuming that sh.getGrade() returns an object of the type Grade which is defined outside the scope of this code. And then a method called getSchoolId() is called on that object which returns the ID which is then passed to the sd.setId method.
So it is equivalent to this:
Grade grade = sh.getGrade();
String id = grade.getSchoolId();
sd.setId(id);
You're just skipping the extra variables by chaining the methods together,
As you might be already aware that Java is Object oriented programming. Hence mostly you would be dealing with objects.
Always remember to read from left to right. So in your case you are trying to set the 'id' field in 'schoolDetailsId' object.
The 'id' field is obtained from another object which is in 'grade' object which is inside 'SchoolHistory (sh)'
SchoolHistory --> grade --> schoolId
You could refer this link to understand more.

How to return value from the function without exiting from the function?

I am adding byte arrays from an array list of another array list of bytes. So, basically, I am playing with nested byte arrays. I am able to add the first index of each byte array but I am unable to return it immediately. The function will return the whole byte array when all indexes are added. But, I want to return the sum of each index separately.
public static byte[] final_stream(ArrayList<ArrayList<byte[]>> outerstream) {
ArrayList<byte[]> streams = new ArrayList<byte[]>();
int x = 0;
while (x < outerstream.size()) {
streams = new ArrayList<byte[]>();
for (ArrayList<byte[]> bytes : outerstream) {
streams.add(bytes.remove(0));
}
x++;
return stream_Addr(streams); // Here I want to return the value
}
} // Here it gives error to return byte[]
Your code is wrong on many levels, a quick (probably incomplete) list:
you violate java naming conventions. Method names go camelCase(), and variable names (unless constants), too. And you only use the "_" for SOME_CONSTANT
the term "stream" has a very special meaning in Java. A list is not a stream (but you can create a true java stream from a list by yourList.stream())
and yes, what you intend to do in that while loop is beyond my creativity to interpret. Honestly: throw that away, and start from scratch.
Regarding your real question: every "exit" path of a non-void method needs to either throw an exception or to return something.
Finally: what you intend to do isn't possible like that in Java. A caller calls a method, and that method returns one value and then ends.
What you can do, is something like:
thread A creates someList and passes that to some thread B somehow
thread B manipulates that list object, and by using appropriate synchronization the other thread can access that data (while B continues to make updates)
And the real answer is: you can't learn a new language by assuming that the language supports a concept you know from other languages (like pythons generators) to then invent your own syntax or construct in the new language to then be surprised "gosh, it doesn't work". It goes the other way round: you research if your target language has such a concept, if not, you research what else is offered. Then you read a tutorial about that, and follow that.

Identify a method and it's scope while reading a source code with a file reader?

I am trying to search a specific string in the source code so that I can identify the string is found in which method.I tried file reader. But failed . How to identify a method and it's scope while reading a source code with a file reader? Is there any other way to get the method and it's scope / method and it's declaration?
I took another approach where I could manage to get name of methods of the file using classloader. But again failed to get the method declaration.
Any help will be appreciated. TIA.
UPDATED VERSION
Sorry for posting a second answer, but I just want to ascertain that you get notified of this answer.
This is the code for the file:
File file = new File("FilePath");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String t = "";
while((t = br.readLine()) != null)
{
str = str + t + "\n";
}
This is the regex:
Pattern p = Pattern.compile(".*\\s+(.+\\(.*?\\))\\s*\\{(.*"+keyword+"[^\\w*].*)\\}.*\\}$", Pattern.DOTALL);
Matcher m = p.matcher(str);
boolean notFound = true;
while(m.find())
{
System.out.println("Scope of keyword: " + m.group(1));
notFound = false;
}
if(notFound)
{
System.out.println("NOT FOUND");
}
Since you wanted the scope of the keyword, it is the nearest brace brackets within which it is declared, so I have assumed you have written its data type and the variable name in the keyword. I'll see if I can handle more exceptions later :)
.*\\s+ - Takes in everything until the method name
(.+\\(.*?\\))\\s*\\{ - Stores the method name and parameters (In case there are overloaded methods)
(.*"+keyword+" - Finds if the keyword is present after some other code
[^\\w*] Ensures that the keyword exists by itself (Otherwise if the keyword is double x, and double xy is present, then it would be a match)
.*)\\}.*\\}$ - Sees if there are two Braces after the keyword is found (one to end the method or block [in case of if, else if, switch etc.], one to end the class)
While parsing your source file, first you need to know to identify when you reach a method definition (using some pattern/regex). Then, once you're inside some method you should save its name as current method.
Now you continue parsing your keyword. If you found your keyword ==> done, and you know what's the method it was found in.
You would want to know when you're exiting current method. Use a stack data structure in order to know when you leave this method, according to the curly brackets order (Read about the "balanced brackets problem" and about stack if you're not familiar with them. Example here):
{ - means you push it to the stack.
} - means you pop a { from the stack (if it's not empty). If you parse } and it's empty - you did something wrong.
Every time the stack is empty (don't push the class' curly brackets), it means that you're between methods, outside. In every method you reach you repeat the same algorithm.
Few things to note:
This keyword might appear maybe more than one, maybe as part of a string.
Don't forget that the class itself has curly brackets.
Your source code might be "weirder" than the "classic" Java class - sub classes, inner classes, etc. Think about all the different language elements you might have.
Curly brackets might also appear as part of strings. You should know how to avoid these.
Good luck!
EDIT: I have posted my Source Code in another answer. These are just some technical errors that can go unchecked. But it's not really important now.
The problem with identifying the method the keyword is in is:
1) It might be in a constructor,
2) It might be a member variable,
3) It might be in a loop or a condition,
4) Or it might even be in an Overriden method of the initially executed method.
The easiest way is to print the location (Like an address). Example:
Location: if(a!=0), if(a < 500), main(String[] args), class FUBAR
You can use a recursive method with a regex which identifies everything within braces, and searches for the keyword. I can post the entire code in 4-5 hours, if that's fine.

I need.setText parameter to be recognized as method , not as a string

I have a problem with the following code:
question1.setText("question1_" + question_number() + "()");
I have multiple methods that return a string value and are named "question1_x" (x is a number) and the method question_number returns a random number.
When i run the code like this the "question1" text is set to "question1_x()" , but what i need it to do is set the text as the "question1_x()" method returns it. Simply i like ["question1_" + question_number() + "()"] to be seen by ".setText" as a method and not as a string.
Thank you in advance :)
Java won't let you do this unless you resort to reflection. Reflection is almost certainly the wrong solution to this problem.
Any time you want to have numbered methods and/or fields, step back and consider using a proper collection (say, a list) instead. Start by replacing your numbered methods with a single method that takes a number:
String question1(int questionNumber)
If I understand this right, you want to call a method inside setText by its name. It can't be done the way you're trying to do it.
You need to use Reflections Api. Look at the example below:
Class myClass = Class.forName("");
Method m = myClass.getMethod(""question1_" + question_number()", (Class[]) null);
Object retVal = m.invoke(object_to_invoke_on, arguments);
setText(retVal);
However, this has performance penalties. It'll be 2-3 times slower than a normal method call. In your case you can avoid this altogether by designing it right.

variable definition and assignment detect asm bytecode

I am trying to use the ASM bytecode tree API for static analysis of Java Code.
I have a ClassNode cn, MethodNode m and the list of instructions in that method say InsnList list.
Suppose for a given instruction( i.e. AbstractInsnNode) s, I need to find all the definitions/assignments of the variable at s in the above instruction list. To make it more clear, suppose a variable var is defined and initialized on line 2, then assigned some other value on line number 8 and then used on line number 12. Line number 12 is my s, in this case. Also, assume lots of conditional code in the lines in between.
Is this possible to do with ASM? How??
Thanks and Regards,
SJ
For clarity,
public void funcToAnalyze(String k, SomeClass v) {
int numIter = 0;
/*
Do cool stuff here.... modifies member variables and passed params too
*/
if (v.rank > 1 || numIter>200) {
magicFunction(k, 1);
}
}
Here, suppose the conditional is the JumpInsnNode (current instruction) and I need to find if (and where) any of the variables in the conditional (v.rank and numIter in this case) are modified or assigned anywhere in the above code. Keep it simple, just member variables (no static function or delegation to function of another class).
The SourceInterpreter computes SourceValues
for each Frame for a corresponding instruction in MethodNode. Basically it tells which instructions could place value to a given variable or stack slot.
Also see ASM User Guide for more information about ASM analysis package.
However if you just need to detect if certain variable been assigned, then all you have to do is to look for xSTORE instructions with corresponding variable indexes.

Categories

Resources