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