8035820: Optimistic recompilation
Reviewed-by: hannesw, jlaskey, sundar
Contributed-by: attila.szegedi@oracle.com, marcus.lagergren@oracle.com
package jdk.internal.dynalink.support;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
import jdk.nashorn.internal.runtime.RewriteException;
import sun.misc.Unsafe;
/**
* Generates method handles that combine an invocation and a handler for a {@link RewriteException}. Always immediately
* generates compiled bytecode.
*/
public class CatchExceptionCombinator {
static {
System.err.println("*** Running with fast catch combinator handler ***");
}
private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
private static final String METHOD_HANDLE_TYPE_NAME = METHOD_HANDLE_TYPE.getInternalName();
private static final String OBJECT_TYPE_NAME = Type.getInternalName(Object.class);
private static final String HANDLER_TYPE_NAME = "java.lang.invoke.CatchExceptionCombinator$MH";
private static final String INVOKE_METHOD_NAME = "invoke";
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final ConcurrentMap<CombinatorParameters, ClassTemplate> handlerClassBytes = new ConcurrentHashMap<>();
private static final class CombinatorParameters {
final MethodType targetType;
final Class<? extends Throwable> exType;
final MethodType handlerType;
CombinatorParameters(final MethodType targetType, final Class<? extends Throwable> exType, MethodType handlerType) {
this.targetType = targetType;
this.exType = exType;
this.handlerType = handlerType;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof CombinatorParameters) {
final CombinatorParameters p = (CombinatorParameters)obj;
return targetType.equals(p.targetType) && exType.equals(p.exType) && handlerType.equals(p.handlerType);
}
return false;
}
@Override
public int hashCode() {
return targetType.hashCode() ^ exType.hashCode() ^ handlerType.hashCode();
}
}
/**
* Catch exception - create combinator
* @param target target
* @param exType type to check for
* @param handler catch handler
* @return target wrapped in catch handler
*/
public static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
final MethodType targetType = target.type();
final MethodType handlerType = handler.type();
final ClassTemplate classTemplate = handlerClassBytes.computeIfAbsent(
new CombinatorParameters(targetType, exType, handlerType), new Function<CombinatorParameters, ClassTemplate>() {
@Override
public ClassTemplate apply(final CombinatorParameters parameters) {
return generateClassTemplate(parameters);
}
});
return classTemplate.instantiate(target, handler, targetType);
}
private static final class ClassTemplate {
final byte[] bytes;
final int target_cp_index;
final int handler_cp_index;
final int cp_size;
ClassTemplate(final byte[] bytes, final int target_cp_index, final int handler_cp_index, final int cp_size) {
this.bytes = bytes;
this.target_cp_index = target_cp_index;
this.handler_cp_index = handler_cp_index;
this.cp_size = cp_size;
}
MethodHandle instantiate(final MethodHandle target, final MethodHandle handler, final MethodType type) {
final Object[] cpPatch = new Object[cp_size];
cpPatch[target_cp_index] = target;
cpPatch[handler_cp_index] = handler;
final Class<?> handlerClass = UNSAFE.defineAnonymousClass(CatchExceptionCombinator.class, bytes, cpPatch);
try {
return MethodHandles.lookup().findStatic(handlerClass, INVOKE_METHOD_NAME, type);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
}
private static ClassTemplate generateClassTemplate(final CombinatorParameters combinatorParameters) {
final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
w.visit(V1_7, ACC_PUBLIC | ACC_SUPER, HANDLER_TYPE_NAME, null, OBJECT_TYPE_NAME, null);
final MethodType targetType = combinatorParameters.targetType;
final Class<? extends Throwable> exType = combinatorParameters.exType;
final String methodDescriptor = targetType.toMethodDescriptorString();
final Class<?> returnType = targetType.returnType();
final MethodType handlerType = combinatorParameters.handlerType;
// NOTE: must use strings as placeholders in the constant pool, even if we'll be replacing them with method handles.
final String targetPlaceholder = "T_PLACEHOLDER";
final String handlerPlaceholder = "H_PLACEHOLDER";
final int target_cp_index = w.newConst(targetPlaceholder);
final int handler_cp_index = w.newConst(handlerPlaceholder);
final InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC | ACC_STATIC, INVOKE_METHOD_NAME, methodDescriptor, null, null));
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
mv.visitCode();
final Label _try = new Label();
final Label _end_try= new Label();
mv.visitLabel(_try);
// Invoke
mv.aconst(targetPlaceholder);
mv.checkcast(METHOD_HANDLE_TYPE);
final Class<?>[] paramTypes = targetType.parameterArray();
for(int i = 0, slot = 0; i < paramTypes.length; ++i) {
final Type paramType = Type.getType(paramTypes[i]);
mv.load(slot, paramType);
slot += paramType.getSize();
}
generateInvokeBasic(mv, methodDescriptor);
final Type asmReturnType = Type.getType(returnType);
mv.areturn(asmReturnType);
mv.visitTryCatchBlock(_try, _end_try, _end_try, Type.getInternalName(exType));
mv.visitLabel(_end_try);
// Handle exception
mv.aconst(handlerPlaceholder);
mv.checkcast(METHOD_HANDLE_TYPE);
mv.swap();
final Class<?>[] handlerParamTypes = handlerType.parameterArray();
for(int i = 1, slot = 0; i < handlerParamTypes.length; ++i) {
final Type paramType = Type.getType(handlerParamTypes[i]);
mv.load(slot, paramType);
slot += paramType.getSize();
}
generateInvokeBasic(mv, handlerType.toMethodDescriptorString());
mv.areturn(asmReturnType);
mv.visitMaxs(0, 0);
mv.visitEnd();
w.visitEnd();
final byte[] bytes = w.toByteArray();
final int cp_size = (((bytes[8] & 0xFF) << 8) | (bytes[9] & 0xFF));
return new ClassTemplate(bytes, target_cp_index, handler_cp_index, cp_size);
}
private static void generateInvokeBasic(final InstructionAdapter mv, final String methodDesc) {
mv.invokevirtual(METHOD_HANDLE_TYPE_NAME, "invokeBasic", methodDesc, false);
}
}