KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > hook > impl > ClassLoaderPreProcessorImpl


1 /**************************************************************************************
2  * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

8 package org.codehaus.aspectwerkz.hook.impl;
9
10 import org.codehaus.aspectwerkz.hook.ClassLoaderPatcher;
11 import org.codehaus.aspectwerkz.hook.ClassLoaderPreProcessor;
12 import org.objectweb.asm.ClassWriter;
13 import org.objectweb.asm.CodeVisitor;
14 import org.objectweb.asm.Attribute;
15 import org.objectweb.asm.ClassReader;
16 import org.objectweb.asm.ClassVisitor;
17 import org.objectweb.asm.ClassAdapter;
18 import org.objectweb.asm.CodeAdapter;
19 import org.objectweb.asm.Type;
20 import org.objectweb.asm.Constants;
21 import org.objectweb.asm.Label;
22 import org.objectweb.asm.attrs.Attributes;
23
24 import java.io.InputStream JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.File JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Map JavaDoc;
30
31 /**
32  * Instruments the java.lang.ClassLoader to plug in the Class PreProcessor mechanism using ASM. <p/>We are using a
33  * lazy initialization of the class preprocessor to allow all class pre processor logic to be in system classpath and
34  * not in bootclasspath. <p/>This implementation should support IBM custom JRE
35  *
36  * @author Chris Nokleberg
37  * @author <a HREF="mailto:alex@gnilux.com">Alexandre Vasseur </a>
38  */

