1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package vm.mlvm.cp.share;
  25 
  26 import jdk.internal.org.objectweb.asm.ClassWriter;
  27 import jdk.internal.org.objectweb.asm.ClassWriterExt;
  28 import jdk.internal.org.objectweb.asm.Handle;
  29 import jdk.internal.org.objectweb.asm.MethodVisitor;
  30 import jdk.internal.org.objectweb.asm.Opcodes;
  31 import jdk.internal.org.objectweb.asm.Type;
  32 import jdk.internal.org.objectweb.asm.Label;
  33 
  34 import vm.mlvm.share.ClassfileGenerator;
  35 import vm.mlvm.share.Env;
  36 
  37 public class GenManyIndyIncorrectBootstrap extends GenFullCP {
  38 
  39     /**
  40      * Generates a class file and writes it to a file
  41      * @see vm.mlvm.share.ClassfileGenerator
  42      * @param args Parameters for ClassfileGenerator.main() method
  43      */
  44     public static void main(String[] args) {
  45         ClassfileGenerator.main(args);
  46     }
  47 
  48     /**
  49      * Create class constructor, which
  50      * create a call site for target method
  51      * and puts it into static and instance fields
  52      * @param cw Class writer object
  53      */
  54     @Override
  55     protected void createInitMethod(ClassWriter cw) {
  56         MethodVisitor mw = cw.visitMethod(
  57                 Opcodes.ACC_PUBLIC,
  58                 INIT_METHOD_NAME, INIT_METHOD_SIGNATURE,
  59                 null,
  60                 new String[0]);
  61 
  62         mw.visitVarInsn(Opcodes.ALOAD, 0);
  63         mw.visitMethodInsn(Opcodes.INVOKESPECIAL, PARENT_CLASS_NAME,
  64                 INIT_METHOD_NAME, INIT_METHOD_SIGNATURE);
  65 
  66         // Create a call site for the target method and store it into bootstrap fields
  67         mw.visitVarInsn(Opcodes.ALOAD, 0);
  68         mw.visitTypeInsn(Opcodes.NEW, JLI_CONSTANTCALLSITE);
  69         mw.visitInsn(Opcodes.DUP);
  70         mw.visitMethodInsn(Opcodes.INVOKESTATIC, JLI_METHODHANDLES,
  71                 "lookup", "()" + fd(JLI_METHODHANDLES_LOOKUP));
  72         mw.visitLdcInsn(Type.getObjectType(fullClassName));
  73         mw.visitLdcInsn(TARGET_METHOD_NAME);
  74         mw.visitLdcInsn(TARGET_METHOD_SIGNATURE);
  75         mw.visitLdcInsn(Type.getObjectType(fullClassName));
  76         mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, JL_CLASS,
  77                 "getClassLoader", "()" + fd(JL_CLASSLOADER));
  78         mw.visitMethodInsn(Opcodes.INVOKESTATIC, JLI_METHODTYPE,
  79                 "fromMethodDescriptorString", "(" + fd(JL_STRING) + fd(JL_CLASSLOADER) + ")" + fd(JLI_METHODTYPE));
  80         mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, JLI_METHODHANDLES_LOOKUP,
  81                 "findStatic", "(" + fd(JL_CLASS) + fd(JL_STRING) + fd(JLI_METHODTYPE) + ")" + fd(JLI_METHODHANDLE));
  82         mw.visitMethodInsn(Opcodes.INVOKESPECIAL, JLI_CONSTANTCALLSITE,
  83                 INIT_METHOD_NAME, "(" + fd(JLI_METHODHANDLE) + ")V");
  84         mw.visitInsn(Opcodes.DUP);
  85         mw.visitFieldInsn(Opcodes.PUTSTATIC, fullClassName, STATIC_BOOTSTRAP_FIELD_NAME, STATIC_BOOTSTRAP_FIELD_SIGNATURE);
  86         mw.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, INSTANCE_BOOTSTRAP_FIELD_NAME, INSTANCE_BOOTSTRAP_FIELD_SIGNATURE);
  87 
  88         finishMethodCode(mw);
  89     }
  90 
  91     /**
  92      * Creates a target method which always throw. It should not be called,
  93      * since all invokedynamic instructions have invalid bootstrap method types
  94      * @param cw Class writer object
  95      */
  96     @Override
  97     protected void createTargetMethod(ClassWriter cw) {
  98         createThrowRuntimeExceptionMethod(cw, true, TARGET_METHOD_NAME, TARGET_METHOD_SIGNATURE);
  99     }
 100 
 101     /**
 102      * Creates a bootstrap method which always throw. It should not be called,
 103      * since all invokedynamic instructions have invalid bootstrap method types
 104      * @param cw Class writer object
 105      */
 106     @Override
 107     protected void createBootstrapMethod(ClassWriter cw) {
 108         createThrowRuntimeExceptionMethod(cw, true, BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_SIGNATURE);
 109     }
 110 
 111     /**
 112      * Generates common data for class plus two fields that hold CallSite
 113      * and used as bootstrap targets
 114      * @param cw Class writer object
 115      */
 116     @Override
 117     protected void generateCommonData(ClassWriterExt cw) {
 118         cw.setCacheInvokeDynamic(false);
 119 
 120         cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
 121                 STATIC_BOOTSTRAP_FIELD_NAME,
 122                 STATIC_BOOTSTRAP_FIELD_SIGNATURE, null, null);
 123 
 124         cw.visitField(Opcodes.ACC_PUBLIC,
 125                 INSTANCE_BOOTSTRAP_FIELD_NAME,
 126                 INSTANCE_BOOTSTRAP_FIELD_SIGNATURE, null, null);
 127 
 128         super.generateCommonData(cw);
 129 
 130         createThrowRuntimeExceptionMethod(cw, false, INSTANCE_BOOTSTRAP_METHOD_NAME, INSTANCE_BOOTSTRAP_METHOD_SIGNATURE);
 131     }
 132 
 133     Label throwMethodLabel;
 134 
 135     // The exception to expect that is wrapped in a BootstrapMethodError
 136     static final String WRAPPED_EXCEPTION = "java/lang/invoke/WrongMethodTypeException";
 137 
 138     // The error to expect that is not wrapped in a BootstrapMethodError and
 139     // is thrown directly
 140     static final String DIRECT_ERROR = "java/lang/IncompatibleClassChangeError";
 141 
 142     /**
 143      * Generates an invokedynamic instruction (plus CP entry)
 144      * which has invalid reference kind in the CP method handle entry for the bootstrap method
 145      * @param cw Class writer object
 146      * @param mw Method writer object
 147      */
 148     @Override
 149     protected void generateCPEntryData(ClassWriter cw, MethodVisitor mw) {
 150         HandleType[] types = HandleType.values();
 151         HandleType type = types[Env.getRNG().nextInt(types.length)];
 152 
 153         switch (type) {
 154             case GETFIELD:
 155             case PUTFIELD:
 156             case GETSTATIC:
 157             case PUTSTATIC:
 158             case INVOKESPECIAL:
 159             case INVOKEVIRTUAL:
 160             case INVOKEINTERFACE:
 161                 // Handle these cases
 162                 break;
 163             default:
 164                 // And don't generate code for all other cases
 165                 return;
 166         }
 167 
 168         Label indyThrowableBegin = new Label();
 169         Label indyThrowableEnd = new Label();
 170         Label catchThrowableLabel = new Label();
 171 
 172         Label indyBootstrapBegin = new Label();
 173         Label indyBootstrapEnd = new Label();
 174         Label catchBootstrapLabel = new Label();
 175 
 176         mw.visitTryCatchBlock(indyBootstrapBegin, indyBootstrapEnd, catchBootstrapLabel, JL_BOOTSTRAPMETHODERROR);
 177         mw.visitLabel(indyBootstrapBegin);
 178 
 179         mw.visitTryCatchBlock(indyThrowableBegin, indyThrowableEnd, catchThrowableLabel, JL_THROWABLE);
 180         mw.visitLabel(indyThrowableBegin);
 181 
 182         Handle bsm;
 183         switch (type) {
 184             case GETFIELD:
 185             case PUTFIELD:
 186                 bsm = new Handle(type.asmTag,
 187                         fullClassName,
 188                         INSTANCE_BOOTSTRAP_FIELD_NAME,
 189                         INSTANCE_BOOTSTRAP_FIELD_SIGNATURE);
 190                 break;
 191             case GETSTATIC:
 192             case PUTSTATIC:
 193                 bsm = new Handle(type.asmTag,
 194                         fullClassName,
 195                         STATIC_BOOTSTRAP_FIELD_NAME,
 196                         STATIC_BOOTSTRAP_FIELD_SIGNATURE);
 197                 break;
 198             case INVOKESPECIAL:
 199             case INVOKEVIRTUAL:
 200             case INVOKEINTERFACE:
 201                 bsm = new Handle(type.asmTag,
 202                         fullClassName,
 203                         INSTANCE_BOOTSTRAP_METHOD_NAME,
 204                         INSTANCE_BOOTSTRAP_METHOD_SIGNATURE);
 205                 break;
 206             default:
 207                 throw new Error("Unexpected handle type " + type);
 208         }
 209 
 210         mw.visitInvokeDynamicInsn(TARGET_METHOD_NAME,
 211                 TARGET_METHOD_SIGNATURE,
 212                 bsm);
 213 
 214         mw.visitLabel(indyBootstrapEnd);
 215         mw.visitLabel(indyThrowableEnd);
 216 
 217         // No exception at all, throw error
 218         Label throwLabel = new Label();
 219         mw.visitJumpInsn(Opcodes.GOTO, throwLabel);
 220 
 221         // JDK-8079697 workaround: we have to generate stackmaps manually
 222         mw.visitFrame(Opcodes.F_SAME1, 0, new Object[0], 1, new Object[] { JL_BOOTSTRAPMETHODERROR });
 223 
 224         // Got a bootstrapmethoderror as expected, check that it is wrapping what we expect
 225         mw.visitLabel(catchBootstrapLabel);
 226 
 227         // Save error in case we need to rethrow it
 228         mw.visitInsn(Opcodes.DUP);
 229         mw.visitVarInsn(Opcodes.ASTORE, 1);
 230         mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, JL_THROWABLE, "getCause", "()" + fd(JL_THROWABLE));
 231 
 232         // If it is the expected exception, goto next block
 233         mw.visitTypeInsn(Opcodes.INSTANCEOF, WRAPPED_EXCEPTION);
 234         Label nextBlockLabel = new Label();
 235         mw.visitJumpInsn(Opcodes.IFNE, nextBlockLabel);
 236 
 237         // Not the exception we were expectiong, throw error
 238         mw.visitVarInsn(Opcodes.ALOAD, 1); // Use full chain as cause
 239         createThrowRuntimeExceptionCodeWithCause(mw,
 240                 "invokedynamic got an unexpected wrapped exception (expected " + WRAPPED_EXCEPTION
 241                 + ", bootstrap type=" + type
 242                 + ", opcode=" + type.asmTag + ")!");
 243 
 244         // JDK-8079697 workaround: we have to generate stackmaps manually
 245         mw.visitFrame(Opcodes.F_SAME1, 0, new Object[0], 1, new Object[] { JL_THROWABLE });
 246         mw.visitLabel(catchThrowableLabel);
 247 
 248         // Save error in case we need to rethrow it
 249         mw.visitInsn(Opcodes.DUP);
 250         mw.visitVarInsn(Opcodes.ASTORE, 1);
 251 
 252         // If it is the expected exception, goto next block
 253         mw.visitTypeInsn(Opcodes.INSTANCEOF, DIRECT_ERROR);
 254         mw.visitJumpInsn(Opcodes.IFNE, nextBlockLabel);
 255 
 256         // Not the exception we were expectiong, throw error
 257         mw.visitVarInsn(Opcodes.ALOAD, 1); // Use full chain as cause
 258         createThrowRuntimeExceptionCodeWithCause(mw,
 259                 "invokedynamic got an unexpected exception (expected " + DIRECT_ERROR
 260                 + ", bootstrap type" + type
 261                 + ", opcode=" + type.asmTag + ")!");
 262 
 263         // JDK-8079697 workaround: we have to generate stackmaps manually
 264         mw.visitFrame(Opcodes.F_CHOP, 0, new Object[0], 0, new Object[0]);
 265 
 266         // Unable to place this code once in the method epilog due to bug in ASM
 267         mw.visitLabel(throwLabel);
 268         createThrowRuntimeExceptionCode(mw,
 269                 "invokedynamic should always throw (bootstrap type" + type +", opcode=" + type.asmTag + ")!");
 270 
 271         mw.visitFrame(Opcodes.F_SAME, 0, new Object[0], 0, new Object[0]);
 272         mw.visitLabel(nextBlockLabel);
 273     }
 274 }