package com.infomancers.collections.yield.asm.delayed; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import java.util.Deque; import java.util.LinkedList; import java.util.Stack; /** * Copyright (c) 2007, Aviad Ben Dov *

* All rights reserved. *

* Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: *

* 1. Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * 3. Neither the name of Infomancers, Ltd. nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ public class DelayedMethodVisitor extends MethodAdapter { private static class MiniFrame { public Deque workQueue = new LinkedList(); int stackSize = 0; } private Stack miniFrames = new Stack(); /** * Constructs a new {@link org.objectweb.asm.MethodAdapter} object. * * @param mv the code visitor to which this adapter must delegate calls. */ public DelayedMethodVisitor(MethodVisitor mv) { super(mv); } ///////////////////////////////////////////////////////////////// // All the visitXXX methods are here. @Override public void visitCode() { super.visitCode(); startMiniFrame(); } @Override public void visitEnd() { if (!insideMiniFrame()) { throw new IllegalStateException("Should have the bounding frame by the end of code!"); } // converge all miniframes into one. while (miniFrames.size() > 1) { endMiniFrame(); } // emit the miniframe and end it. emitAll(mv); endMiniFrame(); super.visitEnd(); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.MAXS.createEmitter(-1, maxStack, maxLocals)); } else { super.visitMaxs(maxStack, maxLocals); } } @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.METHOD.createEmitter(opcode, owner, name, desc)); } else { super.visitMethodInsn(opcode, owner, name, desc); } } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.FIELD.createEmitter(opcode, owner, name, desc)); } else { super.visitFieldInsn(opcode, owner, name, desc); } } @Override public void visitInsn(final int opcode) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.INSN.createEmitter(opcode)); } else { super.visitInsn(opcode); } } @Override public void visitIntInsn(final int opcode, final int operand) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.INT.createEmitter(opcode, operand)); } else { super.visitIntInsn(opcode, operand); } } @Override public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.FRAME.createEmitter(-1, type, nLocal, local, nStack, stack)); } else { super.visitFrame(type, nLocal, local, nStack, stack); } } @Override public void visitVarInsn(final int opcode, final int var) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.VAR.createEmitter(opcode, var)); } else { super.visitVarInsn(opcode, var); } } @Override public void visitTypeInsn(final int opcode, final String desc) { if (insideMiniFrame()) throw new IllegalStateException(); super.visitTypeInsn(opcode, desc); //To change body of overridden methods use File | Settings | File Templates. } @Override public void visitJumpInsn(final int opcode, final Label label) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.JUMP.createEmitter(opcode, label)); } else { super.visitJumpInsn(opcode, label); } } @Override public void visitLdcInsn(final Object cst) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.LDC.createEmitter(-1, cst)); } else { super.visitLdcInsn(cst); } } @Override public void visitLabel(final Label label) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.LABEL.createEmitter(-1, label)); } else { super.visitLabel(label); } } @Override public void visitIincInsn(final int var, final int increment) { if (insideMiniFrame()) throw new IllegalStateException(); super.visitIincInsn(var, increment); //To change body of overridden methods use File | Settings | File Templates. } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label labels[]) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.TABLESWITCH.createEmitter(-1, min, max, dflt, labels)); } else { super.visitTableSwitchInsn(min, max, dflt, labels); //To change body of overridden methods use File | Settings | File Templates. } } @Override public void visitLookupSwitchInsn(final Label dflt, final int keys[], final Label labels[]) { if (insideMiniFrame()) throw new IllegalStateException(); super.visitLookupSwitchInsn(dflt, keys, labels); //To change body of overridden methods use File | Settings | File Templates. } @Override public void visitMultiANewArrayInsn(final String desc, final int dims) { if (insideMiniFrame()) throw new IllegalStateException(); super.visitMultiANewArrayInsn(desc, dims); //To change body of overridden methods use File | Settings | File Templates. } @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { if (insideMiniFrame()) throw new IllegalStateException(); super.visitTryCatchBlock(start, end, handler, type); //To change body of overridden methods use File | Settings | File Templates. } @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.LOCALVAR.createEmitter(-1, name, desc, signature, start, end, index)); } else { super.visitLocalVariable(name, desc, signature, start, end, index); //To change body of overridden methods use File | Settings | File Templates. } } @Override public void visitLineNumber(final int line, final Label start) { if (insideMiniFrame()) { delayInsn(DelayedInstruction.LINE.createEmitter(-1, line, start)); } else { super.visitLineNumber(line, start); } } ///////////////////////////////////////////////////////////////// // Mini-frame management and manipulations here. protected final void startMiniFrame() { startMiniFrame(insideMiniFrame() ? getCurrentMiniFrame().stackSize : 0); } private final void startMiniFrame(int initialStack) { miniFrames.push(new MiniFrame()); getCurrentMiniFrame().stackSize = initialStack; } private void delayInsn(DelayedInstructionEmitter emitter) { getCurrentMiniFrame().workQueue.offer(emitter); handleStack(emitter); } protected final void delayPriorityInsn(DelayedInstructionEmitter emitter) { getCurrentMiniFrame().workQueue.offerFirst(emitter); handleStack(emitter); } private void handleStack(DelayedInstructionEmitter emitter) { final int stackChange = emitter.pushAmount() - emitter.popAmount(); getCurrentMiniFrame().stackSize += stackChange; boolean changedStackSize = stackChange != 0; if (getCurrentMiniFrame().stackSize < 0) { throw new IllegalStateException("Mini frame's stack <= 0 - must be a missing push"); } else if (changedStackSize && getCurrentMiniFrame().stackSize == 0) { handleEmptyStack(); } } protected final void emit(MethodVisitor mv, int count) { // pops the current mini frame, and emit the instructions // into the previous mini-frame, unless this is the last // mini frame available in which case, a real emition is made. if (count <= 0) { throw new IllegalArgumentException("count <= 0"); } MiniFrame miniFrame = miniFrames.pop(); while (count-- > 0) { if (insideMiniFrame()) { getCurrentMiniFrame().workQueue.offer(miniFrame.workQueue.poll()); } else { miniFrame.workQueue.poll().emit(mv); } } miniFrames.push(miniFrame); } protected final void emitAll(MethodVisitor mv) { emit(mv, getCurrentMiniFrame().workQueue.size()); } protected void handleEmptyStack() { } protected final void endMiniFrame() { MiniFrame previous = miniFrames.pop(); if (insideMiniFrame()) { // the stack size of the mini frame previous to the one being popped now was added // when it was created. getCurrentMiniFrame().stackSize += previous.stackSize - getCurrentMiniFrame().stackSize; while (previous.workQueue.size() > 0) { getCurrentMiniFrame().workQueue.offer(previous.workQueue.poll()); } } } protected final boolean insideMiniFrame() { return !miniFrames.isEmpty(); } private MiniFrame getCurrentMiniFrame() { return insideMiniFrame() ? miniFrames.peek() : null; } }