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