001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.hook.impl;
008:
009: import org.codehaus.aspectwerkz.hook.ClassLoaderPatcher;
010: import org.codehaus.aspectwerkz.hook.ClassLoaderPreProcessor;
011: import org.objectweb.asm.ClassWriter;
012: import org.objectweb.asm.CodeVisitor;
013: import org.objectweb.asm.Attribute;
014: import org.objectweb.asm.ClassReader;
015: import org.objectweb.asm.ClassVisitor;
016: import org.objectweb.asm.ClassAdapter;
017: import org.objectweb.asm.CodeAdapter;
018: import org.objectweb.asm.Type;
019: import org.objectweb.asm.Constants;
020: import org.objectweb.asm.Label;
021: import org.objectweb.asm.attrs.Attributes;
022:
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.io.FileOutputStream;
026: import java.io.File;
027: import java.util.HashMap;
028: import java.util.Map;
029:
030: /**
031: * Instruments the java.lang.ClassLoader to plug in the Class PreProcessor mechanism using ASM. <p/>We are using a
032: * lazy initialization of the class preprocessor to allow all class pre processor logic to be in system classpath and
033: * not in bootclasspath. <p/>This implementation should support IBM custom JRE
034: *
035: * @author Chris Nokleberg
036: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
037: */
038: public class ClassLoaderPreProcessorImpl implements
039: ClassLoaderPreProcessor {
040:
041: private final static String CLASSLOADER_CLASS_NAME = "java/lang/ClassLoader";
042: private final static String DEFINECLASS0_METHOD_NAME = "defineClass0";
043: private final static String DEFINECLASS1_METHOD_NAME = "defineClass1";//For JDK 5
044: private final static String DEFINECLASS2_METHOD_NAME = "defineClass2";//For JDK 5
045:
046: private static final String DESC_CORE = "Ljava/lang/String;[BIILjava/security/ProtectionDomain;";
047: private static final String DESC_PREFIX = "(" + DESC_CORE;
048: private static final String DESC_HELPER = "(Ljava/lang/ClassLoader;"
049: + DESC_CORE + ")[B";
050:
051: private static final String DESC_BYTEBUFFER_CORE = "Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;";
052: private static final String DESC_BYTEBUFFER_PREFIX = "("
053: + DESC_BYTEBUFFER_CORE;
054: private static final String DESC_BYTEBUFFER_HELPER = "(Ljava/lang/ClassLoader;"
055: + DESC_BYTEBUFFER_CORE + ")[B";
056:
057: public ClassLoaderPreProcessorImpl() {
058: }
059:
060: /**
061: * Patch caller side of defineClass0
062: * byte[] weaved = ..hook.impl.ClassPreProcessorHelper.defineClass0Pre(this, args..);
063: * klass = defineClass0(name, weaved, 0, weaved.length, protectionDomain);
064: *
065: * @param classLoaderBytecode
066: * @return
067: */
068: public byte[] preProcess(byte[] classLoaderBytecode) {
069: try {
070: ClassWriter cw = new ClassWriter(true);
071: ClassLoaderVisitor cv = new ClassLoaderVisitor(cw);
072: ClassReader cr = new ClassReader(classLoaderBytecode);
073: cr.accept(cv, Attributes.getDefaultAttributes(), false);
074: return cw.toByteArray();
075: } catch (Exception e) {
076: System.err.println("failed to patch ClassLoader:");
077: e.printStackTrace();
078: return classLoaderBytecode;
079: }
080: }
081:
082: private static class ClassLoaderVisitor extends ClassAdapter {
083: public ClassLoaderVisitor(ClassVisitor cv) {
084: super (cv);
085: }
086:
087: public CodeVisitor visitMethod(int access, String name,
088: String desc, String[] exceptions, Attribute attrs) {
089: CodeVisitor cv = super .visitMethod(access, name, desc,
090: exceptions, attrs);
091: Type[] args = Type.getArgumentTypes(desc);
092: return new PreProcessingVisitor(cv, access, args);
093: }
094: }
095:
096: /**
097: * @author Chris Nokleberg
098: */
099: private static class PreProcessingVisitor extends
100: RemappingCodeVisitor {
101: public PreProcessingVisitor(CodeVisitor cv, int access,
102: Type[] args) {
103: super (cv, access, args);
104: }
105:
106: public void visitMethodInsn(int opcode, String owner,
107: String name, String desc) {
108: if ((DEFINECLASS0_METHOD_NAME.equals(name) || (DEFINECLASS1_METHOD_NAME
109: .equals(name)))
110: && CLASSLOADER_CLASS_NAME.equals(owner)) {
111: Type[] args = Type.getArgumentTypes(desc);
112: if (args.length < 5 || !desc.startsWith(DESC_PREFIX)) {
113: throw new Error(
114: "non supported JDK, native call not supported: "
115: + desc);
116: }
117: // store all args in local variables
118: int[] locals = new int[args.length];
119: for (int i = args.length - 1; i >= 0; i--) {
120: cv.visitVarInsn(
121: args[i].getOpcode(Constants.ISTORE),
122: locals[i] = nextLocal(args[i].getSize()));
123: }
124: for (int i = 0; i < 5; i++) {
125: cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD),
126: locals[i]);
127: }
128: super
129: .visitMethodInsn(
130: Constants.INVOKESTATIC,
131: "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
132: "defineClass0Pre", DESC_HELPER);
133: cv.visitVarInsn(Constants.ASTORE, locals[1]);
134: cv.visitVarInsn(Constants.ALOAD, 0);
135: cv.visitVarInsn(Constants.ALOAD, locals[0]); // name
136: cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
137: cv.visitInsn(Constants.ICONST_0); // offset
138: cv.visitVarInsn(Constants.ALOAD, locals[1]);
139: cv.visitInsn(Constants.ARRAYLENGTH); // length
140: cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
141: for (int i = 5; i < args.length; i++) {
142: cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD),
143: locals[i]);
144: }
145: } else if (DEFINECLASS2_METHOD_NAME.equals(name)
146: && CLASSLOADER_CLASS_NAME.equals(owner)) {
147: Type[] args = Type.getArgumentTypes(desc);
148: if (args.length < 5
149: || !desc.startsWith(DESC_BYTEBUFFER_PREFIX)) {
150: throw new Error(
151: "non supported JDK, bytebuffer native call not supported: "
152: + desc);
153: }
154: // store all args in local variables
155: int[] locals = new int[args.length];
156: for (int i = args.length - 1; i >= 0; i--) {
157: cv.visitVarInsn(
158: args[i].getOpcode(Constants.ISTORE),
159: locals[i] = nextLocal(args[i].getSize()));
160: }
161: for (int i = 0; i < 5; i++) {
162: cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD),
163: locals[i]);
164: }
165: super
166: .visitMethodInsn(
167: Constants.INVOKESTATIC,
168: "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
169: "defineClass0Pre",
170: DESC_BYTEBUFFER_HELPER);
171: cv.visitVarInsn(Constants.ASTORE, locals[1]);
172: cv.visitVarInsn(Constants.ALOAD, 0);
173: cv.visitVarInsn(Constants.ALOAD, locals[0]); // name
174: cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
175: cv.visitInsn(Constants.ICONST_0); // offset
176: cv.visitVarInsn(Constants.ALOAD, locals[1]);
177: cv.visitMethodInsn(Constants.INVOKEVIRTUAL,
178: "Ljava/nio/Buffer;", "remaining", "()I");
179: cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
180: for (int i = 5; i < args.length; i++) {
181: cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD),
182: locals[i]);
183: }
184: // we should rebuild a new ByteBuffer...
185:
186: }
187: super .visitMethodInsn(opcode, owner, name, desc);
188: }
189: }
190:
191: /**
192: * @author Chris Nokleberg
193: */
194: private static class State {
195: Map locals = new HashMap();
196: int firstLocal;
197: int nextLocal;
198:
199: State(int access, Type[] args) {
200: nextLocal = ((Constants.ACC_STATIC & access) != 0) ? 0 : 1;
201: for (int i = 0; i < args.length; i++) {
202: nextLocal += args[i].getSize();
203: }
204: firstLocal = nextLocal;
205: }
206: }
207:
208: /**
209: * @author Chris Nokleberg
210: */
211: private static class IntRef {
212: int key;
213:
214: public boolean equals(Object o) {
215: return key == ((IntRef) o).key;
216: }
217:
218: public int hashCode() {
219: return key;
220: }
221: }
222:
223: /**
224: * @author Chris Nokleberg
225: */
226: private static class RemappingCodeVisitor extends CodeAdapter {
227: private State state;
228: private IntRef check = new IntRef();
229:
230: public RemappingCodeVisitor(CodeVisitor v, int access,
231: Type[] args) {
232: super (v);
233: state = new State(access, args);
234: }
235:
236: public RemappingCodeVisitor(RemappingCodeVisitor wrap) {
237: super (wrap.cv);
238: this .state = wrap.state;
239: }
240:
241: protected int nextLocal(int size) {
242: int var = state.nextLocal;
243: state.nextLocal += size;
244: return var;
245: }
246:
247: private int remap(int var, int size) {
248: if (var < state.firstLocal) {
249: return var;
250: }
251: check.key = (size == 2) ? ~var : var;
252: Integer value = (Integer) state.locals.get(check);
253: if (value == null) {
254: IntRef ref = new IntRef();
255: ref.key = check.key;
256: state.locals.put(ref, value = new Integer(
257: nextLocal(size)));
258: }
259: return value.intValue();
260: }
261:
262: public void visitIincInsn(int var, int increment) {
263: cv.visitIincInsn(remap(var, 1), increment);
264: }
265:
266: public void visitLocalVariable(String name, String desc,
267: Label start, Label end, int index) {
268: cv.visitLocalVariable(name, desc, start, end, remap(index,
269: 0));
270: }
271:
272: public void visitVarInsn(int opcode, int var) {
273: int size;
274: switch (opcode) {
275: case Constants.LLOAD:
276: case Constants.LSTORE:
277: case Constants.DLOAD:
278: case Constants.DSTORE:
279: size = 2;
280: break;
281: default:
282: size = 1;
283: }
284: cv.visitVarInsn(opcode, remap(var, size));
285: }
286:
287: public void visitMaxs(int maxStack, int maxLocals) {
288: cv.visitMaxs(0, 0);
289: }
290: }
291:
292: public static void main(String args[]) throws Exception {
293: ClassLoaderPreProcessor me = new ClassLoaderPreProcessorImpl();
294: InputStream is = ClassLoader.getSystemClassLoader().getParent()
295: .getResourceAsStream("java/lang/ClassLoader.class");
296: byte[] out = me.preProcess(ClassLoaderPatcher
297: .inputStreamToByteArray(is));
298: is.close();
299: File dir = new File("_boot/java/lang/");
300: dir.mkdirs();
301: OutputStream os = new FileOutputStream(
302: "_boot/java/lang/ClassLoader.class");
303: os.write(out);
304: os.close();
305: }
306: }
|