All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl Maven / Gradle / Ivy

The newest version!
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.bytecode.internal.bytebuddy;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.Callable;

import net.bytebuddy.TypeCache;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate;
import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate;
import net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.jar.asm.Type;
import net.bytebuddy.matcher.ElementMatchers;

public class BytecodeProviderImpl implements BytecodeProvider {

	private final TypeCache FAST_CLASSES = new TypeCache.WithInlineExpunction(TypeCache.Sort.SOFT);
	private final TypeCache BULK_ACCESSORS = new TypeCache.WithInlineExpunction(TypeCache.Sort.SOFT);

	@Override
	public ProxyFactoryFactory getProxyFactoryFactory() {
		return new ProxyFactoryFactoryImpl();
	}

	@Override
	public ReflectionOptimizer getReflectionOptimizer(
			final Class clazz,
			final String[] getterNames,
			final String[] setterNames,
			final Class[] types) {
		final Method[] getters = new Method[getterNames.length];
		final Method[] setters = new Method[setterNames.length];
		findAccessors( clazz, getterNames, setterNames, types, getters, setters );
		final Constructor constructor = findConstructor( clazz );

		Class fastClass = FAST_CLASSES.findOrInsert( clazz.getClassLoader(), clazz.getName(), new Callable>() {
			@Override
			public Class call() throws Exception {
				return new ByteBuddy()
						.with(TypeValidation.DISABLED)
						.with(new NamingStrategy.SuffixingRandom("HibernateInstantiator"))
						.subclass(ReflectionOptimizer.InstantiationOptimizer.class)
						.method(ElementMatchers.named("newInstance"))
						.intercept(MethodCall.construct(constructor))
						.make()
						.load(clazz.getClassLoader())
						.getLoaded();
			}
		}, FAST_CLASSES);

		fastClass = FAST_CLASSES.insert( clazz.getClassLoader(), clazz.getName(), fastClass );

		Class bulkAccessor = BULK_ACCESSORS.findOrInsert( clazz.getClassLoader(), clazz.getName(), new Callable>() {
			@Override
			public Class call() throws Exception {
				return new ByteBuddy()
						.with(TypeValidation.DISABLED)
						.with(new NamingStrategy.SuffixingRandom("HibernateAccessOptimizer"))
						.subclass(ReflectionOptimizer.AccessOptimizer.class)
						.method(ElementMatchers.named("getPropertyValues"))
						.intercept(new Implementation.Simple(new GetPropertyValues(clazz, getters)))
						.method(ElementMatchers.named("setPropertyValues"))
						.intercept(new Implementation.Simple(new SetPropertyValues(clazz, setters)))
						.method(ElementMatchers.named("getPropertyNames"))
						.intercept(MethodCall.call(new CloningPropertyCall(getterNames)))
						.make()
						.load(clazz.getClassLoader())
						.getLoaded();
			}
		}, BULK_ACCESSORS);

		try {
			return new ReflectionOptimizerImpl(
					(ReflectionOptimizer.InstantiationOptimizer) fastClass.newInstance(),
					(ReflectionOptimizer.AccessOptimizer) bulkAccessor.newInstance()
			);
		}
		catch (Exception exception) {
			throw new HibernateException( exception );
		}
	}

	private static class GetPropertyValues implements ByteCodeAppender {

		private final Class clazz;

		private final Method[] getters;

		public GetPropertyValues(Class clazz, Method[] getters) {
			this.clazz = clazz;
			this.getters = getters;
		}

