View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2006-2007 Bartek Teodorczyk <barteo@barteo.net>
4    *  Copyright (C) 2006-2007 Vlad Skarzhevskyy
5    *
6    *  It is licensed under the following two licenses as alternatives:
7    *    1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
8    *    2. Apache License (the "AL") Version 2.0
9    *
10   *  You may not use this file except in compliance with at least one of
11   *  the above two licenses.
12   *
13   *  You may obtain a copy of the LGPL at
14   *      http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
15   *
16   *  You may obtain a copy of the AL at
17   *      http://www.apache.org/licenses/LICENSE-2.0
18   *
19   *  Unless required by applicable law or agreed to in writing, software
20   *  distributed under the License is distributed on an "AS IS" BASIS,
21   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22   *  See the LGPL or the AL for the specific language governing permissions and
23   *  limitations.
24   *
25   *  @version $Id: ChangeCallsMethodVisitor.java 1605 2008-02-25 21:07:14Z barteo $
26   */
27  package org.microemu.app.classloader;
28  
29  import java.util.HashMap;
30  
31  import org.microemu.Injected;
32  import org.microemu.app.util.MIDletThread;
33  import org.microemu.app.util.MIDletTimer;
34  import org.objectweb.asm.Label;
35  import org.objectweb.asm.MethodAdapter;
36  import org.objectweb.asm.MethodVisitor;
37  import org.objectweb.asm.Opcodes;
38  
39  /**
40   * @author vlads
41   *
42   */
43  public class ChangeCallsMethodVisitor extends MethodAdapter implements Opcodes {
44  
45  	private static final String INJECTED_CLASS = codeName(Injected.class);
46  	
47  	static String NEW_SYSTEM_OUT_CLASS = INJECTED_CLASS;
48  	
49  	static String NEW_SYSTEM_PROPERTIES_CLASS = INJECTED_CLASS;
50  	
51  	static String NEW_RESOURCE_LOADER_CLASS = INJECTED_CLASS;
52  	
53  	private HashMap catchInfo;
54  	
55  	private InstrumentationConfig config;
56  	
57  	private static class CatchInformation {
58  		
59  		Label label; 
60  		
61  		String type;
62  
63  		public CatchInformation(String type) {
64  			this.label = new Label();
65  			this.type = type;
66  		}
67  	}
68  	
69  	public ChangeCallsMethodVisitor(MethodVisitor mv, InstrumentationConfig config) {
70  		super(mv);
71  		this.config = config;
72  	}
73  
74  	public static String codeName(Class klass) {
75  		return klass.getName().replace('.', '/');
76  	}
77  
78      public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
79  		switch (opcode) {
80  		case GETSTATIC:
81  			if ((name.equals("out")) && (owner.equals("java/lang/System"))) {
82  				//System.out.println("owner " + owner + " name " + name + " desc " + desc);
83  				// GETSTATIC System.out : PrintStream
84  				mv.visitFieldInsn(opcode, NEW_SYSTEM_OUT_CLASS, name, desc);
85  				return;
86  			}
87  			if ((name.equals("err")) && (owner.equals("java/lang/System"))) {
88  				//System.out.println("owner " + owner + " name " + name + " desc " + desc);
89  				// GETSTATIC System.out : PrintStream
90  				mv.visitFieldInsn(opcode, NEW_SYSTEM_OUT_CLASS, name, desc);
91  				return;
92  			}
93  			break;
94  
95  		}
96  		mv.visitFieldInsn(opcode, owner, name, desc);
97  	}
98      
99  	public void visitMethodInsn(int opcode, String owner, String name, String desc) {
100 		switch (opcode) {
101 		case INVOKESTATIC:
102 			//System.out.println("Method owner " + owner + " name " + name + " desc " + desc);
103 			if ((name.equals("getProperty")) && (owner.equals("java/lang/System"))) {
104 				// INVOKESTATIC
105                 // java/lang/System.getProperty(Ljava/lang/String;)Ljava/lang/String;
106 				mv.visitMethodInsn(opcode, NEW_SYSTEM_PROPERTIES_CLASS, name, desc);
107 				return;
108 			}
109 			break;
110 		case INVOKEVIRTUAL:
111 			if ((name.equals("getResourceAsStream")) && (owner.equals("java/lang/Class"))) {
112 				// INVOKEVIRTUAL
113 		        // java/lang/Class.getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;
114 				// "org/microemu/ResourceLoader", "getResourceAsStream", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/io/InputStream;");
115 				mv.visitMethodInsn(INVOKESTATIC, NEW_RESOURCE_LOADER_CLASS, name, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/io/InputStream;");
116 				return;
117 			} else if ((name.equals("printStackTrace")) && (owner.equals("java/lang/Throwable"))) {
118 				// INVOKEVIRTUAL java/lang/Throwable.printStackTrace()V
119 				mv.visitMethodInsn(INVOKESTATIC, INJECTED_CLASS, name, "(Ljava/lang/Throwable;)V");
120 				return;
121 			}
122 			break;
123 		case INVOKESPECIAL:
124 			if  ((config.isEnhanceThreadCreation()) && (name.equals("<init>"))) {
125 				if (owner.equals("java/util/Timer")) {
126 					owner = codeName(MIDletTimer.class);
127 				} else if (owner.equals("java/lang/Thread")) {
128 					owner = codeName(MIDletThread.class);
129 				}
130 			}
131 			break;
132 		}
133 
134 		mv.visitMethodInsn(opcode, owner, name, desc);
135 	}
136 	
137     public void visitTypeInsn(final int opcode, String desc) {
138     	if ((opcode == NEW) && (config.isEnhanceThreadCreation())) {
139     		if ("java/util/Timer".equals(desc)) {
140     			desc = codeName(MIDletTimer.class);
141     		} else if ("java/lang/Thread".equals(desc)) {
142     			desc = codeName(MIDletThread.class);
143     		}
144     	} 
145     	mv.visitTypeInsn(opcode, desc);
146     }
147     
148     public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
149     	if (config.isEnhanceCatchBlock() && type != null) {
150     		if (catchInfo == null) {
151     			catchInfo = new HashMap(); 
152     		}
153     		CatchInformation newHandler = (CatchInformation)catchInfo.get(handler);
154     		if (newHandler == null) {
155     			newHandler = new CatchInformation(type);
156     			catchInfo.put(handler, newHandler);
157     		}
158     		mv.visitTryCatchBlock(start, end, newHandler.label, type);
159     	} else {
160     		mv.visitTryCatchBlock(start, end, handler, type);
161     	}
162 	}
163     
164     //TODO make this work for gMaps case
165     public void visitLabel(Label label) {
166     	if (config.isEnhanceCatchBlock() && catchInfo != null) {
167     		CatchInformation newHandler = (CatchInformation)catchInfo.get(label);
168     		if (newHandler != null) {
169     			mv.visitLabel(newHandler.label);
170     			// no push, just use current Throwable in stack
171     			mv.visitMethodInsn(INVOKESTATIC, INJECTED_CLASS, "handleCatchThrowable", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
172     			// stack contains Throwable, just verify that it is right type for this handler
173         		mv.visitTypeInsn(CHECKCAST, newHandler.type);
174     		}	
175     	}
176     	mv.visitLabel(label);
177     }
178 	
179 }