001: /***
002: * Retrotranslator: a Java bytecode transformer that translates Java classes
003: * compiled with JDK 5.0 into classes that can be run on JVM 1.4.
004: *
005: * Copyright (c) 2005 - 2008 Taras Puchko
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * 3. Neither the name of the copyright holders nor the names of its
017: * contributors may be used to endorse or promote products derived from
018: * this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
024: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
025: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
026: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
027: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
028: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
029: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
030: * THE POSSIBILITY OF SUCH DAMAGE.
031: */package net.sf.retrotranslator.transformer;
032:
033: import edu.emory.mathcs.backport.java.util.concurrent.*;
034: import edu.emory.mathcs.backport.java.util.concurrent.locks.*;
035: import java.lang.ref.*;
036: import java.util.Collections;
037: import net.sf.retrotranslator.runtime.asm.*;
038: import static net.sf.retrotranslator.runtime.asm.Opcodes.*;
039: import net.sf.retrotranslator.runtime.impl.RuntimeTools;
040:
041: /**
042: * @author Taras Puchko
043: */
044: class SpecificReplacementVisitor extends ClassAdapter {
045:
046: private static final String THREAD_NAME = Type
047: .getInternalName(Thread.class);
048: private static final String DELAY_QUEUE_NAME = Type
049: .getInternalName(DelayQueue.class);
050: private static final String SOFT_REFERENCE_NAME = Type
051: .getInternalName(SoftReference.class);
052: private static final String WEAK_REFERENCE_NAME = Type
053: .getInternalName(WeakReference.class);
054: private static final String COLLECTIONS_NAME = Type
055: .getInternalName(Collections.class);
056: private static final String REENTRANT_READ_WRITE_LOCK_NAME = Type
057: .getInternalName(ReentrantReadWriteLock.class);
058: private static final MemberKey UNCAUGHT_EXCEPTION_HANDLER_KEY = new MemberKey(
059: true, "handleUncaughtException", TransformerTools
060: .descriptor(void.class, Throwable.class));
061:
062: private final ClassVersion target;
063: private final ReplacementLocator locator;
064: private final OperationMode mode;
065: private MemberReplacement uncaughtExceptionHandler;
066:
067: public SpecificReplacementVisitor(ClassVisitor visitor,
068: ClassVersion target, ReplacementLocator locator,
069: OperationMode mode) {
070: super (visitor);
071: this .target = target;
072: this .locator = locator;
073: this .mode = mode;
074: }
075:
076: public void visit(int version, int access, String name,
077: String signature, String super Name, String[] interfaces) {
078: super .visit(version, access, name, signature, super Name,
079: interfaces);
080: uncaughtExceptionHandler = getUncaughtExceptionHandler(super Name);
081: }
082:
083: public MethodVisitor visitMethod(int access, String name,
084: String desc, String signature, String[] exceptions) {
085: MethodVisitor visitor = super .visitMethod(access, name, desc,
086: signature, exceptions);
087: if (visitor == null) {
088: return null;
089: }
090: visitor = new SpecificReplacementMethodVisitor(visitor);
091: if (uncaughtExceptionHandler != null && name.equals("run")
092: && desc.equals(TransformerTools.descriptor(void.class))) {
093: visitor = new RunMethodVisitor(visitor);
094: }
095: return visitor;
096: }
097:
098: private class RunMethodVisitor extends MethodAdapter {
099:
100: private final Label start = new Label();
101:
102: public RunMethodVisitor(MethodVisitor visitor) {
103: super (visitor);
104: }
105:
106: public void visitCode() {
107: mv.visitLabel(start);
108: super .visitCode();
109: }
110:
111: public void visitMaxs(final int maxStack, final int maxLocals) {
112: Label end = new Label();
113: mv.visitLabel(end);
114: mv.visitMethodInsn(INVOKESTATIC, uncaughtExceptionHandler
115: .getOwner(), uncaughtExceptionHandler.getName(),
116: uncaughtExceptionHandler.getDesc());
117: mv.visitInsn(RETURN);
118: mv.visitTryCatchBlock(start, end, end, Type
119: .getInternalName(Throwable.class));
120: super .visitMaxs(maxStack, maxLocals);
121: }
122: }
123:
124: private class SpecificReplacementMethodVisitor extends
125: MethodAdapter {
126:
127: public SpecificReplacementMethodVisitor(MethodVisitor visitor) {
128: super (visitor);
129: }
130:
131: public void visitMethodInsn(int opcode, String owner,
132: String name, String desc) {
133: if (fixReference(opcode, owner, name, desc)) {
134: return;
135: }
136: if (fixDelayQueue(opcode, owner, name, desc)) {
137: return;
138: }
139: if (fixLock(opcode, owner, name, desc)) {
140: return;
141: }
142: if (fixCollections(opcode, owner, name, desc)) {
143: return;
144: }
145: super .visitMethodInsn(opcode, owner, name, desc);
146: }
147:
148: private boolean fixReference(int opcode, String owner,
149: String name, String desc) {
150: if (opcode != INVOKESPECIAL
151: || !name.equals(RuntimeTools.CONSTRUCTOR_NAME)) {
152: return false;
153: }
154: if (owner.equals(SOFT_REFERENCE_NAME)) {
155: if (!mode
156: .isSupportedFeature("SoftReference.NullReferenceQueue")) {
157: return false;
158: }
159: } else if (owner.equals(WEAK_REFERENCE_NAME)) {
160: if (!mode
161: .isSupportedFeature("WeakReference.NullReferenceQueue")) {
162: return false;
163: }
164: } else {
165: return false;
166: }
167: if (!target.isBefore(ClassVersion.VERSION_15)) {
168: return false;
169: }
170: if (!desc.equals(TransformerTools.descriptor(void.class,
171: Object.class, ReferenceQueue.class))) {
172: return false;
173: }
174: Label notNullLabel = new Label();
175: Label continueLabel = new Label();
176: mv.visitInsn(DUP);
177: mv.visitJumpInsn(IFNONNULL, notNullLabel);
178: mv.visitInsn(POP);
179: mv.visitMethodInsn(INVOKESPECIAL, owner,
180: RuntimeTools.CONSTRUCTOR_NAME, TransformerTools
181: .descriptor(void.class, Object.class));
182: mv.visitJumpInsn(GOTO, continueLabel);
183: mv.visitLabel(notNullLabel);
184: mv.visitMethodInsn(INVOKESPECIAL, owner,
185: RuntimeTools.CONSTRUCTOR_NAME, desc);
186: mv.visitLabel(continueLabel);
187: return true;
188: }
189:
190: private boolean fixDelayQueue(int opcode, String owner,
191: String name, String desc) {
192: if (!owner.equals(DELAY_QUEUE_NAME)) {
193: return false;
194: }
195: String fixedDesc = new NameTranslator() {
196: protected String typeName(String s) {
197: return Type.getInternalName(Delayed.class)
198: .equals(s) ? Type
199: .getInternalName(Object.class) : s;
200: }
201: }.methodDescriptor(desc);
202: mv.visitMethodInsn(opcode, owner, name, fixedDesc);
203: Type returnType = Type.getReturnType(desc);
204: if (returnType.equals(Type.getType(Delayed.class))) {
205: mv.visitTypeInsn(CHECKCAST, returnType
206: .getInternalName());
207: }
208: return true;
209: }
210:
211: private boolean fixLock(int opcode, String owner, String name,
212: String desc) {
213: if (!owner.equals(REENTRANT_READ_WRITE_LOCK_NAME)) {
214: return false;
215: }
216: Type returnType = Type.getReturnType(desc);
217: if (returnType.equals(Type
218: .getType(ReentrantReadWriteLock.ReadLock.class))
219: || returnType
220: .equals(Type
221: .getType(ReentrantReadWriteLock.WriteLock.class))) {
222: desc = Type.getMethodDescriptor(Type
223: .getType(Lock.class), Type
224: .getArgumentTypes(desc));
225: mv.visitMethodInsn(opcode, owner, name, desc);
226: mv.visitTypeInsn(CHECKCAST, returnType
227: .getInternalName());
228: return true;
229: }
230: return false;
231: }
232:
233: private boolean fixCollections(int opcode, String owner,
234: String name, String desc) {
235: if (opcode != INVOKESTATIC
236: || !owner.equals(COLLECTIONS_NAME)) {
237: return false;
238: }
239: String field = getReplacementField(name);
240: if (field == null) {
241: return false;
242: }
243: mv.visitFieldInsn(GETSTATIC, COLLECTIONS_NAME, field, Type
244: .getReturnType(desc).toString());
245: return true;
246: }
247: }
248:
249: private String getReplacementField(String methodName) {
250: if (!target.isBefore(ClassVersion.VERSION_15)) {
251: return null;
252: }
253: if (target.isBefore(ClassVersion.VERSION_12)) {
254: return null;
255: }
256: if (methodName.equals("emptyList")) {
257: return "EMPTY_LIST";
258: }
259: if (methodName.equals("emptySet")) {
260: return "EMPTY_SET";
261: }
262: if (target.isBefore(ClassVersion.VERSION_13)) {
263: return null;
264: }
265: if (methodName.equals("emptyMap")) {
266: return "EMPTY_MAP";
267: }
268: return null;
269: }
270:
271: private MemberReplacement getUncaughtExceptionHandler(
272: String super Name) {
273: if (!THREAD_NAME.equals(super Name)) {
274: return null;
275: }
276: if (!mode
277: .isSupportedFeature("Thread.setDefaultUncaughtExceptionHandler")
278: && !mode
279: .isSupportedFeature("Thread.setUncaughtExceptionHandler")) {
280: return null;
281: }
282: ClassReplacement threadReplacement = locator
283: .getReplacement(THREAD_NAME);
284: if (threadReplacement == null) {
285: return null;
286: }
287: return threadReplacement.getMethodReplacements().get(
288: UNCAUGHT_EXCEPTION_HANDLER_KEY);
289: }
290:
291: }
|