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      */
main(String[] args)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
createInitMethod(ClassWriter cw)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
createTargetMethod(ClassWriter cw)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
createBootstrapMethod(ClassWriter cw)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
generateCommonData(ClassWriterExt cw)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
generateCPEntryData(ClassWriter cw, MethodVisitor mw)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 }
275