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