/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * This file is available under and governed by the GNU General Public
 * License version 2 only, as published by the Free Software Foundation.
 * However, the following notice accompanied the original version of this
 * file:
 *
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2011 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package jdk.internal.org.objectweb.asm.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ConstantDynamic;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.TypePath;
import jdk.internal.org.objectweb.asm.TypeReference;
import jdk.internal.org.objectweb.asm.tree.MethodNode;
import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier;

A MethodVisitor that checks that its methods are properly used. More precisely this method adapter checks each instruction individually, i.e., each visit method checks some preconditions based only on its arguments - such as the fact that the given opcode is correct for a given visit method. This adapter can also perform some basic data flow checks (more precisely those that can be performed without the full class hierarchy - see BasicVerifier). For instance in a method whose signature is void m (), the invalid instruction IRETURN, or the invalid sequence IADD L2I will be detected if the data flow checks are enabled. These checks are enabled by using the CheckMethodAdapter(int, String, String, MethodVisitor, Map<Label,Integer>) constructor. They are not performed if any other constructor is used.
Author:Eric Bruneton
/** * A {@link MethodVisitor} that checks that its methods are properly used. More precisely this * method adapter checks each instruction individually, i.e., each visit method checks some * preconditions based <i>only</i> on its arguments - such as the fact that the given opcode is * correct for a given visit method. This adapter can also perform some basic data flow checks (more * precisely those that can be performed without the full class hierarchy - see {@link * jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is * {@code void m ()}, the invalid instruction IRETURN, or the invalid sequence IADD L2I will be * detected if the data flow checks are enabled. These checks are enabled by using the {@link * #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. They are not performed if * any other constructor is used. * * @author Eric Bruneton */
public class CheckMethodAdapter extends MethodVisitor {
The 'generic' instruction visit methods (i.e. those that take an opcode argument).
/** The 'generic' instruction visit methods (i.e. those that take an opcode argument). */
private enum Method { VISIT_INSN, VISIT_INT_INSN, VISIT_VAR_INSN, VISIT_TYPE_INSN, VISIT_FIELD_INSN, VISIT_METHOD_INSN, VISIT_JUMP_INSN }
The method to use to visit each instruction. Only generic methods are represented here.
/** The method to use to visit each instruction. Only generic methods are represented here. */
private static final Method[] OPCODE_METHODS = { Method.VISIT_INSN, // NOP Method.VISIT_INSN, // ACONST_NULL Method.VISIT_INSN, // ICONST_M1 Method.VISIT_INSN, // ICONST_0 Method.VISIT_INSN, // ICONST_1 Method.VISIT_INSN, // ICONST_2 Method.VISIT_INSN, // ICONST_3 Method.VISIT_INSN, // ICONST_4 Method.VISIT_INSN, // ICONST_5 Method.VISIT_INSN, // LCONST_0 Method.VISIT_INSN, // LCONST_1 Method.VISIT_INSN, // FCONST_0 Method.VISIT_INSN, // FCONST_1 Method.VISIT_INSN, // FCONST_2 Method.VISIT_INSN, // DCONST_0 Method.VISIT_INSN, // DCONST_1 Method.VISIT_INT_INSN, // BIPUSH Method.VISIT_INT_INSN, // SIPUSH null, // LDC null, // LDC_W null, // LDC2_W Method.VISIT_VAR_INSN, // ILOAD Method.VISIT_VAR_INSN, // LLOAD Method.VISIT_VAR_INSN, // FLOAD Method.VISIT_VAR_INSN, // DLOAD Method.VISIT_VAR_INSN, // ALOAD null, // ILOAD_0 null, // ILOAD_1 null, // ILOAD_2 null, // ILOAD_3 null, // LLOAD_0 null, // LLOAD_1 null, // LLOAD_2 null, // LLOAD_3 null, // FLOAD_0 null, // FLOAD_1 null, // FLOAD_2 null, // FLOAD_3 null, // DLOAD_0 null, // DLOAD_1 null, // DLOAD_2 null, // DLOAD_3 null, // ALOAD_0 null, // ALOAD_1 null, // ALOAD_2 null, // ALOAD_3 Method.VISIT_INSN, // IALOAD Method.VISIT_INSN, // LALOAD Method.VISIT_INSN, // FALOAD Method.VISIT_INSN, // DALOAD Method.VISIT_INSN, // AALOAD Method.VISIT_INSN, // BALOAD Method.VISIT_INSN, // CALOAD Method.VISIT_INSN, // SALOAD Method.VISIT_VAR_INSN, // ISTORE Method.VISIT_VAR_INSN, // LSTORE Method.VISIT_VAR_INSN, // FSTORE Method.VISIT_VAR_INSN, // DSTORE Method.VISIT_VAR_INSN, // ASTORE null, // ISTORE_0 null, // ISTORE_1 null, // ISTORE_2 null, // ISTORE_3 null, // LSTORE_0 null, // LSTORE_1 null, // LSTORE_2 null, // LSTORE_3 null, // FSTORE_0 null, // FSTORE_1 null, // FSTORE_2 null, // FSTORE_3 null, // DSTORE_0 null, // DSTORE_1 null, // DSTORE_2 null, // DSTORE_3 null, // ASTORE_0 null, // ASTORE_1 null, // ASTORE_2 null, // ASTORE_3 Method.VISIT_INSN, // IASTORE Method.VISIT_INSN, // LASTORE Method.VISIT_INSN, // FASTORE Method.VISIT_INSN, // DASTORE Method.VISIT_INSN, // AASTORE Method.VISIT_INSN, // BASTORE Method.VISIT_INSN, // CASTORE Method.VISIT_INSN, // SASTORE Method.VISIT_INSN, // POP Method.VISIT_INSN, // POP2 Method.VISIT_INSN, // DUP Method.VISIT_INSN, // DUP_X1 Method.VISIT_INSN, // DUP_X2 Method.VISIT_INSN, // DUP2 Method.VISIT_INSN, // DUP2_X1 Method.VISIT_INSN, // DUP2_X2 Method.VISIT_INSN, // SWAP Method.VISIT_INSN, // IADD Method.VISIT_INSN, // LADD Method.VISIT_INSN, // FADD Method.VISIT_INSN, // DADD Method.VISIT_INSN, // ISUB Method.VISIT_INSN, // LSUB Method.VISIT_INSN, // FSUB Method.VISIT_INSN, // DSUB Method.VISIT_INSN, // IMUL Method.VISIT_INSN, // LMUL Method.VISIT_INSN, // FMUL Method.VISIT_INSN, // DMUL Method.VISIT_INSN, // IDIV Method.VISIT_INSN, // LDIV Method.VISIT_INSN, // FDIV Method.VISIT_INSN, // DDIV Method.VISIT_INSN, // IREM Method.VISIT_INSN, // LREM Method.VISIT_INSN, // FREM Method.VISIT_INSN, // DREM Method.VISIT_INSN, // INEG Method.VISIT_INSN, // LNEG Method.VISIT_INSN, // FNEG Method.VISIT_INSN, // DNEG Method.VISIT_INSN, // ISHL Method.VISIT_INSN, // LSHL Method.VISIT_INSN, // ISHR Method.VISIT_INSN, // LSHR Method.VISIT_INSN, // IUSHR Method.VISIT_INSN, // LUSHR Method.VISIT_INSN, // IAND Method.VISIT_INSN, // LAND Method.VISIT_INSN, // IOR Method.VISIT_INSN, // LOR Method.VISIT_INSN, // IXOR Method.VISIT_INSN, // LXOR null, // IINC Method.VISIT_INSN, // I2L Method.VISIT_INSN, // I2F Method.VISIT_INSN, // I2D Method.VISIT_INSN, // L2I Method.VISIT_INSN, // L2F Method.VISIT_INSN, // L2D Method.VISIT_INSN, // F2I Method.VISIT_INSN, // F2L Method.VISIT_INSN, // F2D Method.VISIT_INSN, // D2I Method.VISIT_INSN, // D2L Method.VISIT_INSN, // D2F Method.VISIT_INSN, // I2B Method.VISIT_INSN, // I2C Method.VISIT_INSN, // I2S Method.VISIT_INSN, // LCMP Method.VISIT_INSN, // FCMPL Method.VISIT_INSN, // FCMPG Method.VISIT_INSN, // DCMPL Method.VISIT_INSN, // DCMPG Method.VISIT_JUMP_INSN, // IFEQ Method.VISIT_JUMP_INSN, // IFNE Method.VISIT_JUMP_INSN, // IFLT Method.VISIT_JUMP_INSN, // IFGE Method.VISIT_JUMP_INSN, // IFGT Method.VISIT_JUMP_INSN, // IFLE Method.VISIT_JUMP_INSN, // IF_ICMPEQ Method.VISIT_JUMP_INSN, // IF_ICMPNE Method.VISIT_JUMP_INSN, // IF_ICMPLT Method.VISIT_JUMP_INSN, // IF_ICMPGE Method.VISIT_JUMP_INSN, // IF_ICMPGT Method.VISIT_JUMP_INSN, // IF_ICMPLE Method.VISIT_JUMP_INSN, // IF_ACMPEQ Method.VISIT_JUMP_INSN, // IF_ACMPNE Method.VISIT_JUMP_INSN, // GOTO Method.VISIT_JUMP_INSN, // JSR Method.VISIT_VAR_INSN, // RET null, // TABLESWITCH null, // LOOKUPSWITCH Method.VISIT_INSN, // IRETURN Method.VISIT_INSN, // LRETURN Method.VISIT_INSN, // FRETURN Method.VISIT_INSN, // DRETURN Method.VISIT_INSN, // ARETURN Method.VISIT_INSN, // RETURN Method.VISIT_FIELD_INSN, // GETSTATIC Method.VISIT_FIELD_INSN, // PUTSTATIC Method.VISIT_FIELD_INSN, // GETFIELD Method.VISIT_FIELD_INSN, // PUTFIELD Method.VISIT_METHOD_INSN, // INVOKEVIRTUAL Method.VISIT_METHOD_INSN, // INVOKESPECIAL Method.VISIT_METHOD_INSN, // INVOKESTATIC Method.VISIT_METHOD_INSN, // INVOKEINTERFACE null, // INVOKEDYNAMIC Method.VISIT_TYPE_INSN, // NEW Method.VISIT_INT_INSN, // NEWARRAY Method.VISIT_TYPE_INSN, // ANEWARRAY Method.VISIT_INSN, // ARRAYLENGTH Method.VISIT_INSN, // ATHROW Method.VISIT_TYPE_INSN, // CHECKCAST Method.VISIT_TYPE_INSN, // INSTANCEOF Method.VISIT_INSN, // MONITORENTER Method.VISIT_INSN, // MONITOREXIT null, // WIDE null, // MULTIANEWARRAY Method.VISIT_JUMP_INSN, // IFNULL Method.VISIT_JUMP_INSN // IFNONNULL }; private static final String INVALID = "Invalid "; private static final String INVALID_DESCRIPTOR = "Invalid descriptor: "; private static final String INVALID_TYPE_REFERENCE = "Invalid type reference sort 0x"; private static final String INVALID_LOCAL_VARIABLE_INDEX = "Invalid local variable index"; private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)"; private static final String START_LABEL = "start label"; private static final String END_LABEL = "end label";
The class version number.
/** The class version number. */
public int version;
The access flags of the visited method.
/** The access flags of the visited method. */
private int access;
The number of method parameters that can have runtime visible annotations. 0 means that all the parameters from the method descriptor can have annotations.
/** * The number of method parameters that can have runtime visible annotations. 0 means that all the * parameters from the method descriptor can have annotations. */
private int visibleAnnotableParameterCount;
The number of method parameters that can have runtime invisible annotations. 0 means that all the parameters from the method descriptor can have annotations.
/** * The number of method parameters that can have runtime invisible annotations. 0 means that all * the parameters from the method descriptor can have annotations. */
private int invisibleAnnotableParameterCount;
Whether the visitCode method has been called.
/** Whether the {@link #visitCode} method has been called. */
private boolean visitCodeCalled;
Whether the visitMaxs method has been called.
/** Whether the {@link #visitMaxs} method has been called. */
private boolean visitMaxCalled;
Whether the visitEnd method has been called.
/** Whether the {@link #visitEnd} method has been called. */
private boolean visitEndCalled;
The number of visited instructions so far.
/** The number of visited instructions so far. */
private int insnCount;
The index of the instruction designated by each visited label.
/** The index of the instruction designated by each visited label. */
private final Map<Label, Integer> labelInsnIndices;
The labels referenced by the visited method.
/** The labels referenced by the visited method. */
private Set<Label> referencedLabels;
The index of the instruction corresponding to the last visited stack map frame.
/** The index of the instruction corresponding to the last visited stack map frame. */
private int lastFrameInsnIndex = -1;
The number of visited frames in expanded form.
/** The number of visited frames in expanded form. */
private int numExpandedFrames;
The number of visited frames in compressed form.
/** The number of visited frames in compressed form. */
private int numCompressedFrames;
The exception handler ranges. Each pair of list element contains the start and end labels of an exception handler block.
/** * The exception handler ranges. Each pair of list element contains the start and end labels of an * exception handler block. */
private List<Label> handlers;
Constructs a new CheckMethodAdapter object. This method adapter will not perform any data flow check (see CheckMethodAdapter(int, String, String, MethodVisitor, Map<Label,Integer>)). Subclasses must not use this constructor. Instead, they must use the CheckMethodAdapter(int, MethodVisitor, Map<Label,Integer>) version.
Params:
  • methodvisitor – the method visitor to which this adapter must delegate calls.
/** * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link * #CheckMethodAdapter(int, MethodVisitor, Map)} version. * * @param methodvisitor the method visitor to which this adapter must delegate calls. */
public CheckMethodAdapter(final MethodVisitor methodvisitor) { this(methodvisitor, new HashMap<Label, Integer>()); }
Constructs a new CheckMethodAdapter object. This method adapter will not perform any data flow check (see CheckMethodAdapter(int, String, String, MethodVisitor, Map<Label,Integer>)). Subclasses must not use this constructor. Instead, they must use the CheckMethodAdapter(int, MethodVisitor, Map<Label,Integer>) version.
Params:
  • methodVisitor – the method visitor to which this adapter must delegate calls.
  • labelInsnIndices – the index of the instruction designated by each visited label so far (in other methods). This map is updated with the labels from the visited method.
Throws:
/** * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link * #CheckMethodAdapter(int, MethodVisitor, Map)} version. * * @param methodVisitor the method visitor to which this adapter must delegate calls. * @param labelInsnIndices the index of the instruction designated by each visited label so far * (in other methods). This map is updated with the labels from the visited method. * @throws IllegalStateException If a subclass calls this constructor. */
public CheckMethodAdapter( final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { this(Opcodes.ASM7, methodVisitor, labelInsnIndices); if (getClass() != CheckMethodAdapter.class) { throw new IllegalStateException(); } }
Constructs a new CheckMethodAdapter object. This method adapter will not perform any data flow check (see CheckMethodAdapter(int, String, String, MethodVisitor, Map<Label,Integer>)).
Params:
  • api – the ASM API version implemented by this CheckMethodAdapter. Must be one of Opcodes.ASM4, Opcodes.ASM5, Opcodes.ASM6 or Opcodes.ASM7.
  • methodVisitor – the method visitor to which this adapter must delegate calls.
  • labelInsnIndices – the index of the instruction designated by each visited label so far (in other methods). This map is updated with the labels from the visited method.
/** * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). * * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. * @param methodVisitor the method visitor to which this adapter must delegate calls. * @param labelInsnIndices the index of the instruction designated by each visited label so far * (in other methods). This map is updated with the labels from the visited method. */
protected CheckMethodAdapter( final int api, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { super(api, methodVisitor); this.labelInsnIndices = labelInsnIndices; this.referencedLabels = new HashSet<Label>(); this.handlers = new ArrayList<Label>(); }
Constructs a new CheckMethodAdapter object. This method adapter will perform basic data flow checks. For instance in a method whose signature is void m (), the invalid instruction IRETURN, or the invalid sequence IADD L2I will be detected. Subclasses must not use this constructor. Instead, they must use the CheckMethodAdapter(int, int, String, String, MethodVisitor, Map<Label,Integer>) version.
Params:
  • access – the method's access flags.
  • name – the method's name.
  • descriptor – the method's descriptor (see Type).
  • methodVisitor – the method visitor to which this adapter must delegate calls.
  • labelInsnIndices – the index of the instruction designated by each visited label so far (in other methods). This map is updated with the labels from the visited method.
/** * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid * instruction IRETURN, or the invalid sequence IADD L2I will be detected. <i>Subclasses must not * use this constructor</i>. Instead, they must use the {@link * #CheckMethodAdapter(int,int,String,String,MethodVisitor,Map)} version. * * @param access the method's access flags. * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param methodVisitor the method visitor to which this adapter must delegate calls. * @param labelInsnIndices the index of the instruction designated by each visited label so far * (in other methods). This map is updated with the labels from the visited method. */
public CheckMethodAdapter( final int access, final String name, final String descriptor, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { this(Opcodes.ASM7, access, name, descriptor, methodVisitor, labelInsnIndices); if (getClass() != CheckMethodAdapter.class) { throw new IllegalStateException(); } }
Constructs a new CheckMethodAdapter object. This method adapter will perform basic data flow checks. For instance in a method whose signature is void m (), the invalid instruction IRETURN, or the invalid sequence IADD L2I will be detected.
Params:
  • api – the ASM API version implemented by this CheckMethodAdapter. Must be one of Opcodes.ASM4, Opcodes.ASM5, Opcodes.ASM6 or Opcodes.ASM7.
  • access – the method's access flags.
  • name – the method's name.
  • descriptor – the method's descriptor (see Type).
  • methodVisitor – the method visitor to which this adapter must delegate calls.
  • labelInsnIndices – the index of the instruction designated by each visited label so far (in other methods). This map is updated with the labels from the visited method.
/** * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid * instruction IRETURN, or the invalid sequence IADD L2I will be detected. * * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. * @param access the method's access flags. * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param methodVisitor the method visitor to which this adapter must delegate calls. * @param labelInsnIndices the index of the instruction designated by each visited label so far * (in other methods). This map is updated with the labels from the visited method. */
protected CheckMethodAdapter( final int api, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { this( api, new MethodNode(api, access, name, descriptor, null, null) { @Override public void visitEnd() { Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(new BasicVerifier()); try { analyzer.analyze("dummy", this); } catch (IndexOutOfBoundsException e) { if (maxLocals == 0 && maxStack == 0) { throw new IllegalArgumentException( "Data flow checking option requires valid, non zero maxLocals and maxStack.", e); } throwError(analyzer, e); } catch (AnalyzerException e) { throwError(analyzer, e); } accept(methodVisitor); } private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter, true); CheckClassAdapter.printAnalyzerResult(this, analyzer, printWriter); printWriter.close(); throw new IllegalArgumentException(e.getMessage() + ' ' + stringWriter.toString(), e); } }, labelInsnIndices); this.access = access; } @Override public void visitParameter(final String name, final int access) { if (name != null) { checkUnqualifiedName(version, name, "name"); } CheckClassAdapter.checkAccess( access, Opcodes.ACC_FINAL + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); super.visitParameter(name, access); } @Override public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { checkVisitEndNotCalled(); checkDescriptor(version, descriptor, false); return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible)); } @Override public AnnotationVisitor visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { checkVisitEndNotCalled(); int sort = new TypeReference(typeRef).getSort(); if (sort != TypeReference.METHOD_TYPE_PARAMETER && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND && sort != TypeReference.METHOD_RETURN && sort != TypeReference.METHOD_RECEIVER && sort != TypeReference.METHOD_FORMAL_PARAMETER && sort != TypeReference.THROWS) { throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); } CheckClassAdapter.checkTypeRef(typeRef); CheckMethodAdapter.checkDescriptor(version, descriptor, false); return new CheckAnnotationAdapter( super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); } @Override public AnnotationVisitor visitAnnotationDefault() { checkVisitEndNotCalled(); return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); } @Override public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { checkVisitEndNotCalled(); if (visible) { visibleAnnotableParameterCount = parameterCount; } else { invisibleAnnotableParameterCount = parameterCount; } super.visitAnnotableParameterCount(parameterCount, visible); } @Override public AnnotationVisitor visitParameterAnnotation( final int parameter, final String descriptor, final boolean visible) { checkVisitEndNotCalled(); if ((visible && visibleAnnotableParameterCount > 0 && parameter >= visibleAnnotableParameterCount) || (!visible && invisibleAnnotableParameterCount > 0 && parameter >= invisibleAnnotableParameterCount)) { throw new IllegalArgumentException("Invalid parameter index"); } checkDescriptor(version, descriptor, false); return new CheckAnnotationAdapter( super.visitParameterAnnotation(parameter, descriptor, visible)); } @Override public void visitAttribute(final Attribute attribute) { checkVisitEndNotCalled(); if (attribute == null) { throw new IllegalArgumentException("Invalid attribute (must not be null)"); } super.visitAttribute(attribute); } @Override public void visitCode() { if ((access & Opcodes.ACC_ABSTRACT) != 0) { throw new UnsupportedOperationException("Abstract methods cannot have code"); } visitCodeCalled = true; super.visitCode(); } @Override public void visitFrame( final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack) { if (insnCount == lastFrameInsnIndex) { throw new IllegalStateException("At most one frame can be visited at a given code location."); } lastFrameInsnIndex = insnCount; int maxNumLocal; int maxNumStack; switch (type) { case Opcodes.F_NEW: case Opcodes.F_FULL: maxNumLocal = Integer.MAX_VALUE; maxNumStack = Integer.MAX_VALUE; break; case Opcodes.F_SAME: maxNumLocal = 0; maxNumStack = 0; break; case Opcodes.F_SAME1: maxNumLocal = 0; maxNumStack = 1; break; case Opcodes.F_APPEND: case Opcodes.F_CHOP: maxNumLocal = 3; maxNumStack = 0; break; default: throw new IllegalArgumentException("Invalid frame type " + type); } if (numLocal > maxNumLocal) { throw new IllegalArgumentException( "Invalid numLocal=" + numLocal + " for frame type " + type); } if (numStack > maxNumStack) { throw new IllegalArgumentException( "Invalid numStack=" + numStack + " for frame type " + type); } if (type != Opcodes.F_CHOP) { if (numLocal > 0 && (local == null || local.length < numLocal)) { throw new IllegalArgumentException("Array local[] is shorter than numLocal"); } for (int i = 0; i < numLocal; ++i) { checkFrameValue(local[i]); } } if (numStack > 0 && (stack == null || stack.length < numStack)) { throw new IllegalArgumentException("Array stack[] is shorter than numStack"); } for (int i = 0; i < numStack; ++i) { checkFrameValue(stack[i]); } if (type == Opcodes.F_NEW) { ++numExpandedFrames; } else { ++numCompressedFrames; } if (numExpandedFrames > 0 && numCompressedFrames > 0) { throw new IllegalArgumentException("Expanded and compressed frames must not be mixed."); } super.visitFrame(type, numLocal, local, numStack, stack); } @Override public void visitInsn(final int opcode) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_INSN); super.visitInsn(opcode); ++insnCount; } @Override public void visitIntInsn(final int opcode, final int operand) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_INT_INSN); switch (opcode) { case Opcodes.BIPUSH: checkSignedByte(operand, "Invalid operand"); break; case Opcodes.SIPUSH: checkSignedShort(operand, "Invalid operand"); break; case Opcodes.NEWARRAY: if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { throw new IllegalArgumentException( "Invalid operand (must be an array type code T_...): " + operand); } break; default: throw new AssertionError(); } super.visitIntInsn(opcode, operand); ++insnCount; } @Override public void visitVarInsn(final int opcode, final int var) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_VAR_INSN); checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); super.visitVarInsn(opcode, var); ++insnCount; } @Override public void visitTypeInsn(final int opcode, final String type) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_TYPE_INSN); checkInternalName(version, type, "type"); if (opcode == Opcodes.NEW && type.charAt(0) == '[') { throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type); } super.visitTypeInsn(opcode, type); ++insnCount; } @Override public void visitFieldInsn( final int opcode, final String owner, final String name, final String descriptor) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_FIELD_INSN); checkInternalName(version, owner, "owner"); checkUnqualifiedName(version, name, "name"); checkDescriptor(version, descriptor, false); super.visitFieldInsn(opcode, owner, name, descriptor); ++insnCount; }
Deprecated.
Deprecated:use visitMethodInsn(int, String, String, String, boolean) instead.
/** * Deprecated. * * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. */
@Deprecated @Override public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { if (api >= Opcodes.ASM5) { super.visitMethodInsn(opcode, owner, name, descriptor); return; } doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE); } @Override public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface) { if (api < Opcodes.ASM5) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); return; } doVisitMethodInsn(opcode, owner, name, descriptor, isInterface); } private void doVisitMethodInsn( final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN); if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) { checkMethodIdentifier(version, name, "name"); } checkInternalName(version, owner, "owner"); checkMethodDescriptor(version, descriptor); if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) { throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces"); } if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) { throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes"); } if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) { throw new IllegalArgumentException( "INVOKESPECIAL can't be used with interfaces prior to Java 8"); } // Calling super.visitMethodInsn requires to call the correct version depending on this.api // (otherwise infinite loops can occur). To simplify and to make it easier to automatically // remove the backward compatibility code, we inline the code of the overridden method here. if (mv != null) { mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } ++insnCount; } @Override public void visitInvokeDynamicInsn( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkMethodIdentifier(version, name, "name"); checkMethodDescriptor(version, descriptor); if (bootstrapMethodHandle.getTag() != Opcodes.H_INVOKESTATIC && bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) { throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag()); } for (Object bootstrapMethodArgument : bootstrapMethodArguments) { checkLdcConstant(bootstrapMethodArgument); } super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); ++insnCount; } @Override public void visitJumpInsn(final int opcode, final Label label) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN); checkLabel(label, false, "label"); super.visitJumpInsn(opcode, label); referencedLabels.add(label); ++insnCount; } @Override public void visitLabel(final Label label) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkLabel(label, false, "label"); if (labelInsnIndices.get(label) != null) { throw new IllegalArgumentException("Already visited label"); } labelInsnIndices.put(label, insnCount); super.visitLabel(label); } @Override public void visitLdcInsn(final Object value) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkLdcConstant(value); super.visitLdcInsn(value); ++insnCount; } @Override public void visitIincInsn(final int var, final int increment) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); checkSignedShort(increment, "Invalid increment"); super.visitIincInsn(var, increment); ++insnCount; } @Override public void visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label... labels) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); if (max < min) { throw new IllegalArgumentException( "Max = " + max + " must be greater than or equal to min = " + min); } checkLabel(dflt, false, "default label"); if (labels == null || labels.length != max - min + 1) { throw new IllegalArgumentException("There must be max - min + 1 labels"); } for (int i = 0; i < labels.length; ++i) { checkLabel(labels[i], false, "label at index " + i); } super.visitTableSwitchInsn(min, max, dflt, labels); for (Label label : labels) { referencedLabels.add(label); } ++insnCount; } @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { checkVisitMaxsNotCalled(); checkVisitCodeCalled(); checkLabel(dflt, false, "default label"); if (keys == null || labels == null || keys.length != labels.length) { throw new IllegalArgumentException("There must be the same number of keys and labels"); } for (int i = 0; i < labels.length; ++i) { checkLabel(labels[i], false, "label at index " + i); } super.visitLookupSwitchInsn(dflt, keys, labels); referencedLabels.add(dflt); for (Label label : labels) { referencedLabels.add(label); } ++insnCount; } @Override public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkDescriptor(version, descriptor, false); if (descriptor.charAt(0) != '[') { throw new IllegalArgumentException( "Invalid descriptor (must be an array type descriptor): " + descriptor); } if (numDimensions < 1) { throw new IllegalArgumentException( "Invalid dimensions (must be greater than 0): " + numDimensions); } if (numDimensions > descriptor.lastIndexOf('[') + 1) { throw new IllegalArgumentException( "Invalid dimensions (must not be greater than numDimensions(descriptor)): " + numDimensions); } super.visitMultiANewArrayInsn(descriptor, numDimensions); ++insnCount; } @Override public AnnotationVisitor visitInsnAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); int sort = new TypeReference(typeRef).getSort(); if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW && sort != TypeReference.CONSTRUCTOR_REFERENCE && sort != TypeReference.METHOD_REFERENCE && sort != TypeReference.CAST && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); } CheckClassAdapter.checkTypeRef(typeRef); CheckMethodAdapter.checkDescriptor(version, descriptor, false); return new CheckAnnotationAdapter( super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)); } @Override public void visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkLabel(start, false, START_LABEL); checkLabel(end, false, END_LABEL); checkLabel(handler, false, "handler label"); if (labelInsnIndices.get(start) != null || labelInsnIndices.get(end) != null || labelInsnIndices.get(handler) != null) { throw new IllegalStateException("Try catch blocks must be visited before their labels"); } if (type != null) { checkInternalName(version, type, "type"); } super.visitTryCatchBlock(start, end, handler, type); handlers.add(start); handlers.add(end); } @Override public AnnotationVisitor visitTryCatchAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); int sort = new TypeReference(typeRef).getSort(); if (sort != TypeReference.EXCEPTION_PARAMETER) { throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); } CheckClassAdapter.checkTypeRef(typeRef); CheckMethodAdapter.checkDescriptor(version, descriptor, false); return new CheckAnnotationAdapter( super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)); } @Override public void visitLocalVariable( final String name, final String descriptor, final String signature, final Label start, final Label end, final int index) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkUnqualifiedName(version, name, "name"); checkDescriptor(version, descriptor, false); checkLabel(start, true, START_LABEL); checkLabel(end, true, END_LABEL); checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX); int startInsnIndex = labelInsnIndices.get(start).intValue(); int endInsnIndex = labelInsnIndices.get(end).intValue(); if (endInsnIndex < startInsnIndex) { throw new IllegalArgumentException( "Invalid start and end labels (end must be greater than start)"); } super.visitLocalVariable(name, descriptor, signature, start, end, index); } @Override public AnnotationVisitor visitLocalVariableAnnotation( final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); int sort = new TypeReference(typeRef).getSort(); if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) { throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); } CheckClassAdapter.checkTypeRef(typeRef); checkDescriptor(version, descriptor, false); if (start == null || end == null || index == null || end.length != start.length || index.length != start.length) { throw new IllegalArgumentException( "Invalid start, end and index arrays (must be non null and of identical length"); } for (int i = 0; i < start.length; ++i) { checkLabel(start[i], true, START_LABEL); checkLabel(end[i], true, END_LABEL); checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX); int startInsnIndex = labelInsnIndices.get(start[i]).intValue(); int endInsnIndex = labelInsnIndices.get(end[i]).intValue(); if (endInsnIndex < startInsnIndex) { throw new IllegalArgumentException( "Invalid start and end labels (end must be greater than start)"); } } return super.visitLocalVariableAnnotation( typeRef, typePath, start, end, index, descriptor, visible); } @Override public void visitLineNumber(final int line, final Label start) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkUnsignedShort(line, "Invalid line number"); checkLabel(start, true, START_LABEL); super.visitLineNumber(line, start); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); visitMaxCalled = true; for (Label l : referencedLabels) { if (labelInsnIndices.get(l) == null) { throw new IllegalStateException("Undefined label used"); } } for (int i = 0; i < handlers.size(); i += 2) { Integer startInsnIndex = labelInsnIndices.get(handlers.get(i)); Integer endInsnIndex = labelInsnIndices.get(handlers.get(i + 1)); if (startInsnIndex == null || endInsnIndex == null) { throw new IllegalStateException("Undefined try catch block labels"); } if (endInsnIndex.intValue() <= startInsnIndex.intValue()) { throw new IllegalStateException("Emty try catch block handler range"); } } checkUnsignedShort(maxStack, "Invalid max stack"); checkUnsignedShort(maxLocals, "Invalid max locals"); super.visitMaxs(maxStack, maxLocals); } @Override public void visitEnd() { checkVisitEndNotCalled(); visitEndCalled = true; super.visitEnd(); } // ----------------------------------------------------------------------------------------------- // Utility methods // -----------------------------------------------------------------------------------------------
Checks that the visitCode method has been called.
/** Checks that the {@link #visitCode} method has been called. */
private void checkVisitCodeCalled() { if (!visitCodeCalled) { throw new IllegalStateException( "Cannot visit instructions before visitCode has been called."); } }
Checks that the visitMaxs method has not been called.
/** Checks that the {@link #visitMaxs} method has not been called. */
private void checkVisitMaxsNotCalled() { if (visitMaxCalled) { throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); } }
Checks that the visitEnd method has not been called.
/** Checks that the {@link #visitEnd} method has not been called. */
private void checkVisitEndNotCalled() { if (visitEndCalled) { throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); } }
Checks a stack frame value.
Params:
  • value – the value to be checked.
