Java tutorial
/******************************************************************************* * Copyright (c) 2013 Robin Salkeld * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package edu.ubc.mirrors.holograms; import static edu.ubc.mirrors.Reflection.getMirrorType; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; import org.objectweb.asm.commons.LocalVariablesSorter; import org.objectweb.asm.commons.Remapper; import edu.ubc.mirrors.ArrayMirror; import edu.ubc.mirrors.ClassMirror; import edu.ubc.mirrors.FieldMirror; import edu.ubc.mirrors.InstanceMirror; import edu.ubc.mirrors.MethodMirror; import edu.ubc.mirrors.ObjectArrayMirror; import edu.ubc.mirrors.ObjectMirror; import edu.ubc.mirrors.Reflection; import edu.ubc.mirrors.holographs.ClassHolograph; import edu.ubc.mirrors.holographs.MirrorInvocationHandler; import edu.ubc.mirrors.holographs.ThreadHolograph; import edu.ubc.mirrors.raw.NativeInstanceMirror; public class HologramClassGenerator extends ClassVisitor { public static final String VERSION = "1.5"; public static Type objectMirrorType = Type.getType(ObjectMirror.class); public static Type throwableType = Type.getType(Throwable.class); public static Type classMirrorType = Type.getType(ClassMirror.class); public static Type instanceMirrorType = Type.getType(InstanceMirror.class); public static Type arrayMirrorType = Type.getType(ArrayMirror.class); public static Type objectArrayMirrorType = Type.getType(ObjectArrayMirror.class); public static Type objectHologramType = Type.getType(ObjectHologram.class); public static Type instanceHologramType = Type.getType(InstanceHologram.class); public static Type hologramType = Type.getType(Hologram.class); public static Type hologramArrayType = Reflection.makeArrayType(1, Type.getType(Hologram.class)); public static Type fieldMirrorType = Type.getType(FieldMirror.class); public static Type nativeObjectMirrorType = Type.getType(NativeInstanceMirror.class); public static Type objectType = Type.getType(Object.class); public static Type objectArrayType = Reflection.makeArrayType(1, Type.getType(Object.class)); public static Type stringType = Type.getType(String.class); public static Type hologramStringType = getHologramType(String.class); public static Type hologramThrowableType = getHologramType(Throwable.class); public static Type classType = Type.getType(Class.class); public static Type stackTraceElementType = Type.getType(StackTraceElement.class); public static Type stackTraceType = Type.getType(StackTraceElement[].class); public static Remapper REMAPPER = new Remapper() { public String map(String typeName) { return getHologramType(Type.getObjectType(typeName), false).getInternalName(); } public String mapDesc(String desc) { Type t = Type.getType(desc); return mapTypeCorrected(t).getDescriptor(); } private Type mapTypeCorrected(Type t) { switch (t.getSort()) { case Type.ARRAY: case Type.OBJECT: String s = map(t.getInternalName()); return s != null ? Type.getObjectType(s) : t; case Type.METHOD: return Type.getMethodType(mapMethodDesc(t.getDescriptor())); } return t; } public String mapType(String type) { if (type == null) { return null; } return mapTypeCorrected(Type.getObjectType(type)).getInternalName(); }; public Object mapValue(Object value) { if (value instanceof Type) { return mapTypeCorrected((Type) value); } return super.mapValue(value); }; }; private final ClassMirror classMirror; private boolean isInterface; private String name; private String superName; private boolean hasClinit = false; public HologramClassGenerator(ClassMirror classMirror, ClassVisitor output) { super(Opcodes.ASM4, output); this.classMirror = classMirror; } private static int forcePublic(int access) { return (~(Opcodes.ACC_ENUM | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED) & access) | Opcodes.ACC_PUBLIC; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.name = name; this.isInterface = (Opcodes.ACC_INTERFACE & access) != 0; this.superName = getHologramSuperclassName(isInterface, name, superName); interfaces = getHologramInterfaces(name, isInterface, interfaces); // Force everything to be public, since HologramClassLoader has to reflectively // construct holograms. Again, not a problem because the VM will see the original flags on the ClassMirror instead. // Also remove enum flags. int hologramAccess = forcePublic(access); // Also remove abstract flag. Shouldn't be necessary, but the VM (OpenJDK at least) // creates objects that claim to be an instance of VirtualMachineError, which is abstract. if (name.equals("hologram/java/lang/VirtualMachineError")) { hologramAccess = ~Opcodes.ACC_ABSTRACT & access; } // We need at least 1.5 to use class literal constants // TODO-RS: Work out a better way to interpret 45.X numbers correctly if (version == Opcodes.V1_1 || version < Opcodes.V1_5) { version = 49; } super.visit(version, hologramAccess, name, signature, this.superName, interfaces); if (this.name.equals(hologramThrowableType.getInternalName())) { // Generate aliases for the original superclass' fillInStackTrace and getStackTrace methods, // so we can call them in stubs without hitting hologram code. MethodVisitor v = super.visitMethod(Opcodes.ACC_PUBLIC, "superFillInStackTrace", Type.getMethodDescriptor(Type.VOID_TYPE), null, null); v.visitCode(); v.visitVarInsn(Opcodes.ALOAD, 0); v.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Throwable.class), "fillInStackTrace", Type.getMethodDescriptor(Type.getType(Throwable.class))); v.visitInsn(Opcodes.RETURN); v.visitMaxs(1, 1); v.visitEnd(); v = super.visitMethod(Opcodes.ACC_PUBLIC, "superGetStackTrace", Type.getMethodDescriptor(Type.getType(StackTraceElement[].class)), null, null); v.visitCode(); v.visitVarInsn(Opcodes.ALOAD, 0); v.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Throwable.class), "getStackTrace", Type.getMethodDescriptor(Type.getType(StackTraceElement[].class))); v.visitInsn(Opcodes.ARETURN); v.visitMaxs(1, 1); v.visitEnd(); } } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { int hologramAccess = ~Opcodes.ACC_ENUM & access; super.visitInnerClass(name, outerName, innerName, hologramAccess); } public static String getHologramSuperclassName(boolean isInterface, String hologramName, String hologramSuperName) { if (getHologramType(Type.getType(Throwable.class), true).getInternalName().equals(hologramName)) { return Type.getInternalName(Throwable.class); } else if (Type.getInternalName(Hologram.class).equals(hologramSuperName)) { if (isInterface) { return Type.getInternalName(Object.class); } else { return Type.getInternalName(ObjectHologram.class); } } else { return hologramSuperName; } } public static String[] getHologramInterfaces(String name, boolean isInterface, String[] hologramInterfaces) { if (isInterface || name.equals(hologramThrowableType.getInternalName())) { String[] newInterfaces = new String[hologramInterfaces.length + 1]; System.arraycopy(hologramInterfaces, 0, newInterfaces, 0, hologramInterfaces.length); newInterfaces[newInterfaces.length - 1] = Type.getInternalName(Hologram.class); return newInterfaces; } else { return hologramInterfaces; } } public static String getHologramBinaryClassName(String className, boolean arrayImpl) { if (className == null) { return null; } return getHologramType(Reflection.typeForClassName(className), arrayImpl).getClassName(); } public static String getSortName(int sort) { switch (sort) { case Type.BOOLEAN: return "Boolean"; case Type.BYTE: return "Byte"; case Type.CHAR: return "Char"; case Type.SHORT: return "Short"; case Type.INT: return "Int"; case Type.LONG: return "Long"; case Type.FLOAT: return "Float"; case Type.DOUBLE: return "Double"; case Type.ARRAY: return "Array"; case Type.OBJECT: return "Object"; default: throw new IllegalStateException("Bad sort: " + sort); } } public static boolean isRefType(Type t) { return t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY; } public static Type getTypeForSortName(String name) { if (name.equals("Boolean")) { return Type.BOOLEAN_TYPE; } else if (name.equals("Byte")) { return Type.BYTE_TYPE; } else if (name.equals("Char")) { return Type.CHAR_TYPE; } else if (name.equals("Short")) { return Type.SHORT_TYPE; } else if (name.equals("Int")) { return Type.INT_TYPE; } else if (name.equals("Long")) { return Type.LONG_TYPE; } else if (name.equals("Float")) { return Type.FLOAT_TYPE; } else if (name.equals("Double")) { return Type.DOUBLE_TYPE; } else { return null; } } public static Type getPrimitiveArrayHologramType(Type elementType) { return Type .getObjectType("edu/ubc/mirrors/holograms/" + getSortName(elementType.getSort()) + "ArrayHologram"); } public static Type getPrimitiveArrayMirrorType(Type elementType) { return Type.getObjectType("edu/ubc/mirrors/" + getSortName(elementType.getSort()) + "ArrayMirror"); } public static Type getHologramType(Type type, boolean impl) { if (type == null) { return null; } if (type.equals(objectType)) { return impl ? objectHologramType : hologramType; } if (type.equals(objectArrayType) || type.equals(hologramArrayType)) { return impl ? Type.getType(ObjectArrayHologram.class) : Type.getType(ObjectArrayMirror.class); } if (type.getSort() == Type.ARRAY) { Type elementType = type.getElementType(); int elementSort = elementType.getSort(); int dims = type.getDimensions(); // Primitive array if (dims == 1 && elementSort != Type.OBJECT) { return getPrimitiveArrayHologramType(elementType); } else { String elementName = (elementSort == Type.OBJECT ? elementType.getInternalName() : getSortName(elementSort)); return Type .getObjectType((impl ? "hologramarrayimpl" : "hologramarray") + dims + "/" + elementName); } } String internalName = type.getInternalName(); if (!internalName.startsWith("hologram")) { return Type.getObjectType("hologram/" + internalName); } else { return type; } } public static boolean isImplementationClass(String hologramClassBinaryName) { return hologramClassBinaryName.equals(ObjectHologram.class.getName()) || hologramClassBinaryName.equals(ObjectArrayHologram.class.getName()) || hologramClassBinaryName.startsWith("hologramarrayimpl"); } public static String getOriginalInternalClassName(String hologramClassName) { if (hologramClassName == null) { return null; } Matcher m = Pattern.compile("hologramarray(?:impl)?(\\d+)[./](.*)").matcher(hologramClassName); if (m.matches()) { int dims = Integer.parseInt(m.group(1)); String hologramElementName = m.group(2); Type originalElementType = getTypeForSortName(hologramElementName); if (originalElementType == null) { originalElementType = Type.getObjectType(getOriginalInternalClassName(hologramElementName)); } return Reflection.makeArrayType(dims, originalElementType).getInternalName(); } if (Type.getInternalName(Hologram.class).equals(hologramClassName) || Type.getInternalName(ObjectHologram.class).equals(hologramClassName)) { return "java/lang/Object"; } if (Type.getInternalName(ObjectArrayHologram.class).equals(hologramClassName)) { return "[Ljava/lang/Object;"; } if (Type.getInternalName(ArrayHologram.class).equals(hologramClassName)) { return hologramClassName; } m = Pattern.compile("edu/ubc/mirrors/holograms/(.*)ArrayHologram").matcher(hologramClassName); if (m.matches()) { String sortName = m.group(1); return "[" + getTypeForSortName(sortName).getDescriptor(); } if (hologramClassName.equals("edu/ubc/mirrors/ObjectArrayMirror")) { return "[Ljava/lang/Object;"; } if (hologramClassName.startsWith("hologram")) { return hologramClassName.substring("hologram".length() + 1); } else { return hologramClassName; } } public static String getOriginalBinaryClassName(String hologramBinaryName) { return getOriginalType(Reflection.typeForClassName(hologramBinaryName)).getClassName(); } public static Type getHologramType(Type type) { return getHologramType(type, false); } public static Type getOriginalType(Type type) { if (type.getSort() == Type.METHOD) { Type[] argumentTypes = type.getArgumentTypes(); Type[] originalArgTypes = new Type[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) { originalArgTypes[i] = getOriginalType(argumentTypes[i]); } return Type.getMethodType(getOriginalType(type.getReturnType()), originalArgTypes); } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { return Type.getObjectType(getOriginalInternalClassName(type.getInternalName())); } else { return type; } } public static Type getHologramType(Class<?> c) { return getHologramType(Type.getType(c)); } public static String addMirrorParam(String desc) { Type type = Type.getMethodType(desc); Type argTypes[] = type.getArgumentTypes(); Type newArgTypes[] = new Type[argTypes.length + 1]; System.arraycopy(argTypes, 0, newArgTypes, 0, argTypes.length); newArgTypes[newArgTypes.length - 1] = instanceMirrorType; return Type.getMethodDescriptor(type.getReturnType(), newArgTypes); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (name.equals("<clinit>")) { hasClinit = true; } // TODO-RS: Remove me - avoiding a race condition in ZipFileInflaterInputStream... if (name.equals("finalize")) { return null; } if (name.equals("<init>")) { // Add the implicit mirror argument desc = addMirrorParam(desc); } // toString() is a special case - it's defined in java.lang.Object, which this class must ultimately // extend, so we have to return a real String rather than a hologram. boolean isToString = name.equals("toString") && desc.equals(Type.getMethodDescriptor(getHologramType(Type.getType(String.class)))); if (isToString) { desc = Type.getMethodDescriptor(Type.getType(String.class)); } if (name.equals("equals") && desc.equals(Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Hologram.class)))) { desc = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)); } boolean isGetStackTrace = this.name.equals(hologramThrowableType.getInternalName()) && name.equals("getStackTrace") && desc.equals(Type.getMethodDescriptor(getHologramType(Type.getType(StackTraceElement[].class)))); if (isGetStackTrace) { desc = Type.getMethodDescriptor(Type.getType(StackTraceElement[].class)); } if (name.equals("fillInStackTrace") && this.name.equals(hologramThrowableType.getInternalName())) { // Omit this - we'll use the Throwable superclass version return null; } // Take off the native keyword if it's there - we're going to fill in an actual // method (even if it's a stub that throws an exception). int hologramAccess = ~Opcodes.ACC_NATIVE & access; // Mild hack: generated method accessors are defined using ClassDefiner and Unsafe, // allowing them to make illegal access to this package-private constructor. if (this.name.equals("hologram/sun/reflect/MethodAccessorImpl") && name.equals("<init>")) { hologramAccess = forcePublic(hologramAccess); } MethodVisitor superVisitor = super.visitMethod(hologramAccess, name, desc, signature, exceptions); HologramMethodGenerator generator = new HologramMethodGenerator(this.name, hologramAccess, name, desc, superVisitor, isToString, isGetStackTrace); LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, generator); generator.setLocalVariablesSorter(lvs); boolean needsThunk = (Opcodes.ACC_NATIVE & access) != 0; if (!needsThunk && !name.startsWith("<")) { if ((Opcodes.ACC_ABSTRACT & access) == 0) { MethodMirror method; try { method = Reflection.getDeclaredMethod(classMirror, name, getOriginalType(Type.getMethodType(desc))); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } MirrorInvocationHandler handler = ClassHolograph.getMethodHandler(method); if (handler != null) { needsThunk = true; } } } if (needsThunk) { generator.generateNativeThunk(); return null; } return lvs; } public static Type getMirrorTypeForHologramType(Type hologramType) { return getMirrorType(getOriginalType(hologramType)); } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { // Remove all field definitions return null; } @Override public void visitEnd() { // Generate the static field used to store the corresponding ClassMirror int staticAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC; super.visitField(staticAccess, "classMirror", classMirrorType.getDescriptor(), null, null); // Generate the constructor that takes a mirror instance as an Object parameter String constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class)); if (name.equals(getHologramType(Type.getType(Throwable.class), true).getInternalName())) { // This doesn't extend ObjectHologram so we have to set the field directly super.visitField(Opcodes.ACC_PUBLIC, "mirror", objectMirrorType.getDescriptor(), null, null); MethodVisitor methodVisitor = super.visitMethod(Opcodes.ACC_PUBLIC, "<init>", constructorDesc, null, null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE)); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, objectMirrorType.getInternalName()); methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, name, "mirror", Type.getDescriptor(ObjectMirror.class)); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(2, 2); methodVisitor.visitEnd(); methodVisitor = super.visitMethod(Opcodes.ACC_PUBLIC, "getMirror", Type.getMethodDescriptor(objectMirrorType), null, null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, name, "mirror", Type.getDescriptor(ObjectMirror.class)); methodVisitor.visitInsn(Opcodes.ARETURN); methodVisitor.visitMaxs(1, 1); methodVisitor.visitEnd(); } else if (!isInterface) { MethodVisitor methodVisitor = super.visitMethod(Opcodes.ACC_PUBLIC, "<init>", constructorDesc, null, null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", constructorDesc); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(2, 2); methodVisitor.visitEnd(); } // Add a class initialization method to initialize the static fields, // if one doesn't exist already. if (!hasClinit) { InstructionAdapter mv = new InstructionAdapter( super.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)); mv.visitCode(); HologramMethodGenerator.initializeStaticFields(Type.getObjectType(this.name), mv); mv.areturn(Type.VOID_TYPE); mv.visitMaxs(2, 2); mv.visitEnd(); } super.visitEnd(); } public static void generateArray(ClassVisitor visitor, HologramClassLoader loader, HologramClassMirror hologramClassMirror) { boolean isInterface = !hologramClassMirror.isImplementationClass(); ClassMirror classMirror = hologramClassMirror.getOriginal(); Type originalType = Reflection.typeForClassMirror(classMirror); Type originalElementType = originalType.getElementType(); int dims = originalType.getDimensions(); String internalName = getHologramType(originalType, !isInterface).getInternalName(); ClassMirror superClassMirror = null; String superName = isInterface ? Type.getInternalName(Object.class) : Type.getInternalName(ObjectArrayHologram.class); Set<String> interfaces = new HashSet<String>(); int access = Opcodes.ACC_PUBLIC | (isInterface ? Opcodes.ACC_INTERFACE : 0); if (originalElementType.getSort() == Type.OBJECT || originalElementType.getSort() == Type.ARRAY) { ClassMirror elementClass = loader.loadOriginalClassMirror(originalElementType.getClassName()); superClassMirror = elementClass.getSuperClassMirror(); if (isInterface) { if (superClassMirror != null) { Type superType = Reflection.makeArrayType(dims, Type.getObjectType(superClassMirror.getClassName().replace('.', '/'))); String superInterfaceName = getHologramType(superType).getInternalName(); interfaces.add(superInterfaceName); } for (ClassMirror interfaceMirror : elementClass.getInterfaceMirrors()) { Type superType = Reflection.makeArrayType(dims, Type.getObjectType(interfaceMirror.getClassName().replace('.', '/'))); String interfaceName = getHologramType(superType).getInternalName(); interfaces.add(interfaceName); } interfaces.add(hologramType.getInternalName()); Type nMinus1Type = Reflection.makeArrayType(dims - 1, Type.getType(Object.class)); interfaces.add(getHologramType(nMinus1Type).getInternalName()); } } if (!isInterface) { interfaces.add(getHologramType(originalType, false).getInternalName()); } visitor.visit(Opcodes.V1_5, access, internalName, null, superName, interfaces.toArray(new String[0])); if (isInterface) { // Generate clone() String cloneDesc = Type.getMethodDescriptor(objectType); MethodVisitor mv = visitor.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, "clone", cloneDesc, null, null); mv.visitEnd(); } else { // Generate thunk constructors String initDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(ObjectArrayMirror.class)); MethodVisitor mv = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", initDesc, null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", initDesc); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); initDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE); mv = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", initDesc, null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", initDesc); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); } // Generate the static field used to store the corresponding ClassMirror and the static initializer to set it int staticAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC; visitor.visitField(staticAccess, "classMirror", classMirrorType.getDescriptor(), null, null); InstructionAdapter mv = new InstructionAdapter( visitor.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)); mv.visitCode(); HologramMethodGenerator.initializeStaticFields(Type.getObjectType(internalName), mv); mv.areturn(Type.VOID_TYPE); mv.visitMaxs(2, 2); mv.visitEnd(); visitor.visitEnd(); } }