01b0f9ea7d
Reviewed-by: erikj, mseledtsov, vlivanov
317 lines
13 KiB
Java
317 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2011, 2018, 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 vm.mlvm.cp.share;
|
|
|
|
import jdk.internal.org.objectweb.asm.ByteVector;
|
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
|
import jdk.internal.org.objectweb.asm.ClassWriterExt;
|
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
|
import jdk.internal.org.objectweb.asm.Type;
|
|
|
|
import vm.mlvm.share.ClassfileGenerator;
|
|
import vm.mlvm.share.Env;
|
|
|
|
public abstract class GenFullCP extends ClassfileGenerator {
|
|
|
|
/**
|
|
* Generate field description for object type from class name:
|
|
* return "L" + className + ";";
|
|
* @param className Class name
|
|
* @return field descriptor representing the class type
|
|
*/
|
|
protected static String fd(String className) {
|
|
return "L" + className + ";";
|
|
}
|
|
|
|
// Universal constants
|
|
protected static final String JL_OBJECT = "java/lang/Object";
|
|
protected static final String JL_CLASS = "java/lang/Class";
|
|
protected static final String JL_CLASSLOADER = "java/lang/ClassLoader";
|
|
protected static final String JL_STRING = "java/lang/String";
|
|
protected static final String JL_RUNTIMEEXCEPTION = "java/lang/RuntimeException";
|
|
protected static final String JL_BOOTSTRAPMETHODERROR = "java/lang/BootstrapMethodError";
|
|
protected static final String JL_THROWABLE = "java/lang/Throwable";
|
|
protected static final String JLI_METHODTYPE = "java/lang/invoke/MethodType";
|
|
protected static final String JLI_METHODHANDLE = "java/lang/invoke/MethodHandle";
|
|
protected static final String JLI_METHODHANDLES = "java/lang/invoke/MethodHandles";
|
|
protected static final String JLI_METHODHANDLES_LOOKUP = "java/lang/invoke/MethodHandles$Lookup";
|
|
protected static final String JLI_CALLSITE = "java/lang/invoke/CallSite";
|
|
protected static final String JLI_CONSTANTCALLSITE = "java/lang/invoke/ConstantCallSite";
|
|
|
|
protected static final String VOID_NO_ARG_METHOD_SIGNATURE = "()V";
|
|
|
|
protected static final String NEW_INVOKE_SPECIAL_CLASS_NAME = "java/lang/invoke/NewInvokeSpecialCallSite";
|
|
protected static final String NEW_INVOKE_SPECIAL_BOOTSTRAP_METHOD_SIGNATURE = "(" + fd(JLI_METHODHANDLES_LOOKUP) + fd(JL_STRING) + fd(JLI_METHODTYPE) + ")V";
|
|
|
|
protected static final String INIT_METHOD_NAME = "<init>";
|
|
protected static final String STATIC_INIT_METHOD_NAME = "<clinit>";
|
|
|
|
// Generated class constants
|
|
protected static final int CLASSFILE_VERSION = 51;
|
|
|
|
protected static final int CP_CONST_COUNT = 65400;
|
|
protected static final int MAX_METHOD_SIZE = 65400;
|
|
protected static final int BYTES_PER_LDC = 5;
|
|
protected static final int LDC_PER_METHOD = MAX_METHOD_SIZE / BYTES_PER_LDC;
|
|
protected static final int METHOD_COUNT = CP_CONST_COUNT / LDC_PER_METHOD;
|
|
|
|
protected static final String PARENT_CLASS_NAME = JL_OBJECT;
|
|
|
|
protected static final String INIT_METHOD_SIGNATURE = VOID_NO_ARG_METHOD_SIGNATURE;
|
|
|
|
protected static final String MAIN_METHOD_NAME = "main";
|
|
protected static final String MAIN_METHOD_SIGNATURE = "(" + "[" + fd(JL_STRING) + ")V";
|
|
|
|
protected static final String TEST_METHOD_NAME = "test";
|
|
protected static final String TEST_METHOD_SIGNATURE = VOID_NO_ARG_METHOD_SIGNATURE;
|
|
|
|
protected static final String STATIC_FIELD_NAME = "testStatic";
|
|
protected static final String STATIC_FIELD_SIGNATURE = "Z";
|
|
|
|
protected static final String INSTANCE_FIELD_NAME = "testInstance";
|
|
protected static final String INSTANCE_FIELD_SIGNATURE = "Z";
|
|
|
|
protected static final String STATIC_BOOTSTRAP_FIELD_NAME = "testCSStatic";
|
|
protected static final String STATIC_BOOTSTRAP_FIELD_SIGNATURE = fd(JLI_CALLSITE);
|
|
|
|
protected static final String INSTANCE_BOOTSTRAP_FIELD_NAME = "testCSInstance";
|
|
protected static final String INSTANCE_BOOTSTRAP_FIELD_SIGNATURE = fd(JLI_CALLSITE);
|
|
|
|
protected static final String BOOTSTRAP_METHOD_NAME = "bootstrap";
|
|
protected static final String BOOTSTRAP_METHOD_SIGNATURE = "(" + fd(JLI_METHODHANDLES_LOOKUP) + fd(JL_STRING) + fd(JLI_METHODTYPE) + ")" + fd(JLI_CALLSITE);
|
|
|
|
protected static final String INSTANCE_BOOTSTRAP_METHOD_NAME = "bootstrapInstance";
|
|
protected static final String INSTANCE_BOOTSTRAP_METHOD_SIGNATURE = BOOTSTRAP_METHOD_SIGNATURE;
|
|
|
|
protected static final String TARGET_METHOD_NAME = "target";
|
|
protected static final String TARGET_METHOD_SIGNATURE = VOID_NO_ARG_METHOD_SIGNATURE;
|
|
|
|
protected static final String INSTANCE_TARGET_METHOD_NAME = "targetInstance";
|
|
protected static final String INSTANCE_TARGET_METHOD_SIGNATURE = VOID_NO_ARG_METHOD_SIGNATURE;
|
|
|
|
protected interface DummyInterface {
|
|
public void targetInstance();
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
protected static String getDummyInterfaceClassName() {
|
|
return DummyInterface.class.getName().replace('.', '/');
|
|
}
|
|
|
|
protected static void createLogMsgCode(MethodVisitor mv, String msg) {
|
|
mv.visitLdcInsn(msg);
|
|
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "vm/mlvm/share/Env", "traceVerbose", "(Ljava/lang/String;)V");
|
|
}
|
|
|
|
protected static void createThrowRuntimeExceptionCode(MethodVisitor mv, String msg) {
|
|
createThrowRuntimeExceptionCodeHelper(mv, msg, false);
|
|
}
|
|
|
|
// Expects a throwable (the cause) to be on top of the stack when called.
|
|
protected static void createThrowRuntimeExceptionCodeWithCause(MethodVisitor mv, String msg) {
|
|
createThrowRuntimeExceptionCodeHelper(mv, msg, true);
|
|
}
|
|
|
|
// If set_cause is true it expects a Throwable (the cause) to be on top of the stack when called.
|
|
protected static void createThrowRuntimeExceptionCodeHelper(MethodVisitor mv, String msg, boolean set_cause) {
|
|
mv.visitTypeInsn(Opcodes.NEW, JL_RUNTIMEEXCEPTION);
|
|
mv.visitInsn(Opcodes.DUP);
|
|
mv.visitLdcInsn(msg);
|
|
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, JL_RUNTIMEEXCEPTION,
|
|
INIT_METHOD_NAME, "(" + fd(JL_STRING) + ")V");
|
|
if (set_cause) {
|
|
mv.visitInsn(Opcodes.SWAP);
|
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, JL_RUNTIMEEXCEPTION,
|
|
"initCause", "(" + fd(JL_THROWABLE) + ")"+ fd(JL_THROWABLE));
|
|
}
|
|
mv.visitInsn(Opcodes.ATHROW);
|
|
}
|
|
|
|
protected static void createThrowRuntimeExceptionMethod(ClassWriter cw, boolean isStatic, String methodName, String methodSignature) {
|
|
MethodVisitor mv = cw.visitMethod(
|
|
Opcodes.ACC_PUBLIC | (isStatic ? Opcodes.ACC_STATIC : 0),
|
|
methodName, methodSignature,
|
|
null,
|
|
new String[0]);
|
|
|
|
createThrowRuntimeExceptionCode(mv, "Method " + methodName + methodSignature + " should not be called!");
|
|
|
|
mv.visitMaxs(-1, -1);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
protected static void finishMethodCode(MethodVisitor mv) {
|
|
finishMethodCode(mv, Opcodes.RETURN);
|
|
}
|
|
|
|
protected static void finishMethodCode(MethodVisitor mv, int returnOpcode) {
|
|
mv.visitInsn(returnOpcode);
|
|
mv.visitMaxs(-1, -1);
|
|
mv.visitEnd();
|
|
}
|
|
|
|
protected void createClassInitMethod(ClassWriter cw) {
|
|
}
|
|
|
|
protected void createInitMethod(ClassWriter cw) {
|
|
MethodVisitor mv = cw.visitMethod(
|
|
Opcodes.ACC_PUBLIC,
|
|
INIT_METHOD_NAME, INIT_METHOD_SIGNATURE,
|
|
null,
|
|
new String[0]);
|
|
|
|
mv.visitIntInsn(Opcodes.ALOAD, 0);
|
|
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
|
|
PARENT_CLASS_NAME,
|
|
INIT_METHOD_NAME, INIT_METHOD_SIGNATURE);
|
|
|
|
createLogMsgCode(mv, fullClassName + " constructor called");
|
|
|
|
finishMethodCode(mv);
|
|
}
|
|
|
|
protected void createTargetMethod(ClassWriter cw) {
|
|
MethodVisitor mv = cw.visitMethod(
|
|
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
|
|
TARGET_METHOD_NAME, TARGET_METHOD_SIGNATURE,
|
|
null,
|
|
new String[0]);
|
|
|
|
createLogMsgCode(mv, fullClassName + "." + TARGET_METHOD_NAME + TARGET_METHOD_SIGNATURE + " called");
|
|
|
|
finishMethodCode(mv);
|
|
}
|
|
|
|
protected void createBootstrapMethod(ClassWriter cw) {
|
|
createBootstrapMethod(cw, true, BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_SIGNATURE);
|
|
}
|
|
|
|
protected void createBootstrapMethod(ClassWriter cw, boolean isStatic, String methodName, String methodSignature) {
|
|
MethodVisitor mv = cw.visitMethod(
|
|
(isStatic ? Opcodes.ACC_STATIC : 0) | Opcodes.ACC_PUBLIC,
|
|
methodName, methodSignature,
|
|
null, new String[0]);
|
|
|
|
createLogMsgCode(mv, fullClassName + "." + BOOTSTRAP_METHOD_NAME + BOOTSTRAP_METHOD_SIGNATURE + " called");
|
|
|
|
int argShift = isStatic ? 0 : 1;
|
|
|
|
mv.visitTypeInsn(Opcodes.NEW, JLI_CONSTANTCALLSITE);
|
|
mv.visitInsn(Opcodes.DUP);
|
|
mv.visitVarInsn(Opcodes.ALOAD, 0 + argShift);
|
|
mv.visitLdcInsn(Type.getObjectType(fullClassName));
|
|
mv.visitVarInsn(Opcodes.ALOAD, 1 + argShift);
|
|
mv.visitVarInsn(Opcodes.ALOAD, 2 + argShift);
|
|
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
|
JLI_METHODHANDLES_LOOKUP, "findStatic",
|
|
"(" + fd(JL_CLASS) + fd(JL_STRING) + fd(JLI_METHODTYPE) + ")" + fd(JLI_METHODHANDLE));
|
|
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, JLI_CONSTANTCALLSITE,
|
|
INIT_METHOD_NAME, "(" + fd(JLI_METHODHANDLE) + ")V");
|
|
|
|
finishMethodCode(mv, Opcodes.ARETURN);
|
|
}
|
|
|
|
@Override
|
|
public Klass[] generateBytecodes() {
|
|
|
|
// COMPUTE_FRAMES were disabled due to JDK-8079697
|
|
ClassWriterExt cw = new ClassWriterExt(/*ClassWriter.COMPUTE_FRAMES |*/ ClassWriter.COMPUTE_MAXS);
|
|
|
|
String[] interfaces = new String[1];
|
|
interfaces[0] = getDummyInterfaceClassName();
|
|
cw.visit(CLASSFILE_VERSION, Opcodes.ACC_PUBLIC, fullClassName, null, PARENT_CLASS_NAME, interfaces);
|
|
|
|
generateCommonData(cw);
|
|
|
|
MethodVisitor mainMV = cw.visitMethod(
|
|
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
|
|
MAIN_METHOD_NAME, MAIN_METHOD_SIGNATURE,
|
|
null, new String[0]);
|
|
|
|
mainMV.visitTypeInsn(Opcodes.NEW, fullClassName);
|
|
mainMV.visitInsn(Opcodes.DUP);
|
|
mainMV.visitMethodInsn(Opcodes.INVOKESPECIAL, fullClassName, INIT_METHOD_NAME, INIT_METHOD_SIGNATURE);
|
|
|
|
int constCount = 0;
|
|
int methodNum = 0;
|
|
|
|
// TODO: check real CP size and also limit number of iterations in this cycle
|
|
while (constCount < CP_CONST_COUNT) {
|
|
final String methodName = TEST_METHOD_NAME + String.format("%02d", methodNum);
|
|
|
|
MethodVisitor mw = cw.visitMethod(
|
|
Opcodes.ACC_PUBLIC,
|
|
methodName, TEST_METHOD_SIGNATURE,
|
|
null, new String[0]);
|
|
|
|
generateTestMethodProlog(mw);
|
|
|
|
// TODO: check real CP size and also limit number of iterations in this cycle
|
|
while (constCount < CP_CONST_COUNT && cw.getBytecodeLength(mw) < MAX_METHOD_SIZE) {
|
|
generateCPEntryData(cw, mw);
|
|
++constCount;
|
|
}
|
|
|
|
generateTestMethodEpilog(mw);
|
|
|
|
mw.visitMaxs(-1, -1);
|
|
mw.visitEnd();
|
|
|
|
Env.traceNormal("Method " + fullClassName + "." + methodName + "(): "
|
|
+ constCount + " constants in CP, "
|
|
+ cw.getBytecodeLength(mw) + " bytes of code");
|
|
|
|
mainMV.visitInsn(Opcodes.DUP);
|
|
mainMV.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, methodName, TEST_METHOD_SIGNATURE);
|
|
|
|
++methodNum;
|
|
}
|
|
|
|
mainMV.visitInsn(Opcodes.POP);
|
|
finishMethodCode(mainMV);
|
|
|
|
cw.visitEnd();
|
|
return new Klass[] { new Klass(this.pkgName, this.shortClassName, MAIN_METHOD_NAME, MAIN_METHOD_SIGNATURE, cw.toByteArray()) };
|
|
}
|
|
|
|
protected void generateCommonData(ClassWriterExt cw) {
|
|
createClassInitMethod(cw);
|
|
createInitMethod(cw);
|
|
createTargetMethod(cw);
|
|
createBootstrapMethod(cw);
|
|
}
|
|
|
|
protected void generateTestMethodProlog(MethodVisitor mw) {
|
|
}
|
|
|
|
protected abstract void generateCPEntryData(ClassWriter cw, MethodVisitor mw);
|
|
|
|
protected void generateTestMethodEpilog(MethodVisitor mw) {
|
|
mw.visitInsn(Opcodes.RETURN);
|
|
}
|
|
|
|
}
|