/** * Checks a stack frame value. * * @param value the value to be checked. */
private void checkFrameValue(final Object value) { if (value == Opcodes.TOP || value == Opcodes.INTEGER || value == Opcodes.FLOAT || value == Opcodes.LONG || value == Opcodes.DOUBLE || value == Opcodes.NULL || value == Opcodes.UNINITIALIZED_THIS) { return; } else if (value instanceof String) { checkInternalName(version, (String) value, "Invalid stack frame value"); } else if (value instanceof Label) { referencedLabels.add((Label) value); } else { throw new IllegalArgumentException("Invalid stack frame value: " + value); } }
Checks that the method to visit the given opcode is equal to the given method.
Params:
  • opcode – the opcode to be checked.
  • method – the expected visit method.
/** * Checks that the method to visit the given opcode is equal to the given method. * * @param opcode the opcode to be checked. * @param method the expected visit method. */
private static void checkOpcodeMethod(final int opcode, final Method method) { if (opcode < Opcodes.NOP || opcode > Opcodes.IFNONNULL || OPCODE_METHODS[opcode] != method) { throw new IllegalArgumentException("Invalid opcode: " + opcode); } }
Checks that the given value is a signed byte.
Params:
  • value – the value to be checked.
  • message – the message to use in case of error.
/** * Checks that the given value is a signed byte. * * @param value the value to be checked. * @param message the message to use in case of error. */
private static void checkSignedByte(final int value, final String message) { if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { throw new IllegalArgumentException(message + " (must be a signed byte): " + value); } }
Checks that the given value is a signed short.
Params:
  • value – the value to be checked.
  • message – the message to use in case of error.
