org.jadira.reflection.access.invokedynamic.InvokeDynamicClassAccess Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloning Show documentation
Show all versions of cloning Show documentation
Cloning for Jadira Framework
/*
* 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 The Class to be accessed
*/
public abstract class InvokeDynamicClassAccess extends AbstractClassAccess implements ClassAccess {
private static final ConcurrentHashMap, InvokeDynamicClassAccess>> CLASS_ACCESSES = new ConcurrentHashMap, 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 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 The type of class
* @return New InvokeDynamicClassAccess instance
*/
public static InvokeDynamicClassAccess get(Class clazz) {
@SuppressWarnings("unchecked")
InvokeDynamicClassAccess access = (InvokeDynamicClassAccess) 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" + CLASS_ACCESS_NM + ";";
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> c = (Constructor>) 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 String determineEnclosingClassNm(Class 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, "", "(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, "", "(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, "", "()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, "", "()V");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEVIRTUAL, classNm, "getClass", "()Ljava/lang/Class;");
mv.visitInsn(POP);
mv.visitMethodInsn(INVOKESPECIAL, classNm, "", "(L" + enclosingClassNm + ";)V");
mv.visitInsn(ARETURN);
mv.visitMaxs(4, 1);
mv.visitEnd();
}
@Override
public abstract C newInstance();
@Override
protected MethodAccess constructMethodAccess(Method method) {
return InvokeDynamicMethodAccess.get(method);
}
@Override
protected FieldAccess constructFieldAccess(Field field) {
return InvokeDynamicFieldAccess.get(this, field);
}
@Override
protected ClassAccess constructClassAccess(Class clazz) {
return InvokeDynamicClassAccess.get(clazz);
}
}