1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.  Oracle designates this
7  * particular file as subject to the "Classpath" exception as provided
8  * by Oracle in the LICENSE file that accompanied this code.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 
25 /*
26  * This file is available under and governed by the GNU General Public
27  * License version 2 only, as published by the Free Software Foundation.
28  * However, the following notice accompanied the original version of this
29  * file:
30  *
31  * ASM: a very small and fast Java bytecode manipulation framework
32  * Copyright (c) 2000-2011 INRIA, France Telecom
33  * All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. Neither the name of the copyright holders nor the names of its
44  *    contributors may be used to endorse or promote products derived from
45  *    this software without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57  * THE POSSIBILITY OF SUCH DAMAGE.
58  */
59 package jdk.internal.org.objectweb.asm.util;
60 
61 import java.io.PrintWriter;
62 import java.io.StringWriter;
63 import java.util.ArrayList;
64 import java.util.Collections;
65 import java.util.HashMap;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Set;
70 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
71 import jdk.internal.org.objectweb.asm.Attribute;
72 import jdk.internal.org.objectweb.asm.ConstantDynamic;
73 import jdk.internal.org.objectweb.asm.Handle;
74 import jdk.internal.org.objectweb.asm.Label;
75 import jdk.internal.org.objectweb.asm.MethodVisitor;
76 import jdk.internal.org.objectweb.asm.Opcodes;
77 import jdk.internal.org.objectweb.asm.Type;
78 import jdk.internal.org.objectweb.asm.TypePath;
79 import jdk.internal.org.objectweb.asm.TypeReference;
80 import jdk.internal.org.objectweb.asm.tree.MethodNode;
81 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
82 import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
83 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
84 import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier;
85 
86 /**
87  * A {@link MethodVisitor} that checks that its methods are properly used. More precisely this
88  * method adapter checks each instruction individually, i.e., each visit method checks some
89  * preconditions based <i>only</i> on its arguments - such as the fact that the given opcode is
90  * correct for a given visit method. This adapter can also perform some basic data flow checks (more
91  * precisely those that can be performed without the full class hierarchy - see {@link
92  * jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is
93  * {@code void m ()}, the invalid instruction IRETURN, or the invalid sequence IADD L2I will be
94  * detected if the data flow checks are enabled. These checks are enabled by using the {@link
95  * #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. They are not performed if
96  * any other constructor is used.
97  *
98  * @author Eric Bruneton
99  */
100 public class CheckMethodAdapter extends MethodVisitor {
101 
102     /** The 'generic' instruction visit methods (i.e. those that take an opcode argument). */
103     private enum Method {
104         VISIT_INSN,
105         VISIT_INT_INSN,
106         VISIT_VAR_INSN,
107         VISIT_TYPE_INSN,
108         VISIT_FIELD_INSN,
109         VISIT_METHOD_INSN,
110         VISIT_JUMP_INSN
111     }
112 
113     /** The method to use to visit each instruction. Only generic methods are represented here. */
114     private static final Method[] OPCODE_METHODS = {
115         Method.VISIT_INSN, // NOP
116         Method.VISIT_INSN, // ACONST_NULL
117         Method.VISIT_INSN, // ICONST_M1
118         Method.VISIT_INSN, // ICONST_0
119         Method.VISIT_INSN, // ICONST_1
120         Method.VISIT_INSN, // ICONST_2
121         Method.VISIT_INSN, // ICONST_3
122         Method.VISIT_INSN, // ICONST_4
123         Method.VISIT_INSN, // ICONST_5
124         Method.VISIT_INSN, // LCONST_0
125         Method.VISIT_INSN, // LCONST_1
126         Method.VISIT_INSN, // FCONST_0
127         Method.VISIT_INSN, // FCONST_1
128         Method.VISIT_INSN, // FCONST_2
129         Method.VISIT_INSN, // DCONST_0
130         Method.VISIT_INSN, // DCONST_1
131         Method.VISIT_INT_INSN, // BIPUSH
132         Method.VISIT_INT_INSN, // SIPUSH
133         null, // LDC
134         null, // LDC_W
135         null, // LDC2_W
136         Method.VISIT_VAR_INSN, // ILOAD
137         Method.VISIT_VAR_INSN, // LLOAD
138         Method.VISIT_VAR_INSN, // FLOAD
139         Method.VISIT_VAR_INSN, // DLOAD
140         Method.VISIT_VAR_INSN, // ALOAD
141         null, // ILOAD_0
142         null, // ILOAD_1
143         null, // ILOAD_2
144         null, // ILOAD_3
145         null, // LLOAD_0
146         null, // LLOAD_1
147         null, // LLOAD_2
148         null, // LLOAD_3
149         null, // FLOAD_0
150         null, // FLOAD_1
151         null, // FLOAD_2
152         null, // FLOAD_3
153         null, // DLOAD_0
154         null, // DLOAD_1
155         null, // DLOAD_2
156         null, // DLOAD_3
157         null, // ALOAD_0
158         null, // ALOAD_1
159         null, // ALOAD_2
160         null, // ALOAD_3
161         Method.VISIT_INSN, // IALOAD
162         Method.VISIT_INSN, // LALOAD
163         Method.VISIT_INSN, // FALOAD
164         Method.VISIT_INSN, // DALOAD
165         Method.VISIT_INSN, // AALOAD
166         Method.VISIT_INSN, // BALOAD
167         Method.VISIT_INSN, // CALOAD
168         Method.VISIT_INSN, // SALOAD
169         Method.VISIT_VAR_INSN, // ISTORE
170         Method.VISIT_VAR_INSN, // LSTORE
171         Method.VISIT_VAR_INSN, // FSTORE
172         Method.VISIT_VAR_INSN, // DSTORE
173         Method.VISIT_VAR_INSN, // ASTORE
174         null, // ISTORE_0
175         null, // ISTORE_1
176         null, // ISTORE_2
177         null, // ISTORE_3
178         null, // LSTORE_0
179         null, // LSTORE_1
180         null, // LSTORE_2
181         null, // LSTORE_3
182         null, // FSTORE_0
183         null, // FSTORE_1
184         null, // FSTORE_2
185         null, // FSTORE_3
186         null, // DSTORE_0
187         null, // DSTORE_1
188         null, // DSTORE_2
189         null, // DSTORE_3
190         null, // ASTORE_0
191         null, // ASTORE_1
192         null, // ASTORE_2
193         null, // ASTORE_3
194         Method.VISIT_INSN, // IASTORE
195         Method.VISIT_INSN, // LASTORE
196         Method.VISIT_INSN, // FASTORE
197         Method.VISIT_INSN, // DASTORE
198         Method.VISIT_INSN, // AASTORE
199         Method.VISIT_INSN, // BASTORE
200         Method.VISIT_INSN, // CASTORE
201         Method.VISIT_INSN, // SASTORE
202         Method.VISIT_INSN, // POP
203         Method.VISIT_INSN, // POP2
204         Method.VISIT_INSN, // DUP
205         Method.VISIT_INSN, // DUP_X1
206         Method.VISIT_INSN, // DUP_X2
207         Method.VISIT_INSN, // DUP2
208         Method.VISIT_INSN, // DUP2_X1
209         Method.VISIT_INSN, // DUP2_X2
210         Method.VISIT_INSN, // SWAP
211         Method.VISIT_INSN, // IADD
212         Method.VISIT_INSN, // LADD
213         Method.VISIT_INSN, // FADD
214         Method.VISIT_INSN, // DADD
215         Method.VISIT_INSN, // ISUB
216         Method.VISIT_INSN, // LSUB
217         Method.VISIT_INSN, // FSUB
218         Method.VISIT_INSN, // DSUB
219         Method.VISIT_INSN, // IMUL
220         Method.VISIT_INSN, // LMUL
221         Method.VISIT_INSN, // FMUL
222         Method.VISIT_INSN, // DMUL
223         Method.VISIT_INSN, // IDIV
224         Method.VISIT_INSN, // LDIV
225         Method.VISIT_INSN, // FDIV
226         Method.VISIT_INSN, // DDIV
227         Method.VISIT_INSN, // IREM
228         Method.VISIT_INSN, // LREM
229         Method.VISIT_INSN, // FREM
230         Method.VISIT_INSN, // DREM
231         Method.VISIT_INSN, // INEG
232         Method.VISIT_INSN, // LNEG
233         Method.VISIT_INSN, // FNEG
234         Method.VISIT_INSN, // DNEG
235         Method.VISIT_INSN, // ISHL
236         Method.VISIT_INSN, // LSHL
237         Method.VISIT_INSN, // ISHR
238         Method.VISIT_INSN, // LSHR
239         Method.VISIT_INSN, // IUSHR
240         Method.VISIT_INSN, // LUSHR
241         Method.VISIT_INSN, // IAND
242         Method.VISIT_INSN, // LAND
243         Method.VISIT_INSN, // IOR
244         Method.VISIT_INSN, // LOR
245         Method.VISIT_INSN, // IXOR
246         Method.VISIT_INSN, // LXOR
247         null, // IINC
248         Method.VISIT_INSN, // I2L
249         Method.VISIT_INSN, // I2F
250         Method.VISIT_INSN, // I2D
251         Method.VISIT_INSN, // L2I
252         Method.VISIT_INSN, // L2F
253         Method.VISIT_INSN, // L2D
254         Method.VISIT_INSN, // F2I
255         Method.VISIT_INSN, // F2L
256         Method.VISIT_INSN, // F2D
257         Method.VISIT_INSN, // D2I
258         Method.VISIT_INSN, // D2L
259         Method.VISIT_INSN, // D2F
260         Method.VISIT_INSN, // I2B
261         Method.VISIT_INSN, // I2C
262         Method.VISIT_INSN, // I2S
263         Method.VISIT_INSN, // LCMP
264         Method.VISIT_INSN, // FCMPL
265         Method.VISIT_INSN, // FCMPG
266         Method.VISIT_INSN, // DCMPL
267         Method.VISIT_INSN, // DCMPG
268         Method.VISIT_JUMP_INSN, // IFEQ
269         Method.VISIT_JUMP_INSN, // IFNE
270         Method.VISIT_JUMP_INSN, // IFLT
271         Method.VISIT_JUMP_INSN, // IFGE
272         Method.VISIT_JUMP_INSN, // IFGT
273         Method.VISIT_JUMP_INSN, // IFLE
274         Method.VISIT_JUMP_INSN, // IF_ICMPEQ
275         Method.VISIT_JUMP_INSN, // IF_ICMPNE
276         Method.VISIT_JUMP_INSN, // IF_ICMPLT
277         Method.VISIT_JUMP_INSN, // IF_ICMPGE
278         Method.VISIT_JUMP_INSN, // IF_ICMPGT
279         Method.VISIT_JUMP_INSN, // IF_ICMPLE
280         Method.VISIT_JUMP_INSN, // IF_ACMPEQ
281         Method.VISIT_JUMP_INSN, // IF_ACMPNE
282         Method.VISIT_JUMP_INSN, // GOTO
283         Method.VISIT_JUMP_INSN, // JSR
284         Method.VISIT_VAR_INSN, // RET
285         null, // TABLESWITCH
286         null, // LOOKUPSWITCH
287         Method.VISIT_INSN, // IRETURN
288         Method.VISIT_INSN, // LRETURN
289         Method.VISIT_INSN, // FRETURN
290         Method.VISIT_INSN, // DRETURN
291         Method.VISIT_INSN, // ARETURN
292         Method.VISIT_INSN, // RETURN
293         Method.VISIT_FIELD_INSN, // GETSTATIC
294         Method.VISIT_FIELD_INSN, // PUTSTATIC
295         Method.VISIT_FIELD_INSN, // GETFIELD
296         Method.VISIT_FIELD_INSN, // PUTFIELD
297         Method.VISIT_METHOD_INSN, // INVOKEVIRTUAL
298         Method.VISIT_METHOD_INSN, // INVOKESPECIAL
299         Method.VISIT_METHOD_INSN, // INVOKESTATIC
300         Method.VISIT_METHOD_INSN, // INVOKEINTERFACE
301         null, // INVOKEDYNAMIC
302         Method.VISIT_TYPE_INSN, // NEW
303         Method.VISIT_INT_INSN, // NEWARRAY
304         Method.VISIT_TYPE_INSN, // ANEWARRAY
305         Method.VISIT_INSN, // ARRAYLENGTH
306         Method.VISIT_INSN, // ATHROW
307         Method.VISIT_TYPE_INSN, // CHECKCAST
308         Method.VISIT_TYPE_INSN, // INSTANCEOF
309         Method.VISIT_INSN, // MONITORENTER
310         Method.VISIT_INSN, // MONITOREXIT
311         null, // WIDE
312         null, // MULTIANEWARRAY
313         Method.VISIT_JUMP_INSN, // IFNULL
314         Method.VISIT_JUMP_INSN // IFNONNULL
315     };
316 
317     private static final String INVALID = "Invalid ";
318     private static final String INVALID_DESCRIPTOR = "Invalid descriptor: ";
319     private static final String INVALID_TYPE_REFERENCE = "Invalid type reference sort 0x";
320     private static final String INVALID_LOCAL_VARIABLE_INDEX = "Invalid local variable index";
321     private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)";
322     private static final String START_LABEL = "start label";
323     private static final String END_LABEL = "end label";
324 
325     /** The class version number. */
326     public int version;
327 
328     /** The access flags of the visited method. */
329     private int access;
330 
331     /**
332       * The number of method parameters that can have runtime visible annotations. 0 means that all the
333       * parameters from the method descriptor can have annotations.
334       */
335     private int visibleAnnotableParameterCount;
336 
337     /**
338       * The number of method parameters that can have runtime invisible annotations. 0 means that all
339       * the parameters from the method descriptor can have annotations.
340       */
341     private int invisibleAnnotableParameterCount;
342 
343     /** Whether the {@link #visitCode} method has been called. */
344     private boolean visitCodeCalled;
345 
346     /** Whether the {@link #visitMaxs} method has been called. */
347     private boolean visitMaxCalled;
348 
349     /** Whether the {@link #visitEnd} method has been called. */
350     private boolean visitEndCalled;
351 
352     /** The number of visited instructions so far. */
353     private int insnCount;
354 
355     /** The index of the instruction designated by each visited label. */
356     private final Map<Label, Integer> labelInsnIndices;
357 
358     /** The labels referenced by the visited method. */
359     private Set<Label> referencedLabels;
360 
361     /** The index of the instruction corresponding to the last visited stack map frame. */
362     private int lastFrameInsnIndex = -1;
363 
364     /** The number of visited frames in expanded form. */
365     private int numExpandedFrames;
366 
367     /** The number of visited frames in compressed form. */
368     private int numCompressedFrames;
369 
370     /**
371       * The exception handler ranges. Each pair of list element contains the start and end labels of an
372       * exception handler block.
373       */
374     private List<Label> handlers;
375 
376     /**
377       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
378       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
379       * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link
380       * #CheckMethodAdapter(int, MethodVisitor, Map)} version.
381       *
382       * @param methodvisitor the method visitor to which this adapter must delegate calls.
383       */
CheckMethodAdapter(final MethodVisitor methodvisitor)384     public CheckMethodAdapter(final MethodVisitor methodvisitor) {
385         this(methodvisitor, new HashMap<Label, Integer>());
386     }
387 
388     /**
389       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
390       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
391       * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link
392       * #CheckMethodAdapter(int, MethodVisitor, Map)} version.
393       *
394       * @param methodVisitor the method visitor to which this adapter must delegate calls.
395       * @param labelInsnIndices the index of the instruction designated by each visited label so far
396       *     (in other methods). This map is updated with the labels from the visited method.
397       * @throws IllegalStateException If a subclass calls this constructor.
398       */
CheckMethodAdapter( final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices)399     public CheckMethodAdapter(
400             final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) {
401         this(/* latest api = */ Opcodes.ASM8, methodVisitor, labelInsnIndices);
402         if (getClass() != CheckMethodAdapter.class) {
403             throw new IllegalStateException();
404         }
405     }
406 
407     /**
408       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
409       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
410       *
411       * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link
412       *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
413       *     Opcodes#ASM8}.
414       * @param methodVisitor the method visitor to which this adapter must delegate calls.
415       * @param labelInsnIndices the index of the instruction designated by each visited label so far
416       *     (in other methods). This map is updated with the labels from the visited method.
417       */
CheckMethodAdapter( final int api, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices)418     protected CheckMethodAdapter(
419             final int api,
420             final MethodVisitor methodVisitor,
421             final Map<Label, Integer> labelInsnIndices) {
422         super(api, methodVisitor);
423         this.labelInsnIndices = labelInsnIndices;
424         this.referencedLabels = new HashSet<>();
425         this.handlers = new ArrayList<>();
426     }
427 
428     /**
429       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data
430       * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid
431       * instruction IRETURN, or the invalid sequence IADD L2I will be detected. <i>Subclasses must not
432       * use this constructor</i>. Instead, they must use the {@link
433       * #CheckMethodAdapter(int,int,String,String,MethodVisitor,Map)} version.
434       *
435       * @param access the method's access flags.
436       * @param name the method's name.
437       * @param descriptor the method's descriptor (see {@link Type}).
438       * @param methodVisitor the method visitor to which this adapter must delegate calls.
439       * @param labelInsnIndices the index of the instruction designated by each visited label so far
440       *     (in other methods). This map is updated with the labels from the visited method.
441       */
CheckMethodAdapter( final int access, final String name, final String descriptor, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices)442     public CheckMethodAdapter(
443             final int access,
444             final String name,
445             final String descriptor,
446             final MethodVisitor methodVisitor,
447             final Map<Label, Integer> labelInsnIndices) {
448         this(
449                 /* latest api = */ Opcodes.ASM8, access, name, descriptor, methodVisitor, labelInsnIndices);
450         if (getClass() != CheckMethodAdapter.class) {
451             throw new IllegalStateException();
452         }
453     }
454 
455     /**
456       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data
457       * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid
458       * instruction IRETURN, or the invalid sequence IADD L2I will be detected.
459       *
460       * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link
461       *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link
462       *     Opcodes#ASM8}.
463       * @param access the method's access flags.
464       * @param name the method's name.
465       * @param descriptor the method's descriptor (see {@link Type}).
466       * @param methodVisitor the method visitor to which this adapter must delegate calls.
467       * @param labelInsnIndices the index of the instruction designated by each visited label so far
468       *     (in other methods). This map is updated with the labels from the visited method.
469       */
CheckMethodAdapter( final int api, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices)470     protected CheckMethodAdapter(
471             final int api,
472             final int access,
473             final String name,
474             final String descriptor,
475             final MethodVisitor methodVisitor,
476             final Map<Label, Integer> labelInsnIndices) {
477         this(
478                 api,
479                 new MethodNode(api, access, name, descriptor, null, null) {
480                     @Override
481                     public void visitEnd() {
482                         Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicVerifier());
483                         try {
484                             analyzer.analyze("dummy", this);
485                         } catch (IndexOutOfBoundsException e) {
486                             if (maxLocals == 0 && maxStack == 0) {
487                                 throw new IllegalArgumentException(
488                                         "Data flow checking option requires valid, non zero maxLocals and maxStack.",
489                                         e);
490                             }
491                             throwError(analyzer, e);
492                         } catch (AnalyzerException e) {
493                             throwError(analyzer, e);
494                         }
495                         if (methodVisitor != null) {
496                             accept(methodVisitor);
497                         }
498                     }
499 
500                     private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) {
501                         StringWriter stringWriter = new StringWriter();
502                         PrintWriter printWriter = new PrintWriter(stringWriter, true);
503                         CheckClassAdapter.printAnalyzerResult(this, analyzer, printWriter);
504                         printWriter.close();
505                         throw new IllegalArgumentException(e.getMessage() + ' ' + stringWriter.toString(), e);
506                     }
507                 },
508                 labelInsnIndices);
509         this.access = access;
510     }
511 
512     @Override
visitParameter(final String name, final int access)513     public void visitParameter(final String name, final int access) {
514         if (name != null) {
515             checkUnqualifiedName(version, name, "name");
516         }
517         CheckClassAdapter.checkAccess(
518                 access, Opcodes.ACC_FINAL + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC);
519         super.visitParameter(name, access);
520     }
521 
522     @Override
visitAnnotation(final String descriptor, final boolean visible)523     public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
524         checkVisitEndNotCalled();
525         checkDescriptor(version, descriptor, false);
526         return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
527     }
528 
529     @Override
visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)530     public AnnotationVisitor visitTypeAnnotation(
531             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
532         checkVisitEndNotCalled();
533         int sort = new TypeReference(typeRef).getSort();
534         if (sort != TypeReference.METHOD_TYPE_PARAMETER
535                 && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND
536                 && sort != TypeReference.METHOD_RETURN
537                 && sort != TypeReference.METHOD_RECEIVER
538                 && sort != TypeReference.METHOD_FORMAL_PARAMETER
539                 && sort != TypeReference.THROWS) {
540             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
541         }
542         CheckClassAdapter.checkTypeRef(typeRef);
543         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
544         return new CheckAnnotationAdapter(
545                 super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
546     }
547 
548     @Override
visitAnnotationDefault()549     public AnnotationVisitor visitAnnotationDefault() {
550         checkVisitEndNotCalled();
551         return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false);
552     }
553 
554     @Override
visitAnnotableParameterCount(final int parameterCount, final boolean visible)555     public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) {
556         checkVisitEndNotCalled();
557         if (visible) {
558             visibleAnnotableParameterCount = parameterCount;
559         } else {
560             invisibleAnnotableParameterCount = parameterCount;
561         }
562         super.visitAnnotableParameterCount(parameterCount, visible);
563     }
564 
565     @Override
visitParameterAnnotation( final int parameter, final String descriptor, final boolean visible)566     public AnnotationVisitor visitParameterAnnotation(
567             final int parameter, final String descriptor, final boolean visible) {
568         checkVisitEndNotCalled();
569         if ((visible
570                         && visibleAnnotableParameterCount > 0
571                         && parameter >= visibleAnnotableParameterCount)
572                 || (!visible
573                         && invisibleAnnotableParameterCount > 0
574                         && parameter >= invisibleAnnotableParameterCount)) {
575             throw new IllegalArgumentException("Invalid parameter index");
576         }
577         checkDescriptor(version, descriptor, false);
578         return new CheckAnnotationAdapter(
579                 super.visitParameterAnnotation(parameter, descriptor, visible));
580     }
581 
582     @Override
visitAttribute(final Attribute attribute)583     public void visitAttribute(final Attribute attribute) {
584         checkVisitEndNotCalled();
585         if (attribute == null) {
586             throw new IllegalArgumentException("Invalid attribute (must not be null)");
587         }
588         super.visitAttribute(attribute);
589     }
590 
591     @Override
visitCode()592     public void visitCode() {
593         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
594             throw new UnsupportedOperationException("Abstract methods cannot have code");
595         }
596         visitCodeCalled = true;
597         super.visitCode();
598     }
599 
600     @Override
visitFrame( final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack)601     public void visitFrame(
602             final int type,
603             final int numLocal,
604             final Object[] local,
605             final int numStack,
606             final Object[] stack) {
607         if (insnCount == lastFrameInsnIndex) {
608             throw new IllegalStateException("At most one frame can be visited at a given code location.");
609         }
610         lastFrameInsnIndex = insnCount;
611         int maxNumLocal;
612         int maxNumStack;
613         switch (type) {
614             case Opcodes.F_NEW:
615             case Opcodes.F_FULL:
616                 maxNumLocal = Integer.MAX_VALUE;
617                 maxNumStack = Integer.MAX_VALUE;
618                 break;
619 
620             case Opcodes.F_SAME:
621                 maxNumLocal = 0;
622                 maxNumStack = 0;
623                 break;
624 
625             case Opcodes.F_SAME1:
626                 maxNumLocal = 0;
627                 maxNumStack = 1;
628                 break;
629 
630             case Opcodes.F_APPEND:
631             case Opcodes.F_CHOP:
632                 maxNumLocal = 3;
633                 maxNumStack = 0;
634                 break;
635 
636             default:
637                 throw new IllegalArgumentException("Invalid frame type " + type);
638         }
639 
640         if (numLocal > maxNumLocal) {
641             throw new IllegalArgumentException(
642                     "Invalid numLocal=" + numLocal + " for frame type " + type);
643         }
644         if (numStack > maxNumStack) {
645             throw new IllegalArgumentException(
646                     "Invalid numStack=" + numStack + " for frame type " + type);
647         }
648 
649         if (type != Opcodes.F_CHOP) {
650             if (numLocal > 0 && (local == null || local.length < numLocal)) {
651                 throw new IllegalArgumentException("Array local[] is shorter than numLocal");
652             }
653             for (int i = 0; i < numLocal; ++i) {
654                 checkFrameValue(local[i]);
655             }
656         }
657         if (numStack > 0 && (stack == null || stack.length < numStack)) {
658             throw new IllegalArgumentException("Array stack[] is shorter than numStack");
659         }
660         for (int i = 0; i < numStack; ++i) {
661             checkFrameValue(stack[i]);
662         }
663         if (type == Opcodes.F_NEW) {
664             ++numExpandedFrames;
665         } else {
666             ++numCompressedFrames;
667         }
668         if (numExpandedFrames > 0 && numCompressedFrames > 0) {
669             throw new IllegalArgumentException("Expanded and compressed frames must not be mixed.");
670         }
671         super.visitFrame(type, numLocal, local, numStack, stack);
672     }
673 
674     @Override
visitInsn(final int opcode)675     public void visitInsn(final int opcode) {
676         checkVisitCodeCalled();
677         checkVisitMaxsNotCalled();
678         checkOpcodeMethod(opcode, Method.VISIT_INSN);
679         super.visitInsn(opcode);
680         ++insnCount;
681     }
682 
683     @Override
visitIntInsn(final int opcode, final int operand)684     public void visitIntInsn(final int opcode, final int operand) {
685         checkVisitCodeCalled();
686         checkVisitMaxsNotCalled();
687         checkOpcodeMethod(opcode, Method.VISIT_INT_INSN);
688         switch (opcode) {
689             case Opcodes.BIPUSH:
690                 checkSignedByte(operand, "Invalid operand");
691                 break;
692             case Opcodes.SIPUSH:
693                 checkSignedShort(operand, "Invalid operand");
694                 break;
695             case Opcodes.NEWARRAY:
696                 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
697                     throw new IllegalArgumentException(
698                             "Invalid operand (must be an array type code T_...): " + operand);
699                 }
700                 break;
701             default:
702                 throw new AssertionError();
703         }
704         super.visitIntInsn(opcode, operand);
705         ++insnCount;
706     }
707 
708     @Override
visitVarInsn(final int opcode, final int var)709     public void visitVarInsn(final int opcode, final int var) {
710         checkVisitCodeCalled();
711         checkVisitMaxsNotCalled();
712         checkOpcodeMethod(opcode, Method.VISIT_VAR_INSN);
713         checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX);
714         super.visitVarInsn(opcode, var);
715         ++insnCount;
716     }
717 
718     @Override
visitTypeInsn(final int opcode, final String type)719     public void visitTypeInsn(final int opcode, final String type) {
720         checkVisitCodeCalled();
721         checkVisitMaxsNotCalled();
722         checkOpcodeMethod(opcode, Method.VISIT_TYPE_INSN);
723         checkInternalName(version, type, "type");
724         if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
725             throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type);
726         }
727         super.visitTypeInsn(opcode, type);
728         ++insnCount;
729     }
730 
731     @Override
visitFieldInsn( final int opcode, final String owner, final String name, final String descriptor)732     public void visitFieldInsn(
733             final int opcode, final String owner, final String name, final String descriptor) {
734         checkVisitCodeCalled();
735         checkVisitMaxsNotCalled();
736         checkOpcodeMethod(opcode, Method.VISIT_FIELD_INSN);
737         checkInternalName(version, owner, "owner");
738         checkUnqualifiedName(version, name, "name");
739         checkDescriptor(version, descriptor, false);
740         super.visitFieldInsn(opcode, owner, name, descriptor);
741         ++insnCount;
742     }
743 
744     @Override
visitMethodInsn( final int opcodeAndSource, final String owner, final String name, final String descriptor, final boolean isInterface)745     public void visitMethodInsn(
746             final int opcodeAndSource,
747             final String owner,
748             final String name,
749             final String descriptor,
750             final boolean isInterface) {
751         if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
752             // Redirect the call to the deprecated version of this method.
753             super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
754             return;
755         }
756         int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
757 
758         checkVisitCodeCalled();
759         checkVisitMaxsNotCalled();
760         checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN);
761         if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
762             checkMethodIdentifier(version, name, "name");
763         }
764         checkInternalName(version, owner, "owner");
765         checkMethodDescriptor(version, descriptor);
766         if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) {
767             throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces");
768         }
769         if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) {
770             throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes");
771         }
772         if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) {
773             throw new IllegalArgumentException(
774                     "INVOKESPECIAL can't be used with interfaces prior to Java 8");
775         }
776         super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
777         ++insnCount;
778     }
779 
780     @Override
visitInvokeDynamicInsn( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments)781     public void visitInvokeDynamicInsn(
782             final String name,
783             final String descriptor,
784             final Handle bootstrapMethodHandle,
785             final Object... bootstrapMethodArguments) {
786         checkVisitCodeCalled();
787         checkVisitMaxsNotCalled();
788         checkMethodIdentifier(version, name, "name");
789         checkMethodDescriptor(version, descriptor);
790         if (bootstrapMethodHandle.getTag() != Opcodes.H_INVOKESTATIC
791                 && bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
792             throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag());
793         }
794         for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
795             checkLdcConstant(bootstrapMethodArgument);
796         }
797         super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
798         ++insnCount;
799     }
800 
801     @Override
visitJumpInsn(final int opcode, final Label label)802     public void visitJumpInsn(final int opcode, final Label label) {
803         checkVisitCodeCalled();
804         checkVisitMaxsNotCalled();
805         checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN);
806         checkLabel(label, false, "label");
807         super.visitJumpInsn(opcode, label);
808         referencedLabels.add(label);
809         ++insnCount;
810     }
811 
812     @Override
visitLabel(final Label label)813     public void visitLabel(final Label label) {
814         checkVisitCodeCalled();
815         checkVisitMaxsNotCalled();
816         checkLabel(label, false, "label");
817         if (labelInsnIndices.get(label) != null) {
818             throw new IllegalArgumentException("Already visited label");
819         }
820         labelInsnIndices.put(label, insnCount);
821         super.visitLabel(label);
822     }
823 
824     @Override
visitLdcInsn(final Object value)825     public void visitLdcInsn(final Object value) {
826         checkVisitCodeCalled();
827         checkVisitMaxsNotCalled();
828         checkLdcConstant(value);
829         super.visitLdcInsn(value);
830         ++insnCount;
831     }
832 
833     @Override
visitIincInsn(final int var, final int increment)834     public void visitIincInsn(final int var, final int increment) {
835         checkVisitCodeCalled();
836         checkVisitMaxsNotCalled();
837         checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX);
838         checkSignedShort(increment, "Invalid increment");
839         super.visitIincInsn(var, increment);
840         ++insnCount;
841     }
842 
843     @Override
visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label... labels)844     public void visitTableSwitchInsn(
845             final int min, final int max, final Label dflt, final Label... labels) {
846         checkVisitCodeCalled();
847         checkVisitMaxsNotCalled();
848         if (max < min) {
849             throw new IllegalArgumentException(
850                     "Max = " + max + " must be greater than or equal to min = " + min);
851         }
852         checkLabel(dflt, false, "default label");
853         if (labels == null || labels.length != max - min + 1) {
854             throw new IllegalArgumentException("There must be max - min + 1 labels");
855         }
856         for (int i = 0; i < labels.length; ++i) {
857             checkLabel(labels[i], false, "label at index " + i);
858         }
859         super.visitTableSwitchInsn(min, max, dflt, labels);
860         Collections.addAll(referencedLabels, labels);
861         ++insnCount;
862     }
863 
864     @Override
visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)865     public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
866         checkVisitMaxsNotCalled();
867         checkVisitCodeCalled();
868         checkLabel(dflt, false, "default label");
869         if (keys == null || labels == null || keys.length != labels.length) {
870             throw new IllegalArgumentException("There must be the same number of keys and labels");
871         }
872         for (int i = 0; i < labels.length; ++i) {
873             checkLabel(labels[i], false, "label at index " + i);
874         }
875         super.visitLookupSwitchInsn(dflt, keys, labels);
876         referencedLabels.add(dflt);
877         Collections.addAll(referencedLabels, labels);
878         ++insnCount;
879     }
880 
881     @Override
visitMultiANewArrayInsn(final String descriptor, final int numDimensions)882     public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) {
883         checkVisitCodeCalled();
884         checkVisitMaxsNotCalled();
885         checkDescriptor(version, descriptor, false);
886         if (descriptor.charAt(0) != '[') {
887             throw new IllegalArgumentException(
888                     "Invalid descriptor (must be an array type descriptor): " + descriptor);
889         }
890         if (numDimensions < 1) {
891             throw new IllegalArgumentException(
892                     "Invalid dimensions (must be greater than 0): " + numDimensions);
893         }
894         if (numDimensions > descriptor.lastIndexOf('[') + 1) {
895             throw new IllegalArgumentException(
896                     "Invalid dimensions (must not be greater than numDimensions(descriptor)): "
897                             + numDimensions);
898         }
899         super.visitMultiANewArrayInsn(descriptor, numDimensions);
900         ++insnCount;
901     }
902 
903     @Override
visitInsnAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)904     public AnnotationVisitor visitInsnAnnotation(
905             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
906         checkVisitCodeCalled();
907         checkVisitMaxsNotCalled();
908         int sort = new TypeReference(typeRef).getSort();
909         if (sort != TypeReference.INSTANCEOF
910                 && sort != TypeReference.NEW
911                 && sort != TypeReference.CONSTRUCTOR_REFERENCE
912                 && sort != TypeReference.METHOD_REFERENCE
913                 && sort != TypeReference.CAST
914                 && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
915                 && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT
916                 && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
917                 && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) {
918             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
919         }
920         CheckClassAdapter.checkTypeRef(typeRef);
921         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
922         return new CheckAnnotationAdapter(
923                 super.visitInsnAnnotation(typeRef, typePath, descriptor, visible));
924     }
925 
926     @Override
visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type)927     public void visitTryCatchBlock(
928             final Label start, final Label end, final Label handler, final String type) {
929         checkVisitCodeCalled();
930         checkVisitMaxsNotCalled();
931         checkLabel(start, false, START_LABEL);
932         checkLabel(end, false, END_LABEL);
933         checkLabel(handler, false, "handler label");
934         if (labelInsnIndices.get(start) != null
935                 || labelInsnIndices.get(end) != null
936                 || labelInsnIndices.get(handler) != null) {
937             throw new IllegalStateException("Try catch blocks must be visited before their labels");
938         }
939         if (type != null) {
940             checkInternalName(version, type, "type");
941         }
942         super.visitTryCatchBlock(start, end, handler, type);
943         handlers.add(start);
944         handlers.add(end);
945     }
946 
947     @Override
visitTryCatchAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)948     public AnnotationVisitor visitTryCatchAnnotation(
949             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
950         checkVisitCodeCalled();
951         checkVisitMaxsNotCalled();
952         int sort = new TypeReference(typeRef).getSort();
953         if (sort != TypeReference.EXCEPTION_PARAMETER) {
954             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
955         }
956         CheckClassAdapter.checkTypeRef(typeRef);
957         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
958         return new CheckAnnotationAdapter(
959                 super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible));
960     }
961 
962     @Override
visitLocalVariable( final String name, final String descriptor, final String signature, final Label start, final Label end, final int index)963     public void visitLocalVariable(
964             final String name,
965             final String descriptor,
966             final String signature,
967             final Label start,
968             final Label end,
969             final int index) {
970         checkVisitCodeCalled();
971         checkVisitMaxsNotCalled();
972         checkUnqualifiedName(version, name, "name");
973         checkDescriptor(version, descriptor, false);
974         if (signature != null) {
975             CheckClassAdapter.checkFieldSignature(signature);
976         }
977         checkLabel(start, true, START_LABEL);
978         checkLabel(end, true, END_LABEL);
979         checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX);
980         int startInsnIndex = labelInsnIndices.get(start).intValue();
981         int endInsnIndex = labelInsnIndices.get(end).intValue();
982         if (endInsnIndex < startInsnIndex) {
983             throw new IllegalArgumentException(
984                     "Invalid start and end labels (end must be greater than start)");
985         }
986         super.visitLocalVariable(name, descriptor, signature, start, end, index);
987     }
988 
989     @Override
visitLocalVariableAnnotation( final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible)990     public AnnotationVisitor visitLocalVariableAnnotation(
991             final int typeRef,
992             final TypePath typePath,
993             final Label[] start,
994             final Label[] end,
995             final int[] index,
996             final String descriptor,
997             final boolean visible) {
998         checkVisitCodeCalled();
999         checkVisitMaxsNotCalled();
1000         int sort = new TypeReference(typeRef).getSort();
1001         if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) {
1002             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
1003         }
1004         CheckClassAdapter.checkTypeRef(typeRef);
1005         checkDescriptor(version, descriptor, false);
1006         if (start == null
1007                 || end == null
1008                 || index == null
1009                 || end.length != start.length
1010                 || index.length != start.length) {
1011             throw new IllegalArgumentException(
1012                     "Invalid start, end and index arrays (must be non null and of identical length");
1013         }
1014         for (int i = 0; i < start.length; ++i) {
1015             checkLabel(start[i], true, START_LABEL);
1016             checkLabel(end[i], true, END_LABEL);
1017             checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX);
1018             int startInsnIndex = labelInsnIndices.get(start[i]).intValue();
1019             int endInsnIndex = labelInsnIndices.get(end[i]).intValue();
1020             if (endInsnIndex < startInsnIndex) {
1021                 throw new IllegalArgumentException(
1022                         "Invalid start and end labels (end must be greater than start)");
1023             }
1024         }
1025         return super.visitLocalVariableAnnotation(
1026                 typeRef, typePath, start, end, index, descriptor, visible);
1027     }
1028 
1029     @Override
visitLineNumber(final int line, final Label start)1030     public void visitLineNumber(final int line, final Label start) {
1031         checkVisitCodeCalled();
1032         checkVisitMaxsNotCalled();
1033         checkUnsignedShort(line, "Invalid line number");
1034         checkLabel(start, true, START_LABEL);
1035         super.visitLineNumber(line, start);
1036     }
1037 
1038     @Override
visitMaxs(final int maxStack, final int maxLocals)1039     public void visitMaxs(final int maxStack, final int maxLocals) {
1040         checkVisitCodeCalled();
1041         checkVisitMaxsNotCalled();
1042         visitMaxCalled = true;
1043         for (Label l : referencedLabels) {
1044             if (labelInsnIndices.get(l) == null) {
1045                 throw new IllegalStateException("Undefined label used");
1046             }
1047         }
1048         for (int i = 0; i < handlers.size(); i += 2) {
1049             Integer startInsnIndex = labelInsnIndices.get(handlers.get(i));
1050             Integer endInsnIndex = labelInsnIndices.get(handlers.get(i + 1));
1051             if (startInsnIndex == null || endInsnIndex == null) {
1052                 throw new IllegalStateException("Undefined try catch block labels");
1053             }
1054             if (endInsnIndex.intValue() <= startInsnIndex.intValue()) {
1055                 throw new IllegalStateException("Emty try catch block handler range");
1056             }
1057         }
1058         checkUnsignedShort(maxStack, "Invalid max stack");
1059         checkUnsignedShort(maxLocals, "Invalid max locals");
1060         super.visitMaxs(maxStack, maxLocals);
1061     }
1062 
1063     @Override
visitEnd()1064     public void visitEnd() {
1065         checkVisitEndNotCalled();
1066         visitEndCalled = true;
1067         super.visitEnd();
1068     }
1069 
1070     // -----------------------------------------------------------------------------------------------
1071     // Utility methods
1072     // -----------------------------------------------------------------------------------------------
1073 
1074     /** Checks that the {@link #visitCode} method has been called. */
checkVisitCodeCalled()1075     private void checkVisitCodeCalled() {
1076         if (!visitCodeCalled) {
1077             throw new IllegalStateException(
1078                     "Cannot visit instructions before visitCode has been called.");
1079         }
1080     }
1081 
1082     /** Checks that the {@link #visitMaxs} method has not been called. */
checkVisitMaxsNotCalled()1083     private void checkVisitMaxsNotCalled() {
1084         if (visitMaxCalled) {
1085             throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
1086         }
1087     }
1088 
1089     /** Checks that the {@link #visitEnd} method has not been called. */
checkVisitEndNotCalled()1090     private void checkVisitEndNotCalled() {
1091         if (visitEndCalled) {
1092             throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
1093         }
1094     }
1095 
1096     /**
1097       * Checks a stack frame value.
1098       *
1099       * @param value the value to be checked.
1100       */
checkFrameValue(final Object value)1101     private void checkFrameValue(final Object value) {
1102         if (value == Opcodes.TOP
1103                 || value == Opcodes.INTEGER
1104                 || value == Opcodes.FLOAT
1105                 || value == Opcodes.LONG
1106                 || value == Opcodes.DOUBLE
1107                 || value == Opcodes.NULL
1108                 || value == Opcodes.UNINITIALIZED_THIS) {
1109             return;
1110         }
1111         if (value instanceof String) {
1112             checkInternalName(version, (String) value, "Invalid stack frame value");
1113         } else if (value instanceof Label) {
1114             referencedLabels.add((Label) value);
1115         } else {
1116             throw new IllegalArgumentException("Invalid stack frame value: " + value);
1117         }
1118     }
1119 
1120     /**
1121       * Checks that the method to visit the given opcode is equal to the given method.
1122       *
1123       * @param opcode the opcode to be checked.
1124       * @param method the expected visit method.
1125       */
checkOpcodeMethod(final int opcode, final Method method)1126     private static void checkOpcodeMethod(final int opcode, final Method method) {
1127         if (opcode < Opcodes.NOP || opcode > Opcodes.IFNONNULL || OPCODE_METHODS[opcode] != method) {
1128             throw new IllegalArgumentException("Invalid opcode: " + opcode);
1129         }
1130     }
1131 
1132     /**
1133       * Checks that the given value is a signed byte.
1134       *
1135       * @param value the value to be checked.
1136       * @param message the message to use in case of error.
1137       */
checkSignedByte(final int value, final String message)1138     private static void checkSignedByte(final int value, final String message) {
1139         if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
1140             throw new IllegalArgumentException(message + " (must be a signed byte): " + value);
1141         }
1142     }
1143 
1144     /**
1145       * Checks that the given value is a signed short.
1146       *
1147       * @param value the value to be checked.
1148       * @param message the message to use in case of error.
1149       */
checkSignedShort(final int value, final String message)1150     private static void checkSignedShort(final int value, final String message) {
1151         if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
1152             throw new IllegalArgumentException(message + " (must be a signed short): " + value);
1153         }
1154     }
1155 
1156     /**
1157       * Checks that the given value is an unsigned short.
1158       *
1159       * @param value the value to be checked.
1160       * @param message the message to use in case of error.
1161       */
checkUnsignedShort(final int value, final String message)1162     private static void checkUnsignedShort(final int value, final String message) {
1163         if (value < 0 || value > 65535) {
1164             throw new IllegalArgumentException(message + " (must be an unsigned short): " + value);
1165         }
1166     }
1167 
1168     /**
1169       * Checks that the given value is an {@link Integer}, {@link Float}, {@link Long}, {@link Double}
1170       * or {@link String} value.
1171       *
1172       * @param value the value to be checked.
1173       */
checkConstant(final Object value)1174     static void checkConstant(final Object value) {
1175         if (!(value instanceof Integer)
1176                 && !(value instanceof Float)
1177                 && !(value instanceof Long)
1178                 && !(value instanceof Double)
1179                 && !(value instanceof String)) {
1180             throw new IllegalArgumentException("Invalid constant: " + value);
1181         }
1182     }
1183 
1184     /**
1185       * Checks that the given value is a valid operand for the LDC instruction.
1186       *
1187       * @param value the value to be checked.
1188       */
checkLdcConstant(final Object value)1189     private void checkLdcConstant(final Object value) {
1190         if (value instanceof Type) {
1191             int sort = ((Type) value).getSort();
1192             if (sort != Type.OBJECT && sort != Type.ARRAY && sort != Type.METHOD) {
1193                 throw new IllegalArgumentException("Illegal LDC constant value");
1194             }
1195             if (sort != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) {
1196                 throw new IllegalArgumentException("ldc of a constant class requires at least version 1.5");
1197             }
1198             if (sort == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) {
1199                 throw new IllegalArgumentException("ldc of a method type requires at least version 1.7");
1200             }
1201         } else if (value instanceof Handle) {
1202             if ((version & 0xFFFF) < Opcodes.V1_7) {
1203                 throw new IllegalArgumentException("ldc of a Handle requires at least version 1.7");
1204             }
1205             Handle handle = (Handle) value;
1206             int tag = handle.getTag();
1207             if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) {
1208                 throw new IllegalArgumentException("invalid handle tag " + tag);
1209             }
1210             checkInternalName(this.version, handle.getOwner(), "handle owner");
1211             if (tag <= Opcodes.H_PUTSTATIC) {
1212                 checkDescriptor(this.version, handle.getDesc(), false);
1213             } else {
1214                 checkMethodDescriptor(this.version, handle.getDesc());
1215             }
1216             String handleName = handle.getName();
1217             if (!("<init>".equals(handleName) && tag == Opcodes.H_NEWINVOKESPECIAL)) {
1218                 checkMethodIdentifier(this.version, handleName, "handle name");
1219             }
1220         } else if (value instanceof ConstantDynamic) {
1221             if ((version & 0xFFFF) < Opcodes.V11) {
1222                 throw new IllegalArgumentException("ldc of a ConstantDynamic requires at least version 11");
1223             }
1224             ConstantDynamic constantDynamic = (ConstantDynamic) value;
1225             checkMethodIdentifier(this.version, constantDynamic.getName(), "constant dynamic name");
1226             checkDescriptor(this.version, constantDynamic.getDescriptor(), false);
1227             checkLdcConstant(constantDynamic.getBootstrapMethod());
1228             int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
1229             for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
1230                 checkLdcConstant(constantDynamic.getBootstrapMethodArgument(i));
1231             }
1232         } else {
1233             checkConstant(value);
1234         }
1235     }
1236 
1237     /**
1238       * Checks that the given string is a valid unqualified name.
1239       *
1240       * @param version the class version.
1241       * @param name the string to be checked.
1242       * @param message the message to use in case of error.
1243       */
checkUnqualifiedName(final int version, final String name, final String message)1244     static void checkUnqualifiedName(final int version, final String name, final String message) {
1245         checkIdentifier(version, name, 0, -1, message);
1246     }
1247 
1248     /**
1249       * Checks that the given substring is a valid Java identifier.
1250       *
1251       * @param version the class version.
1252       * @param name the string to be checked.
1253       * @param startPos the index of the first character of the identifier (inclusive).
1254       * @param endPos the index of the last character of the identifier (exclusive). -1 is equivalent
1255       *     to {@code name.length()} if name is not {@literal null}.
1256       * @param message the message to use in case of error.
1257       */
checkIdentifier( final int version, final String name, final int startPos, final int endPos, final String message)1258     static void checkIdentifier(
1259             final int version,
1260             final String name,
1261             final int startPos,
1262             final int endPos,
1263             final String message) {
1264         if (name == null || (endPos == -1 ? name.length() <= startPos : endPos <= startPos)) {
1265             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1266         }
1267         int max = endPos == -1 ? name.length() : endPos;
1268         if ((version & 0xFFFF) >= Opcodes.V1_5) {
1269             for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) {
1270                 if (".;[/".indexOf(name.codePointAt(i)) != -1) {
1271                     throw new IllegalArgumentException(
1272                             INVALID + message + " (must not contain . ; [ or /): " + name);
1273                 }
1274             }
1275             return;
1276         }
1277         for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) {
1278             if (i == startPos
1279                     ? !Character.isJavaIdentifierStart(name.codePointAt(i))
1280                     : !Character.isJavaIdentifierPart(name.codePointAt(i))) {
1281                 throw new IllegalArgumentException(
1282                         INVALID + message + " (must be a valid Java identifier): " + name);
1283             }
1284         }
1285     }
1286 
1287     /**
1288       * Checks that the given string is a valid Java identifier.
1289       *
1290       * @param version the class version.
1291       * @param name the string to be checked.
1292       * @param message the message to use in case of error.
1293       */
checkMethodIdentifier(final int version, final String name, final String message)1294     static void checkMethodIdentifier(final int version, final String name, final String message) {
1295         if (name == null || name.length() == 0) {
1296             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1297         }
1298         if ((version & 0xFFFF) >= Opcodes.V1_5) {
1299             for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) {
1300                 if (".;[/<>".indexOf(name.codePointAt(i)) != -1) {
1301                     throw new IllegalArgumentException(
1302                             INVALID + message + " (must be a valid unqualified name): " + name);
1303                 }
1304             }
1305             return;
1306         }
1307         for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) {
1308             if (i == 0
1309                     ? !Character.isJavaIdentifierStart(name.codePointAt(i))
1310                     : !Character.isJavaIdentifierPart(name.codePointAt(i))) {
1311                 throw new IllegalArgumentException(
1312                         INVALID
1313                                 + message
1314                                 + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
1315                                 + name);
1316             }
1317         }
1318     }
1319 
1320     /**
1321       * Checks that the given string is a valid internal class name or array type descriptor.
1322       *
1323       * @param version the class version.
1324       * @param name the string to be checked.
1325       * @param message the message to use in case of error.
1326       */
checkInternalName(final int version, final String name, final String message)1327     static void checkInternalName(final int version, final String name, final String message) {
1328         if (name == null || name.length() == 0) {
1329             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1330         }
1331         if (name.charAt(0) == '[') {
1332             checkDescriptor(version, name, false);
1333         } else {
1334             checkInternalClassName(version, name, message);
1335         }
1336     }
1337 
1338     /**
1339       * Checks that the given string is a valid internal class name.
1340       *
1341       * @param version the class version.
1342       * @param name the string to be checked.
1343       * @param message the message to use in case of error.
1344       */
checkInternalClassName( final int version, final String name, final String message)1345     private static void checkInternalClassName(
1346             final int version, final String name, final String message) {
1347         try {
1348             int startIndex = 0;
1349             int slashIndex;
1350             while ((slashIndex = name.indexOf('/', startIndex + 1)) != -1) {
1351                 checkIdentifier(version, name, startIndex, slashIndex, null);
1352                 startIndex = slashIndex + 1;
1353             }
1354             checkIdentifier(version, name, startIndex, name.length(), null);
1355         } catch (IllegalArgumentException e) {
1356             throw new IllegalArgumentException(
1357                     INVALID + message + " (must be an internal class name): " + name, e);
1358         }
1359     }
1360 
1361     /**
1362       * Checks that the given string is a valid type descriptor.
1363       *
1364       * @param version the class version.
1365       * @param descriptor the string to be checked.
1366       * @param canBeVoid {@literal true} if {@code V} can be considered valid.
1367       */
checkDescriptor(final int version, final String descriptor, final boolean canBeVoid)1368     static void checkDescriptor(final int version, final String descriptor, final boolean canBeVoid) {
1369         int endPos = checkDescriptor(version, descriptor, 0, canBeVoid);
1370         if (endPos != descriptor.length()) {
1371             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1372         }
1373     }
1374 
1375     /**
1376       * Checks that a the given substring is a valid type descriptor.
1377       *
1378       * @param version the class version.
1379       * @param descriptor the string to be checked.
1380       * @param startPos the index of the first character of the type descriptor (inclusive).
1381       * @param canBeVoid whether {@code V} can be considered valid.
1382       * @return the index of the last character of the type descriptor, plus one.
1383       */
checkDescriptor( final int version, final String descriptor, final int startPos, final boolean canBeVoid)1384     private static int checkDescriptor(
1385             final int version, final String descriptor, final int startPos, final boolean canBeVoid) {
1386         if (descriptor == null || startPos >= descriptor.length()) {
1387             throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
1388         }
1389         switch (descriptor.charAt(startPos)) {
1390             case 'V':
1391                 if (canBeVoid) {
1392                     return startPos + 1;
1393                 } else {
1394                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1395                 }
1396             case 'Z':
1397             case 'C':
1398             case 'B':
1399             case 'S':
1400             case 'I':
1401             case 'F':
1402             case 'J':
1403             case 'D':
1404                 return startPos + 1;
1405             case '[':
1406                 int pos = startPos + 1;
1407                 while (pos < descriptor.length() && descriptor.charAt(pos) == '[') {
1408                     ++pos;
1409                 }
1410                 if (pos < descriptor.length()) {
1411                     return checkDescriptor(version, descriptor, pos, false);
1412                 } else {
1413                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1414                 }
1415             case 'L':
1416                 int endPos = descriptor.indexOf(';', startPos);
1417                 if (startPos == -1 || endPos - startPos < 2) {
1418                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1419                 }
1420                 try {
1421                     checkInternalClassName(version, descriptor.substring(startPos + 1, endPos), null);
1422                 } catch (IllegalArgumentException e) {
1423                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor, e);
1424                 }
1425                 return endPos + 1;
1426             default:
1427                 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1428         }
1429     }
1430 
1431     /**
1432       * Checks that the given string is a valid method descriptor.
1433       *
1434       * @param version the class version.
1435       * @param descriptor the string to be checked.
1436       */
checkMethodDescriptor(final int version, final String descriptor)1437     static void checkMethodDescriptor(final int version, final String descriptor) {
1438         if (descriptor == null || descriptor.length() == 0) {
1439             throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
1440         }
1441         if (descriptor.charAt(0) != '(' || descriptor.length() < 3) {
1442             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1443         }
1444         int pos = 1;
1445         if (descriptor.charAt(pos) != ')') {
1446             do {
1447                 if (descriptor.charAt(pos) == 'V') {
1448                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1449                 }
1450                 pos = checkDescriptor(version, descriptor, pos, false);
1451             } while (pos < descriptor.length() && descriptor.charAt(pos) != ')');
1452         }
1453         pos = checkDescriptor(version, descriptor, pos + 1, true);
1454         if (pos != descriptor.length()) {
1455             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1456         }
1457     }
1458 
1459     /**
1460       * Checks that the given label is not null. This method can also check that the label has been
1461       * visited.
1462       *
1463       * @param label the label to be checked.
1464       * @param checkVisited whether to check that the label has been visited.
1465       * @param message the message to use in case of error.
1466       */
checkLabel(final Label label, final boolean checkVisited, final String message)1467     private void checkLabel(final Label label, final boolean checkVisited, final String message) {
1468         if (label == null) {
1469             throw new IllegalArgumentException(INVALID + message + " (must not be null)");
1470         }
1471         if (checkVisited && labelInsnIndices.get(label) == null) {
1472             throw new IllegalArgumentException(INVALID + message + " (must be visited first)");
1473         }
1474     }
1475 }
1476