/** * Checks that the given value is a signed short. * * @param value the value to be checked. * @param message the message to use in case of error. */
private static void checkSignedShort(final int value, final String message) { if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { throw new IllegalArgumentException(message + " (must be a signed short): " + value); } }
Checks that the given value is an unsigned short.
Params:
  • value – the value to be checked.
  • message – the message to use in case of error.
/** * Checks that the given value is an unsigned short. * * @param value the value to be checked. * @param message the message to use in case of error. */
private static void checkUnsignedShort(final int value, final String message) { if (value < 0 || value > 65535) { throw new IllegalArgumentException(message + " (must be an unsigned short): " + value); } }
Checks that the given value is an Integer, Float, Long, Double or String value.
Params:
  • value – the value to be checked.
/** * Checks that the given value is an {@link Integer}, {@link Float}, {@link Long}, {@link Double} * or {@link String} value. * * @param value the value to be checked. */
static void checkConstant(final Object value) { if (!(value instanceof Integer) && !(value instanceof Float) && !(value instanceof Long) && !(value instanceof Double) && !(value instanceof String)) { throw new IllegalArgumentException("Invalid constant: " + value); } }
Checks that the given value is a valid operand for the LDC instruction.
Params:
  • value – the value to be checked.
/** * Checks that the given value is a valid operand for the LDC instruction. * * @param value the value to be checked. */
private void checkLdcConstant(final Object value) { if (value instanceof Type) { int sort = ((Type) value).getSort(); if (sort != Type.OBJECT && sort != Type.ARRAY && sort != Type.METHOD) { throw new IllegalArgumentException("Illegal LDC constant value"); } if (sort != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) { throw new IllegalArgumentException("ldc of a constant class requires at least version 1.5"); } if (sort == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) { throw new IllegalArgumentException("ldc of a method type requires at least version 1.7"); } } else if (value instanceof Handle) { if ((version & 0xFFFF) < Opcodes.V1_7) { throw new IllegalArgumentException("ldc of a Handle requires at least version 1.7"); } Handle handle = (Handle) value; int tag = handle.getTag(); if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) { throw new IllegalArgumentException("invalid handle tag " + tag); } checkInternalName(this.version, handle.getOwner(), "handle owner"); if (tag <= Opcodes.H_PUTSTATIC) { checkDescriptor(this.version, handle.getDesc(), false); } else { checkMethodDescriptor(this.version, handle.getDesc()); } String handleName = handle.getName(); if (!("<init>".equals(handleName) && tag == Opcodes.H_NEWINVOKESPECIAL)) { checkMethodIdentifier(this.version, handleName, "handle name"); } } else if (value instanceof ConstantDynamic) { if ((version & 0xFFFF) < Opcodes.V11) { throw new IllegalArgumentException("ldc of a ConstantDynamic requires at least version 11"); } ConstantDynamic constantDynamic = (ConstantDynamic) value; checkMethodIdentifier(this.version, constantDynamic.getName(), "constant dynamic name"); checkDescriptor(this.version, constantDynamic.getDescriptor(), false); checkLdcConstant(constantDynamic.getBootstrapMethod()); int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount(); for (int i = 0; i < bootstrapMethodArgumentCount; ++i) { checkLdcConstant(constantDynamic.getBootstrapMethodArgument(i)); } } else { checkConstant(value); } }
Checks that the given string is a valid unqualified name.
Params:
  • version – the class version.
  • name – the string to be checked.
  • message – the message to use in case of error.