		@Override
		public Size apply(
				MethodVisitor methodVisitor,
				Implementation.Context implementationContext,
				MethodDescription instrumentedMethod) {
			methodVisitor.visitLdcInsn( getters.length );
			methodVisitor.visitTypeInsn( Opcodes.ANEWARRAY, Type.getInternalName( Object.class ) );
			int index = 0;
			for ( Method getter : getters ) {
				methodVisitor.visitInsn( Opcodes.DUP );
				methodVisitor.visitLdcInsn( index++ );
				methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 );
				methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( clazz ) );
				methodVisitor.visitMethodInsn(
						Opcodes.INVOKEVIRTUAL,
						Type.getInternalName( clazz ),
						getter.getName(),
						Type.getMethodDescriptor( getter ),
						false
				);
				if ( getter.getReturnType().isPrimitive() ) {
					PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( getter.getReturnType() ) )
							.assignBoxedTo(
									TypeDescription.Generic.OBJECT,
									ReferenceTypeAwareAssigner.INSTANCE,
									Assigner.Typing.STATIC
							)
							.apply( methodVisitor, implementationContext );
				}
				methodVisitor.visitInsn( Opcodes.AASTORE );
			}
			methodVisitor.visitInsn( Opcodes.ARETURN );
			return new Size( 6, instrumentedMethod.getStackSize() );
		}
	}

	private static class SetPropertyValues implements ByteCodeAppender {

		private final Class clazz;

		private final Method[] setters;

		public SetPropertyValues(Class clazz, Method[] setters) {
			this.clazz = clazz;
			this.setters = setters;
		}

		@Override
		public Size apply(
				MethodVisitor methodVisitor,
				Implementation.Context implementationContext,
				MethodDescription instrumentedMethod) {
			int index = 0;
			for ( Method setter : setters ) {
				methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 );
				methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( clazz ) );
				methodVisitor.visitVarInsn( Opcodes.ALOAD, 2 );
				methodVisitor.visitLdcInsn( index++ );
				methodVisitor.visitInsn( Opcodes.AALOAD );
				if ( setter.getParameterTypes()[0].isPrimitive() ) {
					PrimitiveUnboxingDelegate.forReferenceType( TypeDescription.Generic.OBJECT )
							.assignUnboxedTo(
									new TypeDescription.Generic.OfNonGenericType.ForLoadedType( setter.getParameterTypes()[0] ),
									ReferenceTypeAwareAssigner.INSTANCE,
									Assigner.Typing.DYNAMIC
							)
							.apply( methodVisitor, implementationContext );
				}
				else {
					methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( setter.getParameterTypes()[0] ) );
				}
				methodVisitor.visitMethodInsn(
						Opcodes.INVOKEVIRTUAL,
						Type.getInternalName( clazz ),
						setter.getName(),
						Type.getMethodDescriptor( setter ),
						false
				);
			}
			methodVisitor.visitInsn( Opcodes.RETURN );
			return new Size( 4, instrumentedMethod.getStackSize() );
		}
	}

	private static void findAccessors(
			Class clazz,
			String[] getterNames,
			String[] setterNames,
			Class[] types,
			Method[] getters,
			Method[] setters) {
		final int length = types.length;
		if ( setterNames.length != length || getterNames.length != length ) {
			throw new BulkAccessorException( "bad number of accessors" );
		}

		final Class[] getParam = new Class[0];
		final Class[] setParam = new Class[1];
		for ( int i = 0; i < length; i++ ) {
			if ( getterNames[i] != null ) {
				final Method getter = findAccessor( clazz, getterNames[i], getParam, i );
				if ( getter.getReturnType() != types[i] ) {
					throw new BulkAccessorException( "wrong return type: " + getterNames[i], i );
				}

				getters[i] = getter;
			}

			if ( setterNames[i] != null ) {
				setParam[0] = types[i];
				setters[i] = findAccessor( clazz, setterNames[i], setParam, i );
			}
		}
	}

	@SuppressWarnings("unchecked")
	private static Method findAccessor(Class clazz, String name, Class[] params, int index)
			throws BulkAccessorException {
		try {
			final Method method = clazz.getDeclaredMethod( name, params );
			if ( Modifier.isPrivate( method.getModifiers() ) ) {
				throw new BulkAccessorException( "private property", index );
			}

			return method;
		}
		catch (NoSuchMethodException e) {
			throw new BulkAccessorException( "cannot find an accessor", index );
		}
	}

	private static Constructor findConstructor(Class clazz) {
		try {
			return clazz.getDeclaredConstructor();
		}
		catch (NoSuchMethodException e) {
			throw new HibernateException( e );
		}
	}

	public static class CloningPropertyCall implements Callable {

		private final String[] propertyNames;

		private CloningPropertyCall(String[] propertyNames) {
			this.propertyNames = propertyNames;
		}

		@Override
		public String[] call() {
			return propertyNames.clone();
		}
	}

	@Override
	public Enhancer getEnhancer(EnhancementContext enhancementContext) {
		return new EnhancerImpl( enhancementContext );
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy