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