/** * Checks that the given string is a valid unqualified name. * * @param version the class version. * @param name the string to be checked. * @param message the message to use in case of error. */
static void checkUnqualifiedName(final int version, final String name, final String message) { checkIdentifier(version, name, 0, -1, message); }
Checks that the given substring is a valid Java identifier.
Params:
  • version – the class version.
  • name – the string to be checked.
  • startPos – the index of the first character of the identifier (inclusive).
  • endPos – the index of the last character of the identifier (exclusive). -1 is equivalent to name.length() if name is not null.
  • message – the message to use in case of error.
/** * Checks that the given substring is a valid Java identifier. * * @param version the class version. * @param name the string to be checked. * @param startPos the index of the first character of the identifier (inclusive). * @param endPos the index of the last character of the identifier (exclusive). -1 is equivalent * to {@code name.length()} if name is not {@literal null}. * @param message the message to use in case of error. */
static void checkIdentifier( final int version, final String name, final int startPos, final int endPos, final String message) { if (name == null || (endPos == -1 ? name.length() <= startPos : endPos <= startPos)) { throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); } int max = endPos == -1 ? name.length() : endPos; if ((version & 0xFFFF) >= Opcodes.V1_5) { for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) { if (".;[/".indexOf(name.codePointAt(i)) != -1) { throw new IllegalArgumentException( INVALID + message + " (must not contain . ; [ or /): " + name); } } return; } for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) { if (i == startPos ? !Character.isJavaIdentifierStart(name.codePointAt(i)) : !Character.isJavaIdentifierPart(name.codePointAt(i))) { throw new IllegalArgumentException( INVALID + message + " (must be a valid Java identifier): " + name); } } }
Checks that the given string is a valid Java identifier.
Params:
  • version – the class version.
  • name – the string to be checked.
  • message – the message to use in case of error.
