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

com.g2forge.alexandria.analysis.MethodAnalyzer Maven / Gradle / Ivy

package com.g2forge.alexandria.analysis;

import java.util.stream.Stream;

import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.INVOKEINTERFACE;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.ReturnInstruction;

import com.g2forge.alexandria.java.core.error.RuntimeReflectionException;
import com.g2forge.alexandria.java.reflect.HReflection;
import com.g2forge.alexandria.java.reflect.IJavaAccessorMethod;
import com.g2forge.alexandria.java.reflect.JavaPrimitive;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
class MethodAnalyzer implements IMethodAnalyzer {
	protected static String computePath(org.apache.bcel.classfile.Method method) throws Error {
		if (method.isAbstract()) return new IJavaAccessorMethod() {
			@Override
			public String getName() {
				return method.getName();
			}

			@Override
			public java.lang.reflect.Type[] getParameterTypes() {
				return HBCEL.toJava(method.getArgumentTypes());
			}

			@Override
			public java.lang.reflect.Type getReturnType() {
				return HBCEL.toJava(method.getReturnType());
			}
		}.getFieldName();

		final Code code = method.getCode();
		final Instruction[] instructions = new InstructionList(code.getCode()).getInstructions();
		if (!(instructions[0] instanceof ALOAD) || (((ALOAD) instructions[0]).getIndex() != 0)) throw new Error();
		if (!(instructions[instructions.length - 1] instanceof ReturnInstruction)) throw new Error();

		final StringBuilder retVal = new StringBuilder();
		final ConstantPoolGen constantPoolGen = new ConstantPoolGen(method.getConstantPool());
		for (int i = 1; i < instructions.length - 1; i++) {
			final String field;

			if (instructions[i] instanceof INVOKEVIRTUAL) {
				final InvokeInstruction invoke = (InvokeInstruction) instructions[i];
				try {
					field = T.getField(invoke.getReferenceType(constantPoolGen), invoke.getMethodName(constantPoolGen), invoke.getReturnType(constantPoolGen), invoke.getArgumentTypes(constantPoolGen));
				} catch (ClassNotFoundException e) {
					throw new RuntimeReflectionException(e);
				}
			} else if (instructions[i] instanceof GETFIELD) {
				final GETFIELD get = ((GETFIELD) instructions[i]);
				field = get.getFieldName(constantPoolGen);
			} else if (instructions[i] instanceof INVOKESTATIC) {
				final InvokeInstruction invoke = (InvokeInstruction) instructions[i];
				if (!"valueOf".equals(invoke.getMethodName(constantPoolGen))) throw new Error();
				final String name = invoke.getReferenceType(constantPoolGen).toString();
				if (!Stream.of(JavaPrimitive.values()).filter(primitive -> primitive.getBoxed().getName().equals(name)).findAny().isPresent()) throw new Error();
				field = null;
			} else if (instructions[i] instanceof INVOKEINTERFACE) {
				final INVOKEINTERFACE invoke = ((INVOKEINTERFACE) instructions[i]);
				field = new IJavaAccessorMethod() {
					@Override
					public String getName() {
						return invoke.getMethodName(constantPoolGen);
					}

					@Override
					public java.lang.reflect.Type[] getParameterTypes() {
						return HBCEL.toJava(invoke.getArgumentTypes(constantPoolGen));
					}

					@Override
					public java.lang.reflect.Type getReturnType() {
						return HBCEL.toJava(invoke.getReturnType(constantPoolGen));
					}
				}.getFieldName();
				if (field == null) throw new Error("Method was not an accessor");
			} else throw new Error(String.format("Instruction %d (%s) is of type %s which not recognized!", i, instructions[i], instructions[i].getClass().getSimpleName()));

			if (field != null) {
				if (retVal.length() > 0) retVal.append('.');
				retVal.append(field);
			}
		}
		return retVal.toString();
	}

	protected static org.apache.bcel.classfile.Method toBCELMethod(final T thunk) {
		final JavaClass clazz;
		try {
			clazz = Repository.lookupClass(thunk.getImplClass());
		} catch (ClassNotFoundException e) {
			throw new RuntimeReflectionException(e);
		}

		for (org.apache.bcel.classfile.Method method : clazz.getMethods()) {
			if (method.getName().equals(thunk.getImplMethodName()) && method.getSignature().equals(thunk.getImplMethodSignature())) return method;
		}
		throw new RuntimeReflectionException("Could not find method \"" + thunk.getImplClass() + "." + thunk.getImplMethodName() + "\"");
	}

	protected static java.lang.reflect.Method toJavaMethod(final T thunk) {
		final Class clazz;
		try {
			clazz = thunk.getClass().getClassLoader().loadClass(thunk.getImplClass().replace('/', '.'));
		} catch (ClassNotFoundException exception) {
			throw new RuntimeException(exception);
		}

		for (java.lang.reflect.Method[] methods : new java.lang.reflect.Method[][] { clazz.getMethods(), clazz.getDeclaredMethods() }) {
			for (java.lang.reflect.Method method : methods) {
				if (method.getName().equals(thunk.getImplMethodName())) {
					final String signature = HReflection.toSignature(method);
					if (signature.equals(thunk.getImplMethodSignature())) return method;
				}
			}
		}
		throw new RuntimeReflectionException("Could not find method \"" + thunk.getImplClass() + "." + thunk.getImplMethodName() + "\"");
	}

	@Getter(value = AccessLevel.PROTECTED)
	protected final SerializableLambda lambda;

	@Getter(lazy = true, value = AccessLevel.PROTECTED)
	private final T thunk = T.create(getLambda());

	@Getter(lazy = true)
	private final java.lang.reflect.Method method = toJavaMethod(getThunk());

	@Getter(lazy = true)
	private final String path = computePath(getBCEL());

	@Getter(lazy = true)
	private final org.apache.bcel.classfile.Method BCEL = toBCELMethod(getThunk());
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy