/* * Copyright 2013 MovingBlocks * * 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.terasology.reflection.reflect; import com.esotericsoftware.reflectasm.FieldAccess; import com.esotericsoftware.reflectasm.MethodAccess; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.utilities.ReflectionUtil; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** */ public class ByteCodeReflectFactory implements ReflectFactory { private static final Logger logger = LoggerFactory.getLogger(ByteCodeReflectFactory.class); private ClassPool pool; private CtClass objectConstructorInterface; private ReflectFactory backupFactory = new ReflectionReflectFactory(); public ByteCodeReflectFactory() { try { ClassPool.doPruning = true; pool = ClassPool.getDefault(); objectConstructorInterface = pool.get(ObjectConstructor.class.getName()); } catch (NotFoundException e) { throw new RuntimeException("Error establishing reflection factory", e); } } @Override public <T> ObjectConstructor<T> createConstructor(Class<T> type) throws NoSuchMethodException { String constructorClassName = type.getName() + "_ReflectConstructor"; try { return (ObjectConstructor<T>) type.getClassLoader().loadClass(constructorClassName).getConstructor().newInstance(); } catch (ClassNotFoundException ignored) { try { if (Modifier.isPrivate(type.getDeclaredConstructor().getModifiers())) { logger.warn("Constructor for '{}' exists but is private, falling back on reflection", type); return backupFactory.createConstructor(type); } CtClass constructorClass = pool.makeClass(type.getName() + "_ReflectConstructor"); constructorClass.setInterfaces(new CtClass[]{objectConstructorInterface}); CtMethod method = CtNewMethod.make("public Object construct() { return new " + type.getName() + "();}", constructorClass); constructorClass.addMethod(method); return (ObjectConstructor<T>) (constructorClass.toClass(type.getClassLoader(), type.getProtectionDomain()).getConstructor().newInstance()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | CannotCompileException e) { logger.error("Error instantiating constructor object for '{}', falling back on reflection", type, e); return backupFactory.createConstructor(type); } catch (NoSuchMethodException e) { return null; } } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { logger.error("Error instantiating constructor object for '{}', falling back on reflection", type, e); return backupFactory.createConstructor(type); } } @Override public <T> FieldAccessor<T, ?> createFieldAccessor(Class<T> ownerType, Field field) throws InaccessibleFieldException { return createFieldAccessor(ownerType, field, field.getType()); } @Override public <T, U> FieldAccessor<T, U> createFieldAccessor(Class<T> ownerType, Field field, Class<U> fieldType) throws InaccessibleFieldException { try { return new ReflectASMFieldAccessor<>(ownerType, field, fieldType); } catch (IllegalArgumentException | InaccessibleFieldException e) { logger.warn("Failed to create accessor for field '{}' of type '{}', falling back on reflection", field.getName(), ownerType.getName()); return backupFactory.createFieldAccessor(ownerType, field, fieldType); } } public void setClassPool(ClassPool classPool) { pool = classPool; } private static class ReflectASMFieldAccessor<T, U> implements FieldAccessor<T, U> { private static final int NO_METHOD = -1; private MethodAccess methodAccess; private int getterIndex = NO_METHOD; private int setterIndex = NO_METHOD; private FieldAccess fieldAccess; private int fieldIndex; ReflectASMFieldAccessor(Class<T> ownerType, Field field, Class<U> fieldType) throws InaccessibleFieldException { methodAccess = MethodAccess.get(ownerType); Method getter = ReflectionUtil.findGetter(field); if (getter != null) { getterIndex = methodAccess.getIndex(getter.getName()); } Method setter = ReflectionUtil.findSetter(field); if (setter != null) { setterIndex = methodAccess.getIndex(setter.getName()); } if (getterIndex == NO_METHOD || setterIndex == NO_METHOD) { fieldAccess = FieldAccess.get(ownerType); try { fieldIndex = fieldAccess.getIndex(field.getName()); } catch (IllegalArgumentException e) { throw new InaccessibleFieldException("Failed to create accessor for field '" + field.getName() + "' of type '" + ownerType.getName() + "'", e); } } } @Override @SuppressWarnings("unchecked") public U getValue(T target) { if (getterIndex != NO_METHOD) { return (U) methodAccess.invoke(target, getterIndex); } else { return (U) fieldAccess.get(target, fieldIndex); } } @Override public void setValue(T target, U value) { if (setterIndex != NO_METHOD) { methodAccess.invoke(target, setterIndex, value); } else { fieldAccess.set(target, fieldIndex, value); } } } }