/** * Checks that the given string is a valid Java identifier. * * @param version the class version. * @param name the string to be checked. * @param message the message to use in case of error. */
static void checkMethodIdentifier(final int version, final String name, final String message) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); } if ((version & 0xFFFF) >= Opcodes.V1_5) { for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) { if (".;[/<>".indexOf(name.codePointAt(i)) != -1) { throw new IllegalArgumentException( INVALID + message + " (must be a valid unqualified name): " + name); } } return; } for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) { if (i == 0 ? !Character.isJavaIdentifierStart(name.codePointAt(i)) : !Character.isJavaIdentifierPart(name.codePointAt(i))) { throw new IllegalArgumentException( INVALID + message + " (must be a '<init>', '<clinit>' or a valid Java identifier): " + name); } } }
Checks that the given string is a valid internal class name or array type descriptor.
Params:
  • version – the class version.
  • name – the string to be checked.
  • message – the message to use in case of error.
/** * Checks that the given string is a valid internal class name or array type descriptor. * * @param version the class version. * @param name the string to be checked. * @param message the message to use in case of error. */
static void checkInternalName(final int version, final String name, final String message) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY); } if (name.charAt(0) == '[') { checkDescriptor(version, name, false); } else { checkInternalClassName(version, name, message); } }
Checks that the given string is a valid internal class name.
Params:
  • version – the class version.
  • name – the string to be checked.
  • message – the message to use in case of error.
