/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat and individual contributors * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.jokre.transformer; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import static org.jboss.jokre.transformer.MapAdapterConstants.*; /** * Adapter used to transform implementors of Map.put into a potentially more efficient implementation */ public class MapPutImplementorAdapter extends ClassAdapter { private String className; String[] exceptions; String[] asyncExceptions; String signature; String asyncSignature; public MapPutImplementorAdapter(ClassVisitor cv, ClassLoader loader, String className) { super(cv); this.className = className; this.exceptions = null; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // add the extra interface which we want this class to implement final int length = interfaces.length; String[] newInterfaces = new String[length +1]; for (int i = 0; i < length; i++) { newInterfaces[i]= interfaces[i]; } newInterfaces[length] = MapAdapterConstants.CLASS_NON_RETURN_MAP; super.visit(version, access, name, signature, superName, newInterfaces); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // rename put method to be put$originalSlowPath and make it private if (name.equals(SET_METHOD_NAME) && desc.equals(SET_METHOD_DESC)) { this.asyncExceptions = exceptions; this.asyncSignature = signature; } else if (name.equals(PUT_METHOD_NAME) && desc.equals(PUT_METHOD_DESC)) { this.exceptions = exceptions; this.signature = signature; // rename this as a private method so we can reuse the implementation name = PUT_METHOD_ORIGINAL_SLOW_PATH_NAME; access |= Opcodes.ACC_PRIVATE; access &= ~Opcodes.ACC_PUBLIC; } MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return mv; } public void visitEnd() { int access = Opcodes.ACC_PUBLIC; // generate rewritten put method which is instrumented and calls the original slowpath MethodVisitor mv = super.visitMethod(access, PUT_METHOD_NAME, PUT_METHOD_DESC, signature, exceptions); mv.visitCode(); mv.visitMethodInsn(Opcodes.INVOKESTATIC, CLASS_JOKRE, NOTIFY_MAP_PUT_METHOD_NAME, NOTIFY_MAP_PUT_METHOD_DESC); mv.visitInsn(Opcodes.POP); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, PUT_METHOD_ORIGINAL_SLOW_PATH_NAME, PUT_METHOD_DESC); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(3, 3); mv.visitEnd(); // generate put$fastPath which calls putAsync mv = super.visitMethod(access, PUT_METHOD_FAST_PATH_NAME, SET_METHOD_DESC, asyncSignature, exceptions); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, SET_METHOD_NAME, SET_METHOD_DESC); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(3, 3); mv.visitEnd(); // generate put$alternativeSlowPath which is not instrumented and calls the original slowpath mv = super.visitMethod(access, PUT_METHOD_ALTERNATIVE_SLOW_PATH_NAME, PUT_METHOD_DESC, signature, exceptions); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, PUT_METHOD_ORIGINAL_SLOW_PATH_NAME, PUT_METHOD_DESC); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(3, 3); mv.visitEnd(); } }