/* * JCarder -- cards Java programs to keep threads disentangled * * Copyright (C) 2006-2007 Enea AB * Copyright (C) 2007 Ulrik Svensson * Copyright (C) 2007 Joel Rosdahl * * This program is made available under the GNU GPL version 2, with a special * exception for linking with JUnit. See the accompanying file LICENSE.txt for * details. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ package com.enea.jcarder.agent.instrument; import net.jcip.annotations.NotThreadSafe; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * This Method Adapter simulates a synchronized declaration on a method by * adding a MonitorEnter and MonitorExits. */ @NotThreadSafe class SimulateMethodSyncMethodAdapter extends MethodAdapter { private final String mClassName; private final boolean mIsStatic; private final Label mTryLabel = new Label(); private final Label mFinallyLabel = new Label(); SimulateMethodSyncMethodAdapter(final MethodVisitor visitor, final String className, final boolean isStatic) { super(visitor); mClassName = className; mIsStatic = isStatic; } public void visitCode() { super.visitCode(); /* * This MethodAdapter will only be applied to synchronized methods, and * constructors are not allowed to be declared synchronized. Therefore * we can add instructions at the beginning of the method and do not * have to find the place after the initial constructor byte codes: * * ALOAD 0 : this * INVOKESPECIAL Object.<init>() : void * */ putMonitorObjectReferenceOnStack(); mv.visitInsn(Opcodes.MONITORENTER); mv.visitLabel(mTryLabel); } /** * This method is called just after the last code in the method. */ public void visitMaxs(int arg0, int arg1) { /* * This finally block is needed in order to exit the monitor even when * the method exits by throwing an exception. */ mv.visitLabel(mFinallyLabel); putMonitorObjectReferenceOnStack(); mv.visitInsn(Opcodes.MONITOREXIT); mv.visitInsn(Opcodes.ATHROW); mv.visitTryCatchBlock(mTryLabel, mFinallyLabel, mFinallyLabel, null); super.visitMaxs(arg0, arg1); } public void visitInsn(int inst) { switch (inst) { case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: case Opcodes.RETURN: putMonitorObjectReferenceOnStack(); mv.visitInsn(Opcodes.MONITOREXIT); break; default: // Do nothing. } super.visitInsn(inst); } private void putMonitorObjectReferenceOnStack() { if (mIsStatic) { InstrumentationUtilities.pushClassReferenceToStack(mv, mClassName); } else { mv.visitVarInsn(Opcodes.ALOAD, 0); } } }