Java tutorial
package org.glassfish.pfl.tf.spi; /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010,2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ import org.glassfish.pfl.basic.func.UnaryFunction; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.LocalVariableNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.util.AbstractVisitor; import org.objectweb.asm.util.CheckClassAdapter; import java.io.PrintWriter; /** Some useful utilities for generating code using ASM. Nothing in here * should be specific to the classfile enhancer for tracing. * * @author ken */ public class Util { private final boolean debug; private final int verbose; public Util(final boolean debug, final int verbose) { this.debug = debug; if (debug && (verbose < 1)) { this.verbose = 1; } else { this.verbose = verbose; } } public boolean getDebug() { return debug; } public void info(int level, String str) { if (verbose >= level) { final String format = level > 1 ? "%" + (4 * (level - 1) + 1) + "s" : "%s"; final String pad = String.format(format, ">"); msg(pad + str); } } public void msg(String str) { System.out.println(str); } public void error(String str) { throw new RuntimeException(str); } public void initLocal(MethodVisitor mv, LocalVariableNode var) { info(2, "Initializing variable " + var); Type type = Type.getType(var.desc); switch (type.getSort()) { case Type.BYTE: case Type.BOOLEAN: case Type.CHAR: case Type.SHORT: case Type.INT: mv.visitInsn(Opcodes.ICONST_0); mv.visitVarInsn(Opcodes.ISTORE, var.index); break; case Type.LONG: mv.visitInsn(Opcodes.LCONST_0); mv.visitVarInsn(Opcodes.LSTORE, var.index); break; case Type.FLOAT: mv.visitInsn(Opcodes.FCONST_0); mv.visitVarInsn(Opcodes.FSTORE, var.index); break; case Type.DOUBLE: mv.visitInsn(Opcodes.DCONST_0); mv.visitVarInsn(Opcodes.DSTORE, var.index); break; default: mv.visitInsn(Opcodes.ACONST_NULL); mv.visitVarInsn(Opcodes.ASTORE, var.index); } } public String getFullMethodDescriptor(String name, String desc) { return name + desc; } public String getFullMethodDescriptor(MethodNode mn) { return mn.name + mn.desc; } public String getFullMethodDescriptor(MethodInsnNode mn) { return mn.name + mn.desc; } public String getFullMethodDescriptor(java.lang.reflect.Method method) { final String desc = Type.getMethodDescriptor(method); return method.getName() + desc; } public void newWithSimpleConstructor(MethodVisitor mv, Class<?> cls) { info(2, "generating new for class " + cls); Type type = Type.getType(cls); mv.visitTypeInsn(Opcodes.NEW, type.getInternalName()); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, type.getInternalName(), "<init>", "()V"); } public String augmentInfoMethodDescriptor(String desc) { info(2, "Augmenting infoMethod descriptor " + desc); // Compute new descriptor Type[] oldArgTypes = Type.getArgumentTypes(desc); Type retType = Type.getReturnType(desc); int oldlen = oldArgTypes.length; Type[] argTypes = new Type[oldlen + 2]; System.arraycopy(oldArgTypes, 0, argTypes, 0, oldlen); argTypes[oldlen] = Type.getType(MethodMonitor.class); argTypes[oldlen + 1] = Type.INT_TYPE; String newDesc = Type.getMethodDescriptor(retType, argTypes); info(3, "result is " + newDesc); return newDesc; } public void emitIntConstant(MethodVisitor mv, int val) { info(2, "Emitting constant " + val); if (val <= 5) { switch (val) { case 0: mv.visitInsn(Opcodes.ICONST_0); break; case 1: mv.visitInsn(Opcodes.ICONST_1); break; case 2: mv.visitInsn(Opcodes.ICONST_2); break; case 3: mv.visitInsn(Opcodes.ICONST_3); break; case 4: mv.visitInsn(Opcodes.ICONST_4); break; case 5: mv.visitInsn(Opcodes.ICONST_5); break; } } else { mv.visitLdcInsn(val); } } // Wrap the argument at index argIndex of type atype into // an Object as needed. Returns the index of the next // argument. public int wrapArg(MethodVisitor mv, int argIndex, Type atype) { info(2, "Emitting code to wrap argument at " + argIndex + " of type " + atype); switch (atype.getSort()) { case Type.BOOLEAN: mv.visitVarInsn(Opcodes.ILOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Boolean.class), "valueOf", "(Z)Ljava/lang/Boolean;"); break; case Type.BYTE: mv.visitVarInsn(Opcodes.ILOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Byte.class), "valueOf", "(B)Ljava/lang/Byte;"); break; case Type.CHAR: mv.visitVarInsn(Opcodes.ILOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Character.class), "valueOf", "(C)Ljava/lang/Character;"); break; case Type.SHORT: mv.visitVarInsn(Opcodes.ILOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Short.class), "valueOf", "(S)Ljava/lang/Short;"); break; case Type.INT: mv.visitVarInsn(Opcodes.ILOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", "(I)Ljava/lang/Integer;"); break; case Type.LONG: mv.visitVarInsn(Opcodes.LLOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Long.class), "valueOf", "(J)Ljava/lang/Long;"); break; case Type.DOUBLE: mv.visitVarInsn(Opcodes.DLOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Double.class), "valueOf", "(D)Ljava/lang/Double;"); break; case Type.FLOAT: mv.visitVarInsn(Opcodes.FLOAD, argIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class), "valueOf", "(F)Ljava/lang/Float;"); break; default: mv.visitVarInsn(Opcodes.ALOAD, argIndex); break; } return argIndex + atype.getSize(); } // Emit code to wrap all of the argumnts as Object[], // which is left on the stack public void wrapArgs(MethodVisitor mv, int access, String desc) { info(2, "Wrapping args for descriptor " + desc); Type[] atypes = Type.getArgumentTypes(desc); emitIntConstant(mv, atypes.length); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); int argIndex; if ((access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) { argIndex = 0; } else { argIndex = 1; } for (int ctr = 0; ctr < atypes.length; ctr++) { mv.visitInsn(Opcodes.DUP); emitIntConstant(mv, ctr); argIndex = wrapArg(mv, argIndex, atypes[ctr]); mv.visitInsn(Opcodes.AASTORE); } } public void storeFromXReturn(MethodVisitor mv, int returnOpcode, LocalVariableNode holder) { switch (returnOpcode) { case Opcodes.RETURN: // NOP break; case Opcodes.ARETURN: mv.visitVarInsn(Opcodes.ASTORE, holder.index); break; case Opcodes.IRETURN: mv.visitVarInsn(Opcodes.ISTORE, holder.index); break; case Opcodes.LRETURN: mv.visitVarInsn(Opcodes.LSTORE, holder.index); break; case Opcodes.FRETURN: mv.visitVarInsn(Opcodes.FSTORE, holder.index); break; case Opcodes.DRETURN: mv.visitVarInsn(Opcodes.DSTORE, holder.index); break; } } public void loadFromXReturn(MethodVisitor mv, int returnOpcode, LocalVariableNode holder) { switch (returnOpcode) { case Opcodes.RETURN: // NOP break; case Opcodes.ARETURN: mv.visitVarInsn(Opcodes.ALOAD, holder.index); break; case Opcodes.IRETURN: mv.visitVarInsn(Opcodes.ILOAD, holder.index); break; case Opcodes.LRETURN: mv.visitVarInsn(Opcodes.LLOAD, holder.index); break; case Opcodes.FRETURN: mv.visitVarInsn(Opcodes.FLOAD, holder.index); break; case Opcodes.DRETURN: mv.visitVarInsn(Opcodes.DLOAD, holder.index); break; } } private void verify(byte[] cls) { if (getDebug()) { info(2, "Verifying enhanced class"); ClassReader cr = new ClassReader(cls); PrintWriter pw = new PrintWriter(System.out); CheckClassAdapter.verify(cr, true, pw); } } public boolean hasAccess(int access, int flag) { return (access & flag) == flag; } public static String opcodeToString(int opcode) { String[] opcodes = AbstractVisitor.OPCODES; if ((opcode < 0) || (opcode > opcodes.length)) { return "ILLEGAL[" + opcode + "]"; } else { return opcodes[opcode]; } } public byte[] transform(final boolean debug, final byte[] cls, final UnaryFunction<ClassVisitor, ClassAdapter> factory) { final ClassReader cr = new ClassReader(cls); final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); // ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS ) ; PrintWriter pw = null; // TraceClassVisitor tcv = null ; ClassVisitor cv = cw; if (debug) { pw = new PrintWriter(System.out); // tcv = new TraceClassVisitor( cw, new PrintWriter( System.out ) ) ; // cv = tcv ; } ClassAdapter xform = factory.evaluate(cv); try { cr.accept(xform, ClassReader.SKIP_FRAMES); } catch (TraceEnhancementException exc) { throw exc; } catch (Exception exc) { info(1, "Exception: " + exc); if (debug) { exc.printStackTrace(); } } finally { if (pw != null) { pw.flush(); pw.close(); } } byte[] enhancedClass = cw.toByteArray(); verify(enhancedClass); return enhancedClass; } }