18206 lines
766 KiB
Diff
18206 lines
766 KiB
Diff
|
|
diff --git a/pom.xml b/pom.xml
|
||
|
|
index 5e37c24..9f04c58 100644
|
||
|
|
--- a/pom.xml
|
||
|
|
+++ b/pom.xml
|
||
|
|
@@ -260,6 +260,16 @@
|
||
|
|
<scope>test</scope>
|
||
|
|
</dependency>
|
||
|
|
|
||
|
|
+ <dependency>
|
||
|
|
+ <groupId>org.ow2.asm</groupId>
|
||
|
|
+ <artifactId>asm</artifactId>
|
||
|
|
+ <version>5.0.4</version>
|
||
|
|
+ </dependency>
|
||
|
|
+ <dependency>
|
||
|
|
+ <groupId>org.ow2.asm</groupId>
|
||
|
|
+ <artifactId>asm-util</artifactId>
|
||
|
|
+ <version>5.0.4</version>
|
||
|
|
+ </dependency>
|
||
|
|
</dependencies>
|
||
|
|
|
||
|
|
<distributionManagement>
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/AnnotationVisitor.java b/src/main/java/org/mvel2/asm/AnnotationVisitor.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index c706a8a..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/AnnotationVisitor.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,145 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A visitor to visit a Java annotation. The methods of this class must be called in the following
|
||
|
|
- * order: ( {@code visit} | {@code visitEnum} | {@code visitAnnotation} | {@code visitArray} )*
|
||
|
|
- * {@code visitEnd}.
|
||
|
|
- *
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- * @author Eugene Kuleshov
|
||
|
|
- */
|
||
|
|
-public abstract class AnnotationVisitor {
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- */
|
||
|
|
- protected final int api;
|
||
|
|
-
|
||
|
|
- /** The annotation visitor to which this visitor must delegate method calls. May be null. */
|
||
|
|
- protected AnnotationVisitor av;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link AnnotationVisitor}.
|
||
|
|
- *
|
||
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor(final int api) {
|
||
|
|
- this(api, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link AnnotationVisitor}.
|
||
|
|
- *
|
||
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- * @param annotationVisitor the annotation visitor to which this visitor must delegate method
|
||
|
|
- * calls. May be null.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
|
||
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- this.api = api;
|
||
|
|
- this.av = annotationVisitor;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a primitive value of the annotation.
|
||
|
|
- *
|
||
|
|
- * @param name the value name.
|
||
|
|
- * @param value the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link
|
||
|
|
- * Character}, {@link Short}, {@link Integer} , {@link Long}, {@link Float}, {@link Double},
|
||
|
|
- * {@link String} or {@link Type} of {@link Type#OBJECT} or {@link Type#ARRAY} sort. This
|
||
|
|
- * value can also be an array of byte, boolean, short, char, int, long, float or double values
|
||
|
|
- * (this is equivalent to using {@link #visitArray} and visiting each array element in turn,
|
||
|
|
- * but is more convenient).
|
||
|
|
- */
|
||
|
|
- public void visit(final String name, final Object value) {
|
||
|
|
- if (av != null) {
|
||
|
|
- av.visit(name, value);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits an enumeration value of the annotation.
|
||
|
|
- *
|
||
|
|
- * @param name the value name.
|
||
|
|
- * @param descriptor the class descriptor of the enumeration class.
|
||
|
|
- * @param value the actual enumeration value.
|
||
|
|
- */
|
||
|
|
- public void visitEnum(final String name, final String descriptor, final String value) {
|
||
|
|
- if (av != null) {
|
||
|
|
- av.visitEnum(name, descriptor, value);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a nested annotation value of the annotation.
|
||
|
|
- *
|
||
|
|
- * @param name the value name.
|
||
|
|
- * @param descriptor the class descriptor of the nested annotation class.
|
||
|
|
- * @return a visitor to visit the actual nested annotation value, or {@literal null} if this
|
||
|
|
- * visitor is not interested in visiting this nested annotation. <i>The nested annotation
|
||
|
|
- * value must be fully visited before calling other methods on this annotation visitor</i>.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
|
||
|
|
- if (av != null) {
|
||
|
|
- return av.visitAnnotation(name, descriptor);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits an array value of the annotation. Note that arrays of primitive types (such as byte,
|
||
|
|
- * boolean, short, char, int, long, float or double) can be passed as value to {@link #visit
|
||
|
|
- * visit}. This is what {@link ClassReader} does.
|
||
|
|
- *
|
||
|
|
- * @param name the value name.
|
||
|
|
- * @return a visitor to visit the actual array value elements, or {@literal null} if this visitor
|
||
|
|
- * is not interested in visiting these values. The 'name' parameters passed to the methods of
|
||
|
|
- * this visitor are ignored. <i>All the array values must be visited before calling other
|
||
|
|
- * methods on this annotation visitor</i>.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor visitArray(final String name) {
|
||
|
|
- if (av != null) {
|
||
|
|
- return av.visitArray(name);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /** Visits the end of the annotation. */
|
||
|
|
- public void visitEnd() {
|
||
|
|
- if (av != null) {
|
||
|
|
- av.visitEnd();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/AnnotationWriter.java b/src/main/java/org/mvel2/asm/AnnotationWriter.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index 370135b..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/AnnotationWriter.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,418 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation'
|
||
|
|
- * structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter
|
||
|
|
- * instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations
|
||
|
|
- * attributes can be generated with the {@link #putAnnotations} method. Similarly, arrays of such
|
||
|
|
- * lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes.
|
||
|
|
- *
|
||
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16">JVMS
|
||
|
|
- * 4.7.16</a>
|
||
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS
|
||
|
|
- * 4.7.20</a>
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- * @author Eugene Kuleshov
|
||
|
|
- */
|
||
|
|
-final class AnnotationWriter extends AnnotationVisitor {
|
||
|
|
-
|
||
|
|
- /** Where the constants used in this AnnotationWriter must be stored. */
|
||
|
|
- private final SymbolTable symbolTable;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Whether values are named or not. AnnotationWriter instances used for annotation default and
|
||
|
|
- * annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each
|
||
|
|
- * value, instead of an element_name_index followed by an element_value).
|
||
|
|
- */
|
||
|
|
- private final boolean useNamedValues;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values
|
||
|
|
- * visited so far. All the fields of these structures, except the last one - the
|
||
|
|
- * element_value_pairs array, must be set before this ByteVector is passed to the constructor
|
||
|
|
- * (num_element_value_pairs can be set to 0, it is reset to the correct value in {@link
|
||
|
|
- * #visitEnd()}). The element_value_pairs array is filled incrementally in the various visit()
|
||
|
|
- * methods.
|
||
|
|
- *
|
||
|
|
- * <p>Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a
|
||
|
|
- * single element_value by definition), this ByteVector is initially empty when passed to the
|
||
|
|
- * constructor, and {@link #numElementValuePairsOffset} is set to -1.
|
||
|
|
- */
|
||
|
|
- private final ByteVector annotation;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The offset in {@link #annotation} where {@link #numElementValuePairs} must be stored (or -1 for
|
||
|
|
- * the case of AnnotationDefault attributes).
|
||
|
|
- */
|
||
|
|
- private final int numElementValuePairsOffset;
|
||
|
|
-
|
||
|
|
- /** The number of element value pairs visited so far. */
|
||
|
|
- private int numElementValuePairs;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The previous AnnotationWriter. This field is used to store the list of annotations of a
|
||
|
|
- * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
|
||
|
|
- * (annotation values of annotation type), or for AnnotationDefault attributes.
|
||
|
|
- */
|
||
|
|
- private final AnnotationWriter previousAnnotation;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The next AnnotationWriter. This field is used to store the list of annotations of a
|
||
|
|
- * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
|
||
|
|
- * (annotation values of annotation type), or for AnnotationDefault attributes.
|
||
|
|
- */
|
||
|
|
- private AnnotationWriter nextAnnotation;
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Constructors
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link AnnotationWriter}.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in this AnnotationWriter must be stored.
|
||
|
|
- * @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays
|
||
|
|
- * use unnamed values.
|
||
|
|
- * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
|
||
|
|
- * the visited content must be stored. This ByteVector must already contain all the fields of
|
||
|
|
- * the structure except the last one (the element_value_pairs array).
|
||
|
|
- * @param previousAnnotation the previously visited annotation of the
|
||
|
|
- * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
|
||
|
|
- * other cases (e.g. nested or array annotations).
|
||
|
|
- */
|
||
|
|
- AnnotationWriter(
|
||
|
|
- final SymbolTable symbolTable,
|
||
|
|
- final boolean useNamedValues,
|
||
|
|
- final ByteVector annotation,
|
||
|
|
- final AnnotationWriter previousAnnotation) {
|
||
|
|
- super(Opcodes.ASM7);
|
||
|
|
- this.symbolTable = symbolTable;
|
||
|
|
- this.useNamedValues = useNamedValues;
|
||
|
|
- this.annotation = annotation;
|
||
|
|
- // By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'.
|
||
|
|
- this.numElementValuePairsOffset = annotation.length == 0 ? -1 : annotation.length - 2;
|
||
|
|
- this.previousAnnotation = previousAnnotation;
|
||
|
|
- if (previousAnnotation != null) {
|
||
|
|
- previousAnnotation.nextAnnotation = this;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link AnnotationWriter} using named values.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in this AnnotationWriter must be stored.
|
||
|
|
- * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
|
||
|
|
- * the visited content must be stored. This ByteVector must already contain all the fields of
|
||
|
|
- * the structure except the last one (the element_value_pairs array).
|
||
|
|
- * @param previousAnnotation the previously visited annotation of the
|
||
|
|
- * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
|
||
|
|
- * other cases (e.g. nested or array annotations).
|
||
|
|
- */
|
||
|
|
- AnnotationWriter(
|
||
|
|
- final SymbolTable symbolTable,
|
||
|
|
- final ByteVector annotation,
|
||
|
|
- final AnnotationWriter previousAnnotation) {
|
||
|
|
- this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Implementation of the AnnotationVisitor abstract class
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public void visit(final String name, final Object value) {
|
||
|
|
- // Case of an element_value with a const_value_index, class_info_index or array_index field.
|
||
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||
|
|
- ++numElementValuePairs;
|
||
|
|
- if (useNamedValues) {
|
||
|
|
- annotation.putShort(symbolTable.addConstantUtf8(name));
|
||
|
|
- }
|
||
|
|
- if (value instanceof String) {
|
||
|
|
- annotation.put12('s', symbolTable.addConstantUtf8((String) value));
|
||
|
|
- } else if (value instanceof Byte) {
|
||
|
|
- annotation.put12('B', symbolTable.addConstantInteger(((Byte) value).byteValue()).index);
|
||
|
|
- } else if (value instanceof Boolean) {
|
||
|
|
- int booleanValue = ((Boolean) value).booleanValue() ? 1 : 0;
|
||
|
|
- annotation.put12('Z', symbolTable.addConstantInteger(booleanValue).index);
|
||
|
|
- } else if (value instanceof Character) {
|
||
|
|
- annotation.put12('C', symbolTable.addConstantInteger(((Character) value).charValue()).index);
|
||
|
|
- } else if (value instanceof Short) {
|
||
|
|
- annotation.put12('S', symbolTable.addConstantInteger(((Short) value).shortValue()).index);
|
||
|
|
- } else if (value instanceof Type) {
|
||
|
|
- annotation.put12('c', symbolTable.addConstantUtf8(((Type) value).getDescriptor()));
|
||
|
|
- } else if (value instanceof byte[]) {
|
||
|
|
- byte[] byteArray = (byte[]) value;
|
||
|
|
- annotation.put12('[', byteArray.length);
|
||
|
|
- for (byte byteValue : byteArray) {
|
||
|
|
- annotation.put12('B', symbolTable.addConstantInteger(byteValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof boolean[]) {
|
||
|
|
- boolean[] booleanArray = (boolean[]) value;
|
||
|
|
- annotation.put12('[', booleanArray.length);
|
||
|
|
- for (boolean booleanValue : booleanArray) {
|
||
|
|
- annotation.put12('Z', symbolTable.addConstantInteger(booleanValue ? 1 : 0).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof short[]) {
|
||
|
|
- short[] shortArray = (short[]) value;
|
||
|
|
- annotation.put12('[', shortArray.length);
|
||
|
|
- for (short shortValue : shortArray) {
|
||
|
|
- annotation.put12('S', symbolTable.addConstantInteger(shortValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof char[]) {
|
||
|
|
- char[] charArray = (char[]) value;
|
||
|
|
- annotation.put12('[', charArray.length);
|
||
|
|
- for (char charValue : charArray) {
|
||
|
|
- annotation.put12('C', symbolTable.addConstantInteger(charValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof int[]) {
|
||
|
|
- int[] intArray = (int[]) value;
|
||
|
|
- annotation.put12('[', intArray.length);
|
||
|
|
- for (int intValue : intArray) {
|
||
|
|
- annotation.put12('I', symbolTable.addConstantInteger(intValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof long[]) {
|
||
|
|
- long[] longArray = (long[]) value;
|
||
|
|
- annotation.put12('[', longArray.length);
|
||
|
|
- for (long longValue : longArray) {
|
||
|
|
- annotation.put12('J', symbolTable.addConstantLong(longValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof float[]) {
|
||
|
|
- float[] floatArray = (float[]) value;
|
||
|
|
- annotation.put12('[', floatArray.length);
|
||
|
|
- for (float floatValue : floatArray) {
|
||
|
|
- annotation.put12('F', symbolTable.addConstantFloat(floatValue).index);
|
||
|
|
- }
|
||
|
|
- } else if (value instanceof double[]) {
|
||
|
|
- double[] doubleArray = (double[]) value;
|
||
|
|
- annotation.put12('[', doubleArray.length);
|
||
|
|
- for (double doubleValue : doubleArray) {
|
||
|
|
- annotation.put12('D', symbolTable.addConstantDouble(doubleValue).index);
|
||
|
|
- }
|
||
|
|
- } else {
|
||
|
|
- Symbol symbol = symbolTable.addConstant(value);
|
||
|
|
- annotation.put12(".s.IFJDCS".charAt(symbol.tag), symbol.index);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public void visitEnum(final String name, final String descriptor, final String value) {
|
||
|
|
- // Case of an element_value with an enum_const_value field.
|
||
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||
|
|
- ++numElementValuePairs;
|
||
|
|
- if (useNamedValues) {
|
||
|
|
- annotation.putShort(symbolTable.addConstantUtf8(name));
|
||
|
|
- }
|
||
|
|
- annotation
|
||
|
|
- .put12('e', symbolTable.addConstantUtf8(descriptor))
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(value));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
|
||
|
|
- // Case of an element_value with an annotation_value field.
|
||
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||
|
|
- ++numElementValuePairs;
|
||
|
|
- if (useNamedValues) {
|
||
|
|
- annotation.putShort(symbolTable.addConstantUtf8(name));
|
||
|
|
- }
|
||
|
|
- // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
|
||
|
|
- annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||
|
|
- return new AnnotationWriter(symbolTable, annotation, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public AnnotationVisitor visitArray(final String name) {
|
||
|
|
- // Case of an element_value with an array_value field.
|
||
|
|
- // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1
|
||
|
|
- ++numElementValuePairs;
|
||
|
|
- if (useNamedValues) {
|
||
|
|
- annotation.putShort(symbolTable.addConstantUtf8(name));
|
||
|
|
- }
|
||
|
|
- // Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the
|
||
|
|
- // end of an element_value of array type is similar to the end of an 'annotation' structure: an
|
||
|
|
- // unsigned short num_values followed by num_values element_value, versus an unsigned short
|
||
|
|
- // num_element_value_pairs, followed by num_element_value_pairs { element_name_index,
|
||
|
|
- // element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to
|
||
|
|
- // visit the array elements. Its num_element_value_pairs will correspond to the number of array
|
||
|
|
- // elements and will be stored in what is in fact num_values.
|
||
|
|
- annotation.put12('[', 0);
|
||
|
|
- return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public void visitEnd() {
|
||
|
|
- if (numElementValuePairsOffset != -1) {
|
||
|
|
- byte[] data = annotation.data;
|
||
|
|
- data[numElementValuePairsOffset] = (byte) (numElementValuePairs >>> 8);
|
||
|
|
- data[numElementValuePairsOffset + 1] = (byte) numElementValuePairs;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Utility methods
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation
|
||
|
|
- * and all its <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the attribute name
|
||
|
|
- * to the constant pool of the class (if not null).
|
||
|
|
- *
|
||
|
|
- * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null.
|
||
|
|
- * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
|
||
|
|
- * annotation and all its predecessors. This includes the size of the attribute_name_index and
|
||
|
|
- * attribute_length fields.
|
||
|
|
- */
|
||
|
|
- int computeAnnotationsSize(final String attributeName) {
|
||
|
|
- if (attributeName != null) {
|
||
|
|
- symbolTable.addConstantUtf8(attributeName);
|
||
|
|
- }
|
||
|
|
- // The attribute_name_index, attribute_length and num_annotations fields use 8 bytes.
|
||
|
|
- int attributeSize = 8;
|
||
|
|
- AnnotationWriter annotationWriter = this;
|
||
|
|
- while (annotationWriter != null) {
|
||
|
|
- attributeSize += annotationWriter.annotation.length;
|
||
|
|
- annotationWriter = annotationWriter.previousAnnotation;
|
||
|
|
- }
|
||
|
|
- return attributeSize;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its
|
||
|
|
- * <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector. Annotations are
|
||
|
|
- * put in the same order they have been visited.
|
||
|
|
- *
|
||
|
|
- * @param attributeNameIndex the constant pool index of the attribute name (one of
|
||
|
|
- * "Runtime[In]Visible[Type]Annotations").
|
||
|
|
- * @param output where the attribute must be put.
|
||
|
|
- */
|
||
|
|
- void putAnnotations(final int attributeNameIndex, final ByteVector output) {
|
||
|
|
- int attributeLength = 2; // For num_annotations.
|
||
|
|
- int numAnnotations = 0;
|
||
|
|
- AnnotationWriter annotationWriter = this;
|
||
|
|
- AnnotationWriter firstAnnotation = null;
|
||
|
|
- while (annotationWriter != null) {
|
||
|
|
- // In case the user forgot to call visitEnd().
|
||
|
|
- annotationWriter.visitEnd();
|
||
|
|
- attributeLength += annotationWriter.annotation.length;
|
||
|
|
- numAnnotations++;
|
||
|
|
- firstAnnotation = annotationWriter;
|
||
|
|
- annotationWriter = annotationWriter.previousAnnotation;
|
||
|
|
- }
|
||
|
|
- output.putShort(attributeNameIndex);
|
||
|
|
- output.putInt(attributeLength);
|
||
|
|
- output.putShort(numAnnotations);
|
||
|
|
- annotationWriter = firstAnnotation;
|
||
|
|
- while (annotationWriter != null) {
|
||
|
|
- output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
|
||
|
|
- annotationWriter = annotationWriter.nextAnnotation;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the
|
||
|
|
- * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the
|
||
|
|
- * constant pool of the class.
|
||
|
|
- *
|
||
|
|
- * @param attributeName one of "Runtime[In]VisibleParameterAnnotations".
|
||
|
|
- * @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
|
||
|
|
- * element).
|
||
|
|
- * @param annotableParameterCount the number of elements in annotationWriters to take into account
|
||
|
|
- * (elements [0..annotableParameterCount[ are taken into account).
|
||
|
|
- * @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding
|
||
|
|
- * to the given sub-array of AnnotationWriter lists. This includes the size of the
|
||
|
|
- * attribute_name_index and attribute_length fields.
|
||
|
|
- */
|
||
|
|
- static int computeParameterAnnotationsSize(
|
||
|
|
- final String attributeName,
|
||
|
|
- final AnnotationWriter[] annotationWriters,
|
||
|
|
- final int annotableParameterCount) {
|
||
|
|
- // Note: attributeName is added to the constant pool by the call to computeAnnotationsSize
|
||
|
|
- // below. This assumes that there is at least one non-null element in the annotationWriters
|
||
|
|
- // sub-array (which is ensured by the lazy instantiation of this array in MethodWriter).
|
||
|
|
- // The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each
|
||
|
|
- // element of the parameter_annotations array uses 2 bytes for its num_annotations field.
|
||
|
|
- int attributeSize = 7 + 2 * annotableParameterCount;
|
||
|
|
- for (int i = 0; i < annotableParameterCount; ++i) {
|
||
|
|
- AnnotationWriter annotationWriter = annotationWriters[i];
|
||
|
|
- attributeSize +=
|
||
|
|
- annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(attributeName) - 8;
|
||
|
|
- }
|
||
|
|
- return attributeSize;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists
|
||
|
|
- * from the given AnnotationWriter sub-array in the given ByteVector.
|
||
|
|
- *
|
||
|
|
- * @param attributeNameIndex constant pool index of the attribute name (one of
|
||
|
|
- * Runtime[In]VisibleParameterAnnotations).
|
||
|
|
- * @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
|
||
|
|
- * element).
|
||
|
|
- * @param annotableParameterCount the number of elements in annotationWriters to put (elements
|
||
|
|
- * [0..annotableParameterCount[ are put).
|
||
|
|
- * @param output where the attribute must be put.
|
||
|
|
- */
|
||
|
|
- static void putParameterAnnotations(
|
||
|
|
- final int attributeNameIndex,
|
||
|
|
- final AnnotationWriter[] annotationWriters,
|
||
|
|
- final int annotableParameterCount,
|
||
|
|
- final ByteVector output) {
|
||
|
|
- // The num_parameters field uses 1 byte, and each element of the parameter_annotations array
|
||
|
|
- // uses 2 bytes for its num_annotations field.
|
||
|
|
- int attributeLength = 1 + 2 * annotableParameterCount;
|
||
|
|
- for (int i = 0; i < annotableParameterCount; ++i) {
|
||
|
|
- AnnotationWriter annotationWriter = annotationWriters[i];
|
||
|
|
- attributeLength +=
|
||
|
|
- annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8;
|
||
|
|
- }
|
||
|
|
- output.putShort(attributeNameIndex);
|
||
|
|
- output.putInt(attributeLength);
|
||
|
|
- output.putByte(annotableParameterCount);
|
||
|
|
- for (int i = 0; i < annotableParameterCount; ++i) {
|
||
|
|
- AnnotationWriter annotationWriter = annotationWriters[i];
|
||
|
|
- AnnotationWriter firstAnnotation = null;
|
||
|
|
- int numAnnotations = 0;
|
||
|
|
- while (annotationWriter != null) {
|
||
|
|
- // In case user the forgot to call visitEnd().
|
||
|
|
- annotationWriter.visitEnd();
|
||
|
|
- numAnnotations++;
|
||
|
|
- firstAnnotation = annotationWriter;
|
||
|
|
- annotationWriter = annotationWriter.previousAnnotation;
|
||
|
|
- }
|
||
|
|
- output.putShort(numAnnotations);
|
||
|
|
- annotationWriter = firstAnnotation;
|
||
|
|
- while (annotationWriter != null) {
|
||
|
|
- output.putByteArray(
|
||
|
|
- annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
|
||
|
|
- annotationWriter = annotationWriter.nextAnnotation;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/Attribute.java b/src/main/java/org/mvel2/asm/Attribute.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index 17de1c9..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/Attribute.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,325 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
|
||
|
|
- * Specification (JVMS).
|
||
|
|
- *
|
||
|
|
- * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
|
||
|
|
- * 4.7</a>
|
||
|
|
- * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS
|
||
|
|
- * 4.7.3</a>
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- * @author Eugene Kuleshov
|
||
|
|
- */
|
||
|
|
-public class Attribute {
|
||
|
|
-
|
||
|
|
- /** The type of this attribute, also called its name in the JVMS. */
|
||
|
|
- public final String type;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
|
||
|
|
- * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are <i>not</i>
|
||
|
|
- * included.
|
||
|
|
- */
|
||
|
|
- private byte[] content;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The next attribute in this attribute list (Attribute instances can be linked via this field to
|
||
|
|
- * store a list of class, field, method or code attributes). May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- Attribute nextAttribute;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new empty attribute.
|
||
|
|
- *
|
||
|
|
- * @param type the type of the attribute.
|
||
|
|
- */
|
||
|
|
- protected Attribute(final String type) {
|
||
|
|
- this.type = type;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns {@literal true} if this type of attribute is unknown. This means that the attribute
|
||
|
|
- * content can't be parsed to extract constant pool references, labels, etc. Instead, the
|
||
|
|
- * attribute content is read as an opaque byte array, and written back as is. This can lead to
|
||
|
|
- * invalid attributes, if the content actually contains constant pool references, labels, or other
|
||
|
|
- * symbolic references that need to be updated when there are changes to the constant pool, the
|
||
|
|
- * method bytecode, etc. The default implementation of this method always returns {@literal true}.
|
||
|
|
- *
|
||
|
|
- * @return {@literal true} if this type of attribute is unknown.
|
||
|
|
- */
|
||
|
|
- public boolean isUnknown() {
|
||
|
|
- return true;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns {@literal true} if this type of attribute is a code attribute.
|
||
|
|
- *
|
||
|
|
- * @return {@literal true} if this type of attribute is a code attribute.
|
||
|
|
- */
|
||
|
|
- public boolean isCodeAttribute() {
|
||
|
|
- return false;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the labels corresponding to this attribute.
|
||
|
|
- *
|
||
|
|
- * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
|
||
|
|
- * a code attribute that contains labels.
|
||
|
|
- */
|
||
|
|
- protected Label[] getLabels() {
|
||
|
|
- return new Label[0];
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a {@link #type} attribute. This method must return a <i>new</i> {@link Attribute} object,
|
||
|
|
- * of type {@link #type}, corresponding to the 'length' bytes starting at 'offset', in the given
|
||
|
|
- * ClassReader.
|
||
|
|
- *
|
||
|
|
- * @param classReader the class that contains the attribute to be read.
|
||
|
|
- * @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
|
||
|
|
- * 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
|
||
|
|
- * account here.
|
||
|
|
- * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
|
||
|
|
- * @param charBuffer the buffer to be used to call the ClassReader methods requiring a
|
||
|
|
- * 'charBuffer' parameter.
|
||
|
|
- * @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
|
||
|
|
- * in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
|
||
|
|
- * attribute header bytes (attribute_name_index and attribute_length) are not taken into
|
||
|
|
- * account here.
|
||
|
|
- * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
|
||
|
|
- * is not a code attribute.
|
||
|
|
- * @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
|
||
|
|
- */
|
||
|
|
- protected Attribute read(
|
||
|
|
- final ClassReader classReader,
|
||
|
|
- final int offset,
|
||
|
|
- final int length,
|
||
|
|
- final char[] charBuffer,
|
||
|
|
- final int codeAttributeOffset,
|
||
|
|
- final Label[] labels) {
|
||
|
|
- Attribute attribute = new Attribute(type);
|
||
|
|
- attribute.content = new byte[length];
|
||
|
|
- System.arraycopy(classReader.b, offset, attribute.content, 0, length);
|
||
|
|
- return attribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the byte array form of the content of this attribute. The 6 header bytes
|
||
|
|
- * (attribute_name_index and attribute_length) must <i>not</i> be added in the returned
|
||
|
|
- * ByteVector.
|
||
|
|
- *
|
||
|
|
- * @param classWriter the class to which this attribute must be added. This parameter can be used
|
||
|
|
- * to add the items that corresponds to this attribute to the constant pool of this class.
|
||
|
|
- * @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
|
||
|
|
- * if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
|
||
|
|
- * attribute.
|
||
|
|
- * @param codeLength the length of the bytecode of the method corresponding to this code
|
||
|
|
- * attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
|
||
|
|
- * field of the Code attribute.
|
||
|
|
- * @param maxStack the maximum stack size of the method corresponding to this code attribute, or
|
||
|
|
- * -1 if this attribute is not a code attribute.
|
||
|
|
- * @param maxLocals the maximum number of local variables of the method corresponding to this code
|
||
|
|
- * attribute, or -1 if this attribute is not a code attribute.
|
||
|
|
- * @return the byte array form of this attribute.
|
||
|
|
- */
|
||
|
|
- protected ByteVector write(
|
||
|
|
- final ClassWriter classWriter,
|
||
|
|
- final byte[] code,
|
||
|
|
- final int codeLength,
|
||
|
|
- final int maxStack,
|
||
|
|
- final int maxLocals) {
|
||
|
|
- return new ByteVector(content);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the number of attributes of the attribute list that begins with this attribute.
|
||
|
|
- *
|
||
|
|
- * @return the number of attributes of the attribute list that begins with this attribute.
|
||
|
|
- */
|
||
|
|
- final int getAttributeCount() {
|
||
|
|
- int count = 0;
|
||
|
|
- Attribute attribute = this;
|
||
|
|
- while (attribute != null) {
|
||
|
|
- count += 1;
|
||
|
|
- attribute = attribute.nextAttribute;
|
||
|
|
- }
|
||
|
|
- return count;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the total size in bytes of all the attributes in the attribute list that begins with
|
||
|
|
- * this attribute. This size includes the 6 header bytes (attribute_name_index and
|
||
|
|
- * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in the attributes must be stored.
|
||
|
|
- * @return the size of all the attributes in this attribute list. This size includes the size of
|
||
|
|
- * the attribute headers.
|
||
|
|
- */
|
||
|
|
- final int computeAttributesSize(final SymbolTable symbolTable) {
|
||
|
|
- final byte[] code = null;
|
||
|
|
- final int codeLength = 0;
|
||
|
|
- final int maxStack = -1;
|
||
|
|
- final int maxLocals = -1;
|
||
|
|
- return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the total size in bytes of all the attributes in the attribute list that begins with
|
||
|
|
- * this attribute. This size includes the 6 header bytes (attribute_name_index and
|
||
|
|
- * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in the attributes must be stored.
|
||
|
|
- * @param code the bytecode of the method corresponding to these code attributes, or {@literal
|
||
|
|
- * null} if they are not code attributes. Corresponds to the 'code' field of the Code
|
||
|
|
- * attribute.
|
||
|
|
- * @param codeLength the length of the bytecode of the method corresponding to these code
|
||
|
|
- * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
|
||
|
|
- * the Code attribute.
|
||
|
|
- * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
|
||
|
|
- * -1 if they are not code attributes.
|
||
|
|
- * @param maxLocals the maximum number of local variables of the method corresponding to these
|
||
|
|
- * code attributes, or -1 if they are not code attribute.
|
||
|
|
- * @return the size of all the attributes in this attribute list. This size includes the size of
|
||
|
|
- * the attribute headers.
|
||
|
|
- */
|
||
|
|
- final int computeAttributesSize(
|
||
|
|
- final SymbolTable symbolTable,
|
||
|
|
- final byte[] code,
|
||
|
|
- final int codeLength,
|
||
|
|
- final int maxStack,
|
||
|
|
- final int maxLocals) {
|
||
|
|
- final ClassWriter classWriter = symbolTable.classWriter;
|
||
|
|
- int size = 0;
|
||
|
|
- Attribute attribute = this;
|
||
|
|
- while (attribute != null) {
|
||
|
|
- symbolTable.addConstantUtf8(attribute.type);
|
||
|
|
- size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
|
||
|
|
- attribute = attribute.nextAttribute;
|
||
|
|
- }
|
||
|
|
- return size;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts all the attributes of the attribute list that begins with this attribute, in the given
|
||
|
|
- * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
|
||
|
|
- * attribute.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in the attributes must be stored.
|
||
|
|
- * @param output where the attributes must be written.
|
||
|
|
- */
|
||
|
|
- final void putAttributes(final SymbolTable symbolTable, final ByteVector output) {
|
||
|
|
- final byte[] code = null;
|
||
|
|
- final int codeLength = 0;
|
||
|
|
- final int maxStack = -1;
|
||
|
|
- final int maxLocals = -1;
|
||
|
|
- putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts all the attributes of the attribute list that begins with this attribute, in the given
|
||
|
|
- * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
|
||
|
|
- * attribute.
|
||
|
|
- *
|
||
|
|
- * @param symbolTable where the constants used in the attributes must be stored.
|
||
|
|
- * @param code the bytecode of the method corresponding to these code attributes, or {@literal
|
||
|
|
- * null} if they are not code attributes. Corresponds to the 'code' field of the Code
|
||
|
|
- * attribute.
|
||
|
|
- * @param codeLength the length of the bytecode of the method corresponding to these code
|
||
|
|
- * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
|
||
|
|
- * the Code attribute.
|
||
|
|
- * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
|
||
|
|
- * -1 if they are not code attributes.
|
||
|
|
- * @param maxLocals the maximum number of local variables of the method corresponding to these
|
||
|
|
- * code attributes, or -1 if they are not code attribute.
|
||
|
|
- * @param output where the attributes must be written.
|
||
|
|
- */
|
||
|
|
- final void putAttributes(
|
||
|
|
- final SymbolTable symbolTable,
|
||
|
|
- final byte[] code,
|
||
|
|
- final int codeLength,
|
||
|
|
- final int maxStack,
|
||
|
|
- final int maxLocals,
|
||
|
|
- final ByteVector output) {
|
||
|
|
- final ClassWriter classWriter = symbolTable.classWriter;
|
||
|
|
- Attribute attribute = this;
|
||
|
|
- while (attribute != null) {
|
||
|
|
- ByteVector attributeContent =
|
||
|
|
- attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
|
||
|
|
- // Put attribute_name_index and attribute_length.
|
||
|
|
- output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
|
||
|
|
- output.putByteArray(attributeContent.data, 0, attributeContent.length);
|
||
|
|
- attribute = attribute.nextAttribute;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /** A set of attribute prototypes (attributes with the same type are considered equal). */
|
||
|
|
- static final class Set {
|
||
|
|
-
|
||
|
|
- private static final int SIZE_INCREMENT = 6;
|
||
|
|
-
|
||
|
|
- private int size;
|
||
|
|
- private Attribute[] data = new Attribute[SIZE_INCREMENT];
|
||
|
|
-
|
||
|
|
- void addAttributes(final Attribute attributeList) {
|
||
|
|
- Attribute attribute = attributeList;
|
||
|
|
- while (attribute != null) {
|
||
|
|
- if (!contains(attribute)) {
|
||
|
|
- add(attribute);
|
||
|
|
- }
|
||
|
|
- attribute = attribute.nextAttribute;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- Attribute[] toArray() {
|
||
|
|
- Attribute[] result = new Attribute[size];
|
||
|
|
- System.arraycopy(data, 0, result, 0, size);
|
||
|
|
- return result;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- private boolean contains(final Attribute attribute) {
|
||
|
|
- for (int i = 0; i < size; ++i) {
|
||
|
|
- if (data[i].type.equals(attribute.type)) {
|
||
|
|
- return true;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- return false;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- private void add(final Attribute attribute) {
|
||
|
|
- if (size >= data.length) {
|
||
|
|
- Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
|
||
|
|
- System.arraycopy(data, 0, newData, 0, size);
|
||
|
|
- data = newData;
|
||
|
|
- }
|
||
|
|
- data[size++] = attribute;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/ByteVector.java b/src/main/java/org/mvel2/asm/ByteVector.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index da613be..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/ByteVector.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,361 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream
|
||
|
|
- * on top of a ByteArrayOutputStream, but is more efficient.
|
||
|
|
- *
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- */
|
||
|
|
-public class ByteVector {
|
||
|
|
-
|
||
|
|
- /** The content of this vector. Only the first {@link #length} bytes contain real data. */
|
||
|
|
- byte[] data;
|
||
|
|
-
|
||
|
|
- /** The actual number of bytes in this vector. */
|
||
|
|
- int length;
|
||
|
|
-
|
||
|
|
- /** Constructs a new {@link ByteVector} with a default initial capacity. */
|
||
|
|
- public ByteVector() {
|
||
|
|
- data = new byte[64];
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ByteVector} with the given initial capacity.
|
||
|
|
- *
|
||
|
|
- * @param initialCapacity the initial capacity of the byte vector to be constructed.
|
||
|
|
- */
|
||
|
|
- public ByteVector(final int initialCapacity) {
|
||
|
|
- data = new byte[initialCapacity];
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ByteVector} from the given initial data.
|
||
|
|
- *
|
||
|
|
- * @param data the initial data of the new byte vector.
|
||
|
|
- */
|
||
|
|
- ByteVector(final byte[] data) {
|
||
|
|
- this.data = data;
|
||
|
|
- this.length = data.length;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteValue a byte.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- public ByteVector putByte(final int byteValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 1 > data.length) {
|
||
|
|
- enlarge(1);
|
||
|
|
- }
|
||
|
|
- data[currentLength++] = (byte) byteValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteValue1 a byte.
|
||
|
|
- * @param byteValue2 another byte.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- final ByteVector put11(final int byteValue1, final int byteValue2) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 2 > data.length) {
|
||
|
|
- enlarge(2);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue1;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue2;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a short into this byte vector. The byte vector is automatically enlarged if necessary.
|
||
|
|
- *
|
||
|
|
- * @param shortValue a short.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- public ByteVector putShort(final int shortValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 2 > data.length) {
|
||
|
|
- enlarge(2);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) shortValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if
|
||
|
|
- * necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteValue a byte.
|
||
|
|
- * @param shortValue a short.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- final ByteVector put12(final int byteValue, final int shortValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 3 > data.length) {
|
||
|
|
- enlarge(3);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue;
|
||
|
|
- currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) shortValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if
|
||
|
|
- * necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteValue1 a byte.
|
||
|
|
- * @param byteValue2 another byte.
|
||
|
|
- * @param shortValue a short.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 4 > data.length) {
|
||
|
|
- enlarge(4);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue1;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue2;
|
||
|
|
- currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) shortValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts an int into this byte vector. The byte vector is automatically enlarged if necessary.
|
||
|
|
- *
|
||
|
|
- * @param intValue an int.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- public ByteVector putInt(final int intValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 4 > data.length) {
|
||
|
|
- enlarge(4);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 24);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 16);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) intValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged
|
||
|
|
- * if necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteValue a byte.
|
||
|
|
- * @param shortValue1 a short.
|
||
|
|
- * @param shortValue2 another short.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 5 > data.length) {
|
||
|
|
- enlarge(5);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- currentData[currentLength++] = (byte) byteValue;
|
||
|
|
- currentData[currentLength++] = (byte) (shortValue1 >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) shortValue1;
|
||
|
|
- currentData[currentLength++] = (byte) (shortValue2 >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) shortValue2;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts a long into this byte vector. The byte vector is automatically enlarged if necessary.
|
||
|
|
- *
|
||
|
|
- * @param longValue a long.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- public ByteVector putLong(final long longValue) {
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 8 > data.length) {
|
||
|
|
- enlarge(8);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- int intValue = (int) (longValue >>> 32);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 24);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 16);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) intValue;
|
||
|
|
- intValue = (int) longValue;
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 24);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 16);
|
||
|
|
- currentData[currentLength++] = (byte) (intValue >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) intValue;
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
|
||
|
|
- * necessary.
|
||
|
|
- *
|
||
|
|
- * @param stringValue a String whose UTF8 encoded length must be less than 65536.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||
|
|
- public ByteVector putUTF8(final String stringValue) {
|
||
|
|
- int charLength = stringValue.length();
|
||
|
|
- if (charLength > 65535) {
|
||
|
|
- throw new IllegalArgumentException("UTF8 string too large");
|
||
|
|
- }
|
||
|
|
- int currentLength = length;
|
||
|
|
- if (currentLength + 2 + charLength > data.length) {
|
||
|
|
- enlarge(2 + charLength);
|
||
|
|
- }
|
||
|
|
- byte[] currentData = data;
|
||
|
|
- // Optimistic algorithm: instead of computing the byte length and then serializing the string
|
||
|
|
- // (which requires two loops), we assume the byte length is equal to char length (which is the
|
||
|
|
- // most frequent case), and we start serializing the string right away. During the
|
||
|
|
- // serialization, if we find that this assumption is wrong, we continue with the general method.
|
||
|
|
- currentData[currentLength++] = (byte) (charLength >>> 8);
|
||
|
|
- currentData[currentLength++] = (byte) charLength;
|
||
|
|
- for (int i = 0; i < charLength; ++i) {
|
||
|
|
- char charValue = stringValue.charAt(i);
|
||
|
|
- if (charValue >= '\u0001' && charValue <= '\u007F') {
|
||
|
|
- currentData[currentLength++] = (byte) charValue;
|
||
|
|
- } else {
|
||
|
|
- length = currentLength;
|
||
|
|
- return encodeUtf8(stringValue, i, 65535);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
|
||
|
|
- * necessary. The string length is encoded in two bytes before the encoded characters, if there is
|
||
|
|
- * space for that (i.e. if this.length - offset - 2 >= 0).
|
||
|
|
- *
|
||
|
|
- * @param stringValue the String to encode.
|
||
|
|
- * @param offset the index of the first character to encode. The previous characters are supposed
|
||
|
|
- * to have already been encoded, using only one byte per character.
|
||
|
|
- * @param maxByteLength the maximum byte length of the encoded string, including the already
|
||
|
|
- * encoded characters.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) {
|
||
|
|
- int charLength = stringValue.length();
|
||
|
|
- int byteLength = offset;
|
||
|
|
- for (int i = offset; i < charLength; ++i) {
|
||
|
|
- char charValue = stringValue.charAt(i);
|
||
|
|
- if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||
|
|
- byteLength++;
|
||
|
|
- } else if (charValue <= 0x07FF) {
|
||
|
|
- byteLength += 2;
|
||
|
|
- } else {
|
||
|
|
- byteLength += 3;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- if (byteLength > maxByteLength) {
|
||
|
|
- throw new IllegalArgumentException("UTF8 string too large");
|
||
|
|
- }
|
||
|
|
- // Compute where 'byteLength' must be stored in 'data', and store it at this location.
|
||
|
|
- int byteLengthOffset = length - offset - 2;
|
||
|
|
- if (byteLengthOffset >= 0) {
|
||
|
|
- data[byteLengthOffset] = (byte) (byteLength >>> 8);
|
||
|
|
- data[byteLengthOffset + 1] = (byte) byteLength;
|
||
|
|
- }
|
||
|
|
- if (length + byteLength - offset > data.length) {
|
||
|
|
- enlarge(byteLength - offset);
|
||
|
|
- }
|
||
|
|
- int currentLength = length;
|
||
|
|
- for (int i = offset; i < charLength; ++i) {
|
||
|
|
- char charValue = stringValue.charAt(i);
|
||
|
|
- if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||
|
|
- data[currentLength++] = (byte) charValue;
|
||
|
|
- } else if (charValue <= 0x07FF) {
|
||
|
|
- data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
|
||
|
|
- data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
||
|
|
- } else {
|
||
|
|
- data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF);
|
||
|
|
- data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F);
|
||
|
|
- data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- length = currentLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if
|
||
|
|
- * necessary.
|
||
|
|
- *
|
||
|
|
- * @param byteArrayValue an array of bytes. May be {@literal null} to put {@code byteLength} null
|
||
|
|
- * bytes into this byte vector.
|
||
|
|
- * @param byteOffset index of the first byte of byteArrayValue that must be copied.
|
||
|
|
- * @param byteLength number of bytes of byteArrayValue that must be copied.
|
||
|
|
- * @return this byte vector.
|
||
|
|
- */
|
||
|
|
- public ByteVector putByteArray(
|
||
|
|
- final byte[] byteArrayValue, final int byteOffset, final int byteLength) {
|
||
|
|
- if (length + byteLength > data.length) {
|
||
|
|
- enlarge(byteLength);
|
||
|
|
- }
|
||
|
|
- if (byteArrayValue != null) {
|
||
|
|
- System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength);
|
||
|
|
- }
|
||
|
|
- length += byteLength;
|
||
|
|
- return this;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Enlarges this byte vector so that it can receive 'size' more bytes.
|
||
|
|
- *
|
||
|
|
- * @param size number of additional bytes that this byte vector should be able to receive.
|
||
|
|
- */
|
||
|
|
- private void enlarge(final int size) {
|
||
|
|
- int doubleCapacity = 2 * data.length;
|
||
|
|
- int minimalCapacity = length + size;
|
||
|
|
- byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity];
|
||
|
|
- System.arraycopy(data, 0, newData, 0, length);
|
||
|
|
- data = newData;
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/ClassReader.java b/src/main/java/org/mvel2/asm/ClassReader.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index e6a4f54..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/ClassReader.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,3603 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-import java.io.ByteArrayOutputStream;
|
||
|
|
-import java.io.IOException;
|
||
|
|
-import java.io.InputStream;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java
|
||
|
|
- * Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the
|
||
|
|
- * appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode
|
||
|
|
- * instruction encountered.
|
||
|
|
- *
|
||
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html">JVMS 4</a>
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- * @author Eugene Kuleshov
|
||
|
|
- */
|
||
|
|
-public class ClassReader {
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed
|
||
|
|
- * nor visited.
|
||
|
|
- */
|
||
|
|
- public static final int SKIP_CODE = 1;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable
|
||
|
|
- * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor
|
||
|
|
- * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and
|
||
|
|
- * {@link MethodVisitor#visitLineNumber} are not called).
|
||
|
|
- */
|
||
|
|
- public static final int SKIP_DEBUG = 2;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes
|
||
|
|
- * are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag
|
||
|
|
- * is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames
|
||
|
|
- * that will be ignored and recomputed from scratch.
|
||
|
|
- */
|
||
|
|
- public static final int SKIP_FRAMES = 4;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to expand the stack map frames. By default stack map frames are visited in their
|
||
|
|
- * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed"
|
||
|
|
- * for the other classes). If this flag is set, stack map frames are always visited in expanded
|
||
|
|
- * format (this option adds a decompression/compression step in ClassReader and ClassWriter which
|
||
|
|
- * degrades performance quite a lot).
|
||
|
|
- */
|
||
|
|
- public static final int EXPAND_FRAMES = 8;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode
|
||
|
|
- * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset
|
||
|
|
- * reserved for it is not sufficient to store the bytecode offset. In this case the jump
|
||
|
|
- * instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes
|
||
|
|
- * offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing
|
||
|
|
- * such instructions, in order to replace them with standard instructions. In addition, when this
|
||
|
|
- * flag is used, goto_w and jsr_w are <i>not</i> converted into goto and jsr, to make sure that
|
||
|
|
- * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a
|
||
|
|
- * goto_w in ClassWriter cannot occur.
|
||
|
|
- */
|
||
|
|
- static final int EXPAND_ASM_INSNS = 256;
|
||
|
|
-
|
||
|
|
- /** The size of the temporary byte array used to read class input streams chunk by chunk. */
|
||
|
|
- private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A byte array containing the JVMS ClassFile structure to be parsed. <i>The content of this array
|
||
|
|
- * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
|
||
|
|
- * not needed by class visitors.</i>
|
||
|
|
- *
|
||
|
|
- * <p>NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not
|
||
|
|
- * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
|
||
|
|
- * ClassFile element offsets within this byte array.
|
||
|
|
- */
|
||
|
|
- // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
|
||
|
|
- public final byte[] b;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool
|
||
|
|
- * array, <i>plus one</i>. In other words, the offset of constant pool entry i is given by
|
||
|
|
- * cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - 1].
|
||
|
|
- */
|
||
|
|
- private final int[] cpInfoOffsets;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids
|
||
|
|
- * multiple parsing of a given CONSTANT_Utf8 constant pool item.
|
||
|
|
- */
|
||
|
|
- private final String[] constantUtf8Values;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This
|
||
|
|
- * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item.
|
||
|
|
- */
|
||
|
|
- private final ConstantDynamic[] constantDynamicValues;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
|
||
|
|
- * BootstrapMethods attribute).
|
||
|
|
- *
|
||
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
|
||
|
|
- * 4.7.23</a>
|
||
|
|
- */
|
||
|
|
- private final int[] bootstrapMethodOffsets;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A conservative estimate of the maximum length of the strings contained in the constant pool of
|
||
|
|
- * the class.
|
||
|
|
- */
|
||
|
|
- private final int maxStringLength;
|
||
|
|
-
|
||
|
|
- /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
|
||
|
|
- public final int header;
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Constructors
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassReader} object.
|
||
|
|
- *
|
||
|
|
- * @param classFile the JVMS ClassFile structure to be read.
|
||
|
|
- */
|
||
|
|
- public ClassReader(final byte[] classFile) {
|
||
|
|
- this(classFile, 0, classFile.length);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassReader} object.
|
||
|
|
- *
|
||
|
|
- * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
|
||
|
|
- * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
|
||
|
|
- * @param classFileLength the length in bytes of the ClassFile to be read.
|
||
|
|
- */
|
||
|
|
- public ClassReader(
|
||
|
|
- final byte[] classFileBuffer,
|
||
|
|
- final int classFileOffset,
|
||
|
|
- final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
|
||
|
|
- this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassReader} object. <i>This internal constructor must not be exposed
|
||
|
|
- * as a public API</i>.
|
||
|
|
- *
|
||
|
|
- * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
|
||
|
|
- * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
|
||
|
|
- * @param checkClassVersion whether to check the class version or not.
|
||
|
|
- */
|
||
|
|
- ClassReader(
|
||
|
|
- final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
|
||
|
|
- b = classFileBuffer;
|
||
|
|
- // Check the class' major_version. This field is after the magic and minor_version fields, which
|
||
|
|
- // use 4 and 2 bytes respectively.
|
||
|
|
- if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
|
||
|
|
- throw new IllegalArgumentException(
|
||
|
|
- "Unsupported class file major version " + readShort(classFileOffset + 6));
|
||
|
|
- }
|
||
|
|
- // Create the constant pool arrays. The constant_pool_count field is after the magic,
|
||
|
|
- // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
|
||
|
|
- int constantPoolCount = readUnsignedShort(classFileOffset + 8);
|
||
|
|
- cpInfoOffsets = new int[constantPoolCount];
|
||
|
|
- constantUtf8Values = new String[constantPoolCount];
|
||
|
|
- // Compute the offset of each constant pool entry, as well as a conservative estimate of the
|
||
|
|
- // maximum length of the constant pool strings. The first constant pool entry is after the
|
||
|
|
- // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2
|
||
|
|
- // bytes respectively.
|
||
|
|
- int currentCpInfoIndex = 1;
|
||
|
|
- int currentCpInfoOffset = classFileOffset + 10;
|
||
|
|
- int currentMaxStringLength = 0;
|
||
|
|
- boolean hasConstantDynamic = false;
|
||
|
|
- boolean hasConstantInvokeDynamic = false;
|
||
|
|
- // The offset of the other entries depend on the total size of all the previous entries.
|
||
|
|
- while (currentCpInfoIndex < constantPoolCount) {
|
||
|
|
- cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
|
||
|
|
- int cpInfoSize;
|
||
|
|
- switch (classFileBuffer[currentCpInfoOffset]) {
|
||
|
|
- case Symbol.CONSTANT_FIELDREF_TAG:
|
||
|
|
- case Symbol.CONSTANT_METHODREF_TAG:
|
||
|
|
- case Symbol.CONSTANT_INTERFACE_METHODREF_TAG:
|
||
|
|
- case Symbol.CONSTANT_INTEGER_TAG:
|
||
|
|
- case Symbol.CONSTANT_FLOAT_TAG:
|
||
|
|
- case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
|
||
|
|
- cpInfoSize = 5;
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_DYNAMIC_TAG:
|
||
|
|
- cpInfoSize = 5;
|
||
|
|
- hasConstantDynamic = true;
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
|
||
|
|
- cpInfoSize = 5;
|
||
|
|
- hasConstantInvokeDynamic = true;
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_LONG_TAG:
|
||
|
|
- case Symbol.CONSTANT_DOUBLE_TAG:
|
||
|
|
- cpInfoSize = 9;
|
||
|
|
- currentCpInfoIndex++;
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_UTF8_TAG:
|
||
|
|
- cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1);
|
||
|
|
- if (cpInfoSize > currentMaxStringLength) {
|
||
|
|
- // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate
|
||
|
|
- // of the length in characters of the corresponding string, and is much cheaper to
|
||
|
|
- // compute than this exact length.
|
||
|
|
- currentMaxStringLength = cpInfoSize;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
||
|
|
- cpInfoSize = 4;
|
||
|
|
- break;
|
||
|
|
- case Symbol.CONSTANT_CLASS_TAG:
|
||
|
|
- case Symbol.CONSTANT_STRING_TAG:
|
||
|
|
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
|
||
|
|
- case Symbol.CONSTANT_PACKAGE_TAG:
|
||
|
|
- case Symbol.CONSTANT_MODULE_TAG:
|
||
|
|
- cpInfoSize = 3;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- currentCpInfoOffset += cpInfoSize;
|
||
|
|
- }
|
||
|
|
- maxStringLength = currentMaxStringLength;
|
||
|
|
- // The Classfile's access_flags field is just after the last constant pool entry.
|
||
|
|
- header = currentCpInfoOffset;
|
||
|
|
-
|
||
|
|
- // Allocate the cache of ConstantDynamic values, if there is at least one.
|
||
|
|
- constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;
|
||
|
|
-
|
||
|
|
- // Read the BootstrapMethods attribute, if any (only get the offset of each method).
|
||
|
|
- bootstrapMethodOffsets =
|
||
|
|
- (hasConstantDynamic | hasConstantInvokeDynamic)
|
||
|
|
- ? readBootstrapMethodsAttribute(currentMaxStringLength)
|
||
|
|
- : null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassReader} object.
|
||
|
|
- *
|
||
|
|
- * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input
|
||
|
|
- * stream must contain nothing more than the ClassFile structure itself. It is read from its
|
||
|
|
- * current position to its end.
|
||
|
|
- * @throws IOException if a problem occurs during reading.
|
||
|
|
- */
|
||
|
|
- public ClassReader(final InputStream inputStream) throws IOException {
|
||
|
|
- this(readStream(inputStream, false));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassReader} object.
|
||
|
|
- *
|
||
|
|
- * @param className the fully qualified name of the class to be read. The ClassFile structure is
|
||
|
|
- * retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}.
|
||
|
|
- * @throws IOException if an exception occurs during reading.
|
||
|
|
- */
|
||
|
|
- public ClassReader(final String className) throws IOException {
|
||
|
|
- this(
|
||
|
|
- readStream(
|
||
|
|
- ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads the given input stream and returns its content as a byte array.
|
||
|
|
- *
|
||
|
|
- * @param inputStream an input stream.
|
||
|
|
- * @param close true to close the input stream after reading.
|
||
|
|
- * @return the content of the given input stream.
|
||
|
|
- * @throws IOException if a problem occurs during reading.
|
||
|
|
- */
|
||
|
|
- private static byte[] readStream(final InputStream inputStream, final boolean close)
|
||
|
|
- throws IOException {
|
||
|
|
- if (inputStream == null) {
|
||
|
|
- throw new IOException("Class not found");
|
||
|
|
- }
|
||
|
|
- try {
|
||
|
|
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||
|
|
- byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
|
||
|
|
- int bytesRead;
|
||
|
|
- while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
|
||
|
|
- outputStream.write(data, 0, bytesRead);
|
||
|
|
- }
|
||
|
|
- outputStream.flush();
|
||
|
|
- return outputStream.toByteArray();
|
||
|
|
- } finally {
|
||
|
|
- if (close) {
|
||
|
|
- inputStream.close();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Accessors
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated
|
||
|
|
- * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes.
|
||
|
|
- *
|
||
|
|
- * @return the class access flags.
|
||
|
|
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||
|
|
- */
|
||
|
|
- public int getAccess() {
|
||
|
|
- return readUnsignedShort(header);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the internal name of the class (see {@link Type#getInternalName()}).
|
||
|
|
- *
|
||
|
|
- * @return the internal class name.
|
||
|
|
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||
|
|
- */
|
||
|
|
- public String getClassName() {
|
||
|
|
- // this_class is just after the access_flags field (using 2 bytes).
|
||
|
|
- return readClass(header + 2, new char[maxStringLength]);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
|
||
|
|
- * interfaces, the super class is {@link Object}.
|
||
|
|
- *
|
||
|
|
- * @return the internal name of the super class, or {@literal null} for {@link Object} class.
|
||
|
|
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||
|
|
- */
|
||
|
|
- public String getSuperName() {
|
||
|
|
- // super_class is after the access_flags and this_class fields (2 bytes each).
|
||
|
|
- return readClass(header + 4, new char[maxStringLength]);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}).
|
||
|
|
- *
|
||
|
|
- * @return the internal names of the directly implemented interfaces. Inherited implemented
|
||
|
|
- * interfaces are not returned.
|
||
|
|
- * @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||
|
|
- */
|
||
|
|
- public String[] getInterfaces() {
|
||
|
|
- // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each).
|
||
|
|
- int currentOffset = header + 6;
|
||
|
|
- int interfacesCount = readUnsignedShort(currentOffset);
|
||
|
|
- String[] interfaces = new String[interfacesCount];
|
||
|
|
- if (interfacesCount > 0) {
|
||
|
|
- char[] charBuffer = new char[maxStringLength];
|
||
|
|
- for (int i = 0; i < interfacesCount; ++i) {
|
||
|
|
- currentOffset += 2;
|
||
|
|
- interfaces[i] = readClass(currentOffset, charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- return interfaces;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Public methods
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
|
||
|
|
- * {@link ClassReader}.
|
||
|
|
- *
|
||
|
|
- * @param classVisitor the visitor that must visit this class.
|
||
|
|
- * @param parsingOptions the options to use to parse this class. One or more of {@link
|
||
|
|
- * #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
|
||
|
|
- */
|
||
|
|
- public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
|
||
|
|
- accept(classVisitor, new Attribute[0], parsingOptions);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
|
||
|
|
- * {@link ClassReader}.
|
||
|
|
- *
|
||
|
|
- * @param classVisitor the visitor that must visit this class.
|
||
|
|
- * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
|
||
|
|
- * the class. Any attribute whose type is not equal to the type of one the prototypes will not
|
||
|
|
- * be parsed: its byte array value will be passed unchanged to the ClassWriter. <i>This may
|
||
|
|
- * corrupt it if this value contains references to the constant pool, or has syntactic or
|
||
|
|
- * semantic links with a class element that has been transformed by a class adapter between
|
||
|
|
- * the reader and the writer</i>.
|
||
|
|
- * @param parsingOptions the options to use to parse this class. One or more of {@link
|
||
|
|
- * #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
|
||
|
|
- */
|
||
|
|
- public void accept(
|
||
|
|
- final ClassVisitor classVisitor,
|
||
|
|
- final Attribute[] attributePrototypes,
|
||
|
|
- final int parsingOptions) {
|
||
|
|
- Context context = new Context();
|
||
|
|
- context.attributePrototypes = attributePrototypes;
|
||
|
|
- context.parsingOptions = parsingOptions;
|
||
|
|
- context.charBuffer = new char[maxStringLength];
|
||
|
|
-
|
||
|
|
- // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
|
||
|
|
- char[] charBuffer = context.charBuffer;
|
||
|
|
- int currentOffset = header;
|
||
|
|
- int accessFlags = readUnsignedShort(currentOffset);
|
||
|
|
- String thisClass = readClass(currentOffset + 2, charBuffer);
|
||
|
|
- String superClass = readClass(currentOffset + 4, charBuffer);
|
||
|
|
- String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
|
||
|
|
- currentOffset += 8;
|
||
|
|
- for (int i = 0; i < interfaces.length; ++i) {
|
||
|
|
- interfaces[i] = readClass(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the class attributes (the variables are ordered as in Section 4.7 of the JVMS).
|
||
|
|
- // Attribute offsets exclude the attribute_name_index and attribute_length fields.
|
||
|
|
- // - The offset of the InnerClasses attribute, or 0.
|
||
|
|
- int innerClassesOffset = 0;
|
||
|
|
- // - The offset of the EnclosingMethod attribute, or 0.
|
||
|
|
- int enclosingMethodOffset = 0;
|
||
|
|
- // - The string corresponding to the Signature attribute, or null.
|
||
|
|
- String signature = null;
|
||
|
|
- // - The string corresponding to the SourceFile attribute, or null.
|
||
|
|
- String sourceFile = null;
|
||
|
|
- // - The string corresponding to the SourceDebugExtension attribute, or null.
|
||
|
|
- String sourceDebugExtension = null;
|
||
|
|
- // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the Module attribute, or 0.
|
||
|
|
- int moduleOffset = 0;
|
||
|
|
- // - The offset of the ModulePackages attribute, or 0.
|
||
|
|
- int modulePackagesOffset = 0;
|
||
|
|
- // - The string corresponding to the ModuleMainClass attribute, or null.
|
||
|
|
- String moduleMainClass = null;
|
||
|
|
- // - The string corresponding to the NestHost attribute, or null.
|
||
|
|
- String nestHostClass = null;
|
||
|
|
- // - The offset of the NestMembers attribute, or 0.
|
||
|
|
- int nestMembersOffset = 0;
|
||
|
|
- // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
|
||
|
|
- // This list in the <i>reverse order</i> or their order in the ClassFile structure.
|
||
|
|
- Attribute attributes = null;
|
||
|
|
-
|
||
|
|
- int currentAttributeOffset = getFirstAttributeOffset();
|
||
|
|
- for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
|
||
|
|
- // Read the attribute_info's attribute_name and attribute_length fields.
|
||
|
|
- String attributeName = readUTF8(currentAttributeOffset, charBuffer);
|
||
|
|
- int attributeLength = readInt(currentAttributeOffset + 2);
|
||
|
|
- currentAttributeOffset += 6;
|
||
|
|
- // The tests are sorted in decreasing frequency order (based on frequencies observed on
|
||
|
|
- // typical classes).
|
||
|
|
- if (Constants.SOURCE_FILE.equals(attributeName)) {
|
||
|
|
- sourceFile = readUTF8(currentAttributeOffset, charBuffer);
|
||
|
|
- } else if (Constants.INNER_CLASSES.equals(attributeName)) {
|
||
|
|
- innerClassesOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
|
||
|
|
- enclosingMethodOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.NEST_HOST.equals(attributeName)) {
|
||
|
|
- nestHostClass = readClass(currentAttributeOffset, charBuffer);
|
||
|
|
- } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
|
||
|
|
- nestMembersOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.SIGNATURE.equals(attributeName)) {
|
||
|
|
- signature = readUTF8(currentAttributeOffset, charBuffer);
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleAnnotationsOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.DEPRECATED.equals(attributeName)) {
|
||
|
|
- accessFlags |= Opcodes.ACC_DEPRECATED;
|
||
|
|
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
|
||
|
|
- accessFlags |= Opcodes.ACC_SYNTHETIC;
|
||
|
|
- } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
|
||
|
|
- sourceDebugExtension =
|
||
|
|
- readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.MODULE.equals(attributeName)) {
|
||
|
|
- moduleOffset = currentAttributeOffset;
|
||
|
|
- } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
|
||
|
|
- moduleMainClass = readClass(currentAttributeOffset, charBuffer);
|
||
|
|
- } else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
|
||
|
|
- modulePackagesOffset = currentAttributeOffset;
|
||
|
|
- } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
|
||
|
|
- // The BootstrapMethods attribute is read in the constructor.
|
||
|
|
- Attribute attribute =
|
||
|
|
- readAttribute(
|
||
|
|
- attributePrototypes,
|
||
|
|
- attributeName,
|
||
|
|
- currentAttributeOffset,
|
||
|
|
- attributeLength,
|
||
|
|
- charBuffer,
|
||
|
|
- -1,
|
||
|
|
- null);
|
||
|
|
- attribute.nextAttribute = attributes;
|
||
|
|
- attributes = attribute;
|
||
|
|
- }
|
||
|
|
- currentAttributeOffset += attributeLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
|
||
|
|
- // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
|
||
|
|
- classVisitor.visit(
|
||
|
|
- readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);
|
||
|
|
-
|
||
|
|
- // Visit the SourceFile and SourceDebugExtenstion attributes.
|
||
|
|
- if ((parsingOptions & SKIP_DEBUG) == 0
|
||
|
|
- && (sourceFile != null || sourceDebugExtension != null)) {
|
||
|
|
- classVisitor.visitSource(sourceFile, sourceDebugExtension);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the Module, ModulePackages and ModuleMainClass attributes.
|
||
|
|
- if (moduleOffset != 0) {
|
||
|
|
- readModuleAttributes(
|
||
|
|
- classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the NestHost attribute.
|
||
|
|
- if (nestHostClass != null) {
|
||
|
|
- classVisitor.visitNestHost(nestHostClass);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the EnclosingMethod attribute.
|
||
|
|
- if (enclosingMethodOffset != 0) {
|
||
|
|
- String className = readClass(enclosingMethodOffset, charBuffer);
|
||
|
|
- int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
|
||
|
|
- String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
|
||
|
|
- String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
|
||
|
|
- classVisitor.visitOuterClass(className, name, type);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleAnnotations attribute.
|
||
|
|
- if (runtimeVisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeVisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- classVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- classVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the non standard attributes.
|
||
|
|
- while (attributes != null) {
|
||
|
|
- // Copy and reset the nextAttribute field so that it can also be used in ClassWriter.
|
||
|
|
- Attribute nextAttribute = attributes.nextAttribute;
|
||
|
|
- attributes.nextAttribute = null;
|
||
|
|
- classVisitor.visitAttribute(attributes);
|
||
|
|
- attributes = nextAttribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the NestedMembers attribute.
|
||
|
|
- if (nestMembersOffset != 0) {
|
||
|
|
- int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
|
||
|
|
- int currentNestMemberOffset = nestMembersOffset + 2;
|
||
|
|
- while (numberOfNestMembers-- > 0) {
|
||
|
|
- classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
|
||
|
|
- currentNestMemberOffset += 2;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the InnerClasses attribute.
|
||
|
|
- if (innerClassesOffset != 0) {
|
||
|
|
- int numberOfClasses = readUnsignedShort(innerClassesOffset);
|
||
|
|
- int currentClassesOffset = innerClassesOffset + 2;
|
||
|
|
- while (numberOfClasses-- > 0) {
|
||
|
|
- classVisitor.visitInnerClass(
|
||
|
|
- readClass(currentClassesOffset, charBuffer),
|
||
|
|
- readClass(currentClassesOffset + 2, charBuffer),
|
||
|
|
- readUTF8(currentClassesOffset + 4, charBuffer),
|
||
|
|
- readUnsignedShort(currentClassesOffset + 6));
|
||
|
|
- currentClassesOffset += 8;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the fields and methods.
|
||
|
|
- int fieldsCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (fieldsCount-- > 0) {
|
||
|
|
- currentOffset = readField(classVisitor, context, currentOffset);
|
||
|
|
- }
|
||
|
|
- int methodsCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (methodsCount-- > 0) {
|
||
|
|
- currentOffset = readMethod(classVisitor, context, currentOffset);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the end of the class.
|
||
|
|
- classVisitor.visitEnd();
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
- // Methods to parse modules, fields and methods
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads the Module, ModulePackages and ModuleMainClass attributes and visit them.
|
||
|
|
- *
|
||
|
|
- * @param classVisitor the current class visitor
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param moduleOffset the offset of the Module attribute (excluding the attribute_info's
|
||
|
|
- * attribute_name_index and attribute_length fields).
|
||
|
|
- * @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the
|
||
|
|
- * attribute_info's attribute_name_index and attribute_length fields), or 0.
|
||
|
|
- * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
|
||
|
|
- */
|
||
|
|
- private void readModuleAttributes(
|
||
|
|
- final ClassVisitor classVisitor,
|
||
|
|
- final Context context,
|
||
|
|
- final int moduleOffset,
|
||
|
|
- final int modulePackagesOffset,
|
||
|
|
- final String moduleMainClass) {
|
||
|
|
- char[] buffer = context.charBuffer;
|
||
|
|
-
|
||
|
|
- // Read the module_name_index, module_flags and module_version_index fields and visit them.
|
||
|
|
- int currentOffset = moduleOffset;
|
||
|
|
- String moduleName = readModule(currentOffset, buffer);
|
||
|
|
- int moduleFlags = readUnsignedShort(currentOffset + 2);
|
||
|
|
- String moduleVersion = readUTF8(currentOffset + 4, buffer);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion);
|
||
|
|
- if (moduleVisitor == null) {
|
||
|
|
- return;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the ModuleMainClass attribute.
|
||
|
|
- if (moduleMainClass != null) {
|
||
|
|
- moduleVisitor.visitMainClass(moduleMainClass);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the ModulePackages attribute.
|
||
|
|
- if (modulePackagesOffset != 0) {
|
||
|
|
- int packageCount = readUnsignedShort(modulePackagesOffset);
|
||
|
|
- int currentPackageOffset = modulePackagesOffset + 2;
|
||
|
|
- while (packageCount-- > 0) {
|
||
|
|
- moduleVisitor.visitPackage(readPackage(currentPackageOffset, buffer));
|
||
|
|
- currentPackageOffset += 2;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the 'requires_count' and 'requires' fields.
|
||
|
|
- int requiresCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (requiresCount-- > 0) {
|
||
|
|
- // Read the requires_index, requires_flags and requires_version fields and visit them.
|
||
|
|
- String requires = readModule(currentOffset, buffer);
|
||
|
|
- int requiresFlags = readUnsignedShort(currentOffset + 2);
|
||
|
|
- String requiresVersion = readUTF8(currentOffset + 4, buffer);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- moduleVisitor.visitRequire(requires, requiresFlags, requiresVersion);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the 'exports_count' and 'exports' fields.
|
||
|
|
- int exportsCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (exportsCount-- > 0) {
|
||
|
|
- // Read the exports_index, exports_flags, exports_to_count and exports_to_index fields
|
||
|
|
- // and visit them.
|
||
|
|
- String exports = readPackage(currentOffset, buffer);
|
||
|
|
- int exportsFlags = readUnsignedShort(currentOffset + 2);
|
||
|
|
- int exportsToCount = readUnsignedShort(currentOffset + 4);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- String[] exportsTo = null;
|
||
|
|
- if (exportsToCount != 0) {
|
||
|
|
- exportsTo = new String[exportsToCount];
|
||
|
|
- for (int i = 0; i < exportsToCount; ++i) {
|
||
|
|
- exportsTo[i] = readModule(currentOffset, buffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- moduleVisitor.visitExport(exports, exportsFlags, exportsTo);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Reads the 'opens_count' and 'opens' fields.
|
||
|
|
- int opensCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (opensCount-- > 0) {
|
||
|
|
- // Read the opens_index, opens_flags, opens_to_count and opens_to_index fields and visit them.
|
||
|
|
- String opens = readPackage(currentOffset, buffer);
|
||
|
|
- int opensFlags = readUnsignedShort(currentOffset + 2);
|
||
|
|
- int opensToCount = readUnsignedShort(currentOffset + 4);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- String[] opensTo = null;
|
||
|
|
- if (opensToCount != 0) {
|
||
|
|
- opensTo = new String[opensToCount];
|
||
|
|
- for (int i = 0; i < opensToCount; ++i) {
|
||
|
|
- opensTo[i] = readModule(currentOffset, buffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- moduleVisitor.visitOpen(opens, opensFlags, opensTo);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the 'uses_count' and 'uses' fields.
|
||
|
|
- int usesCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (usesCount-- > 0) {
|
||
|
|
- moduleVisitor.visitUse(readClass(currentOffset, buffer));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the 'provides_count' and 'provides' fields.
|
||
|
|
- int providesCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (providesCount-- > 0) {
|
||
|
|
- // Read the provides_index, provides_with_count and provides_with_index fields and visit them.
|
||
|
|
- String provides = readClass(currentOffset, buffer);
|
||
|
|
- int providesWithCount = readUnsignedShort(currentOffset + 2);
|
||
|
|
- currentOffset += 4;
|
||
|
|
- String[] providesWith = new String[providesWithCount];
|
||
|
|
- for (int i = 0; i < providesWithCount; ++i) {
|
||
|
|
- providesWith[i] = readClass(currentOffset, buffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- }
|
||
|
|
- moduleVisitor.visitProvide(provides, providesWith);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the end of the module attributes.
|
||
|
|
- moduleVisitor.visitEnd();
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS field_info structure and makes the given visitor visit it.
|
||
|
|
- *
|
||
|
|
- * @param classVisitor the visitor that must visit the field.
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param fieldInfoOffset the start offset of the field_info structure.
|
||
|
|
- * @return the offset of the first byte following the field_info structure.
|
||
|
|
- */
|
||
|
|
- private int readField(
|
||
|
|
- final ClassVisitor classVisitor, final Context context, final int fieldInfoOffset) {
|
||
|
|
- char[] charBuffer = context.charBuffer;
|
||
|
|
-
|
||
|
|
- // Read the access_flags, name_index and descriptor_index fields.
|
||
|
|
- int currentOffset = fieldInfoOffset;
|
||
|
|
- int accessFlags = readUnsignedShort(currentOffset);
|
||
|
|
- String name = readUTF8(currentOffset + 2, charBuffer);
|
||
|
|
- String descriptor = readUTF8(currentOffset + 4, charBuffer);
|
||
|
|
- currentOffset += 6;
|
||
|
|
-
|
||
|
|
- // Read the field attributes (the variables are ordered as in Section 4.7 of the JVMS).
|
||
|
|
- // Attribute offsets exclude the attribute_name_index and attribute_length fields.
|
||
|
|
- // - The value corresponding to the ConstantValue attribute, or null.
|
||
|
|
- Object constantValue = null;
|
||
|
|
- // - The string corresponding to the Signature attribute, or null.
|
||
|
|
- String signature = null;
|
||
|
|
- // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
|
||
|
|
- // This list in the <i>reverse order</i> or their order in the ClassFile structure.
|
||
|
|
- Attribute attributes = null;
|
||
|
|
-
|
||
|
|
- int attributesCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (attributesCount-- > 0) {
|
||
|
|
- // Read the attribute_info's attribute_name and attribute_length fields.
|
||
|
|
- String attributeName = readUTF8(currentOffset, charBuffer);
|
||
|
|
- int attributeLength = readInt(currentOffset + 2);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- // The tests are sorted in decreasing frequency order (based on frequencies observed on
|
||
|
|
- // typical classes).
|
||
|
|
- if (Constants.CONSTANT_VALUE.equals(attributeName)) {
|
||
|
|
- int constantvalueIndex = readUnsignedShort(currentOffset);
|
||
|
|
- constantValue = constantvalueIndex == 0 ? null : readConst(constantvalueIndex, charBuffer);
|
||
|
|
- } else if (Constants.SIGNATURE.equals(attributeName)) {
|
||
|
|
- signature = readUTF8(currentOffset, charBuffer);
|
||
|
|
- } else if (Constants.DEPRECATED.equals(attributeName)) {
|
||
|
|
- accessFlags |= Opcodes.ACC_DEPRECATED;
|
||
|
|
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
|
||
|
|
- accessFlags |= Opcodes.ACC_SYNTHETIC;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleTypeAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleTypeAnnotationsOffset = currentOffset;
|
||
|
|
- } else {
|
||
|
|
- Attribute attribute =
|
||
|
|
- readAttribute(
|
||
|
|
- context.attributePrototypes,
|
||
|
|
- attributeName,
|
||
|
|
- currentOffset,
|
||
|
|
- attributeLength,
|
||
|
|
- charBuffer,
|
||
|
|
- -1,
|
||
|
|
- null);
|
||
|
|
- attribute.nextAttribute = attributes;
|
||
|
|
- attributes = attribute;
|
||
|
|
- }
|
||
|
|
- currentOffset += attributeLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the field declaration.
|
||
|
|
- FieldVisitor fieldVisitor =
|
||
|
|
- classVisitor.visitField(accessFlags, name, descriptor, signature, constantValue);
|
||
|
|
- if (fieldVisitor == null) {
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleAnnotations attribute.
|
||
|
|
- if (runtimeVisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeVisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- fieldVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- fieldVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the non standard attributes.
|
||
|
|
- while (attributes != null) {
|
||
|
|
- // Copy and reset the nextAttribute field so that it can also be used in FieldWriter.
|
||
|
|
- Attribute nextAttribute = attributes.nextAttribute;
|
||
|
|
- attributes.nextAttribute = null;
|
||
|
|
- fieldVisitor.visitAttribute(attributes);
|
||
|
|
- attributes = nextAttribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the end of the field.
|
||
|
|
- fieldVisitor.visitEnd();
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS method_info structure and makes the given visitor visit it.
|
||
|
|
- *
|
||
|
|
- * @param classVisitor the visitor that must visit the method.
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param methodInfoOffset the start offset of the method_info structure.
|
||
|
|
- * @return the offset of the first byte following the method_info structure.
|
||
|
|
- */
|
||
|
|
- private int readMethod(
|
||
|
|
- final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) {
|
||
|
|
- char[] charBuffer = context.charBuffer;
|
||
|
|
-
|
||
|
|
- // Read the access_flags, name_index and descriptor_index fields.
|
||
|
|
- int currentOffset = methodInfoOffset;
|
||
|
|
- context.currentMethodAccessFlags = readUnsignedShort(currentOffset);
|
||
|
|
- context.currentMethodName = readUTF8(currentOffset + 2, charBuffer);
|
||
|
|
- context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer);
|
||
|
|
- currentOffset += 6;
|
||
|
|
-
|
||
|
|
- // Read the method attributes (the variables are ordered as in Section 4.7 of the JVMS).
|
||
|
|
- // Attribute offsets exclude the attribute_name_index and attribute_length fields.
|
||
|
|
- // - The offset of the Code attribute, or 0.
|
||
|
|
- int codeOffset = 0;
|
||
|
|
- // - The offset of the Exceptions attribute, or 0.
|
||
|
|
- int exceptionsOffset = 0;
|
||
|
|
- // - The strings corresponding to the Exceptions attribute, or null.
|
||
|
|
- String[] exceptions = null;
|
||
|
|
- // - Whether the method has a Synthetic attribute.
|
||
|
|
- boolean synthetic = false;
|
||
|
|
- // - The constant pool index contained in the Signature attribute, or 0.
|
||
|
|
- int signatureIndex = 0;
|
||
|
|
- // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeVisibleParameterAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleParameterAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleParameterAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeVisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
|
||
|
|
- int runtimeInvisibleTypeAnnotationsOffset = 0;
|
||
|
|
- // - The offset of the AnnotationDefault attribute, or 0.
|
||
|
|
- int annotationDefaultOffset = 0;
|
||
|
|
- // - The offset of the MethodParameters attribute, or 0.
|
||
|
|
- int methodParametersOffset = 0;
|
||
|
|
- // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
|
||
|
|
- // This list in the <i>reverse order</i> or their order in the ClassFile structure.
|
||
|
|
- Attribute attributes = null;
|
||
|
|
-
|
||
|
|
- int attributesCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (attributesCount-- > 0) {
|
||
|
|
- // Read the attribute_info's attribute_name and attribute_length fields.
|
||
|
|
- String attributeName = readUTF8(currentOffset, charBuffer);
|
||
|
|
- int attributeLength = readInt(currentOffset + 2);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- // The tests are sorted in decreasing frequency order (based on frequencies observed on
|
||
|
|
- // typical classes).
|
||
|
|
- if (Constants.CODE.equals(attributeName)) {
|
||
|
|
- if ((context.parsingOptions & SKIP_CODE) == 0) {
|
||
|
|
- codeOffset = currentOffset;
|
||
|
|
- }
|
||
|
|
- } else if (Constants.EXCEPTIONS.equals(attributeName)) {
|
||
|
|
- exceptionsOffset = currentOffset;
|
||
|
|
- exceptions = new String[readUnsignedShort(exceptionsOffset)];
|
||
|
|
- int currentExceptionOffset = exceptionsOffset + 2;
|
||
|
|
- for (int i = 0; i < exceptions.length; ++i) {
|
||
|
|
- exceptions[i] = readClass(currentExceptionOffset, charBuffer);
|
||
|
|
- currentExceptionOffset += 2;
|
||
|
|
- }
|
||
|
|
- } else if (Constants.SIGNATURE.equals(attributeName)) {
|
||
|
|
- signatureIndex = readUnsignedShort(currentOffset);
|
||
|
|
- } else if (Constants.DEPRECATED.equals(attributeName)) {
|
||
|
|
- context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleTypeAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) {
|
||
|
|
- annotationDefaultOffset = currentOffset;
|
||
|
|
- } else if (Constants.SYNTHETIC.equals(attributeName)) {
|
||
|
|
- synthetic = true;
|
||
|
|
- context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleTypeAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeVisibleParameterAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- runtimeInvisibleParameterAnnotationsOffset = currentOffset;
|
||
|
|
- } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) {
|
||
|
|
- methodParametersOffset = currentOffset;
|
||
|
|
- } else {
|
||
|
|
- Attribute attribute =
|
||
|
|
- readAttribute(
|
||
|
|
- context.attributePrototypes,
|
||
|
|
- attributeName,
|
||
|
|
- currentOffset,
|
||
|
|
- attributeLength,
|
||
|
|
- charBuffer,
|
||
|
|
- -1,
|
||
|
|
- null);
|
||
|
|
- attribute.nextAttribute = attributes;
|
||
|
|
- attributes = attribute;
|
||
|
|
- }
|
||
|
|
- currentOffset += attributeLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the method declaration.
|
||
|
|
- MethodVisitor methodVisitor =
|
||
|
|
- classVisitor.visitMethod(
|
||
|
|
- context.currentMethodAccessFlags,
|
||
|
|
- context.currentMethodName,
|
||
|
|
- context.currentMethodDescriptor,
|
||
|
|
- signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer),
|
||
|
|
- exceptions);
|
||
|
|
- if (methodVisitor == null) {
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method
|
||
|
|
- // adapter between the reader and the writer. In this case, it might be possible to copy
|
||
|
|
- // the method attributes directly into the writer. If so, return early without visiting
|
||
|
|
- // the content of these attributes.
|
||
|
|
- if (methodVisitor instanceof MethodWriter) {
|
||
|
|
- MethodWriter methodWriter = (MethodWriter) methodVisitor;
|
||
|
|
- if (methodWriter.canCopyMethodAttributes(
|
||
|
|
- this,
|
||
|
|
- methodInfoOffset,
|
||
|
|
- currentOffset - methodInfoOffset,
|
||
|
|
- synthetic,
|
||
|
|
- (context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0,
|
||
|
|
- readUnsignedShort(methodInfoOffset + 4),
|
||
|
|
- signatureIndex,
|
||
|
|
- exceptionsOffset)) {
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the MethodParameters attribute.
|
||
|
|
- if (methodParametersOffset != 0) {
|
||
|
|
- int parametersCount = readByte(methodParametersOffset);
|
||
|
|
- int currentParameterOffset = methodParametersOffset + 1;
|
||
|
|
- while (parametersCount-- > 0) {
|
||
|
|
- // Read the name_index and access_flags fields and visit them.
|
||
|
|
- methodVisitor.visitParameter(
|
||
|
|
- readUTF8(currentParameterOffset, charBuffer),
|
||
|
|
- readUnsignedShort(currentParameterOffset + 2));
|
||
|
|
- currentParameterOffset += 4;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the AnnotationDefault attribute.
|
||
|
|
- if (annotationDefaultOffset != 0) {
|
||
|
|
- AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault();
|
||
|
|
- readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer);
|
||
|
|
- if (annotationVisitor != null) {
|
||
|
|
- annotationVisitor.visitEnd();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleAnnotations attribute.
|
||
|
|
- if (runtimeVisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeVisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleTypeAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleTypeAnnotationsOffset != 0) {
|
||
|
|
- int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
|
||
|
|
- int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentAnnotationOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitTypeAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeVisibleParameterAnnotations attribute.
|
||
|
|
- if (runtimeVisibleParameterAnnotationsOffset != 0) {
|
||
|
|
- readParameterAnnotations(
|
||
|
|
- methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the RuntimeInvisibleParameterAnnotations attribute.
|
||
|
|
- if (runtimeInvisibleParameterAnnotationsOffset != 0) {
|
||
|
|
- readParameterAnnotations(
|
||
|
|
- methodVisitor,
|
||
|
|
- context,
|
||
|
|
- runtimeInvisibleParameterAnnotationsOffset,
|
||
|
|
- /* visible = */ false);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the non standard attributes.
|
||
|
|
- while (attributes != null) {
|
||
|
|
- // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
|
||
|
|
- Attribute nextAttribute = attributes.nextAttribute;
|
||
|
|
- attributes.nextAttribute = null;
|
||
|
|
- methodVisitor.visitAttribute(attributes);
|
||
|
|
- attributes = nextAttribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the Code attribute.
|
||
|
|
- if (codeOffset != 0) {
|
||
|
|
- methodVisitor.visitCode();
|
||
|
|
- readCode(methodVisitor, context, codeOffset);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the end of the method.
|
||
|
|
- methodVisitor.visitEnd();
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
- // Methods to parse a Code attribute
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS 'Code' attribute and makes the given visitor visit it.
|
||
|
|
- *
|
||
|
|
- * @param methodVisitor the visitor that must visit the Code attribute.
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
|
||
|
|
- * attribute_name_index and attribute_length fields.
|
||
|
|
- */
|
||
|
|
- private void readCode(
|
||
|
|
- final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
|
||
|
|
- int currentOffset = codeOffset;
|
||
|
|
-
|
||
|
|
- // Read the max_stack, max_locals and code_length fields.
|
||
|
|
- final byte[] classFileBuffer = b;
|
||
|
|
- final char[] charBuffer = context.charBuffer;
|
||
|
|
- final int maxStack = readUnsignedShort(currentOffset);
|
||
|
|
- final int maxLocals = readUnsignedShort(currentOffset + 2);
|
||
|
|
- final int codeLength = readInt(currentOffset + 4);
|
||
|
|
- currentOffset += 8;
|
||
|
|
-
|
||
|
|
- // Read the bytecode 'code' array to create a label for each referenced instruction.
|
||
|
|
- final int bytecodeStartOffset = currentOffset;
|
||
|
|
- final int bytecodeEndOffset = currentOffset + codeLength;
|
||
|
|
- final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
|
||
|
|
- while (currentOffset < bytecodeEndOffset) {
|
||
|
|
- final int bytecodeOffset = currentOffset - bytecodeStartOffset;
|
||
|
|
- final int opcode = classFileBuffer[currentOffset] & 0xFF;
|
||
|
|
- switch (opcode) {
|
||
|
|
- case Constants.NOP:
|
||
|
|
- case Constants.ACONST_NULL:
|
||
|
|
- case Constants.ICONST_M1:
|
||
|
|
- case Constants.ICONST_0:
|
||
|
|
- case Constants.ICONST_1:
|
||
|
|
- case Constants.ICONST_2:
|
||
|
|
- case Constants.ICONST_3:
|
||
|
|
- case Constants.ICONST_4:
|
||
|
|
- case Constants.ICONST_5:
|
||
|
|
- case Constants.LCONST_0:
|
||
|
|
- case Constants.LCONST_1:
|
||
|
|
- case Constants.FCONST_0:
|
||
|
|
- case Constants.FCONST_1:
|
||
|
|
- case Constants.FCONST_2:
|
||
|
|
- case Constants.DCONST_0:
|
||
|
|
- case Constants.DCONST_1:
|
||
|
|
- case Constants.IALOAD:
|
||
|
|
- case Constants.LALOAD:
|
||
|
|
- case Constants.FALOAD:
|
||
|
|
- case Constants.DALOAD:
|
||
|
|
- case Constants.AALOAD:
|
||
|
|
- case Constants.BALOAD:
|
||
|
|
- case Constants.CALOAD:
|
||
|
|
- case Constants.SALOAD:
|
||
|
|
- case Constants.IASTORE:
|
||
|
|
- case Constants.LASTORE:
|
||
|
|
- case Constants.FASTORE:
|
||
|
|
- case Constants.DASTORE:
|
||
|
|
- case Constants.AASTORE:
|
||
|
|
- case Constants.BASTORE:
|
||
|
|
- case Constants.CASTORE:
|
||
|
|
- case Constants.SASTORE:
|
||
|
|
- case Constants.POP:
|
||
|
|
- case Constants.POP2:
|
||
|
|
- case Constants.DUP:
|
||
|
|
- case Constants.DUP_X1:
|
||
|
|
- case Constants.DUP_X2:
|
||
|
|
- case Constants.DUP2:
|
||
|
|
- case Constants.DUP2_X1:
|
||
|
|
- case Constants.DUP2_X2:
|
||
|
|
- case Constants.SWAP:
|
||
|
|
- case Constants.IADD:
|
||
|
|
- case Constants.LADD:
|
||
|
|
- case Constants.FADD:
|
||
|
|
- case Constants.DADD:
|
||
|
|
- case Constants.ISUB:
|
||
|
|
- case Constants.LSUB:
|
||
|
|
- case Constants.FSUB:
|
||
|
|
- case Constants.DSUB:
|
||
|
|
- case Constants.IMUL:
|
||
|
|
- case Constants.LMUL:
|
||
|
|
- case Constants.FMUL:
|
||
|
|
- case Constants.DMUL:
|
||
|
|
- case Constants.IDIV:
|
||
|
|
- case Constants.LDIV:
|
||
|
|
- case Constants.FDIV:
|
||
|
|
- case Constants.DDIV:
|
||
|
|
- case Constants.IREM:
|
||
|
|
- case Constants.LREM:
|
||
|
|
- case Constants.FREM:
|
||
|
|
- case Constants.DREM:
|
||
|
|
- case Constants.INEG:
|
||
|
|
- case Constants.LNEG:
|
||
|
|
- case Constants.FNEG:
|
||
|
|
- case Constants.DNEG:
|
||
|
|
- case Constants.ISHL:
|
||
|
|
- case Constants.LSHL:
|
||
|
|
- case Constants.ISHR:
|
||
|
|
- case Constants.LSHR:
|
||
|
|
- case Constants.IUSHR:
|
||
|
|
- case Constants.LUSHR:
|
||
|
|
- case Constants.IAND:
|
||
|
|
- case Constants.LAND:
|
||
|
|
- case Constants.IOR:
|
||
|
|
- case Constants.LOR:
|
||
|
|
- case Constants.IXOR:
|
||
|
|
- case Constants.LXOR:
|
||
|
|
- case Constants.I2L:
|
||
|
|
- case Constants.I2F:
|
||
|
|
- case Constants.I2D:
|
||
|
|
- case Constants.L2I:
|
||
|
|
- case Constants.L2F:
|
||
|
|
- case Constants.L2D:
|
||
|
|
- case Constants.F2I:
|
||
|
|
- case Constants.F2L:
|
||
|
|
- case Constants.F2D:
|
||
|
|
- case Constants.D2I:
|
||
|
|
- case Constants.D2L:
|
||
|
|
- case Constants.D2F:
|
||
|
|
- case Constants.I2B:
|
||
|
|
- case Constants.I2C:
|
||
|
|
- case Constants.I2S:
|
||
|
|
- case Constants.LCMP:
|
||
|
|
- case Constants.FCMPL:
|
||
|
|
- case Constants.FCMPG:
|
||
|
|
- case Constants.DCMPL:
|
||
|
|
- case Constants.DCMPG:
|
||
|
|
- case Constants.IRETURN:
|
||
|
|
- case Constants.LRETURN:
|
||
|
|
- case Constants.FRETURN:
|
||
|
|
- case Constants.DRETURN:
|
||
|
|
- case Constants.ARETURN:
|
||
|
|
- case Constants.RETURN:
|
||
|
|
- case Constants.ARRAYLENGTH:
|
||
|
|
- case Constants.ATHROW:
|
||
|
|
- case Constants.MONITORENTER:
|
||
|
|
- case Constants.MONITOREXIT:
|
||
|
|
- case Constants.ILOAD_0:
|
||
|
|
- case Constants.ILOAD_1:
|
||
|
|
- case Constants.ILOAD_2:
|
||
|
|
- case Constants.ILOAD_3:
|
||
|
|
- case Constants.LLOAD_0:
|
||
|
|
- case Constants.LLOAD_1:
|
||
|
|
- case Constants.LLOAD_2:
|
||
|
|
- case Constants.LLOAD_3:
|
||
|
|
- case Constants.FLOAD_0:
|
||
|
|
- case Constants.FLOAD_1:
|
||
|
|
- case Constants.FLOAD_2:
|
||
|
|
- case Constants.FLOAD_3:
|
||
|
|
- case Constants.DLOAD_0:
|
||
|
|
- case Constants.DLOAD_1:
|
||
|
|
- case Constants.DLOAD_2:
|
||
|
|
- case Constants.DLOAD_3:
|
||
|
|
- case Constants.ALOAD_0:
|
||
|
|
- case Constants.ALOAD_1:
|
||
|
|
- case Constants.ALOAD_2:
|
||
|
|
- case Constants.ALOAD_3:
|
||
|
|
- case Constants.ISTORE_0:
|
||
|
|
- case Constants.ISTORE_1:
|
||
|
|
- case Constants.ISTORE_2:
|
||
|
|
- case Constants.ISTORE_3:
|
||
|
|
- case Constants.LSTORE_0:
|
||
|
|
- case Constants.LSTORE_1:
|
||
|
|
- case Constants.LSTORE_2:
|
||
|
|
- case Constants.LSTORE_3:
|
||
|
|
- case Constants.FSTORE_0:
|
||
|
|
- case Constants.FSTORE_1:
|
||
|
|
- case Constants.FSTORE_2:
|
||
|
|
- case Constants.FSTORE_3:
|
||
|
|
- case Constants.DSTORE_0:
|
||
|
|
- case Constants.DSTORE_1:
|
||
|
|
- case Constants.DSTORE_2:
|
||
|
|
- case Constants.DSTORE_3:
|
||
|
|
- case Constants.ASTORE_0:
|
||
|
|
- case Constants.ASTORE_1:
|
||
|
|
- case Constants.ASTORE_2:
|
||
|
|
- case Constants.ASTORE_3:
|
||
|
|
- currentOffset += 1;
|
||
|
|
- break;
|
||
|
|
- case Constants.IFEQ:
|
||
|
|
- case Constants.IFNE:
|
||
|
|
- case Constants.IFLT:
|
||
|
|
- case Constants.IFGE:
|
||
|
|
- case Constants.IFGT:
|
||
|
|
- case Constants.IFLE:
|
||
|
|
- case Constants.IF_ICMPEQ:
|
||
|
|
- case Constants.IF_ICMPNE:
|
||
|
|
- case Constants.IF_ICMPLT:
|
||
|
|
- case Constants.IF_ICMPGE:
|
||
|
|
- case Constants.IF_ICMPGT:
|
||
|
|
- case Constants.IF_ICMPLE:
|
||
|
|
- case Constants.IF_ACMPEQ:
|
||
|
|
- case Constants.IF_ACMPNE:
|
||
|
|
- case Constants.GOTO:
|
||
|
|
- case Constants.JSR:
|
||
|
|
- case Constants.IFNULL:
|
||
|
|
- case Constants.IFNONNULL:
|
||
|
|
- createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.ASM_IFEQ:
|
||
|
|
- case Constants.ASM_IFNE:
|
||
|
|
- case Constants.ASM_IFLT:
|
||
|
|
- case Constants.ASM_IFGE:
|
||
|
|
- case Constants.ASM_IFGT:
|
||
|
|
- case Constants.ASM_IFLE:
|
||
|
|
- case Constants.ASM_IF_ICMPEQ:
|
||
|
|
- case Constants.ASM_IF_ICMPNE:
|
||
|
|
- case Constants.ASM_IF_ICMPLT:
|
||
|
|
- case Constants.ASM_IF_ICMPGE:
|
||
|
|
- case Constants.ASM_IF_ICMPGT:
|
||
|
|
- case Constants.ASM_IF_ICMPLE:
|
||
|
|
- case Constants.ASM_IF_ACMPEQ:
|
||
|
|
- case Constants.ASM_IF_ACMPNE:
|
||
|
|
- case Constants.ASM_GOTO:
|
||
|
|
- case Constants.ASM_JSR:
|
||
|
|
- case Constants.ASM_IFNULL:
|
||
|
|
- case Constants.ASM_IFNONNULL:
|
||
|
|
- createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.GOTO_W:
|
||
|
|
- case Constants.JSR_W:
|
||
|
|
- case Constants.ASM_GOTO_W:
|
||
|
|
- createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
|
||
|
|
- currentOffset += 5;
|
||
|
|
- break;
|
||
|
|
- case Constants.WIDE:
|
||
|
|
- switch (classFileBuffer[currentOffset + 1] & 0xFF) {
|
||
|
|
- case Constants.ILOAD:
|
||
|
|
- case Constants.FLOAD:
|
||
|
|
- case Constants.ALOAD:
|
||
|
|
- case Constants.LLOAD:
|
||
|
|
- case Constants.DLOAD:
|
||
|
|
- case Constants.ISTORE:
|
||
|
|
- case Constants.FSTORE:
|
||
|
|
- case Constants.ASTORE:
|
||
|
|
- case Constants.LSTORE:
|
||
|
|
- case Constants.DSTORE:
|
||
|
|
- case Constants.RET:
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- case Constants.IINC:
|
||
|
|
- currentOffset += 6;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case Constants.TABLESWITCH:
|
||
|
|
- // Skip 0 to 3 padding bytes.
|
||
|
|
- currentOffset += 4 - (bytecodeOffset & 3);
|
||
|
|
- // Read the default label and the number of table entries.
|
||
|
|
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
|
||
|
|
- int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
|
||
|
|
- currentOffset += 12;
|
||
|
|
- // Read the table labels.
|
||
|
|
- while (numTableEntries-- > 0) {
|
||
|
|
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
|
||
|
|
- currentOffset += 4;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case Constants.LOOKUPSWITCH:
|
||
|
|
- // Skip 0 to 3 padding bytes.
|
||
|
|
- currentOffset += 4 - (bytecodeOffset & 3);
|
||
|
|
- // Read the default label and the number of switch cases.
|
||
|
|
- createLabel(bytecodeOffset + readInt(currentOffset), labels);
|
||
|
|
- int numSwitchCases = readInt(currentOffset + 4);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- // Read the switch labels.
|
||
|
|
- while (numSwitchCases-- > 0) {
|
||
|
|
- createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case Constants.ILOAD:
|
||
|
|
- case Constants.LLOAD:
|
||
|
|
- case Constants.FLOAD:
|
||
|
|
- case Constants.DLOAD:
|
||
|
|
- case Constants.ALOAD:
|
||
|
|
- case Constants.ISTORE:
|
||
|
|
- case Constants.LSTORE:
|
||
|
|
- case Constants.FSTORE:
|
||
|
|
- case Constants.DSTORE:
|
||
|
|
- case Constants.ASTORE:
|
||
|
|
- case Constants.RET:
|
||
|
|
- case Constants.BIPUSH:
|
||
|
|
- case Constants.NEWARRAY:
|
||
|
|
- case Constants.LDC:
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case Constants.SIPUSH:
|
||
|
|
- case Constants.LDC_W:
|
||
|
|
- case Constants.LDC2_W:
|
||
|
|
- case Constants.GETSTATIC:
|
||
|
|
- case Constants.PUTSTATIC:
|
||
|
|
- case Constants.GETFIELD:
|
||
|
|
- case Constants.PUTFIELD:
|
||
|
|
- case Constants.INVOKEVIRTUAL:
|
||
|
|
- case Constants.INVOKESPECIAL:
|
||
|
|
- case Constants.INVOKESTATIC:
|
||
|
|
- case Constants.NEW:
|
||
|
|
- case Constants.ANEWARRAY:
|
||
|
|
- case Constants.CHECKCAST:
|
||
|
|
- case Constants.INSTANCEOF:
|
||
|
|
- case Constants.IINC:
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.INVOKEINTERFACE:
|
||
|
|
- case Constants.INVOKEDYNAMIC:
|
||
|
|
- currentOffset += 5;
|
||
|
|
- break;
|
||
|
|
- case Constants.MULTIANEWARRAY:
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the 'exception_table_length' and 'exception_table' field to create a label for each
|
||
|
|
- // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
|
||
|
|
- int exceptionTableLength = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (exceptionTableLength-- > 0) {
|
||
|
|
- Label start = createLabel(readUnsignedShort(currentOffset), labels);
|
||
|
|
- Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
|
||
|
|
- Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
|
||
|
|
- String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Read the Code attributes to create a label for each referenced instruction (the variables
|
||
|
|
- // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
|
||
|
|
- // attribute_name_index and attribute_length fields.
|
||
|
|
- // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
|
||
|
|
- // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
|
||
|
|
- // updated after each stack_map_frame is read.
|
||
|
|
- int stackMapFrameOffset = 0;
|
||
|
|
- // - The end offset of the StackMap[Table] attribute, or 0.
|
||
|
|
- int stackMapTableEndOffset = 0;
|
||
|
|
- // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
|
||
|
|
- boolean compressedFrames = true;
|
||
|
|
- // - The offset of the LocalVariableTable attribute, or 0.
|
||
|
|
- int localVariableTableOffset = 0;
|
||
|
|
- // - The offset of the LocalVariableTypeTable attribute, or 0.
|
||
|
|
- int localVariableTypeTableOffset = 0;
|
||
|
|
- // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
|
||
|
|
- // attribute, or null.
|
||
|
|
- int[] visibleTypeAnnotationOffsets = null;
|
||
|
|
- // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
|
||
|
|
- // attribute, or null.
|
||
|
|
- int[] invisibleTypeAnnotationOffsets = null;
|
||
|
|
- // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
|
||
|
|
- // This list in the <i>reverse order</i> or their order in the ClassFile structure.
|
||
|
|
- Attribute attributes = null;
|
||
|
|
-
|
||
|
|
- int attributesCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (attributesCount-- > 0) {
|
||
|
|
- // Read the attribute_info's attribute_name and attribute_length fields.
|
||
|
|
- String attributeName = readUTF8(currentOffset, charBuffer);
|
||
|
|
- int attributeLength = readInt(currentOffset + 2);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
|
||
|
|
- if ((context.parsingOptions & SKIP_DEBUG) == 0) {
|
||
|
|
- localVariableTableOffset = currentOffset;
|
||
|
|
- // Parse the attribute to find the corresponding (debug only) labels.
|
||
|
|
- int currentLocalVariableTableOffset = currentOffset;
|
||
|
|
- int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
|
||
|
|
- currentLocalVariableTableOffset += 2;
|
||
|
|
- while (localVariableTableLength-- > 0) {
|
||
|
|
- int startPc = readUnsignedShort(currentLocalVariableTableOffset);
|
||
|
|
- createDebugLabel(startPc, labels);
|
||
|
|
- int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
|
||
|
|
- createDebugLabel(startPc + length, labels);
|
||
|
|
- // Skip the name_index, descriptor_index and index fields (2 bytes each).
|
||
|
|
- currentLocalVariableTableOffset += 10;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
|
||
|
|
- localVariableTypeTableOffset = currentOffset;
|
||
|
|
- // Here we do not extract the labels corresponding to the attribute content. We assume they
|
||
|
|
- // are the same or a subset of those of the LocalVariableTable attribute.
|
||
|
|
- } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
|
||
|
|
- if ((context.parsingOptions & SKIP_DEBUG) == 0) {
|
||
|
|
- // Parse the attribute to find the corresponding (debug only) labels.
|
||
|
|
- int currentLineNumberTableOffset = currentOffset;
|
||
|
|
- int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
|
||
|
|
- currentLineNumberTableOffset += 2;
|
||
|
|
- while (lineNumberTableLength-- > 0) {
|
||
|
|
- int startPc = readUnsignedShort(currentLineNumberTableOffset);
|
||
|
|
- int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
|
||
|
|
- currentLineNumberTableOffset += 4;
|
||
|
|
- createDebugLabel(startPc, labels);
|
||
|
|
- labels[startPc].addLineNumber(lineNumber);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- visibleTypeAnnotationOffsets =
|
||
|
|
- readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
|
||
|
|
- // Here we do not extract the labels corresponding to the attribute content. This would
|
||
|
|
- // require a full parsing of the attribute, which would need to be repeated when parsing
|
||
|
|
- // the bytecode instructions (see below). Instead, the content of the attribute is read one
|
||
|
|
- // type annotation at a time (i.e. after a type annotation has been visited, the next type
|
||
|
|
- // annotation is read), and the labels it contains are also extracted one annotation at a
|
||
|
|
- // time. This assumes that type annotations are ordered by increasing bytecode offset.
|
||
|
|
- } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
|
||
|
|
- invisibleTypeAnnotationOffsets =
|
||
|
|
- readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
|
||
|
|
- // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
|
||
|
|
- } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
|
||
|
|
- if ((context.parsingOptions & SKIP_FRAMES) == 0) {
|
||
|
|
- stackMapFrameOffset = currentOffset + 2;
|
||
|
|
- stackMapTableEndOffset = currentOffset + attributeLength;
|
||
|
|
- }
|
||
|
|
- // Here we do not extract the labels corresponding to the attribute content. This would
|
||
|
|
- // require a full parsing of the attribute, which would need to be repeated when parsing
|
||
|
|
- // the bytecode instructions (see below). Instead, the content of the attribute is read one
|
||
|
|
- // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
|
||
|
|
- // labels it contains are also extracted one frame at a time. Thanks to the ordering of
|
||
|
|
- // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
|
||
|
|
- // see an offset smaller than the offset of the current instruction and for which no Label
|
||
|
|
- // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
|
||
|
|
- // table without a full decoding (see below).
|
||
|
|
- } else if ("StackMap".equals(attributeName)) {
|
||
|
|
- if ((context.parsingOptions & SKIP_FRAMES) == 0) {
|
||
|
|
- stackMapFrameOffset = currentOffset + 2;
|
||
|
|
- stackMapTableEndOffset = currentOffset + attributeLength;
|
||
|
|
- compressedFrames = false;
|
||
|
|
- }
|
||
|
|
- // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
|
||
|
|
- // although this is not guaranteed by the attribute format. This allows an incremental
|
||
|
|
- // extraction of the labels corresponding to this attribute (see the comment above for the
|
||
|
|
- // StackMapTable attribute).
|
||
|
|
- } else {
|
||
|
|
- Attribute attribute =
|
||
|
|
- readAttribute(
|
||
|
|
- context.attributePrototypes,
|
||
|
|
- attributeName,
|
||
|
|
- currentOffset,
|
||
|
|
- attributeLength,
|
||
|
|
- charBuffer,
|
||
|
|
- codeOffset,
|
||
|
|
- labels);
|
||
|
|
- attribute.nextAttribute = attributes;
|
||
|
|
- attributes = attribute;
|
||
|
|
- }
|
||
|
|
- currentOffset += attributeLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Initialize the context fields related to stack map frames, and generate the first
|
||
|
|
- // (implicit) stack map frame, if needed.
|
||
|
|
- final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
|
||
|
|
- if (stackMapFrameOffset != 0) {
|
||
|
|
- // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
|
||
|
|
- // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
|
||
|
|
- // "offset_delta + 1" rule in all cases.
|
||
|
|
- context.currentFrameOffset = -1;
|
||
|
|
- context.currentFrameType = 0;
|
||
|
|
- context.currentFrameLocalCount = 0;
|
||
|
|
- context.currentFrameLocalCountDelta = 0;
|
||
|
|
- context.currentFrameLocalTypes = new Object[maxLocals];
|
||
|
|
- context.currentFrameStackCount = 0;
|
||
|
|
- context.currentFrameStackTypes = new Object[maxStack];
|
||
|
|
- if (expandFrames) {
|
||
|
|
- computeImplicitFrame(context);
|
||
|
|
- }
|
||
|
|
- // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
|
||
|
|
- // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
|
||
|
|
- // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
|
||
|
|
- // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
|
||
|
|
- // and the only consequence will be the creation of an unneeded label. This is better than
|
||
|
|
- // creating a label for each NEW instruction, and faster than fully decoding the whole stack
|
||
|
|
- // map table.
|
||
|
|
- for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
|
||
|
|
- if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
|
||
|
|
- int potentialBytecodeOffset = readUnsignedShort(offset + 1);
|
||
|
|
- if (potentialBytecodeOffset >= 0
|
||
|
|
- && potentialBytecodeOffset < codeLength
|
||
|
|
- && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
|
||
|
|
- == Opcodes.NEW) {
|
||
|
|
- createLabel(potentialBytecodeOffset, labels);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
|
||
|
|
- // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
|
||
|
|
- // does not currently have any frame. These inserted frames must be computed by simulating the
|
||
|
|
- // effect of the bytecode instructions, one by one, starting from the implicit first frame.
|
||
|
|
- // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
|
||
|
|
- // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
|
||
|
|
- // computed in MethodWriter).
|
||
|
|
- methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
|
||
|
|
- // of the type annotations.
|
||
|
|
-
|
||
|
|
- // Index of the next runtime visible type annotation to read (in the
|
||
|
|
- // visibleTypeAnnotationOffsets array).
|
||
|
|
- int currentVisibleTypeAnnotationIndex = 0;
|
||
|
|
- // The bytecode offset of the next runtime visible type annotation to read, or -1.
|
||
|
|
- int currentVisibleTypeAnnotationBytecodeOffset =
|
||
|
|
- getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
|
||
|
|
- // Index of the next runtime invisible type annotation to read (in the
|
||
|
|
- // invisibleTypeAnnotationOffsets array).
|
||
|
|
- int currentInvisibleTypeAnnotationIndex = 0;
|
||
|
|
- // The bytecode offset of the next runtime invisible type annotation to read, or -1.
|
||
|
|
- int currentInvisibleTypeAnnotationBytecodeOffset =
|
||
|
|
- getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);
|
||
|
|
-
|
||
|
|
- // Whether a F_INSERT stack map frame must be inserted before the current instruction.
|
||
|
|
- boolean insertFrame = false;
|
||
|
|
-
|
||
|
|
- // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
|
||
|
|
- // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
|
||
|
|
- // instructions).
|
||
|
|
- final int wideJumpOpcodeDelta =
|
||
|
|
- (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;
|
||
|
|
-
|
||
|
|
- currentOffset = bytecodeStartOffset;
|
||
|
|
- while (currentOffset < bytecodeEndOffset) {
|
||
|
|
- final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
|
||
|
|
-
|
||
|
|
- // Visit the label and the line number(s) for this bytecode offset, if any.
|
||
|
|
- Label currentLabel = labels[currentBytecodeOffset];
|
||
|
|
- if (currentLabel != null) {
|
||
|
|
- currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the stack map frame for this bytecode offset, if any.
|
||
|
|
- while (stackMapFrameOffset != 0
|
||
|
|
- && (context.currentFrameOffset == currentBytecodeOffset
|
||
|
|
- || context.currentFrameOffset == -1)) {
|
||
|
|
- // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
|
||
|
|
- // next stack map frame if there is one.
|
||
|
|
- if (context.currentFrameOffset != -1) {
|
||
|
|
- if (!compressedFrames || expandFrames) {
|
||
|
|
- methodVisitor.visitFrame(
|
||
|
|
- Opcodes.F_NEW,
|
||
|
|
- context.currentFrameLocalCount,
|
||
|
|
- context.currentFrameLocalTypes,
|
||
|
|
- context.currentFrameStackCount,
|
||
|
|
- context.currentFrameStackTypes);
|
||
|
|
- } else {
|
||
|
|
- methodVisitor.visitFrame(
|
||
|
|
- context.currentFrameType,
|
||
|
|
- context.currentFrameLocalCountDelta,
|
||
|
|
- context.currentFrameLocalTypes,
|
||
|
|
- context.currentFrameStackCount,
|
||
|
|
- context.currentFrameStackTypes);
|
||
|
|
- }
|
||
|
|
- // Since there is already a stack map frame for this bytecode offset, there is no need to
|
||
|
|
- // insert a new one.
|
||
|
|
- insertFrame = false;
|
||
|
|
- }
|
||
|
|
- if (stackMapFrameOffset < stackMapTableEndOffset) {
|
||
|
|
- stackMapFrameOffset =
|
||
|
|
- readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
|
||
|
|
- } else {
|
||
|
|
- stackMapFrameOffset = 0;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
|
||
|
|
- // true during the previous iteration. The actual frame content is computed in MethodWriter.
|
||
|
|
- if (insertFrame) {
|
||
|
|
- if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
|
||
|
|
- methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
|
||
|
|
- }
|
||
|
|
- insertFrame = false;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the instruction at this bytecode offset.
|
||
|
|
- int opcode = classFileBuffer[currentOffset] & 0xFF;
|
||
|
|
- switch (opcode) {
|
||
|
|
- case Constants.NOP:
|
||
|
|
- case Constants.ACONST_NULL:
|
||
|
|
- case Constants.ICONST_M1:
|
||
|
|
- case Constants.ICONST_0:
|
||
|
|
- case Constants.ICONST_1:
|
||
|
|
- case Constants.ICONST_2:
|
||
|
|
- case Constants.ICONST_3:
|
||
|
|
- case Constants.ICONST_4:
|
||
|
|
- case Constants.ICONST_5:
|
||
|
|
- case Constants.LCONST_0:
|
||
|
|
- case Constants.LCONST_1:
|
||
|
|
- case Constants.FCONST_0:
|
||
|
|
- case Constants.FCONST_1:
|
||
|
|
- case Constants.FCONST_2:
|
||
|
|
- case Constants.DCONST_0:
|
||
|
|
- case Constants.DCONST_1:
|
||
|
|
- case Constants.IALOAD:
|
||
|
|
- case Constants.LALOAD:
|
||
|
|
- case Constants.FALOAD:
|
||
|
|
- case Constants.DALOAD:
|
||
|
|
- case Constants.AALOAD:
|
||
|
|
- case Constants.BALOAD:
|
||
|
|
- case Constants.CALOAD:
|
||
|
|
- case Constants.SALOAD:
|
||
|
|
- case Constants.IASTORE:
|
||
|
|
- case Constants.LASTORE:
|
||
|
|
- case Constants.FASTORE:
|
||
|
|
- case Constants.DASTORE:
|
||
|
|
- case Constants.AASTORE:
|
||
|
|
- case Constants.BASTORE:
|
||
|
|
- case Constants.CASTORE:
|
||
|
|
- case Constants.SASTORE:
|
||
|
|
- case Constants.POP:
|
||
|
|
- case Constants.POP2:
|
||
|
|
- case Constants.DUP:
|
||
|
|
- case Constants.DUP_X1:
|
||
|
|
- case Constants.DUP_X2:
|
||
|
|
- case Constants.DUP2:
|
||
|
|
- case Constants.DUP2_X1:
|
||
|
|
- case Constants.DUP2_X2:
|
||
|
|
- case Constants.SWAP:
|
||
|
|
- case Constants.IADD:
|
||
|
|
- case Constants.LADD:
|
||
|
|
- case Constants.FADD:
|
||
|
|
- case Constants.DADD:
|
||
|
|
- case Constants.ISUB:
|
||
|
|
- case Constants.LSUB:
|
||
|
|
- case Constants.FSUB:
|
||
|
|
- case Constants.DSUB:
|
||
|
|
- case Constants.IMUL:
|
||
|
|
- case Constants.LMUL:
|
||
|
|
- case Constants.FMUL:
|
||
|
|
- case Constants.DMUL:
|
||
|
|
- case Constants.IDIV:
|
||
|
|
- case Constants.LDIV:
|
||
|
|
- case Constants.FDIV:
|
||
|
|
- case Constants.DDIV:
|
||
|
|
- case Constants.IREM:
|
||
|
|
- case Constants.LREM:
|
||
|
|
- case Constants.FREM:
|
||
|
|
- case Constants.DREM:
|
||
|
|
- case Constants.INEG:
|
||
|
|
- case Constants.LNEG:
|
||
|
|
- case Constants.FNEG:
|
||
|
|
- case Constants.DNEG:
|
||
|
|
- case Constants.ISHL:
|
||
|
|
- case Constants.LSHL:
|
||
|
|
- case Constants.ISHR:
|
||
|
|
- case Constants.LSHR:
|
||
|
|
- case Constants.IUSHR:
|
||
|
|
- case Constants.LUSHR:
|
||
|
|
- case Constants.IAND:
|
||
|
|
- case Constants.LAND:
|
||
|
|
- case Constants.IOR:
|
||
|
|
- case Constants.LOR:
|
||
|
|
- case Constants.IXOR:
|
||
|
|
- case Constants.LXOR:
|
||
|
|
- case Constants.I2L:
|
||
|
|
- case Constants.I2F:
|
||
|
|
- case Constants.I2D:
|
||
|
|
- case Constants.L2I:
|
||
|
|
- case Constants.L2F:
|
||
|
|
- case Constants.L2D:
|
||
|
|
- case Constants.F2I:
|
||
|
|
- case Constants.F2L:
|
||
|
|
- case Constants.F2D:
|
||
|
|
- case Constants.D2I:
|
||
|
|
- case Constants.D2L:
|
||
|
|
- case Constants.D2F:
|
||
|
|
- case Constants.I2B:
|
||
|
|
- case Constants.I2C:
|
||
|
|
- case Constants.I2S:
|
||
|
|
- case Constants.LCMP:
|
||
|
|
- case Constants.FCMPL:
|
||
|
|
- case Constants.FCMPG:
|
||
|
|
- case Constants.DCMPL:
|
||
|
|
- case Constants.DCMPG:
|
||
|
|
- case Constants.IRETURN:
|
||
|
|
- case Constants.LRETURN:
|
||
|
|
- case Constants.FRETURN:
|
||
|
|
- case Constants.DRETURN:
|
||
|
|
- case Constants.ARETURN:
|
||
|
|
- case Constants.RETURN:
|
||
|
|
- case Constants.ARRAYLENGTH:
|
||
|
|
- case Constants.ATHROW:
|
||
|
|
- case Constants.MONITORENTER:
|
||
|
|
- case Constants.MONITOREXIT:
|
||
|
|
- methodVisitor.visitInsn(opcode);
|
||
|
|
- currentOffset += 1;
|
||
|
|
- break;
|
||
|
|
- case Constants.ILOAD_0:
|
||
|
|
- case Constants.ILOAD_1:
|
||
|
|
- case Constants.ILOAD_2:
|
||
|
|
- case Constants.ILOAD_3:
|
||
|
|
- case Constants.LLOAD_0:
|
||
|
|
- case Constants.LLOAD_1:
|
||
|
|
- case Constants.LLOAD_2:
|
||
|
|
- case Constants.LLOAD_3:
|
||
|
|
- case Constants.FLOAD_0:
|
||
|
|
- case Constants.FLOAD_1:
|
||
|
|
- case Constants.FLOAD_2:
|
||
|
|
- case Constants.FLOAD_3:
|
||
|
|
- case Constants.DLOAD_0:
|
||
|
|
- case Constants.DLOAD_1:
|
||
|
|
- case Constants.DLOAD_2:
|
||
|
|
- case Constants.DLOAD_3:
|
||
|
|
- case Constants.ALOAD_0:
|
||
|
|
- case Constants.ALOAD_1:
|
||
|
|
- case Constants.ALOAD_2:
|
||
|
|
- case Constants.ALOAD_3:
|
||
|
|
- opcode -= Constants.ILOAD_0;
|
||
|
|
- methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
|
||
|
|
- currentOffset += 1;
|
||
|
|
- break;
|
||
|
|
- case Constants.ISTORE_0:
|
||
|
|
- case Constants.ISTORE_1:
|
||
|
|
- case Constants.ISTORE_2:
|
||
|
|
- case Constants.ISTORE_3:
|
||
|
|
- case Constants.LSTORE_0:
|
||
|
|
- case Constants.LSTORE_1:
|
||
|
|
- case Constants.LSTORE_2:
|
||
|
|
- case Constants.LSTORE_3:
|
||
|
|
- case Constants.FSTORE_0:
|
||
|
|
- case Constants.FSTORE_1:
|
||
|
|
- case Constants.FSTORE_2:
|
||
|
|
- case Constants.FSTORE_3:
|
||
|
|
- case Constants.DSTORE_0:
|
||
|
|
- case Constants.DSTORE_1:
|
||
|
|
- case Constants.DSTORE_2:
|
||
|
|
- case Constants.DSTORE_3:
|
||
|
|
- case Constants.ASTORE_0:
|
||
|
|
- case Constants.ASTORE_1:
|
||
|
|
- case Constants.ASTORE_2:
|
||
|
|
- case Constants.ASTORE_3:
|
||
|
|
- opcode -= Constants.ISTORE_0;
|
||
|
|
- methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
|
||
|
|
- currentOffset += 1;
|
||
|
|
- break;
|
||
|
|
- case Constants.IFEQ:
|
||
|
|
- case Constants.IFNE:
|
||
|
|
- case Constants.IFLT:
|
||
|
|
- case Constants.IFGE:
|
||
|
|
- case Constants.IFGT:
|
||
|
|
- case Constants.IFLE:
|
||
|
|
- case Constants.IF_ICMPEQ:
|
||
|
|
- case Constants.IF_ICMPNE:
|
||
|
|
- case Constants.IF_ICMPLT:
|
||
|
|
- case Constants.IF_ICMPGE:
|
||
|
|
- case Constants.IF_ICMPGT:
|
||
|
|
- case Constants.IF_ICMPLE:
|
||
|
|
- case Constants.IF_ACMPEQ:
|
||
|
|
- case Constants.IF_ACMPNE:
|
||
|
|
- case Constants.GOTO:
|
||
|
|
- case Constants.JSR:
|
||
|
|
- case Constants.IFNULL:
|
||
|
|
- case Constants.IFNONNULL:
|
||
|
|
- methodVisitor.visitJumpInsn(
|
||
|
|
- opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.GOTO_W:
|
||
|
|
- case Constants.JSR_W:
|
||
|
|
- methodVisitor.visitJumpInsn(
|
||
|
|
- opcode - wideJumpOpcodeDelta,
|
||
|
|
- labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
|
||
|
|
- currentOffset += 5;
|
||
|
|
- break;
|
||
|
|
- case Constants.ASM_IFEQ:
|
||
|
|
- case Constants.ASM_IFNE:
|
||
|
|
- case Constants.ASM_IFLT:
|
||
|
|
- case Constants.ASM_IFGE:
|
||
|
|
- case Constants.ASM_IFGT:
|
||
|
|
- case Constants.ASM_IFLE:
|
||
|
|
- case Constants.ASM_IF_ICMPEQ:
|
||
|
|
- case Constants.ASM_IF_ICMPNE:
|
||
|
|
- case Constants.ASM_IF_ICMPLT:
|
||
|
|
- case Constants.ASM_IF_ICMPGE:
|
||
|
|
- case Constants.ASM_IF_ICMPGT:
|
||
|
|
- case Constants.ASM_IF_ICMPLE:
|
||
|
|
- case Constants.ASM_IF_ACMPEQ:
|
||
|
|
- case Constants.ASM_IF_ACMPNE:
|
||
|
|
- case Constants.ASM_GOTO:
|
||
|
|
- case Constants.ASM_JSR:
|
||
|
|
- case Constants.ASM_IFNULL:
|
||
|
|
- case Constants.ASM_IFNONNULL:
|
||
|
|
- {
|
||
|
|
- // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
|
||
|
|
- // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:...,
|
||
|
|
- // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
|
||
|
|
- // where <L> designates the instruction just after the GOTO_W.
|
||
|
|
- // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
|
||
|
|
- // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
|
||
|
|
- opcode =
|
||
|
|
- opcode < Constants.ASM_IFNULL
|
||
|
|
- ? opcode - Constants.ASM_OPCODE_DELTA
|
||
|
|
- : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
|
||
|
|
- Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
|
||
|
|
- if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
|
||
|
|
- // Replace GOTO with GOTO_W and JSR with JSR_W.
|
||
|
|
- methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
|
||
|
|
- } else {
|
||
|
|
- // Compute the "opposite" of opcode. This can be done by flipping the least
|
||
|
|
- // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
|
||
|
|
- // (with a pre and post offset by 1).
|
||
|
|
- opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
|
||
|
|
- Label endif = createLabel(currentBytecodeOffset + 3, labels);
|
||
|
|
- methodVisitor.visitJumpInsn(opcode, endif);
|
||
|
|
- methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
|
||
|
|
- // endif designates the instruction just after GOTO_W, and is visited as part of the
|
||
|
|
- // next instruction. Since it is a jump target, we need to insert a frame here.
|
||
|
|
- insertFrame = true;
|
||
|
|
- }
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.ASM_GOTO_W:
|
||
|
|
- {
|
||
|
|
- // Replace ASM_GOTO_W with GOTO_W.
|
||
|
|
- methodVisitor.visitJumpInsn(
|
||
|
|
- Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
|
||
|
|
- // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
|
||
|
|
- // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
|
||
|
|
- // here.
|
||
|
|
- insertFrame = true;
|
||
|
|
- currentOffset += 5;
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.WIDE:
|
||
|
|
- opcode = classFileBuffer[currentOffset + 1] & 0xFF;
|
||
|
|
- if (opcode == Opcodes.IINC) {
|
||
|
|
- methodVisitor.visitIincInsn(
|
||
|
|
- readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
|
||
|
|
- currentOffset += 6;
|
||
|
|
- } else {
|
||
|
|
- methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
|
||
|
|
- currentOffset += 4;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case Constants.TABLESWITCH:
|
||
|
|
- {
|
||
|
|
- // Skip 0 to 3 padding bytes.
|
||
|
|
- currentOffset += 4 - (currentBytecodeOffset & 3);
|
||
|
|
- // Read the instruction.
|
||
|
|
- Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
|
||
|
|
- int low = readInt(currentOffset + 4);
|
||
|
|
- int high = readInt(currentOffset + 8);
|
||
|
|
- currentOffset += 12;
|
||
|
|
- Label[] table = new Label[high - low + 1];
|
||
|
|
- for (int i = 0; i < table.length; ++i) {
|
||
|
|
- table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
|
||
|
|
- currentOffset += 4;
|
||
|
|
- }
|
||
|
|
- methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.LOOKUPSWITCH:
|
||
|
|
- {
|
||
|
|
- // Skip 0 to 3 padding bytes.
|
||
|
|
- currentOffset += 4 - (currentBytecodeOffset & 3);
|
||
|
|
- // Read the instruction.
|
||
|
|
- Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
|
||
|
|
- int numPairs = readInt(currentOffset + 4);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- int[] keys = new int[numPairs];
|
||
|
|
- Label[] values = new Label[numPairs];
|
||
|
|
- for (int i = 0; i < numPairs; ++i) {
|
||
|
|
- keys[i] = readInt(currentOffset);
|
||
|
|
- values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
|
||
|
|
- currentOffset += 8;
|
||
|
|
- }
|
||
|
|
- methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.ILOAD:
|
||
|
|
- case Constants.LLOAD:
|
||
|
|
- case Constants.FLOAD:
|
||
|
|
- case Constants.DLOAD:
|
||
|
|
- case Constants.ALOAD:
|
||
|
|
- case Constants.ISTORE:
|
||
|
|
- case Constants.LSTORE:
|
||
|
|
- case Constants.FSTORE:
|
||
|
|
- case Constants.DSTORE:
|
||
|
|
- case Constants.ASTORE:
|
||
|
|
- case Constants.RET:
|
||
|
|
- methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case Constants.BIPUSH:
|
||
|
|
- case Constants.NEWARRAY:
|
||
|
|
- methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case Constants.SIPUSH:
|
||
|
|
- methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.LDC:
|
||
|
|
- methodVisitor.visitLdcInsn(
|
||
|
|
- readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case Constants.LDC_W:
|
||
|
|
- case Constants.LDC2_W:
|
||
|
|
- methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.GETSTATIC:
|
||
|
|
- case Constants.PUTSTATIC:
|
||
|
|
- case Constants.GETFIELD:
|
||
|
|
- case Constants.PUTFIELD:
|
||
|
|
- case Constants.INVOKEVIRTUAL:
|
||
|
|
- case Constants.INVOKESPECIAL:
|
||
|
|
- case Constants.INVOKESTATIC:
|
||
|
|
- case Constants.INVOKEINTERFACE:
|
||
|
|
- {
|
||
|
|
- int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
|
||
|
|
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
|
||
|
|
- String owner = readClass(cpInfoOffset, charBuffer);
|
||
|
|
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
|
||
|
|
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
|
||
|
|
- if (opcode < Opcodes.INVOKEVIRTUAL) {
|
||
|
|
- methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
|
||
|
|
- } else {
|
||
|
|
- boolean isInterface =
|
||
|
|
- classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
|
||
|
|
- methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
||
|
|
- }
|
||
|
|
- if (opcode == Opcodes.INVOKEINTERFACE) {
|
||
|
|
- currentOffset += 5;
|
||
|
|
- } else {
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.INVOKEDYNAMIC:
|
||
|
|
- {
|
||
|
|
- int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
|
||
|
|
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
|
||
|
|
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
|
||
|
|
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
|
||
|
|
- int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
|
||
|
|
- Handle handle =
|
||
|
|
- (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
|
||
|
|
- Object[] bootstrapMethodArguments =
|
||
|
|
- new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
|
||
|
|
- bootstrapMethodOffset += 4;
|
||
|
|
- for (int i = 0; i < bootstrapMethodArguments.length; i++) {
|
||
|
|
- bootstrapMethodArguments[i] =
|
||
|
|
- readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
|
||
|
|
- bootstrapMethodOffset += 2;
|
||
|
|
- }
|
||
|
|
- methodVisitor.visitInvokeDynamicInsn(
|
||
|
|
- name, descriptor, handle, bootstrapMethodArguments);
|
||
|
|
- currentOffset += 5;
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- case Constants.NEW:
|
||
|
|
- case Constants.ANEWARRAY:
|
||
|
|
- case Constants.CHECKCAST:
|
||
|
|
- case Constants.INSTANCEOF:
|
||
|
|
- methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.IINC:
|
||
|
|
- methodVisitor.visitIincInsn(
|
||
|
|
- classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case Constants.MULTIANEWARRAY:
|
||
|
|
- methodVisitor.visitMultiANewArrayInsn(
|
||
|
|
- readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new AssertionError();
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the runtime visible instruction annotations, if any.
|
||
|
|
- while (visibleTypeAnnotationOffsets != null
|
||
|
|
- && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
|
||
|
|
- && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
|
||
|
|
- if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- int currentAnnotationOffset =
|
||
|
|
- readTypeAnnotationTarget(
|
||
|
|
- context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitInsnAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ true),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- currentVisibleTypeAnnotationBytecodeOffset =
|
||
|
|
- getTypeAnnotationBytecodeOffset(
|
||
|
|
- visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the runtime invisible instruction annotations, if any.
|
||
|
|
- while (invisibleTypeAnnotationOffsets != null
|
||
|
|
- && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
|
||
|
|
- && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
|
||
|
|
- if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- int currentAnnotationOffset =
|
||
|
|
- readTypeAnnotationTarget(
|
||
|
|
- context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
|
||
|
|
- currentAnnotationOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitInsnAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ false),
|
||
|
|
- currentAnnotationOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- currentInvisibleTypeAnnotationBytecodeOffset =
|
||
|
|
- getTypeAnnotationBytecodeOffset(
|
||
|
|
- invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- if (labels[codeLength] != null) {
|
||
|
|
- methodVisitor.visitLabel(labels[codeLength]);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit LocalVariableTable and LocalVariableTypeTable attributes.
|
||
|
|
- if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
|
||
|
|
- // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
|
||
|
|
- int[] typeTable = null;
|
||
|
|
- if (localVariableTypeTableOffset != 0) {
|
||
|
|
- typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
|
||
|
|
- currentOffset = localVariableTypeTableOffset + 2;
|
||
|
|
- int typeTableIndex = typeTable.length;
|
||
|
|
- while (typeTableIndex > 0) {
|
||
|
|
- // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
|
||
|
|
- typeTable[--typeTableIndex] = currentOffset + 6;
|
||
|
|
- typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
|
||
|
|
- typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 10;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
|
||
|
|
- currentOffset = localVariableTableOffset + 2;
|
||
|
|
- while (localVariableTableLength-- > 0) {
|
||
|
|
- int startPc = readUnsignedShort(currentOffset);
|
||
|
|
- int length = readUnsignedShort(currentOffset + 2);
|
||
|
|
- String name = readUTF8(currentOffset + 4, charBuffer);
|
||
|
|
- String descriptor = readUTF8(currentOffset + 6, charBuffer);
|
||
|
|
- int index = readUnsignedShort(currentOffset + 8);
|
||
|
|
- currentOffset += 10;
|
||
|
|
- String signature = null;
|
||
|
|
- if (typeTable != null) {
|
||
|
|
- for (int i = 0; i < typeTable.length; i += 3) {
|
||
|
|
- if (typeTable[i] == startPc && typeTable[i + 1] == index) {
|
||
|
|
- signature = readUTF8(typeTable[i + 2], charBuffer);
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- methodVisitor.visitLocalVariable(
|
||
|
|
- name, descriptor, signature, labels[startPc], labels[startPc + length], index);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
|
||
|
|
- if (visibleTypeAnnotationOffsets != null) {
|
||
|
|
- for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) {
|
||
|
|
- int targetType = readByte(typeAnnotationOffset);
|
||
|
|
- if (targetType == TypeReference.LOCAL_VARIABLE
|
||
|
|
- || targetType == TypeReference.RESOURCE_VARIABLE) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitLocalVariableAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- context.currentLocalVariableAnnotationRangeStarts,
|
||
|
|
- context.currentLocalVariableAnnotationRangeEnds,
|
||
|
|
- context.currentLocalVariableAnnotationRangeIndices,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ true),
|
||
|
|
- currentOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute.
|
||
|
|
- if (invisibleTypeAnnotationOffsets != null) {
|
||
|
|
- for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) {
|
||
|
|
- int targetType = readByte(typeAnnotationOffset);
|
||
|
|
- if (targetType == TypeReference.LOCAL_VARIABLE
|
||
|
|
- || targetType == TypeReference.RESOURCE_VARIABLE) {
|
||
|
|
- // Parse the target_type, target_info and target_path fields.
|
||
|
|
- currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitLocalVariableAnnotation(
|
||
|
|
- context.currentTypeAnnotationTarget,
|
||
|
|
- context.currentTypeAnnotationTargetPath,
|
||
|
|
- context.currentLocalVariableAnnotationRangeStarts,
|
||
|
|
- context.currentLocalVariableAnnotationRangeEnds,
|
||
|
|
- context.currentLocalVariableAnnotationRangeIndices,
|
||
|
|
- annotationDescriptor,
|
||
|
|
- /* visible = */ false),
|
||
|
|
- currentOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the non standard attributes.
|
||
|
|
- while (attributes != null) {
|
||
|
|
- // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
|
||
|
|
- Attribute nextAttribute = attributes.nextAttribute;
|
||
|
|
- attributes.nextAttribute = null;
|
||
|
|
- methodVisitor.visitAttribute(attributes);
|
||
|
|
- attributes = nextAttribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Visit the max stack and max locals values.
|
||
|
|
- methodVisitor.visitMaxs(maxStack, maxLocals);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the label corresponding to the given bytecode offset. The default implementation of
|
||
|
|
- * this method creates a label for the given offset if it has not been already created.
|
||
|
|
- *
|
||
|
|
- * @param bytecodeOffset a bytecode offset in a method.
|
||
|
|
- * @param labels the already created labels, indexed by their offset. If a label already exists
|
||
|
|
- * for bytecodeOffset this method must not create a new one. Otherwise it must store the new
|
||
|
|
- * label in this array.
|
||
|
|
- * @return a non null Label, which must be equal to labels[bytecodeOffset].
|
||
|
|
- */
|
||
|
|
- protected Label readLabel(final int bytecodeOffset, final Label[] labels) {
|
||
|
|
- if (labels[bytecodeOffset] == null) {
|
||
|
|
- labels[bytecodeOffset] = new Label();
|
||
|
|
- }
|
||
|
|
- return labels[bytecodeOffset];
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Creates a label without the {@link Label#FLAG_DEBUG_ONLY} flag set, for the given bytecode
|
||
|
|
- * offset. The label is created with a call to {@link #readLabel} and its {@link
|
||
|
|
- * Label#FLAG_DEBUG_ONLY} flag is cleared.
|
||
|
|
- *
|
||
|
|
- * @param bytecodeOffset a bytecode offset in a method.
|
||
|
|
- * @param labels the already created labels, indexed by their offset.
|
||
|
|
- * @return a Label without the {@link Label#FLAG_DEBUG_ONLY} flag set.
|
||
|
|
- */
|
||
|
|
- private Label createLabel(final int bytecodeOffset, final Label[] labels) {
|
||
|
|
- Label label = readLabel(bytecodeOffset, labels);
|
||
|
|
- label.flags &= ~Label.FLAG_DEBUG_ONLY;
|
||
|
|
- return label;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Creates a label with the {@link Label#FLAG_DEBUG_ONLY} flag set, if there is no already
|
||
|
|
- * existing label for the given bytecode offset (otherwise does nothing). The label is created
|
||
|
|
- * with a call to {@link #readLabel}.
|
||
|
|
- *
|
||
|
|
- * @param bytecodeOffset a bytecode offset in a method.
|
||
|
|
- * @param labels the already created labels, indexed by their offset.
|
||
|
|
- */
|
||
|
|
- private void createDebugLabel(final int bytecodeOffset, final Label[] labels) {
|
||
|
|
- if (labels[bytecodeOffset] == null) {
|
||
|
|
- readLabel(bytecodeOffset, labels).flags |= Label.FLAG_DEBUG_ONLY;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
- // Methods to parse annotations, type annotations and parameter annotations
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Parses a Runtime[In]VisibleTypeAnnotations attribute to find the offset of each type_annotation
|
||
|
|
- * entry it contains, to find the corresponding labels, and to visit the try catch block
|
||
|
|
- * annotations.
|
||
|
|
- *
|
||
|
|
- * @param methodVisitor the method visitor to be used to visit the try catch block annotations.
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param runtimeTypeAnnotationsOffset the start offset of a Runtime[In]VisibleTypeAnnotations
|
||
|
|
- * attribute, excluding the attribute_info's attribute_name_index and attribute_length fields.
|
||
|
|
- * @param visible true if the attribute to parse is a RuntimeVisibleTypeAnnotations attribute,
|
||
|
|
- * false it is a RuntimeInvisibleTypeAnnotations attribute.
|
||
|
|
- * @return the start offset of each entry of the Runtime[In]VisibleTypeAnnotations_attribute's
|
||
|
|
- * 'annotations' array field.
|
||
|
|
- */
|
||
|
|
- private int[] readTypeAnnotations(
|
||
|
|
- final MethodVisitor methodVisitor,
|
||
|
|
- final Context context,
|
||
|
|
- final int runtimeTypeAnnotationsOffset,
|
||
|
|
- final boolean visible) {
|
||
|
|
- char[] charBuffer = context.charBuffer;
|
||
|
|
- int currentOffset = runtimeTypeAnnotationsOffset;
|
||
|
|
- // Read the num_annotations field and create an array to store the type_annotation offsets.
|
||
|
|
- int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)];
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Parse the 'annotations' array field.
|
||
|
|
- for (int i = 0; i < typeAnnotationsOffsets.length; ++i) {
|
||
|
|
- typeAnnotationsOffsets[i] = currentOffset;
|
||
|
|
- // Parse the type_annotation's target_type and the target_info fields. The size of the
|
||
|
|
- // target_info field depends on the value of target_type.
|
||
|
|
- int targetType = readInt(currentOffset);
|
||
|
|
- switch (targetType >>> 24) {
|
||
|
|
- case TypeReference.LOCAL_VARIABLE:
|
||
|
|
- case TypeReference.RESOURCE_VARIABLE:
|
||
|
|
- // A localvar_target has a variable size, which depends on the value of their table_length
|
||
|
|
- // field. It also references bytecode offsets, for which we need labels.
|
||
|
|
- int tableLength = readUnsignedShort(currentOffset + 1);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- while (tableLength-- > 0) {
|
||
|
|
- int startPc = readUnsignedShort(currentOffset);
|
||
|
|
- int length = readUnsignedShort(currentOffset + 2);
|
||
|
|
- // Skip the index field (2 bytes).
|
||
|
|
- currentOffset += 6;
|
||
|
|
- createLabel(startPc, context.currentMethodLabels);
|
||
|
|
- createLabel(startPc + length, context.currentMethodLabels);
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case TypeReference.CAST:
|
||
|
|
- case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.CLASS_EXTENDS:
|
||
|
|
- case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
|
||
|
|
- case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
|
||
|
|
- case TypeReference.THROWS:
|
||
|
|
- case TypeReference.EXCEPTION_PARAMETER:
|
||
|
|
- case TypeReference.INSTANCEOF:
|
||
|
|
- case TypeReference.NEW:
|
||
|
|
- case TypeReference.CONSTRUCTOR_REFERENCE:
|
||
|
|
- case TypeReference.METHOD_REFERENCE:
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.CLASS_TYPE_PARAMETER:
|
||
|
|
- case TypeReference.METHOD_TYPE_PARAMETER:
|
||
|
|
- case TypeReference.METHOD_FORMAL_PARAMETER:
|
||
|
|
- case TypeReference.FIELD:
|
||
|
|
- case TypeReference.METHOD_RETURN:
|
||
|
|
- case TypeReference.METHOD_RECEIVER:
|
||
|
|
- default:
|
||
|
|
- // TypeReference type which can't be used in Code attribute, or which is unknown.
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- // Parse the rest of the type_annotation structure, starting with the target_path structure
|
||
|
|
- // (whose size depends on its path_length field).
|
||
|
|
- int pathLength = readByte(currentOffset);
|
||
|
|
- if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
|
||
|
|
- // Parse the target_path structure and create a corresponding TypePath.
|
||
|
|
- TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
|
||
|
|
- currentOffset += 1 + 2 * pathLength;
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitTryCatchAnnotation(
|
||
|
|
- targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
|
||
|
|
- currentOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- } else {
|
||
|
|
- // We don't want to visit the other target_type annotations, so we just skip them (which
|
||
|
|
- // requires some parsing because the element_value_pairs array has a variable size). First,
|
||
|
|
- // skip the target_path structure:
|
||
|
|
- currentOffset += 3 + 2 * pathLength;
|
||
|
|
- // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them
|
||
|
|
- // with a null AnnotationVisitor).
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- return typeAnnotationsOffsets;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or
|
||
|
|
- * -1 if there is no such type_annotation of if it does not have a bytecode offset.
|
||
|
|
- *
|
||
|
|
- * @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a
|
||
|
|
- * Runtime[In]VisibleTypeAnnotations attribute, or null.
|
||
|
|
- * @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets.
|
||
|
|
- * @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1
|
||
|
|
- * if there is no such type_annotation of if it does not have a bytecode offset.
|
||
|
|
- */
|
||
|
|
- private int getTypeAnnotationBytecodeOffset(
|
||
|
|
- final int[] typeAnnotationOffsets, final int typeAnnotationIndex) {
|
||
|
|
- if (typeAnnotationOffsets == null
|
||
|
|
- || typeAnnotationIndex >= typeAnnotationOffsets.length
|
||
|
|
- || readByte(typeAnnotationOffsets[typeAnnotationIndex]) < TypeReference.INSTANCEOF) {
|
||
|
|
- return -1;
|
||
|
|
- }
|
||
|
|
- return readUnsignedShort(typeAnnotationOffsets[typeAnnotationIndex] + 1);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Parses the header of a JVMS type_annotation structure to extract its target_type, target_info
|
||
|
|
- * and target_path (the result is stored in the given context), and returns the start offset of
|
||
|
|
- * the rest of the type_annotation structure.
|
||
|
|
- *
|
||
|
|
- * @param context information about the class being parsed. This is where the extracted
|
||
|
|
- * target_type and target_path must be stored.
|
||
|
|
- * @param typeAnnotationOffset the start offset of a type_annotation structure.
|
||
|
|
- * @return the start offset of the rest of the type_annotation structure.
|
||
|
|
- */
|
||
|
|
- private int readTypeAnnotationTarget(final Context context, final int typeAnnotationOffset) {
|
||
|
|
- int currentOffset = typeAnnotationOffset;
|
||
|
|
- // Parse and store the target_type structure.
|
||
|
|
- int targetType = readInt(typeAnnotationOffset);
|
||
|
|
- switch (targetType >>> 24) {
|
||
|
|
- case TypeReference.CLASS_TYPE_PARAMETER:
|
||
|
|
- case TypeReference.METHOD_TYPE_PARAMETER:
|
||
|
|
- case TypeReference.METHOD_FORMAL_PARAMETER:
|
||
|
|
- targetType &= 0xFFFF0000;
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.FIELD:
|
||
|
|
- case TypeReference.METHOD_RETURN:
|
||
|
|
- case TypeReference.METHOD_RECEIVER:
|
||
|
|
- targetType &= 0xFF000000;
|
||
|
|
- currentOffset += 1;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.LOCAL_VARIABLE:
|
||
|
|
- case TypeReference.RESOURCE_VARIABLE:
|
||
|
|
- targetType &= 0xFF000000;
|
||
|
|
- int tableLength = readUnsignedShort(currentOffset + 1);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- context.currentLocalVariableAnnotationRangeStarts = new Label[tableLength];
|
||
|
|
- context.currentLocalVariableAnnotationRangeEnds = new Label[tableLength];
|
||
|
|
- context.currentLocalVariableAnnotationRangeIndices = new int[tableLength];
|
||
|
|
- for (int i = 0; i < tableLength; ++i) {
|
||
|
|
- int startPc = readUnsignedShort(currentOffset);
|
||
|
|
- int length = readUnsignedShort(currentOffset + 2);
|
||
|
|
- int index = readUnsignedShort(currentOffset + 4);
|
||
|
|
- currentOffset += 6;
|
||
|
|
- context.currentLocalVariableAnnotationRangeStarts[i] =
|
||
|
|
- createLabel(startPc, context.currentMethodLabels);
|
||
|
|
- context.currentLocalVariableAnnotationRangeEnds[i] =
|
||
|
|
- createLabel(startPc + length, context.currentMethodLabels);
|
||
|
|
- context.currentLocalVariableAnnotationRangeIndices[i] = index;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- case TypeReference.CAST:
|
||
|
|
- case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
|
||
|
|
- case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
|
||
|
|
- targetType &= 0xFF0000FF;
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.CLASS_EXTENDS:
|
||
|
|
- case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
|
||
|
|
- case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
|
||
|
|
- case TypeReference.THROWS:
|
||
|
|
- case TypeReference.EXCEPTION_PARAMETER:
|
||
|
|
- targetType &= 0xFFFFFF00;
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- case TypeReference.INSTANCEOF:
|
||
|
|
- case TypeReference.NEW:
|
||
|
|
- case TypeReference.CONSTRUCTOR_REFERENCE:
|
||
|
|
- case TypeReference.METHOD_REFERENCE:
|
||
|
|
- targetType &= 0xFF000000;
|
||
|
|
- currentOffset += 3;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- context.currentTypeAnnotationTarget = targetType;
|
||
|
|
- // Parse and store the target_path structure.
|
||
|
|
- int pathLength = readByte(currentOffset);
|
||
|
|
- context.currentTypeAnnotationTargetPath =
|
||
|
|
- pathLength == 0 ? null : new TypePath(b, currentOffset);
|
||
|
|
- // Return the start offset of the rest of the type_annotation structure.
|
||
|
|
- return currentOffset + 1 + 2 * pathLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a Runtime[In]VisibleParameterAnnotations attribute and makes the given visitor visit it.
|
||
|
|
- *
|
||
|
|
- * @param methodVisitor the visitor that must visit the parameter annotations.
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- * @param runtimeParameterAnnotationsOffset the start offset of a
|
||
|
|
- * Runtime[In]VisibleParameterAnnotations attribute, excluding the attribute_info's
|
||
|
|
- * attribute_name_index and attribute_length fields.
|
||
|
|
- * @param visible true if the attribute to parse is a RuntimeVisibleParameterAnnotations
|
||
|
|
- * attribute, false it is a RuntimeInvisibleParameterAnnotations attribute.
|
||
|
|
- */
|
||
|
|
- private void readParameterAnnotations(
|
||
|
|
- final MethodVisitor methodVisitor,
|
||
|
|
- final Context context,
|
||
|
|
- final int runtimeParameterAnnotationsOffset,
|
||
|
|
- final boolean visible) {
|
||
|
|
- int currentOffset = runtimeParameterAnnotationsOffset;
|
||
|
|
- int numParameters = b[currentOffset++] & 0xFF;
|
||
|
|
- methodVisitor.visitAnnotableParameterCount(numParameters, visible);
|
||
|
|
- char[] charBuffer = context.charBuffer;
|
||
|
|
- for (int i = 0; i < numParameters; ++i) {
|
||
|
|
- int numAnnotations = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (numAnnotations-- > 0) {
|
||
|
|
- // Parse the type_index field.
|
||
|
|
- String annotationDescriptor = readUTF8(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Parse num_element_value_pairs and element_value_pairs and visit these values.
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible),
|
||
|
|
- currentOffset,
|
||
|
|
- /* named = */ true,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads the element values of a JVMS 'annotation' structure and makes the given visitor visit
|
||
|
|
- * them. This method can also be used to read the values of the JVMS 'array_value' field of an
|
||
|
|
- * annotation's 'element_value'.
|
||
|
|
- *
|
||
|
|
- * @param annotationVisitor the visitor that must visit the values.
|
||
|
|
- * @param annotationOffset the start offset of an 'annotation' structure (excluding its type_index
|
||
|
|
- * field) or of an 'array_value' structure.
|
||
|
|
- * @param named if the annotation values are named or not. This should be true to parse the values
|
||
|
|
- * of a JVMS 'annotation' structure, and false to parse the JVMS 'array_value' of an
|
||
|
|
- * annotation's element_value.
|
||
|
|
- * @param charBuffer the buffer used to read strings in the constant pool.
|
||
|
|
- * @return the end offset of the JVMS 'annotation' or 'array_value' structure.
|
||
|
|
- */
|
||
|
|
- private int readElementValues(
|
||
|
|
- final AnnotationVisitor annotationVisitor,
|
||
|
|
- final int annotationOffset,
|
||
|
|
- final boolean named,
|
||
|
|
- final char[] charBuffer) {
|
||
|
|
- int currentOffset = annotationOffset;
|
||
|
|
- // Read the num_element_value_pairs field (or num_values field for an array_value).
|
||
|
|
- int numElementValuePairs = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- if (named) {
|
||
|
|
- // Parse the element_value_pairs array.
|
||
|
|
- while (numElementValuePairs-- > 0) {
|
||
|
|
- String elementName = readUTF8(currentOffset, charBuffer);
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer);
|
||
|
|
- }
|
||
|
|
- } else {
|
||
|
|
- // Parse the array_value array.
|
||
|
|
- while (numElementValuePairs-- > 0) {
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- if (annotationVisitor != null) {
|
||
|
|
- annotationVisitor.visitEnd();
|
||
|
|
- }
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS 'element_value' structure and makes the given visitor visit it.
|
||
|
|
- *
|
||
|
|
- * @param annotationVisitor the visitor that must visit the element_value structure.
|
||
|
|
- * @param elementValueOffset the start offset in {@link #b} of the element_value structure to be
|
||
|
|
- * read.
|
||
|
|
- * @param elementName the name of the element_value structure to be read, or {@literal null}.
|
||
|
|
- * @param charBuffer the buffer used to read strings in the constant pool.
|
||
|
|
- * @return the end offset of the JVMS 'element_value' structure.
|
||
|
|
- */
|
||
|
|
- private int readElementValue(
|
||
|
|
- final AnnotationVisitor annotationVisitor,
|
||
|
|
- final int elementValueOffset,
|
||
|
|
- final String elementName,
|
||
|
|
- final char[] charBuffer) {
|
||
|
|
- int currentOffset = elementValueOffset;
|
||
|
|
- if (annotationVisitor == null) {
|
||
|
|
- switch (b[currentOffset] & 0xFF) {
|
||
|
|
- case 'e': // enum_const_value
|
||
|
|
- return currentOffset + 5;
|
||
|
|
- case '@': // annotation_value
|
||
|
|
- return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer);
|
||
|
|
- case '[': // array_value
|
||
|
|
- return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer);
|
||
|
|
- default:
|
||
|
|
- return currentOffset + 3;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- switch (b[currentOffset++] & 0xFF) {
|
||
|
|
- case 'B': // const_value_index, CONSTANT_Integer
|
||
|
|
- annotationVisitor.visit(
|
||
|
|
- elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case 'C': // const_value_index, CONSTANT_Integer
|
||
|
|
- annotationVisitor.visit(
|
||
|
|
- elementName, (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case 'D': // const_value_index, CONSTANT_Double
|
||
|
|
- case 'F': // const_value_index, CONSTANT_Float
|
||
|
|
- case 'I': // const_value_index, CONSTANT_Integer
|
||
|
|
- case 'J': // const_value_index, CONSTANT_Long
|
||
|
|
- annotationVisitor.visit(
|
||
|
|
- elementName, readConst(readUnsignedShort(currentOffset), charBuffer));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case 'S': // const_value_index, CONSTANT_Integer
|
||
|
|
- annotationVisitor.visit(
|
||
|
|
- elementName, (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
-
|
||
|
|
- case 'Z': // const_value_index, CONSTANT_Integer
|
||
|
|
- annotationVisitor.visit(
|
||
|
|
- elementName,
|
||
|
|
- readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]) == 0
|
||
|
|
- ? Boolean.FALSE
|
||
|
|
- : Boolean.TRUE);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case 's': // const_value_index, CONSTANT_Utf8
|
||
|
|
- annotationVisitor.visit(elementName, readUTF8(currentOffset, charBuffer));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case 'e': // enum_const_value
|
||
|
|
- annotationVisitor.visitEnum(
|
||
|
|
- elementName,
|
||
|
|
- readUTF8(currentOffset, charBuffer),
|
||
|
|
- readUTF8(currentOffset + 2, charBuffer));
|
||
|
|
- currentOffset += 4;
|
||
|
|
- break;
|
||
|
|
- case 'c': // class_info
|
||
|
|
- annotationVisitor.visit(elementName, Type.getType(readUTF8(currentOffset, charBuffer)));
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case '@': // annotation_value
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- annotationVisitor.visitAnnotation(elementName, readUTF8(currentOffset, charBuffer)),
|
||
|
|
- currentOffset + 2,
|
||
|
|
- true,
|
||
|
|
- charBuffer);
|
||
|
|
- break;
|
||
|
|
- case '[': // array_value
|
||
|
|
- int numValues = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- if (numValues == 0) {
|
||
|
|
- return readElementValues(
|
||
|
|
- annotationVisitor.visitArray(elementName),
|
||
|
|
- currentOffset - 2,
|
||
|
|
- /* named = */ false,
|
||
|
|
- charBuffer);
|
||
|
|
- }
|
||
|
|
- switch (b[currentOffset] & 0xFF) {
|
||
|
|
- case 'B':
|
||
|
|
- byte[] byteValues = new byte[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- byteValues[i] = (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, byteValues);
|
||
|
|
- break;
|
||
|
|
- case 'Z':
|
||
|
|
- boolean[] booleanValues = new boolean[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- booleanValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]) != 0;
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, booleanValues);
|
||
|
|
- break;
|
||
|
|
- case 'S':
|
||
|
|
- short[] shortValues = new short[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- shortValues[i] = (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, shortValues);
|
||
|
|
- break;
|
||
|
|
- case 'C':
|
||
|
|
- char[] charValues = new char[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- charValues[i] = (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, charValues);
|
||
|
|
- break;
|
||
|
|
- case 'I':
|
||
|
|
- int[] intValues = new int[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- intValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, intValues);
|
||
|
|
- break;
|
||
|
|
- case 'J':
|
||
|
|
- long[] longValues = new long[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- longValues[i] = readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]);
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, longValues);
|
||
|
|
- break;
|
||
|
|
- case 'F':
|
||
|
|
- float[] floatValues = new float[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- floatValues[i] =
|
||
|
|
- Float.intBitsToFloat(
|
||
|
|
- readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, floatValues);
|
||
|
|
- break;
|
||
|
|
- case 'D':
|
||
|
|
- double[] doubleValues = new double[numValues];
|
||
|
|
- for (int i = 0; i < numValues; i++) {
|
||
|
|
- doubleValues[i] =
|
||
|
|
- Double.longBitsToDouble(
|
||
|
|
- readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]));
|
||
|
|
- currentOffset += 3;
|
||
|
|
- }
|
||
|
|
- annotationVisitor.visit(elementName, doubleValues);
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- currentOffset =
|
||
|
|
- readElementValues(
|
||
|
|
- annotationVisitor.visitArray(elementName),
|
||
|
|
- currentOffset - 2,
|
||
|
|
- /* named = */ false,
|
||
|
|
- charBuffer);
|
||
|
|
- break;
|
||
|
|
- }
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
- // Methods to parse stack map frames
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Computes the implicit frame of the method currently being parsed (as defined in the given
|
||
|
|
- * {@link Context}) and stores it in the given context.
|
||
|
|
- *
|
||
|
|
- * @param context information about the class being parsed.
|
||
|
|
- */
|
||
|
|
- private void computeImplicitFrame(final Context context) {
|
||
|
|
- String methodDescriptor = context.currentMethodDescriptor;
|
||
|
|
- Object[] locals = context.currentFrameLocalTypes;
|
||
|
|
- int numLocal = 0;
|
||
|
|
- if ((context.currentMethodAccessFlags & Opcodes.ACC_STATIC) == 0) {
|
||
|
|
- if ("<init>".equals(context.currentMethodName)) {
|
||
|
|
- locals[numLocal++] = Opcodes.UNINITIALIZED_THIS;
|
||
|
|
- } else {
|
||
|
|
- locals[numLocal++] = readClass(header + 2, context.charBuffer);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- // Parse the method descriptor, one argument type descriptor at each iteration. Start by
|
||
|
|
- // skipping the first method descriptor character, which is always '('.
|
||
|
|
- int currentMethodDescritorOffset = 1;
|
||
|
|
- while (true) {
|
||
|
|
- int currentArgumentDescriptorStartOffset = currentMethodDescritorOffset;
|
||
|
|
- switch (methodDescriptor.charAt(currentMethodDescritorOffset++)) {
|
||
|
|
- case 'Z':
|
||
|
|
- case 'C':
|
||
|
|
- case 'B':
|
||
|
|
- case 'S':
|
||
|
|
- case 'I':
|
||
|
|
- locals[numLocal++] = Opcodes.INTEGER;
|
||
|
|
- break;
|
||
|
|
- case 'F':
|
||
|
|
- locals[numLocal++] = Opcodes.FLOAT;
|
||
|
|
- break;
|
||
|
|
- case 'J':
|
||
|
|
- locals[numLocal++] = Opcodes.LONG;
|
||
|
|
- break;
|
||
|
|
- case 'D':
|
||
|
|
- locals[numLocal++] = Opcodes.DOUBLE;
|
||
|
|
- break;
|
||
|
|
- case '[':
|
||
|
|
- while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') {
|
||
|
|
- ++currentMethodDescritorOffset;
|
||
|
|
- }
|
||
|
|
- if (methodDescriptor.charAt(currentMethodDescritorOffset) == 'L') {
|
||
|
|
- ++currentMethodDescritorOffset;
|
||
|
|
- while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
|
||
|
|
- ++currentMethodDescritorOffset;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- locals[numLocal++] =
|
||
|
|
- methodDescriptor.substring(
|
||
|
|
- currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset);
|
||
|
|
- break;
|
||
|
|
- case 'L':
|
||
|
|
- while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') {
|
||
|
|
- ++currentMethodDescritorOffset;
|
||
|
|
- }
|
||
|
|
- locals[numLocal++] =
|
||
|
|
- methodDescriptor.substring(
|
||
|
|
- currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++);
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- context.currentFrameLocalCount = numLocal;
|
||
|
|
- return;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS 'stack_map_frame' structure and stores the result in the given {@link Context}
|
||
|
|
- * object. This method can also be used to read a full_frame structure, excluding its frame_type
|
||
|
|
- * field (this is used to parse the legacy StackMap attributes).
|
||
|
|
- *
|
||
|
|
- * @param stackMapFrameOffset the start offset in {@link #b} of the stack_map_frame_value
|
||
|
|
- * structure to be read, or the start offset of a full_frame structure (excluding its
|
||
|
|
- * frame_type field).
|
||
|
|
- * @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame'
|
||
|
|
- * structure without its frame_type field.
|
||
|
|
- * @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}.
|
||
|
|
- * @param context where the parsed stack map frame must be stored.
|
||
|
|
- * @return the end offset of the JVMS 'stack_map_frame' or 'full_frame' structure.
|
||
|
|
- */
|
||
|
|
- private int readStackMapFrame(
|
||
|
|
- final int stackMapFrameOffset,
|
||
|
|
- final boolean compressed,
|
||
|
|
- final boolean expand,
|
||
|
|
- final Context context) {
|
||
|
|
- int currentOffset = stackMapFrameOffset;
|
||
|
|
- final char[] charBuffer = context.charBuffer;
|
||
|
|
- final Label[] labels = context.currentMethodLabels;
|
||
|
|
- int frameType;
|
||
|
|
- if (compressed) {
|
||
|
|
- // Read the frame_type field.
|
||
|
|
- frameType = b[currentOffset++] & 0xFF;
|
||
|
|
- } else {
|
||
|
|
- frameType = Frame.FULL_FRAME;
|
||
|
|
- context.currentFrameOffset = -1;
|
||
|
|
- }
|
||
|
|
- int offsetDelta;
|
||
|
|
- context.currentFrameLocalCountDelta = 0;
|
||
|
|
- if (frameType < Frame.SAME_LOCALS_1_STACK_ITEM_FRAME) {
|
||
|
|
- offsetDelta = frameType;
|
||
|
|
- context.currentFrameType = Opcodes.F_SAME;
|
||
|
|
- context.currentFrameStackCount = 0;
|
||
|
|
- } else if (frameType < Frame.RESERVED) {
|
||
|
|
- offsetDelta = frameType - Frame.SAME_LOCALS_1_STACK_ITEM_FRAME;
|
||
|
|
- currentOffset =
|
||
|
|
- readVerificationTypeInfo(
|
||
|
|
- currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
|
||
|
|
- context.currentFrameType = Opcodes.F_SAME1;
|
||
|
|
- context.currentFrameStackCount = 1;
|
||
|
|
- } else if (frameType >= Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
|
||
|
|
- offsetDelta = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- if (frameType == Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
|
||
|
|
- currentOffset =
|
||
|
|
- readVerificationTypeInfo(
|
||
|
|
- currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels);
|
||
|
|
- context.currentFrameType = Opcodes.F_SAME1;
|
||
|
|
- context.currentFrameStackCount = 1;
|
||
|
|
- } else if (frameType >= Frame.CHOP_FRAME && frameType < Frame.SAME_FRAME_EXTENDED) {
|
||
|
|
- context.currentFrameType = Opcodes.F_CHOP;
|
||
|
|
- context.currentFrameLocalCountDelta = Frame.SAME_FRAME_EXTENDED - frameType;
|
||
|
|
- context.currentFrameLocalCount -= context.currentFrameLocalCountDelta;
|
||
|
|
- context.currentFrameStackCount = 0;
|
||
|
|
- } else if (frameType == Frame.SAME_FRAME_EXTENDED) {
|
||
|
|
- context.currentFrameType = Opcodes.F_SAME;
|
||
|
|
- context.currentFrameStackCount = 0;
|
||
|
|
- } else if (frameType < Frame.FULL_FRAME) {
|
||
|
|
- int local = expand ? context.currentFrameLocalCount : 0;
|
||
|
|
- for (int k = frameType - Frame.SAME_FRAME_EXTENDED; k > 0; k--) {
|
||
|
|
- currentOffset =
|
||
|
|
- readVerificationTypeInfo(
|
||
|
|
- currentOffset, context.currentFrameLocalTypes, local++, charBuffer, labels);
|
||
|
|
- }
|
||
|
|
- context.currentFrameType = Opcodes.F_APPEND;
|
||
|
|
- context.currentFrameLocalCountDelta = frameType - Frame.SAME_FRAME_EXTENDED;
|
||
|
|
- context.currentFrameLocalCount += context.currentFrameLocalCountDelta;
|
||
|
|
- context.currentFrameStackCount = 0;
|
||
|
|
- } else {
|
||
|
|
- final int numberOfLocals = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- context.currentFrameType = Opcodes.F_FULL;
|
||
|
|
- context.currentFrameLocalCountDelta = numberOfLocals;
|
||
|
|
- context.currentFrameLocalCount = numberOfLocals;
|
||
|
|
- for (int local = 0; local < numberOfLocals; ++local) {
|
||
|
|
- currentOffset =
|
||
|
|
- readVerificationTypeInfo(
|
||
|
|
- currentOffset, context.currentFrameLocalTypes, local, charBuffer, labels);
|
||
|
|
- }
|
||
|
|
- final int numberOfStackItems = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- context.currentFrameStackCount = numberOfStackItems;
|
||
|
|
- for (int stack = 0; stack < numberOfStackItems; ++stack) {
|
||
|
|
- currentOffset =
|
||
|
|
- readVerificationTypeInfo(
|
||
|
|
- currentOffset, context.currentFrameStackTypes, stack, charBuffer, labels);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- } else {
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- context.currentFrameOffset += offsetDelta + 1;
|
||
|
|
- createLabel(context.currentFrameOffset, labels);
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a JVMS 'verification_type_info' structure and stores it at the given index in the given
|
||
|
|
- * array.
|
||
|
|
- *
|
||
|
|
- * @param verificationTypeInfoOffset the start offset of the 'verification_type_info' structure to
|
||
|
|
- * read.
|
||
|
|
- * @param frame the array where the parsed type must be stored.
|
||
|
|
- * @param index the index in 'frame' where the parsed type must be stored.
|
||
|
|
- * @param charBuffer the buffer used to read strings in the constant pool.
|
||
|
|
- * @param labels the labels of the method currently being parsed, indexed by their offset. If the
|
||
|
|
- * parsed type is an ITEM_Uninitialized, a new label for the corresponding NEW instruction is
|
||
|
|
- * stored in this array if it does not already exist.
|
||
|
|
- * @return the end offset of the JVMS 'verification_type_info' structure.
|
||
|
|
- */
|
||
|
|
- private int readVerificationTypeInfo(
|
||
|
|
- final int verificationTypeInfoOffset,
|
||
|
|
- final Object[] frame,
|
||
|
|
- final int index,
|
||
|
|
- final char[] charBuffer,
|
||
|
|
- final Label[] labels) {
|
||
|
|
- int currentOffset = verificationTypeInfoOffset;
|
||
|
|
- int tag = b[currentOffset++] & 0xFF;
|
||
|
|
- switch (tag) {
|
||
|
|
- case Frame.ITEM_TOP:
|
||
|
|
- frame[index] = Opcodes.TOP;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_INTEGER:
|
||
|
|
- frame[index] = Opcodes.INTEGER;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_FLOAT:
|
||
|
|
- frame[index] = Opcodes.FLOAT;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_DOUBLE:
|
||
|
|
- frame[index] = Opcodes.DOUBLE;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_LONG:
|
||
|
|
- frame[index] = Opcodes.LONG;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_NULL:
|
||
|
|
- frame[index] = Opcodes.NULL;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_UNINITIALIZED_THIS:
|
||
|
|
- frame[index] = Opcodes.UNINITIALIZED_THIS;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_OBJECT:
|
||
|
|
- frame[index] = readClass(currentOffset, charBuffer);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- case Frame.ITEM_UNINITIALIZED:
|
||
|
|
- frame[index] = createLabel(readUnsignedShort(currentOffset), labels);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- return currentOffset;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
- // Methods to parse attributes
|
||
|
|
- // ----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
|
||
|
|
- *
|
||
|
|
- * @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry.
|
||
|
|
- */
|
||
|
|
- final int getFirstAttributeOffset() {
|
||
|
|
- // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes
|
||
|
|
- // each), as well as the interfaces array field (2 bytes per interface).
|
||
|
|
- int currentOffset = header + 8 + readUnsignedShort(header + 6) * 2;
|
||
|
|
-
|
||
|
|
- // Read the fields_count field.
|
||
|
|
- int fieldsCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- // Skip the 'fields' array field.
|
||
|
|
- while (fieldsCount-- > 0) {
|
||
|
|
- // Invariant: currentOffset is the offset of a field_info structure.
|
||
|
|
- // Skip the access_flags, name_index and descriptor_index fields (2 bytes each), and read the
|
||
|
|
- // attributes_count field.
|
||
|
|
- int attributesCount = readUnsignedShort(currentOffset + 6);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- // Skip the 'attributes' array field.
|
||
|
|
- while (attributesCount-- > 0) {
|
||
|
|
- // Invariant: currentOffset is the offset of an attribute_info structure.
|
||
|
|
- // Read the attribute_length field (2 bytes after the start of the attribute_info) and skip
|
||
|
|
- // this many bytes, plus 6 for the attribute_name_index and attribute_length fields
|
||
|
|
- // (yielding the total size of the attribute_info structure).
|
||
|
|
- currentOffset += 6 + readInt(currentOffset + 2);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Skip the methods_count and 'methods' fields, using the same method as above.
|
||
|
|
- int methodsCount = readUnsignedShort(currentOffset);
|
||
|
|
- currentOffset += 2;
|
||
|
|
- while (methodsCount-- > 0) {
|
||
|
|
- int attributesCount = readUnsignedShort(currentOffset + 6);
|
||
|
|
- currentOffset += 8;
|
||
|
|
- while (attributesCount-- > 0) {
|
||
|
|
- currentOffset += 6 + readInt(currentOffset + 2);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Skip the ClassFile's attributes_count field.
|
||
|
|
- return currentOffset + 2;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads the BootstrapMethods attribute to compute the offset of each bootstrap method.
|
||
|
|
- *
|
||
|
|
- * @param maxStringLength a conservative estimate of the maximum length of the strings contained
|
||
|
|
- * in the constant pool of the class.
|
||
|
|
- * @return the offsets of the bootstrap methods or null.
|
||
|
|
- */
|
||
|
|
- private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
|
||
|
|
- char[] charBuffer = new char[maxStringLength];
|
||
|
|
- int currentAttributeOffset = getFirstAttributeOffset();
|
||
|
|
- int[] currentBootstrapMethodOffsets = null;
|
||
|
|
- for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
|
||
|
|
- // Read the attribute_info's attribute_name and attribute_length fields.
|
||
|
|
- String attributeName = readUTF8(currentAttributeOffset, charBuffer);
|
||
|
|
- int attributeLength = readInt(currentAttributeOffset + 2);
|
||
|
|
- currentAttributeOffset += 6;
|
||
|
|
- if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
|
||
|
|
- // Read the num_bootstrap_methods field and create an array of this size.
|
||
|
|
- currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
|
||
|
|
- // Compute and store the offset of each 'bootstrap_methods' array field entry.
|
||
|
|
- int currentBootstrapMethodOffset = currentAttributeOffset + 2;
|
||
|
|
- for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
|
||
|
|
- currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
|
||
|
|
- // Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
|
||
|
|
- // as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
|
||
|
|
- currentBootstrapMethodOffset +=
|
||
|
|
- 4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
|
||
|
|
- }
|
||
|
|
- return currentBootstrapMethodOffsets;
|
||
|
|
- }
|
||
|
|
- currentAttributeOffset += attributeLength;
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a non standard JVMS 'attribute' structure in {@link #b}.
|
||
|
|
- *
|
||
|
|
- * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
|
||
|
|
- * the class. Any attribute whose type is not equal to the type of one the prototypes will not
|
||
|
|
- * be parsed: its byte array value will be passed unchanged to the ClassWriter.
|
||
|
|
- * @param type the type of the attribute.
|
||
|
|
- * @param offset the start offset of the JVMS 'attribute' structure in {@link #b}. The 6 attribute
|
||
|
|
- * header bytes (attribute_name_index and attribute_length) are not taken into account here.
|
||
|
|
- * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
|
||
|
|
- * @param charBuffer the buffer to be used to read strings in the constant pool.
|
||
|
|
- * @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link #b}, or
|
||
|
|
- * -1 if the attribute to be read is not a code attribute. The 6 attribute header bytes
|
||
|
|
- * (attribute_name_index and attribute_length) are not taken into account here.
|
||
|
|
- * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
|
||
|
|
- * is not a code attribute.
|
||
|
|
- * @return the attribute that has been read.
|
||
|
|
- */
|
||
|
|
- private Attribute readAttribute(
|
||
|
|
- final Attribute[] attributePrototypes,
|
||
|
|
- final String type,
|
||
|
|
- final int offset,
|
||
|
|
- final int length,
|
||
|
|
- final char[] charBuffer,
|
||
|
|
- final int codeAttributeOffset,
|
||
|
|
- final Label[] labels) {
|
||
|
|
- for (Attribute attributePrototype : attributePrototypes) {
|
||
|
|
- if (attributePrototype.type.equals(type)) {
|
||
|
|
- return attributePrototype.read(
|
||
|
|
- this, offset, length, charBuffer, codeAttributeOffset, labels);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- return new Attribute(type).read(this, offset, length, null, -1, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Utility methods: low level parsing
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the number of entries in the class's constant pool table.
|
||
|
|
- *
|
||
|
|
- * @return the number of entries in the class's constant pool table.
|
||
|
|
- */
|
||
|
|
- public int getItemCount() {
|
||
|
|
- return cpInfoOffsets.length;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the start offset in {@link #b} of a JVMS 'cp_info' structure (i.e. a constant pool
|
||
|
|
- * entry), plus one. <i>This method is intended for {@link Attribute} sub classes, and is normally
|
||
|
|
- * not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool
|
||
|
|
- * table.
|
||
|
|
- * @return the start offset in {@link #b} of the corresponding JVMS 'cp_info' structure, plus one.
|
||
|
|
- */
|
||
|
|
- public int getItem(final int constantPoolEntryIndex) {
|
||
|
|
- return cpInfoOffsets[constantPoolEntryIndex];
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns a conservative estimate of the maximum length of the strings contained in the class's
|
||
|
|
- * constant pool table.
|
||
|
|
- *
|
||
|
|
- * @return a conservative estimate of the maximum length of the strings contained in the class's
|
||
|
|
- * constant pool table.
|
||
|
|
- */
|
||
|
|
- public int getMaxStringLength() {
|
||
|
|
- return maxStringLength;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a byte value in {@link #b}. <i>This method is intended for {@link Attribute} sub classes,
|
||
|
|
- * and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of the value to be read in {@link #b}.
|
||
|
|
- * @return the read value.
|
||
|
|
- */
|
||
|
|
- public int readByte(final int offset) {
|
||
|
|
- return b[offset] & 0xFF;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads an unsigned short value in {@link #b}. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start index of the value to be read in {@link #b}.
|
||
|
|
- * @return the read value.
|
||
|
|
- */
|
||
|
|
- public int readUnsignedShort(final int offset) {
|
||
|
|
- byte[] classFileBuffer = b;
|
||
|
|
- return ((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a signed short value in {@link #b}. <i>This method is intended for {@link Attribute} sub
|
||
|
|
- * classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of the value to be read in {@link #b}.
|
||
|
|
- * @return the read value.
|
||
|
|
- */
|
||
|
|
- public short readShort(final int offset) {
|
||
|
|
- byte[] classFileBuffer = b;
|
||
|
|
- return (short) (((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a signed int value in {@link #b}. <i>This method is intended for {@link Attribute} sub
|
||
|
|
- * classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of the value to be read in {@link #b}.
|
||
|
|
- * @return the read value.
|
||
|
|
- */
|
||
|
|
- public int readInt(final int offset) {
|
||
|
|
- byte[] classFileBuffer = b;
|
||
|
|
- return ((classFileBuffer[offset] & 0xFF) << 24)
|
||
|
|
- | ((classFileBuffer[offset + 1] & 0xFF) << 16)
|
||
|
|
- | ((classFileBuffer[offset + 2] & 0xFF) << 8)
|
||
|
|
- | (classFileBuffer[offset + 3] & 0xFF);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a signed long value in {@link #b}. <i>This method is intended for {@link Attribute} sub
|
||
|
|
- * classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of the value to be read in {@link #b}.
|
||
|
|
- * @return the read value.
|
||
|
|
- */
|
||
|
|
- public long readLong(final int offset) {
|
||
|
|
- long l1 = readInt(offset);
|
||
|
|
- long l0 = readInt(offset + 4) & 0xFFFFFFFFL;
|
||
|
|
- return (l1 << 32) | l0;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
|
||
|
|
- * index of a CONSTANT_Utf8 entry in the class's constant pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
||
|
|
- */
|
||
|
|
- // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||
|
|
- public String readUTF8(final int offset, final char[] charBuffer) {
|
||
|
|
- int constantPoolEntryIndex = readUnsignedShort(offset);
|
||
|
|
- if (offset == 0 || constantPoolEntryIndex == 0) {
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
- return readUtf(constantPoolEntryIndex, charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}.
|
||
|
|
- *
|
||
|
|
- * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool
|
||
|
|
- * table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified CONSTANT_Utf8 entry.
|
||
|
|
- */
|
||
|
|
- final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) {
|
||
|
|
- String value = constantUtf8Values[constantPoolEntryIndex];
|
||
|
|
- if (value != null) {
|
||
|
|
- return value;
|
||
|
|
- }
|
||
|
|
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
|
||
|
|
- return constantUtf8Values[constantPoolEntryIndex] =
|
||
|
|
- readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads an UTF8 string in {@link #b}.
|
||
|
|
- *
|
||
|
|
- * @param utfOffset the start offset of the UTF8 string to be read.
|
||
|
|
- * @param utfLength the length of the UTF8 string to be read.
|
||
|
|
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified UTF8 string.
|
||
|
|
- */
|
||
|
|
- private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) {
|
||
|
|
- int currentOffset = utfOffset;
|
||
|
|
- int endOffset = currentOffset + utfLength;
|
||
|
|
- int strLength = 0;
|
||
|
|
- byte[] classFileBuffer = b;
|
||
|
|
- while (currentOffset < endOffset) {
|
||
|
|
- int currentByte = classFileBuffer[currentOffset++];
|
||
|
|
- if ((currentByte & 0x80) == 0) {
|
||
|
|
- charBuffer[strLength++] = (char) (currentByte & 0x7F);
|
||
|
|
- } else if ((currentByte & 0xE0) == 0xC0) {
|
||
|
|
- charBuffer[strLength++] =
|
||
|
|
- (char) (((currentByte & 0x1F) << 6) + (classFileBuffer[currentOffset++] & 0x3F));
|
||
|
|
- } else {
|
||
|
|
- charBuffer[strLength++] =
|
||
|
|
- (char)
|
||
|
|
- (((currentByte & 0xF) << 12)
|
||
|
|
- + ((classFileBuffer[currentOffset++] & 0x3F) << 6)
|
||
|
|
- + (classFileBuffer[currentOffset++] & 0x3F));
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- return new String(charBuffer, 0, strLength);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
|
||
|
|
- * CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
|
||
|
|
- * index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or
|
||
|
|
- * CONSTANT_Package entry in class's constant pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified constant pool entry.
|
||
|
|
- */
|
||
|
|
- private String readStringish(final int offset, final char[] charBuffer) {
|
||
|
|
- // Get the start offset of the cp_info structure (plus one), and read the CONSTANT_Utf8 entry
|
||
|
|
- // designated by the first two bytes of this cp_info.
|
||
|
|
- return readUTF8(cpInfoOffsets[readUnsignedShort(offset)], charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Class constant pool entry in {@link #b}. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
|
||
|
|
- * index of a CONSTANT_Class entry in class's constant pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified CONSTANT_Class entry.
|
||
|
|
- */
|
||
|
|
- public String readClass(final int offset, final char[] charBuffer) {
|
||
|
|
- return readStringish(offset, charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Module constant pool entry in {@link #b}. <i>This method is intended for
|
||
|
|
- * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
|
||
|
|
- * index of a CONSTANT_Module entry in class's constant pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified CONSTANT_Module entry.
|
||
|
|
- */
|
||
|
|
- public String readModule(final int offset, final char[] charBuffer) {
|
||
|
|
- return readStringish(offset, charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Package constant pool entry in {@link #b}. <i>This method is intended for
|
||
|
|
- * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the
|
||
|
|
- * index of a CONSTANT_Package entry in class's constant pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the String corresponding to the specified CONSTANT_Package entry.
|
||
|
|
- */
|
||
|
|
- public String readPackage(final int offset, final char[] charBuffer) {
|
||
|
|
- return readStringish(offset, charBuffer);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a CONSTANT_Dynamic constant pool entry in {@link #b}.
|
||
|
|
- *
|
||
|
|
- * @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant
|
||
|
|
- * pool table.
|
||
|
|
- * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the ConstantDynamic corresponding to the specified CONSTANT_Dynamic entry.
|
||
|
|
- */
|
||
|
|
- private ConstantDynamic readConstantDynamic(
|
||
|
|
- final int constantPoolEntryIndex, final char[] charBuffer) {
|
||
|
|
- ConstantDynamic constantDynamic = constantDynamicValues[constantPoolEntryIndex];
|
||
|
|
- if (constantDynamic != null) {
|
||
|
|
- return constantDynamic;
|
||
|
|
- }
|
||
|
|
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
|
||
|
|
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
|
||
|
|
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
|
||
|
|
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
|
||
|
|
- int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
|
||
|
|
- Handle handle = (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
|
||
|
|
- Object[] bootstrapMethodArguments = new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
|
||
|
|
- bootstrapMethodOffset += 4;
|
||
|
|
- for (int i = 0; i < bootstrapMethodArguments.length; i++) {
|
||
|
|
- bootstrapMethodArguments[i] = readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
|
||
|
|
- bootstrapMethodOffset += 2;
|
||
|
|
- }
|
||
|
|
- return constantDynamicValues[constantPoolEntryIndex] =
|
||
|
|
- new ConstantDynamic(name, descriptor, handle, bootstrapMethodArguments);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Reads a numeric or string constant pool entry in {@link #b}. <i>This method is intended for
|
||
|
|
- * {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
|
||
|
|
- * CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType,
|
||
|
|
- * CONSTANT_MethodHandle or CONSTANT_Dynamic entry in the class's constant pool.
|
||
|
|
- * @param charBuffer the buffer to be used to read strings. This buffer must be sufficiently
|
||
|
|
- * large. It is not automatically resized.
|
||
|
|
- * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String},
|
||
|
|
- * {@link Type}, {@link Handle} or {@link ConstantDynamic} corresponding to the specified
|
||
|
|
- * constant pool entry.
|
||
|
|
- */
|
||
|
|
- public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) {
|
||
|
|
- int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex];
|
||
|
|
- switch (b[cpInfoOffset - 1]) {
|
||
|
|
- case Symbol.CONSTANT_INTEGER_TAG:
|
||
|
|
- return readInt(cpInfoOffset);
|
||
|
|
- case Symbol.CONSTANT_FLOAT_TAG:
|
||
|
|
- return Float.intBitsToFloat(readInt(cpInfoOffset));
|
||
|
|
- case Symbol.CONSTANT_LONG_TAG:
|
||
|
|
- return readLong(cpInfoOffset);
|
||
|
|
- case Symbol.CONSTANT_DOUBLE_TAG:
|
||
|
|
- return Double.longBitsToDouble(readLong(cpInfoOffset));
|
||
|
|
- case Symbol.CONSTANT_CLASS_TAG:
|
||
|
|
- return Type.getObjectType(readUTF8(cpInfoOffset, charBuffer));
|
||
|
|
- case Symbol.CONSTANT_STRING_TAG:
|
||
|
|
- return readUTF8(cpInfoOffset, charBuffer);
|
||
|
|
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
|
||
|
|
- return Type.getMethodType(readUTF8(cpInfoOffset, charBuffer));
|
||
|
|
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
||
|
|
- int referenceKind = readByte(cpInfoOffset);
|
||
|
|
- int referenceCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 1)];
|
||
|
|
- int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(referenceCpInfoOffset + 2)];
|
||
|
|
- String owner = readClass(referenceCpInfoOffset, charBuffer);
|
||
|
|
- String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
|
||
|
|
- String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
|
||
|
|
- boolean isInterface =
|
||
|
|
- b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
|
||
|
|
- return new Handle(referenceKind, owner, name, descriptor, isInterface);
|
||
|
|
- case Symbol.CONSTANT_DYNAMIC_TAG:
|
||
|
|
- return readConstantDynamic(constantPoolEntryIndex, charBuffer);
|
||
|
|
- default:
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/ClassTooLargeException.java b/src/main/java/org/mvel2/asm/ClassTooLargeException.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index bc61a5d..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/ClassTooLargeException.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,71 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * Exception thrown when the constant pool of a class produced by a {@link ClassWriter} is too
|
||
|
|
- * large.
|
||
|
|
- *
|
||
|
|
- * @author Jason Zaugg
|
||
|
|
- */
|
||
|
|
-public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||
|
|
- private static final long serialVersionUID = 160715609518896765L;
|
||
|
|
-
|
||
|
|
- private final String className;
|
||
|
|
- private final int constantPoolCount;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassTooLargeException}.
|
||
|
|
- *
|
||
|
|
- * @param className the internal name of the class.
|
||
|
|
- * @param constantPoolCount the number of constant pool items of the class.
|
||
|
|
- */
|
||
|
|
- public ClassTooLargeException(final String className, final int constantPoolCount) {
|
||
|
|
- super("Class too large: " + className);
|
||
|
|
- this.className = className;
|
||
|
|
- this.constantPoolCount = constantPoolCount;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the internal name of the class.
|
||
|
|
- *
|
||
|
|
- * @return the internal name of the class.
|
||
|
|
- */
|
||
|
|
- public String getClassName() {
|
||
|
|
- return className;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the number of constant pool items of the class.
|
||
|
|
- *
|
||
|
|
- * @return the number of constant pool items of the class.
|
||
|
|
- */
|
||
|
|
- public int getConstantPoolCount() {
|
||
|
|
- return constantPoolCount;
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/ClassVisitor.java b/src/main/java/org/mvel2/asm/ClassVisitor.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index f511dd9..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/ClassVisitor.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,329 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A visitor to visit a Java class. The methods of this class must be called in the following order:
|
||
|
|
- * {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code
|
||
|
|
- * visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code
|
||
|
|
- * visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} |
|
||
|
|
- * {@code visitMethod} )* {@code visitEnd}.
|
||
|
|
- *
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- */
|
||
|
|
-public abstract class ClassVisitor {
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- */
|
||
|
|
- protected final int api;
|
||
|
|
-
|
||
|
|
- /** The class visitor to which this visitor must delegate method calls. May be null. */
|
||
|
|
- protected ClassVisitor cv;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassVisitor}.
|
||
|
|
- *
|
||
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- */
|
||
|
|
- public ClassVisitor(final int api) {
|
||
|
|
- this(api, null);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassVisitor}.
|
||
|
|
- *
|
||
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||
|
|
- * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||
|
|
- * @param classVisitor the class visitor to which this visitor must delegate method calls. May be
|
||
|
|
- * null.
|
||
|
|
- */
|
||
|
|
- public ClassVisitor(final int api, final ClassVisitor classVisitor) {
|
||
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
||
|
|
- throw new IllegalArgumentException();
|
||
|
|
- }
|
||
|
|
- this.api = api;
|
||
|
|
- this.cv = classVisitor;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits the header of the class.
|
||
|
|
- *
|
||
|
|
- * @param version the class version. The minor version is stored in the 16 most significant bits,
|
||
|
|
- * and the major version in the 16 least significant bits.
|
||
|
|
- * @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if
|
||
|
|
- * the class is deprecated.
|
||
|
|
- * @param name the internal name of the class (see {@link Type#getInternalName()}).
|
||
|
|
- * @param signature the signature of this class. May be {@literal null} if the class is not a
|
||
|
|
- * generic one, and does not extend or implement generic classes or interfaces.
|
||
|
|
- * @param superName the internal of name of the super class (see {@link Type#getInternalName()}).
|
||
|
|
- * For interfaces, the super class is {@link Object}. May be {@literal null}, but only for the
|
||
|
|
- * {@link Object} class.
|
||
|
|
- * @param interfaces the internal names of the class's interfaces (see {@link
|
||
|
|
- * Type#getInternalName()}). May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- public void visit(
|
||
|
|
- final int version,
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String signature,
|
||
|
|
- final String superName,
|
||
|
|
- final String[] interfaces) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visit(version, access, name, signature, superName, interfaces);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits the source of the class.
|
||
|
|
- *
|
||
|
|
- * @param source the name of the source file from which the class was compiled. May be {@literal
|
||
|
|
- * null}.
|
||
|
|
- * @param debug additional debug information to compute the correspondence between source and
|
||
|
|
- * compiled elements of the class. May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- public void visitSource(final String source, final String debug) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitSource(source, debug);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visit the module corresponding to the class.
|
||
|
|
- *
|
||
|
|
- * @param name the fully qualified name (using dots) of the module.
|
||
|
|
- * @param access the module access flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} and {@code
|
||
|
|
- * ACC_MANDATED}.
|
||
|
|
- * @param version the module version, or {@literal null}.
|
||
|
|
- * @return a visitor to visit the module values, or {@literal null} if this visitor is not
|
||
|
|
- * interested in visiting this module.
|
||
|
|
- */
|
||
|
|
- public ModuleVisitor visitModule(final String name, final int access, final String version) {
|
||
|
|
- if (api < Opcodes.ASM6) {
|
||
|
|
- throw new UnsupportedOperationException("This feature requires ASM6");
|
||
|
|
- }
|
||
|
|
- if (cv != null) {
|
||
|
|
- return cv.visitModule(name, access, version);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits the nest host class of the class. A nest is a set of classes of the same package that
|
||
|
|
- * share access to their private members. One of these classes, called the host, lists the other
|
||
|
|
- * members of the nest, which in turn should link to the host of their nest. This method must be
|
||
|
|
- * called only once and only if the visited class is a non-host member of a nest. A class is
|
||
|
|
- * implicitly its own nest, so it's invalid to call this method with the visited class name as
|
||
|
|
- * argument.
|
||
|
|
- *
|
||
|
|
- * @param nestHost the internal name of the host class of the nest.
|
||
|
|
- */
|
||
|
|
- public void visitNestHost(final String nestHost) {
|
||
|
|
- if (api < Opcodes.ASM7) {
|
||
|
|
- throw new UnsupportedOperationException("This feature requires ASM7");
|
||
|
|
- }
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitNestHost(nestHost);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits the enclosing class of the class. This method must be called only if the class has an
|
||
|
|
- * enclosing class.
|
||
|
|
- *
|
||
|
|
- * @param owner internal name of the enclosing class of the class.
|
||
|
|
- * @param name the name of the method that contains the class, or {@literal null} if the class is
|
||
|
|
- * not enclosed in a method of its enclosing class.
|
||
|
|
- * @param descriptor the descriptor of the method that contains the class, or {@literal null} if
|
||
|
|
- * the class is not enclosed in a method of its enclosing class.
|
||
|
|
- */
|
||
|
|
- public void visitOuterClass(final String owner, final String name, final String descriptor) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitOuterClass(owner, name, descriptor);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits an annotation of the class.
|
||
|
|
- *
|
||
|
|
- * @param descriptor the class descriptor of the annotation class.
|
||
|
|
- * @param visible {@literal true} if the annotation is visible at runtime.
|
||
|
|
- * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||
|
|
- * interested in visiting this annotation.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- return cv.visitAnnotation(descriptor, visible);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits an annotation on a type in the class signature.
|
||
|
|
- *
|
||
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
||
|
|
- * {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
|
||
|
|
- * TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
|
||
|
|
- * {@link TypeReference}.
|
||
|
|
- * @param typePath the path to the annotated type argument, wildcard bound, array element type, or
|
||
|
|
- * static inner type within 'typeRef'. May be {@literal null} if the annotation targets
|
||
|
|
- * 'typeRef' as a whole.
|
||
|
|
- * @param descriptor the class descriptor of the annotation class.
|
||
|
|
- * @param visible {@literal true} if the annotation is visible at runtime.
|
||
|
|
- * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||
|
|
- * interested in visiting this annotation.
|
||
|
|
- */
|
||
|
|
- public AnnotationVisitor visitTypeAnnotation(
|
||
|
|
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||
|
|
- if (api < Opcodes.ASM5) {
|
||
|
|
- throw new UnsupportedOperationException("This feature requires ASM5");
|
||
|
|
- }
|
||
|
|
- if (cv != null) {
|
||
|
|
- return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a non standard attribute of the class.
|
||
|
|
- *
|
||
|
|
- * @param attribute an attribute.
|
||
|
|
- */
|
||
|
|
- public void visitAttribute(final Attribute attribute) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitAttribute(attribute);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a member of the nest. A nest is a set of classes of the same package that share access
|
||
|
|
- * to their private members. One of these classes, called the host, lists the other members of the
|
||
|
|
- * nest, which in turn should link to the host of their nest. This method must be called only if
|
||
|
|
- * the visited class is the host of a nest. A nest host is implicitly a member of its own nest, so
|
||
|
|
- * it's invalid to call this method with the visited class name as argument.
|
||
|
|
- *
|
||
|
|
- * @param nestMember the internal name of a nest member.
|
||
|
|
- */
|
||
|
|
- public void visitNestMember(final String nestMember) {
|
||
|
|
- if (api < Opcodes.ASM7) {
|
||
|
|
- throw new UnsupportedOperationException("This feature requires ASM7");
|
||
|
|
- }
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitNestMember(nestMember);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits information about an inner class. This inner class is not necessarily a member of the
|
||
|
|
- * class being visited.
|
||
|
|
- *
|
||
|
|
- * @param name the internal name of an inner class (see {@link Type#getInternalName()}).
|
||
|
|
- * @param outerName the internal name of the class to which the inner class belongs (see {@link
|
||
|
|
- * Type#getInternalName()}). May be {@literal null} for not member classes.
|
||
|
|
- * @param innerName the (simple) name of the inner class inside its enclosing class. May be
|
||
|
|
- * {@literal null} for anonymous inner classes.
|
||
|
|
- * @param access the access flags of the inner class as originally declared in the enclosing
|
||
|
|
- * class.
|
||
|
|
- */
|
||
|
|
- public void visitInnerClass(
|
||
|
|
- final String name, final String outerName, final String innerName, final int access) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitInnerClass(name, outerName, innerName, access);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a field of the class.
|
||
|
|
- *
|
||
|
|
- * @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
|
||
|
|
- * the field is synthetic and/or deprecated.
|
||
|
|
- * @param name the field's name.
|
||
|
|
- * @param descriptor the field's descriptor (see {@link Type}).
|
||
|
|
- * @param signature the field's signature. May be {@literal null} if the field's type does not use
|
||
|
|
- * generic types.
|
||
|
|
- * @param value the field's initial value. This parameter, which may be {@literal null} if the
|
||
|
|
- * field does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link
|
||
|
|
- * Long}, a {@link Double} or a {@link String} (for {@code int}, {@code float}, {@code long}
|
||
|
|
- * or {@code String} fields respectively). <i>This parameter is only used for static
|
||
|
|
- * fields</i>. Its value is ignored for non static fields, which must be initialized through
|
||
|
|
- * bytecode instructions in constructors or methods.
|
||
|
|
- * @return a visitor to visit field annotations and attributes, or {@literal null} if this class
|
||
|
|
- * visitor is not interested in visiting these annotations and attributes.
|
||
|
|
- */
|
||
|
|
- public FieldVisitor visitField(
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final String signature,
|
||
|
|
- final Object value) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- return cv.visitField(access, name, descriptor, signature, value);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits a method of the class. This method <i>must</i> return a new {@link MethodVisitor}
|
||
|
|
- * instance (or {@literal null}) each time it is called, i.e., it should not return a previously
|
||
|
|
- * returned visitor.
|
||
|
|
- *
|
||
|
|
- * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
|
||
|
|
- * the method is synthetic and/or deprecated.
|
||
|
|
- * @param name the method's name.
|
||
|
|
- * @param descriptor the method's descriptor (see {@link Type}).
|
||
|
|
- * @param signature the method's signature. May be {@literal null} if the method parameters,
|
||
|
|
- * return type and exceptions do not use generic types.
|
||
|
|
- * @param exceptions the internal names of the method's exception classes (see {@link
|
||
|
|
- * Type#getInternalName()}). May be {@literal null}.
|
||
|
|
- * @return an object to visit the byte code of the method, or {@literal null} if this class
|
||
|
|
- * visitor is not interested in visiting the code of this method.
|
||
|
|
- */
|
||
|
|
- public MethodVisitor visitMethod(
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final String signature,
|
||
|
|
- final String[] exceptions) {
|
||
|
|
- if (cv != null) {
|
||
|
|
- return cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||
|
|
- }
|
||
|
|
- return null;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Visits the end of the class. This method, which is the last one to be called, is used to inform
|
||
|
|
- * the visitor that all the fields and methods of the class have been visited.
|
||
|
|
- */
|
||
|
|
- public void visitEnd() {
|
||
|
|
- if (cv != null) {
|
||
|
|
- cv.visitEnd();
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
diff --git a/src/main/java/org/mvel2/asm/ClassWriter.java b/src/main/java/org/mvel2/asm/ClassWriter.java
|
||
|
|
deleted file mode 100644
|
||
|
|
index 98a6e31..0000000
|
||
|
|
--- a/src/main/java/org/mvel2/asm/ClassWriter.java
|
||
|
|
+++ /dev/null
|
||
|
|
@@ -1,985 +0,0 @@
|
||
|
|
-// 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 org.mvel2.asm;
|
||
|
|
-
|
||
|
|
-/**
|
||
|
|
- * A {@link ClassVisitor} that generates a corresponding ClassFile structure, as defined in the Java
|
||
|
|
- * Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from
|
||
|
|
- * scratch", or with one or more {@link ClassReader} and adapter {@link ClassVisitor} to generate a
|
||
|
|
- * modified class from one or more existing Java classes.
|
||
|
|
- *
|
||
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html">JVMS 4</a>
|
||
|
|
- * @author Eric Bruneton
|
||
|
|
- */
|
||
|
|
-public class ClassWriter extends ClassVisitor {
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to automatically compute the maximum stack size and the maximum number of local
|
||
|
|
- * variables of methods. If this flag is set, then the arguments of the {@link
|
||
|
|
- * MethodVisitor#visitMaxs} method of the {@link MethodVisitor} returned by the {@link
|
||
|
|
- * #visitMethod} method will be ignored, and computed automatically from the signature and the
|
||
|
|
- * bytecode of each method.
|
||
|
|
- *
|
||
|
|
- * <p><b>Note:</b> for classes whose version is {@link Opcodes#V1_7} of more, this option requires
|
||
|
|
- * valid stack map frames. The maximum stack size is then computed from these frames, and from the
|
||
|
|
- * bytecode instructions in between. If stack map frames are not present or must be recomputed,
|
||
|
|
- * used {@link #COMPUTE_FRAMES} instead.
|
||
|
|
- *
|
||
|
|
- * @see #ClassWriter(int)
|
||
|
|
- */
|
||
|
|
- public static final int COMPUTE_MAXS = 1;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * A flag to automatically compute the stack map frames of methods from scratch. If this flag is
|
||
|
|
- * set, then the calls to the {@link MethodVisitor#visitFrame} method are ignored, and the stack
|
||
|
|
- * map frames are recomputed from the methods bytecode. The arguments of the {@link
|
||
|
|
- * MethodVisitor#visitMaxs} method are also ignored and recomputed from the bytecode. In other
|
||
|
|
- * words, {@link #COMPUTE_FRAMES} implies {@link #COMPUTE_MAXS}.
|
||
|
|
- *
|
||
|
|
- * @see #ClassWriter(int)
|
||
|
|
- */
|
||
|
|
- public static final int COMPUTE_FRAMES = 2;
|
||
|
|
-
|
||
|
|
- // Note: fields are ordered as in the ClassFile structure, and those related to attributes are
|
||
|
|
- // ordered as in Section 4.7 of the JVMS.
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The minor_version and major_version fields of the JVMS ClassFile structure. minor_version is
|
||
|
|
- * stored in the 16 most significant bits, and major_version in the 16 least significant bits.
|
||
|
|
- */
|
||
|
|
- private int version;
|
||
|
|
-
|
||
|
|
- /** The symbol table for this class (contains the constant_pool and the BootstrapMethods). */
|
||
|
|
- private final SymbolTable symbolTable;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
|
||
|
|
- * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
|
||
|
|
- * ClassFile structure.
|
||
|
|
- */
|
||
|
|
- private int accessFlags;
|
||
|
|
-
|
||
|
|
- /** The this_class field of the JVMS ClassFile structure. */
|
||
|
|
- private int thisClass;
|
||
|
|
-
|
||
|
|
- /** The super_class field of the JVMS ClassFile structure. */
|
||
|
|
- private int superClass;
|
||
|
|
-
|
||
|
|
- /** The interface_count field of the JVMS ClassFile structure. */
|
||
|
|
- private int interfaceCount;
|
||
|
|
-
|
||
|
|
- /** The 'interfaces' array of the JVMS ClassFile structure. */
|
||
|
|
- private int[] interfaces;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
|
||
|
|
- * {@link FieldWriter#fv} field. This field stores the first element of this list.
|
||
|
|
- */
|
||
|
|
- private FieldWriter firstField;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their
|
||
|
|
- * {@link FieldWriter#fv} field. This field stores the last element of this list.
|
||
|
|
- */
|
||
|
|
- private FieldWriter lastField;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
|
||
|
|
- * {@link MethodWriter#mv} field. This field stores the first element of this list.
|
||
|
|
- */
|
||
|
|
- private MethodWriter firstMethod;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their
|
||
|
|
- * {@link MethodWriter#mv} field. This field stores the last element of this list.
|
||
|
|
- */
|
||
|
|
- private MethodWriter lastMethod;
|
||
|
|
-
|
||
|
|
- /** The number_of_classes field of the InnerClasses attribute, or 0. */
|
||
|
|
- private int numberOfInnerClasses;
|
||
|
|
-
|
||
|
|
- /** The 'classes' array of the InnerClasses attribute, or {@literal null}. */
|
||
|
|
- private ByteVector innerClasses;
|
||
|
|
-
|
||
|
|
- /** The class_index field of the EnclosingMethod attribute, or 0. */
|
||
|
|
- private int enclosingClassIndex;
|
||
|
|
-
|
||
|
|
- /** The method_index field of the EnclosingMethod attribute. */
|
||
|
|
- private int enclosingMethodIndex;
|
||
|
|
-
|
||
|
|
- /** The signature_index field of the Signature attribute, or 0. */
|
||
|
|
- private int signatureIndex;
|
||
|
|
-
|
||
|
|
- /** The source_file_index field of the SourceFile attribute, or 0. */
|
||
|
|
- private int sourceFileIndex;
|
||
|
|
-
|
||
|
|
- /** The debug_extension field of the SourceDebugExtension attribute, or {@literal null}. */
|
||
|
|
- private ByteVector debugExtension;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The last runtime visible annotation of this class. The previous ones can be accessed with the
|
||
|
|
- * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- private AnnotationWriter lastRuntimeVisibleAnnotation;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The last runtime invisible annotation of this class. The previous ones can be accessed with the
|
||
|
|
- * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- private AnnotationWriter lastRuntimeInvisibleAnnotation;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The last runtime visible type annotation of this class. The previous ones can be accessed with
|
||
|
|
- * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- private AnnotationWriter lastRuntimeVisibleTypeAnnotation;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The last runtime invisible type annotation of this class. The previous ones can be accessed
|
||
|
|
- * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||
|
|
- */
|
||
|
|
- private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
|
||
|
|
-
|
||
|
|
- /** The Module attribute of this class, or {@literal null}. */
|
||
|
|
- private ModuleWriter moduleWriter;
|
||
|
|
-
|
||
|
|
- /** The host_class_index field of the NestHost attribute, or 0. */
|
||
|
|
- private int nestHostClassIndex;
|
||
|
|
-
|
||
|
|
- /** The number_of_classes field of the NestMembers attribute, or 0. */
|
||
|
|
- private int numberOfNestMemberClasses;
|
||
|
|
-
|
||
|
|
- /** The 'classes' array of the NestMembers attribute, or {@literal null}. */
|
||
|
|
- private ByteVector nestMemberClasses;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * The first non standard attribute of this class. The next ones can be accessed with the {@link
|
||
|
|
- * Attribute#nextAttribute} field. May be {@literal null}.
|
||
|
|
- *
|
||
|
|
- * <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
|
||
|
|
- * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
|
||
|
|
- * #toByteArray} method writes the attributes in the order defined by this list, i.e. in the
|
||
|
|
- * reverse order specified by the user.
|
||
|
|
- */
|
||
|
|
- private Attribute firstAttribute;
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link
|
||
|
|
- * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link
|
||
|
|
- * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}.
|
||
|
|
- */
|
||
|
|
- private int compute;
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Constructor
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassWriter} object.
|
||
|
|
- *
|
||
|
|
- * @param flags option flags that can be used to modify the default behavior of this class. Must
|
||
|
|
- * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}.
|
||
|
|
- */
|
||
|
|
- public ClassWriter(final int flags) {
|
||
|
|
- this(null, flags);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode
|
||
|
|
- * transformations. These optimizations are the following:
|
||
|
|
- *
|
||
|
|
- * <ul>
|
||
|
|
- * <li>The constant pool and bootstrap methods from the original class are copied as is in the
|
||
|
|
- * new class, which saves time. New constant pool entries and new bootstrap methods will be
|
||
|
|
- * added at the end if necessary, but unused constant pool entries or bootstrap methods
|
||
|
|
- * <i>won't be removed</i>.
|
||
|
|
- * <li>Methods that are not transformed are copied as is in the new class, directly from the
|
||
|
|
- * original class bytecode (i.e. without emitting visit events for all the method
|
||
|
|
- * instructions), which saves a <i>lot</i> of time. Untransformed methods are detected by
|
||
|
|
- * the fact that the {@link ClassReader} receives {@link MethodVisitor} objects that come
|
||
|
|
- * from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance).
|
||
|
|
- * </ul>
|
||
|
|
- *
|
||
|
|
- * @param classReader the {@link ClassReader} used to read the original class. It will be used to
|
||
|
|
- * copy the entire constant pool and bootstrap methods from the original class and also to
|
||
|
|
- * copy other fragments of original bytecode where applicable.
|
||
|
|
- * @param flags option flags that can be used to modify the default behavior of this class.Must be
|
||
|
|
- * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. <i>These option flags do
|
||
|
|
- * not affect methods that are copied as is in the new class. This means that neither the
|
||
|
|
- * maximum stack size nor the stack frames will be computed for these methods</i>.
|
||
|
|
- */
|
||
|
|
- public ClassWriter(final ClassReader classReader, final int flags) {
|
||
|
|
- super(Opcodes.ASM7);
|
||
|
|
- symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
|
||
|
|
- if ((flags & COMPUTE_FRAMES) != 0) {
|
||
|
|
- this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
|
||
|
|
- } else if ((flags & COMPUTE_MAXS) != 0) {
|
||
|
|
- this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
|
||
|
|
- } else {
|
||
|
|
- this.compute = MethodWriter.COMPUTE_NOTHING;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Implementation of the ClassVisitor abstract class
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visit(
|
||
|
|
- final int version,
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String signature,
|
||
|
|
- final String superName,
|
||
|
|
- final String[] interfaces) {
|
||
|
|
- this.version = version;
|
||
|
|
- this.accessFlags = access;
|
||
|
|
- this.thisClass = symbolTable.setMajorVersionAndClassName(version & 0xFFFF, name);
|
||
|
|
- if (signature != null) {
|
||
|
|
- this.signatureIndex = symbolTable.addConstantUtf8(signature);
|
||
|
|
- }
|
||
|
|
- this.superClass = superName == null ? 0 : symbolTable.addConstantClass(superName).index;
|
||
|
|
- if (interfaces != null && interfaces.length > 0) {
|
||
|
|
- interfaceCount = interfaces.length;
|
||
|
|
- this.interfaces = new int[interfaceCount];
|
||
|
|
- for (int i = 0; i < interfaceCount; ++i) {
|
||
|
|
- this.interfaces[i] = symbolTable.addConstantClass(interfaces[i]).index;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- if (compute == MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL && (version & 0xFFFF) >= Opcodes.V1_7) {
|
||
|
|
- compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visitSource(final String file, final String debug) {
|
||
|
|
- if (file != null) {
|
||
|
|
- sourceFileIndex = symbolTable.addConstantUtf8(file);
|
||
|
|
- }
|
||
|
|
- if (debug != null) {
|
||
|
|
- debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final ModuleVisitor visitModule(
|
||
|
|
- final String name, final int access, final String version) {
|
||
|
|
- return moduleWriter =
|
||
|
|
- new ModuleWriter(
|
||
|
|
- symbolTable,
|
||
|
|
- symbolTable.addConstantModule(name).index,
|
||
|
|
- access,
|
||
|
|
- version == null ? 0 : symbolTable.addConstantUtf8(version));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public void visitNestHost(final String nestHost) {
|
||
|
|
- nestHostClassIndex = symbolTable.addConstantClass(nestHost).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visitOuterClass(
|
||
|
|
- final String owner, final String name, final String descriptor) {
|
||
|
|
- enclosingClassIndex = symbolTable.addConstantClass(owner).index;
|
||
|
|
- if (name != null && descriptor != null) {
|
||
|
|
- enclosingMethodIndex = symbolTable.addConstantNameAndType(name, descriptor);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
|
||
|
|
- // Create a ByteVector to hold an 'annotation' JVMS structure.
|
||
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
|
||
|
|
- ByteVector annotation = new ByteVector();
|
||
|
|
- // Write type_index and reserve space for num_element_value_pairs.
|
||
|
|
- annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||
|
|
- if (visible) {
|
||
|
|
- return lastRuntimeVisibleAnnotation =
|
||
|
|
- new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
|
||
|
|
- } else {
|
||
|
|
- return lastRuntimeInvisibleAnnotation =
|
||
|
|
- new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final AnnotationVisitor visitTypeAnnotation(
|
||
|
|
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||
|
|
- // Create a ByteVector to hold a 'type_annotation' JVMS structure.
|
||
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
|
||
|
|
- ByteVector typeAnnotation = new ByteVector();
|
||
|
|
- // Write target_type, target_info, and target_path.
|
||
|
|
- TypeReference.putTarget(typeRef, typeAnnotation);
|
||
|
|
- TypePath.put(typePath, typeAnnotation);
|
||
|
|
- // Write type_index and reserve space for num_element_value_pairs.
|
||
|
|
- typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||
|
|
- if (visible) {
|
||
|
|
- return lastRuntimeVisibleTypeAnnotation =
|
||
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
|
||
|
|
- } else {
|
||
|
|
- return lastRuntimeInvisibleTypeAnnotation =
|
||
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visitAttribute(final Attribute attribute) {
|
||
|
|
- // Store the attributes in the <i>reverse</i> order of their visit by this method.
|
||
|
|
- attribute.nextAttribute = firstAttribute;
|
||
|
|
- firstAttribute = attribute;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public void visitNestMember(final String nestMember) {
|
||
|
|
- if (nestMemberClasses == null) {
|
||
|
|
- nestMemberClasses = new ByteVector();
|
||
|
|
- }
|
||
|
|
- ++numberOfNestMemberClasses;
|
||
|
|
- nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visitInnerClass(
|
||
|
|
- final String name, final String outerName, final String innerName, final int access) {
|
||
|
|
- if (innerClasses == null) {
|
||
|
|
- innerClasses = new ByteVector();
|
||
|
|
- }
|
||
|
|
- // Section 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the constant_pool table
|
||
|
|
- // which represents a class or interface C that is not a package member must have exactly one
|
||
|
|
- // corresponding entry in the classes array". To avoid duplicates we keep track in the info
|
||
|
|
- // field of the Symbol of each CONSTANT_Class_info entry C whether an inner class entry has
|
||
|
|
- // already been added for C. If so, we store the index of this inner class entry (plus one) in
|
||
|
|
- // the info field. This trick allows duplicate detection in O(1) time.
|
||
|
|
- Symbol nameSymbol = symbolTable.addConstantClass(name);
|
||
|
|
- if (nameSymbol.info == 0) {
|
||
|
|
- ++numberOfInnerClasses;
|
||
|
|
- innerClasses.putShort(nameSymbol.index);
|
||
|
|
- innerClasses.putShort(outerName == null ? 0 : symbolTable.addConstantClass(outerName).index);
|
||
|
|
- innerClasses.putShort(innerName == null ? 0 : symbolTable.addConstantUtf8(innerName));
|
||
|
|
- innerClasses.putShort(access);
|
||
|
|
- nameSymbol.info = numberOfInnerClasses;
|
||
|
|
- }
|
||
|
|
- // Else, compare the inner classes entry nameSymbol.info - 1 with the arguments of this method
|
||
|
|
- // and throw an exception if there is a difference?
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final FieldVisitor visitField(
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final String signature,
|
||
|
|
- final Object value) {
|
||
|
|
- FieldWriter fieldWriter =
|
||
|
|
- new FieldWriter(symbolTable, access, name, descriptor, signature, value);
|
||
|
|
- if (firstField == null) {
|
||
|
|
- firstField = fieldWriter;
|
||
|
|
- } else {
|
||
|
|
- lastField.fv = fieldWriter;
|
||
|
|
- }
|
||
|
|
- return lastField = fieldWriter;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final MethodVisitor visitMethod(
|
||
|
|
- final int access,
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final String signature,
|
||
|
|
- final String[] exceptions) {
|
||
|
|
- MethodWriter methodWriter =
|
||
|
|
- new MethodWriter(symbolTable, access, name, descriptor, signature, exceptions, compute);
|
||
|
|
- if (firstMethod == null) {
|
||
|
|
- firstMethod = methodWriter;
|
||
|
|
- } else {
|
||
|
|
- lastMethod.mv = methodWriter;
|
||
|
|
- }
|
||
|
|
- return lastMethod = methodWriter;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- @Override
|
||
|
|
- public final void visitEnd() {
|
||
|
|
- // Nothing to do.
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Other public methods
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the content of the class file that was built by this ClassWriter.
|
||
|
|
- *
|
||
|
|
- * @return the binary content of the JVMS ClassFile structure that was built by this ClassWriter.
|
||
|
|
- * @throws ClassTooLargeException if the constant pool of the class is too large.
|
||
|
|
- * @throws MethodTooLargeException if the Code attribute of a method is too large.
|
||
|
|
- */
|
||
|
|
- public byte[] toByteArray() throws ClassTooLargeException, MethodTooLargeException {
|
||
|
|
- // First step: compute the size in bytes of the ClassFile structure.
|
||
|
|
- // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version,
|
||
|
|
- // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count,
|
||
|
|
- // methods_count and attributes_count) use 2 bytes each, and each interface uses 2 bytes too.
|
||
|
|
- int size = 24 + 2 * interfaceCount;
|
||
|
|
- int fieldsCount = 0;
|
||
|
|
- FieldWriter fieldWriter = firstField;
|
||
|
|
- while (fieldWriter != null) {
|
||
|
|
- ++fieldsCount;
|
||
|
|
- size += fieldWriter.computeFieldInfoSize();
|
||
|
|
- fieldWriter = (FieldWriter) fieldWriter.fv;
|
||
|
|
- }
|
||
|
|
- int methodsCount = 0;
|
||
|
|
- MethodWriter methodWriter = firstMethod;
|
||
|
|
- while (methodWriter != null) {
|
||
|
|
- ++methodsCount;
|
||
|
|
- size += methodWriter.computeMethodInfoSize();
|
||
|
|
- methodWriter = (MethodWriter) methodWriter.mv;
|
||
|
|
- }
|
||
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
||
|
|
- int attributesCount = 0;
|
||
|
|
- if (innerClasses != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 8 + innerClasses.length;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.INNER_CLASSES);
|
||
|
|
- }
|
||
|
|
- if (enclosingClassIndex != 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 10;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD);
|
||
|
|
- }
|
||
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 6;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.SYNTHETIC);
|
||
|
|
- }
|
||
|
|
- if (signatureIndex != 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 8;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.SIGNATURE);
|
||
|
|
- }
|
||
|
|
- if (sourceFileIndex != 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 8;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.SOURCE_FILE);
|
||
|
|
- }
|
||
|
|
- if (debugExtension != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 6 + debugExtension.length;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION);
|
||
|
|
- }
|
||
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 6;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.DEPRECATED);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size +=
|
||
|
|
- lastRuntimeVisibleAnnotation.computeAnnotationsSize(
|
||
|
|
- Constants.RUNTIME_VISIBLE_ANNOTATIONS);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size +=
|
||
|
|
- lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
|
||
|
|
- Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size +=
|
||
|
|
- lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
||
|
|
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size +=
|
||
|
|
- lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
||
|
|
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
||
|
|
- }
|
||
|
|
- if (symbolTable.computeBootstrapMethodsSize() > 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += symbolTable.computeBootstrapMethodsSize();
|
||
|
|
- }
|
||
|
|
- if (moduleWriter != null) {
|
||
|
|
- attributesCount += moduleWriter.getAttributeCount();
|
||
|
|
- size += moduleWriter.computeAttributesSize();
|
||
|
|
- }
|
||
|
|
- if (nestHostClassIndex != 0) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 8;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.NEST_HOST);
|
||
|
|
- }
|
||
|
|
- if (nestMemberClasses != null) {
|
||
|
|
- ++attributesCount;
|
||
|
|
- size += 8 + nestMemberClasses.length;
|
||
|
|
- symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
|
||
|
|
- }
|
||
|
|
- if (firstAttribute != null) {
|
||
|
|
- attributesCount += firstAttribute.getAttributeCount();
|
||
|
|
- size += firstAttribute.computeAttributesSize(symbolTable);
|
||
|
|
- }
|
||
|
|
- // IMPORTANT: this must be the last part of the ClassFile size computation, because the previous
|
||
|
|
- // statements can add attribute names to the constant pool, thereby changing its size!
|
||
|
|
- size += symbolTable.getConstantPoolLength();
|
||
|
|
- int constantPoolCount = symbolTable.getConstantPoolCount();
|
||
|
|
- if (constantPoolCount > 0xFFFF) {
|
||
|
|
- throw new ClassTooLargeException(symbolTable.getClassName(), constantPoolCount);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Second step: allocate a ByteVector of the correct size (in order to avoid any array copy in
|
||
|
|
- // dynamic resizes) and fill it with the ClassFile content.
|
||
|
|
- ByteVector result = new ByteVector(size);
|
||
|
|
- result.putInt(0xCAFEBABE).putInt(version);
|
||
|
|
- symbolTable.putConstantPool(result);
|
||
|
|
- int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0;
|
||
|
|
- result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass);
|
||
|
|
- result.putShort(interfaceCount);
|
||
|
|
- for (int i = 0; i < interfaceCount; ++i) {
|
||
|
|
- result.putShort(interfaces[i]);
|
||
|
|
- }
|
||
|
|
- result.putShort(fieldsCount);
|
||
|
|
- fieldWriter = firstField;
|
||
|
|
- while (fieldWriter != null) {
|
||
|
|
- fieldWriter.putFieldInfo(result);
|
||
|
|
- fieldWriter = (FieldWriter) fieldWriter.fv;
|
||
|
|
- }
|
||
|
|
- result.putShort(methodsCount);
|
||
|
|
- boolean hasFrames = false;
|
||
|
|
- boolean hasAsmInstructions = false;
|
||
|
|
- methodWriter = firstMethod;
|
||
|
|
- while (methodWriter != null) {
|
||
|
|
- hasFrames |= methodWriter.hasFrames();
|
||
|
|
- hasAsmInstructions |= methodWriter.hasAsmInstructions();
|
||
|
|
- methodWriter.putMethodInfo(result);
|
||
|
|
- methodWriter = (MethodWriter) methodWriter.mv;
|
||
|
|
- }
|
||
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
||
|
|
- result.putShort(attributesCount);
|
||
|
|
- if (innerClasses != null) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES))
|
||
|
|
- .putInt(innerClasses.length + 2)
|
||
|
|
- .putShort(numberOfInnerClasses)
|
||
|
|
- .putByteArray(innerClasses.data, 0, innerClasses.length);
|
||
|
|
- }
|
||
|
|
- if (enclosingClassIndex != 0) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD))
|
||
|
|
- .putInt(4)
|
||
|
|
- .putShort(enclosingClassIndex)
|
||
|
|
- .putShort(enclosingMethodIndex);
|
||
|
|
- }
|
||
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) {
|
||
|
|
- result.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
|
||
|
|
- }
|
||
|
|
- if (signatureIndex != 0) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
|
||
|
|
- .putInt(2)
|
||
|
|
- .putShort(signatureIndex);
|
||
|
|
- }
|
||
|
|
- if (sourceFileIndex != 0) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_FILE))
|
||
|
|
- .putInt(2)
|
||
|
|
- .putShort(sourceFileIndex);
|
||
|
|
- }
|
||
|
|
- if (debugExtension != null) {
|
||
|
|
- int length = debugExtension.length;
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION))
|
||
|
|
- .putInt(length)
|
||
|
|
- .putByteArray(debugExtension.data, 0, length);
|
||
|
|
- }
|
||
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
||
|
|
- result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
||
|
|
- lastRuntimeVisibleAnnotation.putAnnotations(
|
||
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), result);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
||
|
|
- lastRuntimeInvisibleAnnotation.putAnnotations(
|
||
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), result);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
||
|
|
- lastRuntimeVisibleTypeAnnotation.putAnnotations(
|
||
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), result);
|
||
|
|
- }
|
||
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
||
|
|
- lastRuntimeInvisibleTypeAnnotation.putAnnotations(
|
||
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), result);
|
||
|
|
- }
|
||
|
|
- symbolTable.putBootstrapMethods(result);
|
||
|
|
- if (moduleWriter != null) {
|
||
|
|
- moduleWriter.putAttributes(result);
|
||
|
|
- }
|
||
|
|
- if (nestHostClassIndex != 0) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.NEST_HOST))
|
||
|
|
- .putInt(2)
|
||
|
|
- .putShort(nestHostClassIndex);
|
||
|
|
- }
|
||
|
|
- if (nestMemberClasses != null) {
|
||
|
|
- result
|
||
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.NEST_MEMBERS))
|
||
|
|
- .putInt(nestMemberClasses.length + 2)
|
||
|
|
- .putShort(numberOfNestMemberClasses)
|
||
|
|
- .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
|
||
|
|
- }
|
||
|
|
- if (firstAttribute != null) {
|
||
|
|
- firstAttribute.putAttributes(symbolTable, result);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // Third step: replace the ASM specific instructions, if any.
|
||
|
|
- if (hasAsmInstructions) {
|
||
|
|
- return replaceAsmInstructions(result.data, hasFrames);
|
||
|
|
- } else {
|
||
|
|
- return result.data;
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the equivalent of the given class file, with the ASM specific instructions replaced
|
||
|
|
- * with standard ones. This is done with a ClassReader -> ClassWriter round trip.
|
||
|
|
- *
|
||
|
|
- * @param classFile a class file containing ASM specific instructions, generated by this
|
||
|
|
- * ClassWriter.
|
||
|
|
- * @param hasFrames whether there is at least one stack map frames in 'classFile'.
|
||
|
|
- * @return an equivalent of 'classFile', with the ASM specific instructions replaced with standard
|
||
|
|
- * ones.
|
||
|
|
- */
|
||
|
|
- private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) {
|
||
|
|
- final Attribute[] attributes = getAttributePrototypes();
|
||
|
|
- firstField = null;
|
||
|
|
- lastField = null;
|
||
|
|
- firstMethod = null;
|
||
|
|
- lastMethod = null;
|
||
|
|
- lastRuntimeVisibleAnnotation = null;
|
||
|
|
- lastRuntimeInvisibleAnnotation = null;
|
||
|
|
- lastRuntimeVisibleTypeAnnotation = null;
|
||
|
|
- lastRuntimeInvisibleTypeAnnotation = null;
|
||
|
|
- moduleWriter = null;
|
||
|
|
- nestHostClassIndex = 0;
|
||
|
|
- numberOfNestMemberClasses = 0;
|
||
|
|
- nestMemberClasses = null;
|
||
|
|
- firstAttribute = null;
|
||
|
|
- compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
|
||
|
|
- new ClassReader(classFile, 0, /* checkClassVersion = */ false)
|
||
|
|
- .accept(
|
||
|
|
- this,
|
||
|
|
- attributes,
|
||
|
|
- (hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS);
|
||
|
|
- return toByteArray();
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the prototypes of the attributes used by this class, its fields and its methods.
|
||
|
|
- *
|
||
|
|
- * @return the prototypes of the attributes used by this class, its fields and its methods.
|
||
|
|
- */
|
||
|
|
- private Attribute[] getAttributePrototypes() {
|
||
|
|
- Attribute.Set attributePrototypes = new Attribute.Set();
|
||
|
|
- attributePrototypes.addAttributes(firstAttribute);
|
||
|
|
- FieldWriter fieldWriter = firstField;
|
||
|
|
- while (fieldWriter != null) {
|
||
|
|
- fieldWriter.collectAttributePrototypes(attributePrototypes);
|
||
|
|
- fieldWriter = (FieldWriter) fieldWriter.fv;
|
||
|
|
- }
|
||
|
|
- MethodWriter methodWriter = firstMethod;
|
||
|
|
- while (methodWriter != null) {
|
||
|
|
- methodWriter.collectAttributePrototypes(attributePrototypes);
|
||
|
|
- methodWriter = (MethodWriter) methodWriter.mv;
|
||
|
|
- }
|
||
|
|
- return attributePrototypes.toArray();
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Utility methods: constant pool management for Attribute sub classes
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a number or string constant to the constant pool of the class being build. Does nothing if
|
||
|
|
- * the constant pool already contains a similar item. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param value the value of the constant to be added to the constant pool. This parameter must be
|
||
|
|
- * an {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double} or a {@link String}.
|
||
|
|
- * @return the index of a new or already existing constant item with the given value.
|
||
|
|
- */
|
||
|
|
- public int newConst(final Object value) {
|
||
|
|
- return symbolTable.addConstant(value).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds an UTF8 string to the constant pool of the class being build. Does nothing if the constant
|
||
|
|
- * pool already contains a similar item. <i>This method is intended for {@link Attribute} sub
|
||
|
|
- * classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param value the String value.
|
||
|
|
- * @return the index of a new or already existing UTF8 item.
|
||
|
|
- */
|
||
|
|
- // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||
|
|
- public int newUTF8(final String value) {
|
||
|
|
- return symbolTable.addConstantUtf8(value);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a class reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param value the internal name of the class.
|
||
|
|
- * @return the index of a new or already existing class reference item.
|
||
|
|
- */
|
||
|
|
- public int newClass(final String value) {
|
||
|
|
- return symbolTable.addConstantClass(value).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a method type reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param methodDescriptor method descriptor of the method type.
|
||
|
|
- * @return the index of a new or already existing method type reference item.
|
||
|
|
- */
|
||
|
|
- public int newMethodType(final String methodDescriptor) {
|
||
|
|
- return symbolTable.addConstantMethodType(methodDescriptor).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a module reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param moduleName name of the module.
|
||
|
|
- * @return the index of a new or already existing module reference item.
|
||
|
|
- */
|
||
|
|
- public int newModule(final String moduleName) {
|
||
|
|
- return symbolTable.addConstantModule(moduleName).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a package reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param packageName name of the package in its internal form.
|
||
|
|
- * @return the index of a new or already existing module reference item.
|
||
|
|
- */
|
||
|
|
- public int newPackage(final String packageName) {
|
||
|
|
- return symbolTable.addConstantPackage(packageName).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
|
||
|
|
- * already contains a similar item. <i>This method is intended for {@link Attribute} sub classes,
|
||
|
|
- * and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
|
||
|
|
- * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||
|
|
- * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||
|
|
- * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||
|
|
- * @param owner the internal name of the field or method owner class.
|
||
|
|
- * @param name the name of the field or method.
|
||
|
|
- * @param descriptor the descriptor of the field or method.
|
||
|
|
- * @return the index of a new or already existing method type reference item.
|
||
|
|
- * @deprecated this method is superseded by {@link #newHandle(int, String, String, String,
|
||
|
|
- * boolean)}.
|
||
|
|
- */
|
||
|
|
- @Deprecated
|
||
|
|
- public int newHandle(
|
||
|
|
- final int tag, final String owner, final String name, final String descriptor) {
|
||
|
|
- return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool
|
||
|
|
- * already contains a similar item. <i>This method is intended for {@link Attribute} sub classes,
|
||
|
|
- * and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link
|
||
|
|
- * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||
|
|
- * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||
|
|
- * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||
|
|
- * @param owner the internal name of the field or method owner class.
|
||
|
|
- * @param name the name of the field or method.
|
||
|
|
- * @param descriptor the descriptor of the field or method.
|
||
|
|
- * @param isInterface true if the owner is an interface.
|
||
|
|
- * @return the index of a new or already existing method type reference item.
|
||
|
|
- */
|
||
|
|
- public int newHandle(
|
||
|
|
- final int tag,
|
||
|
|
- final String owner,
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final boolean isInterface) {
|
||
|
|
- return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a dynamic constant reference to the constant pool of the class being build. Does nothing
|
||
|
|
- * if the constant pool already contains a similar item. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param name name of the invoked method.
|
||
|
|
- * @param descriptor field descriptor of the constant type.
|
||
|
|
- * @param bootstrapMethodHandle the bootstrap method.
|
||
|
|
- * @param bootstrapMethodArguments the bootstrap method constant arguments.
|
||
|
|
- * @return the index of a new or already existing dynamic constant reference item.
|
||
|
|
- */
|
||
|
|
- public int newConstantDynamic(
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final Handle bootstrapMethodHandle,
|
||
|
|
- final Object... bootstrapMethodArguments) {
|
||
|
|
- return symbolTable.addConstantDynamic(
|
||
|
|
- name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
|
||
|
|
- .index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if
|
||
|
|
- * the constant pool already contains a similar item. <i>This method is intended for {@link
|
||
|
|
- * Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param name name of the invoked method.
|
||
|
|
- * @param descriptor descriptor of the invoke method.
|
||
|
|
- * @param bootstrapMethodHandle the bootstrap method.
|
||
|
|
- * @param bootstrapMethodArguments the bootstrap method constant arguments.
|
||
|
|
- * @return the index of a new or already existing invokedynamic reference item.
|
||
|
|
- */
|
||
|
|
- public int newInvokeDynamic(
|
||
|
|
- final String name,
|
||
|
|
- final String descriptor,
|
||
|
|
- final Handle bootstrapMethodHandle,
|
||
|
|
- final Object... bootstrapMethodArguments) {
|
||
|
|
- return symbolTable.addConstantInvokeDynamic(
|
||
|
|
- name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
|
||
|
|
- .index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a field reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param owner the internal name of the field's owner class.
|
||
|
|
- * @param name the field's name.
|
||
|
|
- * @param descriptor the field's descriptor.
|
||
|
|
- * @return the index of a new or already existing field reference item.
|
||
|
|
- */
|
||
|
|
- public int newField(final String owner, final String name, final String descriptor) {
|
||
|
|
- return symbolTable.addConstantFieldref(owner, name, descriptor).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a method reference to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param owner the internal name of the method's owner class.
|
||
|
|
- * @param name the method's name.
|
||
|
|
- * @param descriptor the method's descriptor.
|
||
|
|
- * @param isInterface {@literal true} if {@code owner} is an interface.
|
||
|
|
- * @return the index of a new or already existing method reference item.
|
||
|
|
- */
|
||
|
|
- public int newMethod(
|
||
|
|
- final String owner, final String name, final String descriptor, final boolean isInterface) {
|
||
|
|
- return symbolTable.addConstantMethodref(owner, name, descriptor, isInterface).index;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Adds a name and type to the constant pool of the class being build. Does nothing if the
|
||
|
|
- * constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||
|
|
- * sub classes, and is normally not needed by class generators or adapters.</i>
|
||
|
|
- *
|
||
|
|
- * @param name a name.
|
||
|
|
- * @param descriptor a type descriptor.
|
||
|
|
- * @return the index of a new or already existing name and type item.
|
||
|
|
- */
|
||
|
|
- public int newNameType(final String name, final String descriptor) {
|
||
|
|
- return symbolTable.addConstantNameAndType(name, descriptor);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
- // Default method to compute common super classes when computing stack map frames
|
||
|
|
- // -----------------------------------------------------------------------------------------------
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the common super type of the two given types. The default implementation of this method
|
||
|
|
- * <i>loads</i> the two given classes and uses the java.lang.Class methods to find the common
|
||
|
|
- * super class. It can be overridden to compute this common super type in other ways, in
|
||
|
|
- * particular without actually loading any class, or to take into account the class that is
|
||
|
|
- * currently being generated by this ClassWriter, which can of course not be loaded since it is
|
||
|
|
- * under construction.
|
||
|
|
- *
|
||
|
|
- * @param type1 the internal name of a class.
|
||
|
|
- * @param type2 the internal name of another class.
|
||
|
|
- * @return the internal name of the common super class of the two given classes.
|
||
|
|
- */
|
||
|
|
- protected String getCommonSuperClass(final String type1, final String type2) {
|
||
|
|
- ClassLoader classLoader = getClassLoader();
|
||
|
|
- Class<?> class1;
|
||
|
|
- try {
|
||
|
|
- class1 = Class.forName(type1.replace('/', '.'), false, classLoader);
|
||
|
|
- } catch (ClassNotFoundException e) {
|
||
|
|
- throw new TypeNotPresentException(type1, e);
|
||
|
|
- }
|
||
|
|
- Class<?> class2;
|
||
|
|
- try {
|
||
|
|
- class2 = Class.forName(type2.replace('/', '.'), false, classLoader);
|
||
|
|
- } catch (ClassNotFoundException e) {
|
||
|
|
- throw new TypeNotPresentException(type2, e);
|
||
|
|
- }
|
||
|
|
- if (class1.isAssignableFrom(class2)) {
|
||
|
|
- return type1;
|
||
|
|
- }
|
||
|
|
- if (class2.isAssignableFrom(class1)) {
|
||
|
|
- return type2;
|
||
|
|
- }
|
||
|
|
- if (class1.isInterface() || class2.isInterface()) {
|
||
|
|
- return "java/lang/Object";
|
||
|
|
- } else {
|
||
|
|
- do {
|
||
|
|
- class1 = class1.getSuperclass();
|
||
|
|
- } while (!class1.isAssignableFrom(class2));
|
||
|
|
- return class1.getName().replace('.', '/');
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- /**
|
||
|
|
- * Returns the {@link ClassLoader} to be used by the default implementation of {@link
|
||
|
|
- * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by
|
||
|
|
- * default.
|
||
|
|
- *
|
||
|
|
- * @return ClassLoader
|
||
|
|
- */
|
||
|
|