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 java.util.*;
034: import net.sf.retrotranslator.runtime.asm.*;
035: import static net.sf.retrotranslator.runtime.asm.Opcodes.*;
036:
037: /**
038: * @author Taras Puchko
039: */
040: public class SynchronizedBlockVisitor extends ClassAdapter {
041:
042: public SynchronizedBlockVisitor(ClassVisitor visitor) {
043: super (visitor);
044: }
045:
046: public MethodVisitor visitMethod(int access, String name,
047: String desc, String signature, String[] exceptions) {
048: MethodVisitor methodVisitor = super .visitMethod(access, name,
049: desc, signature, exceptions);
050: return methodVisitor == null ? null
051: : new SynchronizedBlockMethodVisitor(methodVisitor);
052: }
053:
054: private static class TryCatchBlock {
055: public final Label start;
056: public final Label end;
057: public Label handler;
058: public final String type;
059: public boolean monitorBlock;
060:
061: public TryCatchBlock(Label start, Label end, Label handler,
062: String type) {
063: this .start = start;
064: this .end = end;
065: this .handler = handler;
066: this .type = type;
067: }
068:
069: }
070:
071: private enum Step {
072: BLOCK_STARTED, EXCEPTION_STORED, MONITOR_LOADED, MONITOR_EXITED, BLOCK_FINISHED, EXCEPTION_LOADED
073: }
074:
075: private static class SynchronizedBlockMethodVisitor extends
076: AbstractMethodVisitor {
077:
078: private final List<TryCatchBlock> blocks = new ArrayList<TryCatchBlock>();
079: private Step step;
080: private Label startLabel;
081: private Label endLabel;
082: private Integer exceptionVar;
083: private int monitorVar;
084:
085: public SynchronizedBlockMethodVisitor(MethodVisitor visitor) {
086: super (visitor);
087: }
088:
089: public void visitTryCatchBlock(Label start, Label end,
090: Label handler, String type) {
091: blocks.add(new TryCatchBlock(start, end, handler, type));
092: }
093:
094: public void visitEnd() {
095: for (TryCatchBlock block : blocks) {
096: mv.visitTryCatchBlock(block.start, block.end,
097: block.handler, block.type);
098: }
099: mv.visitEnd();
100: }
101:
102: protected void flush() {
103: step = null;
104: }
105:
106: public void visitLabel(Label label) {
107: mv.visitLabel(label);
108: if (step == Step.MONITOR_EXITED) {
109: endLabel = label;
110: step = Step.BLOCK_FINISHED;
111: } else {
112: startLabel = label;
113: step = Step.BLOCK_STARTED;
114: }
115: }
116:
117: public void visitVarInsn(int opcode, int var) {
118: mv.visitVarInsn(opcode, var);
119: if (step == Step.BLOCK_STARTED && opcode == ASTORE) {
120: exceptionVar = var;
121: step = Step.EXCEPTION_STORED;
122: } else if (step == Step.BLOCK_STARTED && opcode == ALOAD) {
123: exceptionVar = null;
124: monitorVar = var;
125: step = Step.MONITOR_LOADED;
126: } else if (step == Step.EXCEPTION_STORED && opcode == ALOAD) {
127: monitorVar = var;
128: step = Step.MONITOR_LOADED;
129: } else if (step == Step.BLOCK_FINISHED && opcode == ALOAD
130: && var == exceptionVar) {
131: step = Step.EXCEPTION_LOADED;
132: } else {
133: step = null;
134: }
135: }
136:
137: public void visitInsn(int opcode) {
138: mv.visitInsn(opcode);
139: if (step == Step.MONITOR_LOADED && opcode == MONITOREXIT) {
140: step = Step.MONITOR_EXITED;
141: } else if (step == Step.EXCEPTION_LOADED
142: && opcode == ATHROW) {
143: checkBlock();
144: step = null;
145: } else if (step == Step.BLOCK_FINISHED && opcode == ATHROW
146: && exceptionVar == null) {
147: checkBlock();
148: step = null;
149: } else {
150: step = null;
151: }
152: }
153:
154: private void checkBlock() {
155: int blockIndex = findBlockIndex();
156: if (blockIndex < 0) {
157: return;
158: }
159: TryCatchBlock block = blocks.get(blockIndex);
160: block.monitorBlock = true;
161: if (block.handler != block.start) {
162: return;
163: }
164: if (blockIndex > 0
165: && blocks.get(blockIndex - 1).monitorBlock) {
166: return;
167: }
168: Label startLabel = new Label();
169: Label endLabel = new Label();
170: block.handler = startLabel;
171: mv.visitLabel(startLabel);
172: if (exceptionVar != null) {
173: mv.visitVarInsn(ASTORE, exceptionVar);
174: }
175: mv.visitVarInsn(ALOAD, monitorVar);
176: mv.visitInsn(MONITOREXIT);
177: mv.visitLabel(endLabel);
178: if (exceptionVar != null) {
179: mv.visitVarInsn(ALOAD, exceptionVar);
180: }
181: mv.visitInsn(ATHROW);
182: blocks.add(blockIndex + 1, new TryCatchBlock(startLabel,
183: endLabel, startLabel, null));
184: }
185:
186: private int findBlockIndex() {
187: for (int i = 0; i < blocks.size(); i++) {
188: TryCatchBlock block = blocks.get(i);
189: if (block.start == startLabel && block.end == endLabel
190: && block.type == null) {
191: return i;
192: }
193: }
194: return -1;
195: }
196: }
197:
198: }
|