/* * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package shared; import jdk.internal.org.objectweb.asm.ClassWriter; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS; import jdk.internal.org.objectweb.asm.MethodVisitor; import static jdk.internal.org.objectweb.asm.Opcodes.*; import static shared.AccessCheck.*; public class GenericClassGenerator { private static final String targetMethodName = Utils.TARGET_METHOD_NAME; private int flags = 0; private ClassWriter writer; private String fullClassName = null; private String parentClassName = null; /*******************************************************************/ public GenericClassGenerator(String fullClassName) { this(fullClassName, "java/lang/Object"); } /*******************************************************************/ public GenericClassGenerator(String fullClassName, String parentClassName ) { this(fullClassName, parentClassName, ACC_PUBLIC); } /*******************************************************************/ public GenericClassGenerator(String fullClassName, String parentClassName, int flags) { this(fullClassName, parentClassName, flags, new String[0]); } /*******************************************************************/ public GenericClassGenerator(String fullClassName, String parentClassName, int flags, String[] implementedInterfaces) { writer = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS); this.fullClassName = fullClassName; this.flags = flags; // Construct simple class if (parentClassName != null) { this.parentClassName = getInternalName(parentClassName); } else { this.parentClassName = "java/lang/Object"; } String parent = this.parentClassName; String name = getInternalName(fullClassName); if (Utils.isACC_SUPER) { flags = flags | ACC_SUPER; } writer.visit(Utils.version, flags, name, null, parent, implementedInterfaces); // Add constructor if ( !isInterface(flags) ) { MethodVisitor m = writer.visitMethod( ACC_PUBLIC , "" , "()V" , null , null ); m.visitCode(); m.visitVarInsn(ALOAD, 0); m.visitMethodInsn( INVOKESPECIAL , getInternalName(parent) , "" , "()V" ); m.visitInsn(RETURN); m.visitEnd(); m.visitMaxs(0,0); } } /*******************************************************************/ protected static String getInternalName(String fullClassName) { return fullClassName.replaceAll("\\.", "/"); } /*******************************************************************/ public T addTargetConstructor(AccessType access) { // AccessType.UNDEF means that the target method isn't defined, so do nothing if (access == AccessType.UNDEF || isInterface(flags) ) { return (T)this; } // Add target constructor int methodAccessType = access.value(); MethodVisitor m = writer.visitMethod( methodAccessType , "" , "(I)V" , null , null ); // Add a call to parent constructor m.visitCode(); m.visitVarInsn(ALOAD, 0); m.visitMethodInsn( INVOKESPECIAL , getInternalName(parentClassName) , "" , "()V" ); // Add result reporting String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1); m.visitLdcInsn(shortName+"."); m.visitFieldInsn( PUTSTATIC , "Result" , "value" , "Ljava/lang/String;" ); m.visitInsn(RETURN); m.visitEnd(); m.visitMaxs(0,0); return (T)this; } /*******************************************************************/ public T addTargetMethod(AccessType access) { return addTargetMethod(access, 0); } /*******************************************************************/ public T addTargetMethod(AccessType access, int additionalFlags) { // AccessType.UNDEF means that the target method isn't defined, so do nothing if (access == AccessType.UNDEF) { return (T)this; } // Add target method int methodAccessType = access.value(); if ( isInterface(flags) || isAbstract(flags) ) { methodAccessType |= ACC_ABSTRACT; } // Skip method declaration for abstract private case, which doesn't pass // classfile verification stage if ( isPrivate(methodAccessType) && isAbstract(methodAccessType) ) { return (T)this; } MethodVisitor m = writer.visitMethod( methodAccessType | additionalFlags , targetMethodName , "()Ljava/lang/String;" , null , null ); // Don't generate body if the method is abstract if ( (methodAccessType & ACC_ABSTRACT) == 0 ) { String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1); // Simply returns info about itself m.visitCode(); m.visitLdcInsn(shortName+"."+targetMethodName); m.visitInsn(ARETURN); m.visitEnd(); m.visitMaxs(0,0); } return (T)this; } /*******************************************************************/ public T addField(int access, String name, String type) { writer.visitField( access , name , getInternalName(type) , null , null ) .visitEnd(); return (T)this; } /*******************************************************************/ // Add target method call site into current class public T addCaller(String targetClass, int callType) { MethodVisitor m = writer.visitMethod( ACC_PUBLIC | ACC_STATIC , "call" , String.format( "(L%s;)Ljava/lang/String;" , getInternalName(targetClass)) , null , null ); m.visitCode(); m.visitVarInsn(ALOAD, 0); m.visitMethodInsn( callType , getInternalName(targetClass) , targetMethodName , "()Ljava/lang/String;" ); m.visitInsn(ARETURN); m.visitEnd(); m.visitMaxs(0,0); return (T)this; } /*******************************************************************/ public byte[] getClassFile() { writer.visitEnd(); return writer.toByteArray(); } /*******************************************************************/ public String getFullClassName() { return fullClassName; } }