39 public class ClassLoaderPreProcessorImpl implements ClassLoaderPreProcessor {
40
41     private final static String JavaDoc CLASSLOADER_CLASS_NAME = "java/lang/ClassLoader";
42     private final static String JavaDoc DEFINECLASS0_METHOD_NAME = "defineClass0";
43     private final static String JavaDoc DEFINECLASS1_METHOD_NAME = "defineClass1";//For JDK 5
44
private final static String JavaDoc DEFINECLASS2_METHOD_NAME = "defineClass2";//For JDK 5
45

46
47     private static final String JavaDoc DESC_CORE = "Ljava/lang/String;[BIILjava/security/ProtectionDomain;";
48     private static final String JavaDoc DESC_PREFIX = "(" + DESC_CORE;
49     private static final String JavaDoc DESC_HELPER = "(Ljava/lang/ClassLoader;" + DESC_CORE + ")[B";
50
51     private static final String JavaDoc DESC_BYTEBUFFER_CORE = "Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;";
52     private static final String JavaDoc DESC_BYTEBUFFER_PREFIX = "(" + DESC_BYTEBUFFER_CORE;
53     private static final String JavaDoc DESC_BYTEBUFFER_HELPER = "(Ljava/lang/ClassLoader;" + DESC_BYTEBUFFER_CORE + ")[B";
54
55     public ClassLoaderPreProcessorImpl() {
56     }
57
58     /**
59      * Patch caller side of defineClass0
60      * byte[] weaved = ..hook.impl.ClassPreProcessorHelper.defineClass0Pre(this, args..);
61      * klass = defineClass0(name, weaved, 0, weaved.length, protectionDomain);
62      *
63      * @param classLoaderBytecode
64      * @return
65      */

66     public byte[] preProcess(byte[] classLoaderBytecode) {
67         try {
68             ClassWriter cw = new ClassWriter(true);
69             ClassLoaderVisitor cv = new ClassLoaderVisitor(cw);
70             ClassReader cr = new ClassReader(classLoaderBytecode);
71             cr.accept(cv, Attributes.getDefaultAttributes(), false);
72             return cw.toByteArray();
73         } catch (Exception JavaDoc e) {
74             System.err.println("failed to patch ClassLoader:");
75             e.printStackTrace();
76             return classLoaderBytecode;
77         }
78     }
79
80     private static class ClassLoaderVisitor extends ClassAdapter {
81         public ClassLoaderVisitor(ClassVisitor cv) {
82             super(cv);
83         }
84
85         public CodeVisitor visitMethod(int access, String JavaDoc name, String JavaDoc desc, String JavaDoc[] exceptions, Attribute attrs) {
86             CodeVisitor cv = super.visitMethod(access, name, desc, exceptions, attrs);
87             Type[] args = Type.getArgumentTypes(desc);
88             return new PreProcessingVisitor(cv, access, args);
89         }
90     }
91
92     /**
93      * @author Chris Nokleberg
94      */

95     private static class PreProcessingVisitor extends RemappingCodeVisitor {
96         public PreProcessingVisitor(CodeVisitor cv, int access, Type[] args) {
97             super(cv, access, args);
98         }
99
100         public void visitMethodInsn(int opcode, String JavaDoc owner, String JavaDoc name, String JavaDoc desc) {
101             if ((DEFINECLASS0_METHOD_NAME.equals(name) || (DEFINECLASS1_METHOD_NAME.equals(name)))
102                 && CLASSLOADER_CLASS_NAME.equals(owner)) {
103                 Type[] args = Type.getArgumentTypes(desc);
104                 if (args.length < 5 || !desc.startsWith(DESC_PREFIX)) {
105                     throw new Error JavaDoc("non supported JDK, native call not supported: " + desc);
106                 }
107                 // store all args in local variables
108
int[] locals = new int[args.length];
109                 for (int i = args.length - 1; i >= 0; i--) {
110                     cv.visitVarInsn(
111                             args[i].getOpcode(Constants.ISTORE),
112                             locals[i] = nextLocal(args[i].getSize())
113                     );
114                 }
115                 for (int i = 0; i < 5; i++) {
116                     cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
117                 }
118                 super.visitMethodInsn(
119                         Constants.INVOKESTATIC,
120                         "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
121                         "defineClass0Pre",
122                         DESC_HELPER
123                 );
124                 cv.visitVarInsn(Constants.ASTORE, locals[1]);
125                 cv.visitVarInsn(Constants.ALOAD, 0);
126                 cv.visitVarInsn(Constants.ALOAD, locals[0]); // name
127
cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
128
cv.visitInsn(Constants.ICONST_0); // offset
129
cv.visitVarInsn(Constants.ALOAD, locals[1]);
130                 cv.visitInsn(Constants.ARRAYLENGTH); // length
131
cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
132
for (int i = 5; i < args.length; i++) {
133                     cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
134                 }
135             } else if (DEFINECLASS2_METHOD_NAME.equals(name) && CLASSLOADER_CLASS_NAME.equals(owner)) {
136                 Type[] args = Type.getArgumentTypes(desc);
137                 if (args.length < 5 || !desc.startsWith(DESC_BYTEBUFFER_PREFIX)) {
138                     throw new Error JavaDoc("non supported JDK, bytebuffer native call not supported: " + desc);
139                 }
140                 // store all args in local variables
141
int[] locals = new int[args.length];
142                 for (int i = args.length - 1; i >= 0; i--) {
143                     cv.visitVarInsn(
144                             args[i].getOpcode(Constants.ISTORE),
145                             locals[i] = nextLocal(args[i].getSize())
146                     );
147                 }
148                 for (int i = 0; i < 5; i++) {
149                     cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
150                 }
151                 super.visitMethodInsn(
152                         Constants.INVOKESTATIC,
153                         "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
154                         "defineClass0Pre",
155                         DESC_BYTEBUFFER_HELPER
156                 );
157                 cv.visitVarInsn(Constants.ASTORE, locals[1]);
158                 cv.visitVarInsn(Constants.ALOAD, 0);
159                 cv.visitVarInsn(Constants.ALOAD, locals[0]); // name
160
cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
161
cv.visitInsn(Constants.ICONST_0); // offset
162
cv.visitVarInsn(Constants.ALOAD, locals[1]);
163                 cv.visitMethodInsn(Constants.INVOKEVIRTUAL, "Ljava/nio/Buffer;", "remaining", "()I");
164                 cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
165
for (int i = 5; i < args.length; i++) {
166                     cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
167                 }
168                 // we should rebuild a new ByteBuffer...
169

170             }
171             super.visitMethodInsn(opcode, owner, name, desc);
172         }
173     }
174
175     /**
176      * @author Chris Nokleberg
177      */

178     private static class State {
179         Map JavaDoc locals = new HashMap JavaDoc();
180         int firstLocal;
181         int nextLocal;
182
183         State(int access, Type[] args) {
184             nextLocal = ((Constants.ACC_STATIC & access) != 0) ? 0 : 1;
185             for (int i = 0; i < args.length; i++) {
186                 nextLocal += args[i].getSize();
187             }
188             firstLocal = nextLocal;
189         }
190     }
191
192     /**
193      * @author Chris Nokleberg
194      */

195     private static class IntRef {
196         int key;
197
198         public boolean equals(Object JavaDoc o) {
199             return key == ((IntRef) o).key;
200         }
201
202         public int hashCode() {
203             return key;
204         }
205     }
206
207     /**
208      * @author Chris Nokleberg
209      */

210     private static class RemappingCodeVisitor extends CodeAdapter {
211         private State state;
212         private IntRef check = new IntRef();
213
214
215         public RemappingCodeVisitor(CodeVisitor v, int access, Type[] args) {
216             super(v);
217             state = new State(access, args);
218         }
219
220         public RemappingCodeVisitor(RemappingCodeVisitor wrap) {
221             super(wrap.cv);
222             this.state = wrap.state;
223         }
224
225         protected int nextLocal(int size) {
226             int var = state.nextLocal;
227             state.nextLocal += size;
228             return var;
229         }
230
231         private int remap(int var, int size) {
232             if (var < state.firstLocal) {
233                 return var;
234             }
235             check.key = (size == 2) ? ~var : var;
236             Integer JavaDoc value = (Integer JavaDoc) state.locals.get(check);
237             if (value == null) {
238                 IntRef ref = new IntRef();
239                 ref.key = check.key;
240                 state.locals.put(ref, value = new Integer JavaDoc(nextLocal(size)));
241             }
242             return value.intValue();
243         }
244
245         public void visitIincInsn(int var, int increment) {
246             cv.visitIincInsn(remap(var, 1), increment);
247         }
248
249         public void visitLocalVariable(String JavaDoc name, String JavaDoc desc, Label start, Label end, int index) {
250             cv.visitLocalVariable(name, desc, start, end, remap(index, 0));
251         }
252
253         public void visitVarInsn(int opcode, int var) {
254             int size;
255             switch (opcode) {
256                 case Constants.LLOAD:
257                 case Constants.LSTORE:
258                 case Constants.DLOAD:
259                 case Constants.DSTORE:
260                     size = 2;
261                     break;
262                 default:
263                     size = 1;
264             }
265             cv.visitVarInsn(opcode, remap(var, size));
266         }
267
268         public void visitMaxs(int maxStack, int maxLocals) {
269             cv.visitMaxs(0, 0);
270         }
271     }
272
273     public static void main(String JavaDoc args[]) throws Exception JavaDoc {
274         ClassLoaderPreProcessor me = new ClassLoaderPreProcessorImpl();
275         InputStream JavaDoc is = ClassLoader.getSystemClassLoader().getParent().getResourceAsStream(
276                 "java/lang/ClassLoader.class"
277         );
278         byte[] out = me.preProcess(ClassLoaderPatcher.inputStreamToByteArray(is));
279         is.close();
280         File JavaDoc dir = new File JavaDoc("_boot/java/lang/");
281         dir.mkdirs();
282         OutputStream JavaDoc os = new FileOutputStream JavaDoc("_boot/java/lang/ClassLoader.class");
283         os.write(out);
284         os.close();
285     }
286 }
Popular Tags