/** * Checks that the given string is a valid internal class name. * * @param version the class version. * @param name the string to be checked. * @param message the message to use in case of error. */
private static void checkInternalClassName( final int version, final String name, final String message) { try { int startIndex = 0; int slashIndex; while ((slashIndex = name.indexOf('/', startIndex + 1)) != -1) { CheckMethodAdapter.checkIdentifier(version, name, startIndex, slashIndex, null); startIndex = slashIndex + 1; } CheckMethodAdapter.checkIdentifier(version, name, startIndex, name.length(), null); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( INVALID + message + " (must be an internal class name): " + name, e); } }
Checks that the given string is a valid type descriptor.
Params:
  • version – the class version.
  • descriptor – the string to be checked.
  • canBeVoid – true if V can be considered valid.
/** * Checks that the given string is a valid type descriptor. * * @param version the class version. * @param descriptor the string to be checked. * @param canBeVoid {@literal true} if {@code V} can be considered valid. */
static void checkDescriptor(final int version, final String descriptor, final boolean canBeVoid) { int endPos = checkDescriptor(version, descriptor, 0, canBeVoid); if (endPos != descriptor.length()) { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } }
Checks that a the given substring is a valid type descriptor.
Params:
  • version – the class version.
  • descriptor – the string to be checked.
  • startPos – the index of the first character of the type descriptor (inclusive).
  • canBeVoid – whether V can be considered valid.
