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 */
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 */
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 */
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 */
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 */
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
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
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
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
549 public AnnotationVisitor visitAnnotationDefault() {
550 checkVisitEndNotCalled();
551 return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false);
552 }
553
554 @Override
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
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
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
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
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
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
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
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
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
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
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
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
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
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
825 public void visitLdcInsn(final Object value) {
826 checkVisitCodeCalled();
827 checkVisitMaxsNotCalled();
828 checkLdcConstant(value);
829 super.visitLdcInsn(value);
830 ++insnCount;
831 }
832
833 @Override
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
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
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
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
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
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
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
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
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
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
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
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. */
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. */
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. */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 case 'Q':
1417 int endPos = descriptor.indexOf(';', startPos);
1418 if (startPos == -1 || endPos - startPos < 2) {
1419 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1420 }
1421 try {
1422 checkInternalClassName(version, descriptor.substring(startPos + 1, endPos), null);
1423 } catch (IllegalArgumentException e) {
1424 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor, e);
1425 }
1426 return endPos + 1;
1427 default:
1428 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1429 }
1430 }
1431
1432 /**
1433 * Checks that the given string is a valid method descriptor.
1434 *
1435 * @param version the class version.
1436 * @param descriptor the string to be checked.
1437 */
1438 static void checkMethodDescriptor(final int version, final String descriptor) {
1439 if (descriptor == null || descriptor.length() == 0) {
1440 throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
1441 }
1442 if (descriptor.charAt(0) != '(' || descriptor.length() < 3) {
1443 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1444 }
1445 int pos = 1;
1446 if (descriptor.charAt(pos) != ')') {
1447 do {
1448 if (descriptor.charAt(pos) == 'V') {
1449 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1450 }
1451 pos = checkDescriptor(version, descriptor, pos, false);
1452 } while (pos < descriptor.length() && descriptor.charAt(pos) != ')');
1453 }
1454 pos = checkDescriptor(version, descriptor, pos + 1, true);
1455 if (pos != descriptor.length()) {
1456 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1457 }
1458 }
1459
1460 /**
1461 * Checks that the given label is not null. This method can also check that the label has been
1462 * visited.
1463 *
1464 * @param label the label to be checked.
1465 * @param checkVisited whether to check that the label has been visited.
1466 * @param message the message to use in case of error.
1467 */
1468 private void checkLabel(final Label label, final boolean checkVisited, final String message) {
1469 if (label == null) {
1470 throw new IllegalArgumentException(INVALID + message + " (must not be null)");
1471 }
1472 if (checkVisited && labelInsnIndices.get(label) == null) {
1473 throw new IllegalArgumentException(INVALID + message + " (must be visited first)");
1474 }
1475 }
1476 }
--- EOF ---