001/* 002 * Copyright 2013 Christopher Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.reflection.access.invokedynamic; 017 018import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 019import static org.objectweb.asm.Opcodes.ACC_SUPER; 020import static org.objectweb.asm.Opcodes.ALOAD; 021import static org.objectweb.asm.Opcodes.ARETURN; 022import static org.objectweb.asm.Opcodes.DUP; 023import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 024import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 025import static org.objectweb.asm.Opcodes.NEW; 026import static org.objectweb.asm.Opcodes.POP; 027import static org.objectweb.asm.Opcodes.RETURN; 028import static org.objectweb.asm.Opcodes.V1_7; 029 030import java.lang.reflect.Constructor; 031import java.lang.reflect.Field; 032import java.lang.reflect.Method; 033import java.lang.reflect.Modifier; 034import java.util.concurrent.ConcurrentHashMap; 035 036import org.jadira.reflection.access.AbstractClassAccess; 037import org.jadira.reflection.access.api.ClassAccess; 038import org.jadira.reflection.access.api.FieldAccess; 039import org.jadira.reflection.access.api.MethodAccess; 040import org.jadira.reflection.access.classloader.AccessClassLoader; 041import org.objectweb.asm.ClassVisitor; 042import org.objectweb.asm.ClassWriter; 043import org.objectweb.asm.MethodVisitor; 044 045/** 046 * ClassAccess implementation which uses ASM and the invokeDynamic instruction. 047 * InvokeDynamic requests are accessed using a caching callpoint (via Dynalang) which means performance is 048 * similar to standard ASM based access 049 * @param <C> The Class to be accessed 050 */ 051public abstract class InvokeDynamicClassAccess<C> extends AbstractClassAccess<C> implements ClassAccess<C> { 052 053 private static final ConcurrentHashMap<Class<?>, InvokeDynamicClassAccess<?>> CLASS_ACCESSES = new ConcurrentHashMap<Class<?>, InvokeDynamicClassAccess<?>>(); 054 055 private static final String CLASS_ACCESS_NM = ClassAccess.class.getName().replace('.', '/'); 056 057 private static final String INVOKEDYNAMIC_CLASS_ACCESS_NM = InvokeDynamicClassAccess.class.getName().replace('.', '/'); 058 059 private boolean isNonStaticMemberClass; 060 061 /** 062 * Indicates if the class being accessed is a non-static member class 063 * @return True if the class is a non-static member class 064 */ 065 public boolean isNonStaticMemberClass() { 066 return isNonStaticMemberClass; 067 } 068 069 /** 070 * Constructor, intended for use by generated subclasses 071 * @param clazz The Class to be accessed 072 */ 073 protected InvokeDynamicClassAccess(Class<C> clazz) { 074 super(clazz); 075 } 076 077 /** 078 * Get a new instance that can access the given Class. If the ClassAccess for this class 079 * has not been obtained before, then the specific InvokeDynamicClassAccess is created by 080 * generating a specialised subclass of this class and returning it. 081 * @param clazz Class to be accessed 082 * @param <C> The type of class 083 * @return New InvokeDynamicClassAccess instance 084 */ 085 public static <C> InvokeDynamicClassAccess<C> get(Class<C> clazz) { 086 087 @SuppressWarnings("unchecked") 088 InvokeDynamicClassAccess<C> access = (InvokeDynamicClassAccess<C>) CLASS_ACCESSES.get(clazz); 089 if (access != null) { 090 return access; 091 } 092 093 Class<?> enclosingType = clazz.getEnclosingClass(); 094 095 final boolean isNonStaticMemberClass = determineNonStaticMemberClass(clazz, enclosingType); 096 097 String clazzName = clazz.getName(); 098 099 String accessClassName = constructAccessClassName(clazzName); 100 101 Class<?> accessClass = null; 102 103 AccessClassLoader loader = AccessClassLoader.get(clazz); 104 synchronized (loader) { 105 try { 106 accessClass = loader.loadClass(accessClassName); 107 } catch (ClassNotFoundException ignored) { 108 109 String accessClassNm = accessClassName.replace('.', '/'); 110 String clazzNm = clazzName.replace('.', '/'); 111 String enclosingClassNm = determineEnclosingClassNm(clazz, enclosingType, isNonStaticMemberClass); 112 113 String signatureString = "L" + INVOKEDYNAMIC_CLASS_ACCESS_NM + "<L" + clazzNm + ";>;L" + CLASS_ACCESS_NM + "<L" + clazzNm + ";>;"; 114 115 ClassWriter cw = new ClassWriter(0); 116 117// TraceClassVisitor tcv = new TraceClassVisitor(cv, new PrintWriter(System.err)); 118// CheckClassAdapter cw = new CheckClassAdapter(tcv); 119 120 cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, accessClassNm, signatureString, INVOKEDYNAMIC_CLASS_ACCESS_NM, null); 121 122 enhanceForConstructor(cw, accessClassNm, clazzNm); 123 124 if (isNonStaticMemberClass) { 125 enhanceForNewInstanceInner(cw, clazzNm, enclosingClassNm); 126 } else { 127 enhanceForNewInstance(cw, clazzNm); 128 } 129 130 cw.visitEnd(); 131 132 loader.registerClass(accessClassName, cw.toByteArray()); 133 134 try { 135 accessClass = loader.findClass(accessClassName); 136 } catch (ClassNotFoundException e) { 137 throw new IllegalStateException("AccessClass unexpectedly could not be found", e); 138 } 139 } 140 } 141 try { 142 @SuppressWarnings("unchecked") 143 Constructor<InvokeDynamicClassAccess<C>> c = (Constructor<InvokeDynamicClassAccess<C>>) accessClass.getConstructor(new Class[] { Class.class }); 144 access = c.newInstance(clazz); 145 access.isNonStaticMemberClass = isNonStaticMemberClass; 146 147 CLASS_ACCESSES.putIfAbsent(clazz, access); 148 return access; 149 } catch (Exception ex) { 150 throw new RuntimeException("Error constructing constructor access class: " + accessClassName + "{ " + ex.getMessage() + " }", ex); 151 } 152 } 153 154 private static boolean determineNonStaticMemberClass(Class<?> clazz, Class<?> enclosingType) { 155 final boolean isNonStaticMemberClass; 156 if (enclosingType != null && clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) { 157 isNonStaticMemberClass = true; 158 } else { 159 isNonStaticMemberClass = false; 160 } 161 ; 162 return isNonStaticMemberClass; 163 } 164 165 private static <C> String determineEnclosingClassNm(Class<C> clazz, Class<?> enclosingType, final boolean isNonStaticMemberClass) { 166 167 final String enclosingClassNm; 168 169 if (!isNonStaticMemberClass) { 170 171 enclosingClassNm = null; 172 } else { 173 174 enclosingClassNm = enclosingType.getName().replace('.', '/'); 175 } 176 return enclosingClassNm; 177 } 178 179 private static String constructAccessClassName(String clazzName) { 180 181 String accessClassName = clazzName + InvokeDynamicClassAccess.class.getSimpleName(); 182 if (accessClassName.startsWith("java.")) { 183 accessClassName = InvokeDynamicClassAccess.class.getSimpleName().toLowerCase() + accessClassName; 184 } 185 186 return accessClassName; 187 } 188 189 private static void enhanceForConstructor(ClassVisitor cw, String accessClassNm, String clazzNm) { 190 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Class;)V", "(L" + clazzNm + ";)V", null); 191 mv.visitCode(); 192 mv.visitVarInsn(ALOAD, 0); 193 mv.visitVarInsn(ALOAD, 1); 194 mv.visitMethodInsn(INVOKESPECIAL, INVOKEDYNAMIC_CLASS_ACCESS_NM, "<init>", "(Ljava/lang/Class;)V"); 195 mv.visitInsn(RETURN); 196 mv.visitMaxs(2, 2); 197 mv.visitEnd(); 198 } 199 200 private static void enhanceForNewInstance(ClassVisitor cw, String classNm) { 201 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); 202 203 mv.visitCode(); 204 mv.visitTypeInsn(NEW, classNm); 205 mv.visitInsn(DUP); 206 mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "()V"); 207 mv.visitInsn(ARETURN); 208 mv.visitMaxs(2, 1); 209 mv.visitEnd(); 210 } 211 212 private static void enhanceForNewInstanceInner(ClassVisitor cw, String classNm, String enclosingClassNm) { 213 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()LLjava/lang/Object;", null, null); 214 mv.visitCode(); 215 mv.visitTypeInsn(NEW, classNm); 216 mv.visitInsn(DUP); 217 mv.visitTypeInsn(NEW, enclosingClassNm); 218 mv.visitInsn(DUP); 219 mv.visitMethodInsn(INVOKESPECIAL, enclosingClassNm, "<init>", "()V"); 220 mv.visitInsn(DUP); 221 mv.visitMethodInsn(INVOKEVIRTUAL, classNm, "getClass", "()Ljava/lang/Class;"); 222 mv.visitInsn(POP); 223 mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "(L" + enclosingClassNm + ";)V"); 224 mv.visitInsn(ARETURN); 225 mv.visitMaxs(4, 1); 226 mv.visitEnd(); 227 } 228 229 @Override 230 public abstract C newInstance(); 231 232 @Override 233 protected MethodAccess<C> constructMethodAccess(Method method) { 234 return InvokeDynamicMethodAccess.get(method); 235 } 236 237 @Override 238 protected FieldAccess<C> constructFieldAccess(Field field) { 239 return InvokeDynamicFieldAccess.get(this, field); 240 } 241 242 @Override 243 protected <X> ClassAccess<X> constructClassAccess(Class<X> clazz) { 244 return InvokeDynamicClassAccess.get(clazz); 245 } 246}