3789983e89
Reviewed-by: darcy, ihse
260 lines
10 KiB
Java
260 lines
10 KiB
Java
/*
|
|
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package selectionresolution;
|
|
|
|
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
|
import jdk.internal.org.objectweb.asm.Handle;
|
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
|
|
|
import java.lang.invoke.CallSite;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.MethodType;
|
|
|
|
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.ALOAD;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.POP;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
|
|
|
|
class Method {
|
|
public static final String defaultMethodName = "m";
|
|
public static final String defaultMethodDescriptor = "()Ljava/lang/Integer;";
|
|
public static final String methodDescriptorTemplate = "(L%s;)Ljava/lang/Integer;";
|
|
private final ClassConstruct ownerClass;
|
|
private final String ownerClassName;
|
|
private final ClassVisitor cv;
|
|
private final MethodVisitor mv;
|
|
private final ClassBuilder.ExecutionMode execMode;
|
|
|
|
public Method(ClassConstruct ownerClass, ClassVisitor cv, String name, String descriptor, int access,
|
|
ClassBuilder.ExecutionMode execMode) {
|
|
this.ownerClassName = ownerClass.getName();
|
|
this.ownerClass = ownerClass;
|
|
this.execMode = execMode;
|
|
this.cv = cv;
|
|
mv = cv.visitMethod(access, name, descriptor, null, null);
|
|
mv.visitCode();
|
|
}
|
|
/**
|
|
* Add code for the m()Ljava/lang/Integer; method, always returns null
|
|
*/
|
|
public void makeDefaultMethod() {
|
|
mv.visitTypeInsn(NEW, "java/lang/Integer");
|
|
mv.visitInsn(DUP);
|
|
mv.visitLdcInsn(ownerClass.getIndex());
|
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V");
|
|
mv.visitInsn(ARETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
public void makePrivateCallMethod(String className) {
|
|
makeSuperCallMethod(INVOKESPECIAL, className);
|
|
}
|
|
|
|
public void makeSuperCallMethod(int invokeInstruction, String className) {
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
makeCall(invokeInstruction, className, false);
|
|
mv.visitInsn(POP);
|
|
done();
|
|
}
|
|
|
|
public void defaultInvoke(int instr, String className, String objectRef, boolean isInterface) {
|
|
switch (instr) {
|
|
case INVOKEVIRTUAL:
|
|
defaultInvokeVirtual(className, objectRef);
|
|
break;
|
|
case INVOKEINTERFACE:
|
|
defaultInvokeInterface(className, objectRef);
|
|
break;
|
|
case INVOKESTATIC:
|
|
defaultInvokeStatic(className, isInterface);
|
|
break;
|
|
case INVOKESPECIAL:
|
|
defaultInvokeSpecial(className, objectRef, isInterface);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mv.visitInsn(ARETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
private void defaultInvokeVirtual(String className, String objectRef) {
|
|
String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
|
|
makeNewObject(objectRef, objectRefPackageName);
|
|
makeCall(INVOKEVIRTUAL, className, false);
|
|
}
|
|
|
|
private void defaultInvokeInterface(String className, String objectRef) {
|
|
String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
|
|
makeNewObject(objectRef, objectRefPackageName);
|
|
makeCall(INVOKEINTERFACE, className, true);
|
|
}
|
|
|
|
private void defaultInvokeSpecial(String className, String objectRef, boolean isInterface) {
|
|
String objectRefPackageName = objectRef.substring(0, objectRef.lastIndexOf("/"));
|
|
makeNewObject(objectRef, objectRefPackageName);
|
|
makeCall(INVOKESPECIAL, className, isInterface);
|
|
}
|
|
|
|
private void defaultInvokeStatic(String className, boolean isInterface) {
|
|
makeCall(INVOKESTATIC, className, isInterface);
|
|
}
|
|
|
|
private Method makeCall(int invokeInstruction, String className, boolean isInterface) {
|
|
switch(execMode) {
|
|
case DIRECT: {
|
|
mv.visitMethodInsn(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor, isInterface);
|
|
break;
|
|
}
|
|
case INDY: {
|
|
Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor);
|
|
Handle bsm = generateBootstrapMethod(m);
|
|
mv.visitInvokeDynamicInsn(defaultMethodName, defaultMethodDescriptor, bsm);
|
|
break;
|
|
}
|
|
case MH_INVOKE_EXACT:
|
|
case MH_INVOKE_GENERIC: {
|
|
String invokerName = execMode == ClassBuilder.ExecutionMode.MH_INVOKE_GENERIC
|
|
? "invoke" : "invokeExact";
|
|
|
|
Handle m = convertToHandle(invokeInstruction, className, defaultMethodName, defaultMethodDescriptor);
|
|
mv.visitLdcInsn(m);
|
|
mv.visitInsn(SWAP);
|
|
mv.visitMethodInsn(INVOKEVIRTUAL,
|
|
"java/lang/invoke/MethodHandle",
|
|
invokerName,
|
|
String.format(methodDescriptorTemplate, className),
|
|
false);
|
|
break;
|
|
}
|
|
default:
|
|
throw new Error("Unknown execution mode: " + execMode);
|
|
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private Handle generateBootstrapMethod(Handle h) {
|
|
String bootstrapName = "bootstrapMethod";
|
|
MethodType bootstrapType = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
|
|
|
|
MethodVisitor bmv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, bootstrapName, bootstrapType.toMethodDescriptorString(), null, null);
|
|
bmv.visitCode();
|
|
|
|
String constCallSite = "java/lang/invoke/ConstantCallSite";
|
|
bmv.visitTypeInsn(NEW, constCallSite);
|
|
bmv.visitInsn(DUP);
|
|
|
|
bmv.visitLdcInsn(h);
|
|
|
|
bmv.visitMethodInsn(INVOKESPECIAL, constCallSite, "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
|
|
bmv.visitInsn(ARETURN);
|
|
|
|
bmv.visitMaxs(0,0);
|
|
bmv.visitEnd();
|
|
|
|
return new Handle(H_INVOKESTATIC, ownerClassName, bootstrapName, bootstrapType.toMethodDescriptorString());
|
|
}
|
|
|
|
|
|
private static Handle convertToHandle(int invokeInstruction, String className, String methodName, String methodDesc) {
|
|
int tag;
|
|
switch (invokeInstruction) {
|
|
case INVOKEVIRTUAL: tag = H_INVOKEVIRTUAL; break;
|
|
case INVOKEINTERFACE: tag = H_INVOKEINTERFACE; break;
|
|
case INVOKESPECIAL: tag = H_INVOKESPECIAL; break;
|
|
case INVOKESTATIC: tag = H_INVOKESTATIC; break;
|
|
default:
|
|
throw new Error("Unknown invoke instruction: "+invokeInstruction);
|
|
}
|
|
|
|
return new Handle(tag, className, methodName, methodDesc);
|
|
}
|
|
|
|
private void makeNewObject(String objectRef, String objectRefPackageName) {
|
|
String className = objectRef.substring(objectRef.lastIndexOf("/") + 1);
|
|
makeStaticCall( objectRefPackageName + "/Helper",
|
|
"get" + className,
|
|
"()L" + objectRef + ";", false);
|
|
mv.visitVarInsn(ASTORE, 1);
|
|
mv.visitVarInsn(ALOAD, 1);
|
|
}
|
|
|
|
public void makeTestCall(String className) {
|
|
mv.visitTypeInsn(NEW, className);
|
|
mv.visitInsn(DUP);
|
|
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
|
|
mv.visitVarInsn(ASTORE, 1);
|
|
mv.visitVarInsn(ALOAD, 1);
|
|
mv.visitMethodInsn(INVOKEVIRTUAL, className, "test", "()Ljava/lang/Integer;", false);
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(2, 2);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
public Method makeStaticCall(String classname, String method, String descriptor, boolean isInterface) {
|
|
mv.visitMethodInsn(INVOKESTATIC, classname, method, descriptor, isInterface);
|
|
return this;
|
|
}
|
|
|
|
public void makeConstructor(String extending, boolean isInterface) {
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, extending == null ? "java/lang/Object" : extending, "<init>", "()V", isInterface);
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
public void makeInstantiateMethod(String className) {
|
|
mv.visitTypeInsn(NEW, className);
|
|
mv.visitInsn(DUP);
|
|
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
|
|
mv.visitInsn(ARETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
public void done() {
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
}
|