Returns:the index of the last character of the type descriptor, plus one.
/** * Checks that a the given substring is a valid type descriptor. * * @param version the class version. * @param descriptor the string to be checked. * @param startPos the index of the first character of the type descriptor (inclusive). * @param canBeVoid whether {@code V} can be considered valid. * @return the index of the last character of the type descriptor, plus one. */
private static int checkDescriptor( final int version, final String descriptor, final int startPos, final boolean canBeVoid) { if (descriptor == null || startPos >= descriptor.length()) { throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); } switch (descriptor.charAt(startPos)) { case 'V': if (canBeVoid) { return startPos + 1; } else { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } case 'Z': case 'C': case 'B': case 'S': case 'I': case 'F': case 'J': case 'D': return startPos + 1; case '[': int pos = startPos + 1; while (pos < descriptor.length() && descriptor.charAt(pos) == '[') { ++pos; } if (pos < descriptor.length()) { return checkDescriptor(version, descriptor, pos, false); } else { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } case 'L': int endPos = descriptor.indexOf(';', startPos); if (startPos == -1 || endPos - startPos < 2) { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } try { checkInternalClassName(version, descriptor.substring(startPos + 1, endPos), null); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor, e); } return endPos + 1; default: throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } }
Checks that the given string is a valid method descriptor.
Params:
  • version – the class version.
  • descriptor – the string to be checked.
