Vous êtes sur la page 1sur 4

Welcome back!

If you have been following these tutorials up to this part, I'm going to take it
for granted that you are still motivated to learn some more!
If you haven't seen Part 1 or Part 2, I strongly recommend that you read them!
-> Link to Part One
-> Link to Part Two
3.1 - Recap
Last tutorial, I have somewhat explained how adapters work and I've also gave a
brief introduction on how classes are transformed. If you haven't noticed alread
y, the AddGetterAdapter that we have worked on DOES have a flaw. I'll get to tha
t later.
Remember how I mentioned in Part One that there will definitely be learning "gap
s". This is the one of the parts of the tutorial that I will create these "gaps"
. Why? Because you will use what you have learned to create content for yourself
.
If you haven't read the ASM guide already, I strongly recommend that you read at
least the Core API (see Part 2 for a link).
3.2 - AddInterfaceAdapter Introduction
Recall in the last tutorial that we created an AddGetterAdapter to insert our ac
cessor methods. Notice how something was missing?
We need an interface to provide access to these methods!
It's not like reflection; we can't just plainly do something as simple as "#setA
ccessible(true)". Now we need to "link" the accessor method via the interface th
at we inject.
So create a new class, call it AddInterfaceAdapter and set it up like we've setu
p the AddGetterAdapter. Do it without looking at the following spoiler! When you
're done, it should look something like the following:
Spoiler
After this, you will need one more parameter argument in the constructor. Can yo
u guess what it is (it's pretty obvious)? Add it in.
Spoiler
Obviously, the variable name will differ.
Guess what? You're nearly done making this adapter (what?!?!?!?).
The only method that we need to worry about in the ClassVisitor class is visit.
Why? It's because that's where the interfaces are "visited" with the ClassWriter
that you would be using (will get to that soon).
3.3 - AddInterfaceAdapter Continued
So we need to grab all of the "current" interfaces of the class (from the inform
ation passed as a ClassVisitor; in this case, a ClassWriter). Then, all we need
to do is to add the one interface that we want to add as a String in its fully q
ualified name.
For example, if I had an interface called Accessor, in the package "com.dna.asm.
accessors", its fully qualified name is the same thing, but it has slashes ('/')
instead of dots. So therefore, it translates to "com/dna/asm/accessors". Since
the interface is called "Accessor", you obviously need to indicate that as well.
So it should then be "com/dna/asm/accessors/Accessor". Makes sense?
So coming back on topic, let's continue.
You may use an ArrayList to store the fully qualified names of the interfaces as
Strings. All you need to do is to add yours in.
Lets also make the adapter so it can accept the qualified names of more than one
interface so we don't need to keep reusing this Adapter for other getters.
It should now look like this:
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
/**
* Adds an interface to a given class via a ClassVisitor (ClassWriter).
* @author trDna
*/
public class AddInterfaceAdapter extends ClassVisitor implements Opcodes {
private String[] interfacesToAdd;
public AddInterfaceAdapter(final ClassVisitor cv, final String... interfacesToAd
d){
super(ASM4, cv);
this.interfacesToAdd = interfacesToAdd;
}
@Override
public void visit(int version, int access, java.lang.String name, java.lang.Stri
ng signature, java.lang.String superName, java.lang.String[] interfaces){
ArrayList<String> interfaceList = new ArrayList<String>();
if(interfaces != null){
for(String i : interfaces){
interfaceList.add(i);
}
} for(String i : interfacesToAdd){
interfaceList.add(i);
}
cv.visit(version, access, name, signature, superName, interfaceList.toA
rray(new String[interfaceList.size()]));
}
}
Since the visit() method is a void method, it does not return anything. We want
the ClassVisitor (a ClassWriter) to consume our modified method. So we need to "
manually" call the visit method in the ClassVisitor that has been passed through
the constructor.
The ClassVisitor (a ClassWriter) consumes whatever values that are passed throug
h its parameters. This gives us the effect that we want!
Now, you have completed your AddInterface adapter!
3.3 - Useful subclasses of ClassVisitor
If you haven't read the ASM guide by Eric Bruneton, then you probably have no id
ea how any of the useful subclasses that ASM provides function.
-> A ClassReader parses a given class file as a stream of bytes. It also called
the corresponding visit___ methods on the ClassVisitor passed through its accept
method (will explain that later).
It's known as the event producer because it calls these ClassVisitor methods (us
ually, the ClassVisitor is a ClassWriter or an adapter); which ultimately create
s these events.
-> A ClassWriter consumes and builds classes in binary form. It also produces an
y compiled class to a byte array.
This is known as an event consumer because it acts like a "retriever" when the C
lassReader acting on it calls the visit___ methods of the ClassWriter (or any ot
her ClassVisitor, such as adapters).
-> A ClassVisitor "trusts" all of these methods to another ClassVisitor instance
(such as a ClassWriter).
Remember how we used adapters as our "tool" to add things? It's because the Clas
sVisitor acts as a special filter. You can easily guess that if a custom ClassVi
sitor (like an adapter) can add things, it can also remove things as well.
For example, in our AddInterface adapter, if passed a null value to the visit me
thod of the ClassVisitor passed through the constructor, then the interfaces of
the class parsed will be removed. This is because in ASM, you are almost reconst
ructing the classes.
--------------------------------------------------------------------------------
------------------------------------------------------------------
In the interest of time, here's something to do or to think about before proceed
ing to the next part:
How would you create a method in your AbstractClassTransform called addGetter th
at functions?
Remember that you need to read from a JAR to obtain the class in binary format!
Here is a start (note that it's not the complete code):
The following can be cleaned up, but for learning purposes, I think it's fine:
private static String clazz;
private static String theJar; // JarFile dir goes here.
private static JarFile jf;
private static JarEntry entry;
private static ClassReader cr = null;
private ClassWriter cw = null; private ClassVisitor interfaceAdder = null; // Ad
dInterfaceAdapter
private ClassVisitor adder = null; // AddGetterAdapter
private ClassVisitor combinedAdapter = null; //Mixture of all adapters
public abstract void runTransform();
public void setup(final String theJar, final String clazz){
this.theJar = theJar;
this.clazz = clazz;
}
public void start(){
try{
jf = new JarFile(theJar);
entry = new JarEntry(clazz);
Enumeration<JarEntry> en = jf.entries();
cr = new ClassReader(jf.getInputStream(entry));
cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter
.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
;
}catch (Exception ex){
ex.printStackTrace();
}
}
--------------------------------------------------------------------------------
-------------------------------------------------------------------
This is all I have time for, and hopefully I write up Part 4 soon (within a week
, probably), where we can immediately start working on the AbstractClassTransfor
m.
I will explain every step to create the AbstractClassTransform in the next part,
so don't be afraid to attempt to make it!
You can find my examples repository here if you wish to move ahead:
https://github.com/t.../master/com/dna

Vous aimerez peut-être aussi