View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2006-2007 Bartek Teodorczyk <barteo@barteo.net>
4    *  Copyright (C) 2006-2007 Vlad Skarzhevskyy
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or (at your option) any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   *  @version $Id: ChangeCallsMethodVisitor.java 1140 2007-03-30 00:10:36Z vlads $
21   */
22  package org.microemu.app.classloader;
23  
24  import java.util.HashMap;
25  
26  import org.microemu.Injected;
27  import org.microemu.app.util.MIDletThread;
28  import org.microemu.app.util.MIDletTimer;
29  import org.objectweb.asm.Label;
30  import org.objectweb.asm.MethodAdapter;
31  import org.objectweb.asm.MethodVisitor;
32  import org.objectweb.asm.Opcodes;
33  
34  /**
35   * @author vlads
36   *
37   */
38  public class ChangeCallsMethodVisitor extends MethodAdapter implements Opcodes {
39  
40  	private static final String INJECTED_CLASS = codeName(Injected.class);
41  	
42  	static String NEW_SYSTEM_OUT_CLASS = INJECTED_CLASS;
43  	
44  	static String NEW_SYSTEM_PROPERTIES_CLASS = INJECTED_CLASS;
45  	
46  	static String NEW_RESOURCE_LOADER_CLASS = INJECTED_CLASS;
47  	
48  	private HashMap catchInfo;
49  	
50  	private InstrumentationConfig config;
51  	
52  	private static class CatchInformation {
53  		
54  		Label label; 
55  		
56  		String type;
57  
58  		public CatchInformation(String type) {
59  			this.label = new Label();
60  			this.type = type;
61  		}
62  	}
63  	
64  	public ChangeCallsMethodVisitor(MethodVisitor mv, InstrumentationConfig config) {
65  		super(mv);
66  		this.config = config;
67  	}
68  
69  	public static String codeName(Class klass) {
70  		return klass.getName().replace('.', '/');
71  	}
72  
73      public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
74  		switch (opcode) {
75  		case GETSTATIC:
76  			if ((name.equals("out")) && (owner.equals("java/lang/System"))) {
77  				//System.out.println("owner " + owner + " name " + name + " desc " + desc);
78  				// GETSTATIC System.out : PrintStream
79  				mv.visitFieldInsn(opcode, NEW_SYSTEM_OUT_CLASS, name, desc);
80  				return;
81  			}
82  			if ((name.equals("err")) && (owner.equals("java/lang/System"))) {
83  				//System.out.println("owner " + owner + " name " + name + " desc " + desc);
84  				// GETSTATIC System.out : PrintStream
85  				mv.visitFieldInsn(opcode, NEW_SYSTEM_OUT_CLASS, name, desc);
86  				return;
87  			}
88  			break;
89  
90  		}
91  		mv.visitFieldInsn(opcode, owner, name, desc);
92  	}
93      
94  	public void visitMethodInsn(int opcode, String owner, String name, String desc) {
95  		switch (opcode) {
96  		case INVOKESTATIC:
97  			//System.out.println("Method owner " + owner + " name " + name + " desc " + desc);
98  			if ((name.equals("getProperty")) && (owner.equals("java/lang/System"))) {
99  				// INVOKESTATIC
100                 // java/lang/System.getProperty(Ljava/lang/String;)Ljava/lang/String;
101 				mv.visitMethodInsn(opcode, NEW_SYSTEM_PROPERTIES_CLASS, name, desc);
102 				return;
103 			}
104 			break;
105 		case INVOKEVIRTUAL:
106 			if ((name.equals("getResourceAsStream")) && (owner.equals("java/lang/Class"))) {
107 				// INVOKEVIRTUAL
108 		        // java/lang/Class.getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;
109 				// "org/microemu/ResourceLoader", "getResourceAsStream", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/io/InputStream;");
110 				mv.visitMethodInsn(INVOKESTATIC, NEW_RESOURCE_LOADER_CLASS, name, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/io/InputStream;");
111 				return;
112 			} else if ((name.equals("printStackTrace")) && (owner.equals("java/lang/Throwable"))) {
113 				// INVOKEVIRTUAL java/lang/Throwable.printStackTrace()V
114 				mv.visitMethodInsn(INVOKESTATIC, INJECTED_CLASS, name, "(Ljava/lang/Throwable;)V");
115 				return;
116 			}
117 			break;
118 		case INVOKESPECIAL:
119 			if  ((config.isEnhanceThreadCreation()) && (name.equals("<init>"))) {
120 				if (owner.equals("java/util/Timer")) {
121 					owner = codeName(MIDletTimer.class);
122 				} else if (owner.equals("java/lang/Thread")) {
123 					owner = codeName(MIDletThread.class);
124 				}
125 			}
126 			break;
127 		}
128 
129 		mv.visitMethodInsn(opcode, owner, name, desc);
130 	}
131 	
132     public void visitTypeInsn(final int opcode, String desc) {
133     	if ((opcode == NEW) && (config.isEnhanceThreadCreation())) {
134     		if ("java/util/Timer".equals(desc)) {
135     			desc = codeName(MIDletTimer.class);
136     		} else if ("java/lang/Thread".equals(desc)) {
137     			desc = codeName(MIDletThread.class);
138     		}
139     	} 
140     	mv.visitTypeInsn(opcode, desc);
141     }
142     
143     public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
144     	if (config.isEnhanceCatchBlock() && type != null) {
145     		if (catchInfo == null) {
146     			catchInfo = new HashMap(); 
147     		}
148     		CatchInformation newHandler = (CatchInformation)catchInfo.get(handler);
149     		if (newHandler == null) {
150     			newHandler = new CatchInformation(type);
151     			catchInfo.put(handler, newHandler);
152     		}
153     		mv.visitTryCatchBlock(start, end, newHandler.label, type);
154     	} else {
155     		mv.visitTryCatchBlock(start, end, handler, type);
156     	}
157 	}
158     
159     //TODO make this work for gMaps case
160     public void visitLabel(Label label) {
161     	if (config.isEnhanceCatchBlock() && catchInfo != null) {
162     		CatchInformation newHandler = (CatchInformation)catchInfo.get(label);
163     		if (newHandler != null) {
164     			mv.visitLabel(newHandler.label);
165     			// no push, just use current Throwable in stack
166     			mv.visitMethodInsn(INVOKESTATIC, INJECTED_CLASS, "handleCatchThrowable", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
167     			// stack contains Throwable, just verify that it is right type for this handler
168         		mv.visitTypeInsn(CHECKCAST, newHandler.type);
169     		}	
170     	}
171     	mv.visitLabel(label);
172     }
173 	
174 }