/** * Checks that the given string is a valid method descriptor. * * @param version the class version. * @param descriptor the string to be checked. */
static void checkMethodDescriptor(final int version, final String descriptor) { if (descriptor == null || descriptor.isEmpty()) { throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); } if (descriptor.charAt(0) != '(' || descriptor.length() < 3) { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } int pos = 1; if (descriptor.charAt(pos) != ')') { do { if (descriptor.charAt(pos) == 'V') { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } pos = checkDescriptor(version, descriptor, pos, false); } while (pos < descriptor.length() && descriptor.charAt(pos) != ')'); } pos = checkDescriptor(version, descriptor, pos + 1, true); if (pos != descriptor.length()) { throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor); } }
Checks that the given label is not null. This method can also check that the label has been visited.
Params:
  • label – the label to be checked.
  • checkVisited – whether to check that the label has been visited.
  • message – the message to use in case of error.
/** * Checks that the given label is not null. This method can also check that the label has been * visited. * * @param label the label to be checked. * @param checkVisited whether to check that the label has been visited. * @param message the message to use in case of error. */
private void checkLabel(final Label label, final boolean checkVisited, final String message) { if (label == null) { throw new IllegalArgumentException(INVALID + message + " (must not be null)"); } if (checkVisited && labelInsnIndices.get(label) == null) { throw new IllegalArgumentException(INVALID + message + " (must be visited first)"); } } }