/* * Copyright 2013 Christopher Pheby * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jadira.reflection.access.invokedynamic; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARETURN; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.POP; import static org.objectweb.asm.Opcodes.RETURN; import static org.objectweb.asm.Opcodes.V1_7; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.ConcurrentHashMap; import org.jadira.reflection.access.AbstractClassAccess; import org.jadira.reflection.access.api.ClassAccess; import org.jadira.reflection.access.api.FieldAccess; import org.jadira.reflection.access.api.MethodAccess; import org.jadira.reflection.access.classloader.AccessClassLoader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; /** * ClassAccess implementation which uses ASM and the invokeDynamic instruction. * InvokeDynamic requests are accessed using a caching callpoint (via Dynalang) which means performance is * similar to standard ASM based access * @param <C> The Class to be accessed */ public abstract class InvokeDynamicClassAccess<C> extends AbstractClassAccess<C> implements ClassAccess<C> { private static final ConcurrentHashMap<Class<?>, InvokeDynamicClassAccess<?>> CLASS_ACCESSES = new ConcurrentHashMap<Class<?>, InvokeDynamicClassAccess<?>>(); private static final String CLASS_ACCESS_NM = ClassAccess.class.getName().replace('.', '/'); private static final String INVOKEDYNAMIC_CLASS_ACCESS_NM = InvokeDynamicClassAccess.class.getName().replace('.', '/'); private boolean isNonStaticMemberClass; /** * Indicates if the class being accessed is a non-static member class * @return True if the class is a non-static member class */ public boolean isNonStaticMemberClass() { return isNonStaticMemberClass; } /** * Constructor, intended for use by generated subclasses * @param clazz The Class to be accessed */ protected InvokeDynamicClassAccess(Class<C> clazz) { super(clazz); } /** * Get a new instance that can access the given Class. If the ClassAccess for this class * has not been obtained before, then the specific InvokeDynamicClassAccess is created by * generating a specialised subclass of this class and returning it. * @param clazz Class to be accessed * @param <C> The type of class * @return New InvokeDynamicClassAccess instance */ public static <C> InvokeDynamicClassAccess<C> get(Class<C> clazz) { @SuppressWarnings("unchecked") InvokeDynamicClassAccess<C> access = (InvokeDynamicClassAccess<C>) CLASS_ACCESSES.get(clazz); if (access != null) { return access; } Class<?> enclosingType = clazz.getEnclosingClass(); final boolean isNonStaticMemberClass = determineNonStaticMemberClass(clazz, enclosingType); String clazzName = clazz.getName(); String accessClassName = constructAccessClassName(clazzName); Class<?> accessClass = null; AccessClassLoader loader = AccessClassLoader.get(clazz); synchronized (loader) { try { accessClass = loader.loadClass(accessClassName); } catch (ClassNotFoundException ignored) { String accessClassNm = accessClassName.replace('.', '/'); String clazzNm = clazzName.replace('.', '/'); String enclosingClassNm = determineEnclosingClassNm(clazz, enclosingType, isNonStaticMemberClass); String signatureString = "L" + INVOKEDYNAMIC_CLASS_ACCESS_NM + "<L" + clazzNm + ";>;L" + CLASS_ACCESS_NM + "<L" + clazzNm + ";>;"; ClassWriter cw = new ClassWriter(0); // TraceClassVisitor tcv = new TraceClassVisitor(cv, new PrintWriter(System.err)); // CheckClassAdapter cw = new CheckClassAdapter(tcv); cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, accessClassNm, signatureString, INVOKEDYNAMIC_CLASS_ACCESS_NM, null); enhanceForConstructor(cw, accessClassNm, clazzNm); if (isNonStaticMemberClass) { enhanceForNewInstanceInner(cw, clazzNm, enclosingClassNm); } else { enhanceForNewInstance(cw, clazzNm); } cw.visitEnd(); loader.registerClass(accessClassName, cw.toByteArray()); try { accessClass = loader.findClass(accessClassName); } catch (ClassNotFoundException e) { throw new IllegalStateException("AccessClass unexpectedly could not be found", e); } } } try { @SuppressWarnings("unchecked") Constructor<InvokeDynamicClassAccess<C>> c = (Constructor<InvokeDynamicClassAccess<C>>) accessClass.getConstructor(new Class[] { Class.class }); access = c.newInstance(clazz); access.isNonStaticMemberClass = isNonStaticMemberClass; CLASS_ACCESSES.putIfAbsent(clazz, access); return access; } catch (Exception ex) { throw new RuntimeException("Error constructing constructor access class: " + accessClassName + "{ " + ex.getMessage() + " }", ex); } } private static boolean determineNonStaticMemberClass(Class<?> clazz, Class<?> enclosingType) { final boolean isNonStaticMemberClass; if (enclosingType != null && clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) { isNonStaticMemberClass = true; } else { isNonStaticMemberClass = false; } ; return isNonStaticMemberClass; } private static <C> String determineEnclosingClassNm(Class<C> clazz, Class<?> enclosingType, final boolean isNonStaticMemberClass) { final String enclosingClassNm; if (!isNonStaticMemberClass) { enclosingClassNm = null; } else { enclosingClassNm = enclosingType.getName().replace('.', '/'); } return enclosingClassNm; } private static String constructAccessClassName(String clazzName) { String accessClassName = clazzName + InvokeDynamicClassAccess.class.getSimpleName(); if (accessClassName.startsWith("java.")) { accessClassName = InvokeDynamicClassAccess.class.getSimpleName().toLowerCase() + accessClassName; } return accessClassName; } private static void enhanceForConstructor(ClassVisitor cw, String accessClassNm, String clazzNm) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Class;)V", "(L" + clazzNm + ";)V", null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKESPECIAL, INVOKEDYNAMIC_CLASS_ACCESS_NM, "<init>", "(Ljava/lang/Class;)V"); mv.visitInsn(RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); } private static void enhanceForNewInstance(ClassVisitor cw, String classNm) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitTypeInsn(NEW, classNm); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "()V"); mv.visitInsn(ARETURN); mv.visitMaxs(2, 1); mv.visitEnd(); } private static void enhanceForNewInstanceInner(ClassVisitor cw, String classNm, String enclosingClassNm) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()LLjava/lang/Object;", null, null); mv.visitCode(); mv.visitTypeInsn(NEW, classNm); mv.visitInsn(DUP); mv.visitTypeInsn(NEW, enclosingClassNm); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, enclosingClassNm, "<init>", "()V"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKEVIRTUAL, classNm, "getClass", "()Ljava/lang/Class;"); mv.visitInsn(POP); mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "(L" + enclosingClassNm + ";)V"); mv.visitInsn(ARETURN); mv.visitMaxs(4, 1); mv.visitEnd(); } @Override public abstract C newInstance(); @Override protected MethodAccess<C> constructMethodAccess(Method method) { return InvokeDynamicMethodAccess.get(method); } @Override protected FieldAccess<C> constructFieldAccess(Field field) { return InvokeDynamicFieldAccess.get(this, field); } @Override protected <X> ClassAccess<X> constructClassAccess(Class<X> clazz) { return InvokeDynamicClassAccess.get(clazz); } }