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
|
|
- */
|
|
- protected ClassLoader getClassLoader() {
|
|
- return getClass().getClassLoader();
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/ConstantDynamic.java b/src/main/java/org/mvel2/asm/ConstantDynamic.java
|
|
deleted file mode 100644
|
|
index 10515fd..0000000
|
|
--- a/src/main/java/org/mvel2/asm/ConstantDynamic.java
|
|
+++ /dev/null
|
|
@@ -1,178 +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.util.Arrays;
|
|
-
|
|
-/**
|
|
- * A constant whose value is computed at runtime, with a bootstrap method.
|
|
- *
|
|
- * @author Remi Forax
|
|
- */
|
|
-public final class ConstantDynamic {
|
|
-
|
|
- /** The constant name (can be arbitrary). */
|
|
- private final String name;
|
|
-
|
|
- /** The constant type (must be a field descriptor). */
|
|
- private final String descriptor;
|
|
-
|
|
- /** The bootstrap method to use to compute the constant value at runtime. */
|
|
- private final Handle bootstrapMethod;
|
|
-
|
|
- /**
|
|
- * The arguments to pass to the bootstrap method, in order to compute the constant value at
|
|
- * runtime.
|
|
- */
|
|
- private final Object[] bootstrapMethodArguments;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link ConstantDynamic}.
|
|
- *
|
|
- * @param name the constant name (can be arbitrary).
|
|
- * @param descriptor the constant type (must be a field descriptor).
|
|
- * @param bootstrapMethod the bootstrap method to use to compute the constant value at runtime.
|
|
- * @param bootstrapMethodArguments the arguments to pass to the bootstrap method, in order to
|
|
- * compute the constant value at runtime.
|
|
- */
|
|
- public ConstantDynamic(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final Handle bootstrapMethod,
|
|
- final Object... bootstrapMethodArguments) {
|
|
- this.name = name;
|
|
- this.descriptor = descriptor;
|
|
- this.bootstrapMethod = bootstrapMethod;
|
|
- this.bootstrapMethodArguments = bootstrapMethodArguments;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the name of this constant.
|
|
- *
|
|
- * @return the name of this constant.
|
|
- */
|
|
- public String getName() {
|
|
- return name;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the type of this constant.
|
|
- *
|
|
- * @return the type of this constant, as a field descriptor.
|
|
- */
|
|
- public String getDescriptor() {
|
|
- return descriptor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the bootstrap method used to compute the value of this constant.
|
|
- *
|
|
- * @return the bootstrap method used to compute the value of this constant.
|
|
- */
|
|
- public Handle getBootstrapMethod() {
|
|
- return bootstrapMethod;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of arguments passed to the bootstrap method, in order to compute the value
|
|
- * of this constant.
|
|
- *
|
|
- * @return the number of arguments passed to the bootstrap method, in order to compute the value
|
|
- * of this constant.
|
|
- */
|
|
- public int getBootstrapMethodArgumentCount() {
|
|
- return bootstrapMethodArguments.length;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns an argument passed to the bootstrap method, in order to compute the value of this
|
|
- * constant.
|
|
- *
|
|
- * @param index an argument index, between 0 and {@link #getBootstrapMethodArgumentCount()}
|
|
- * (exclusive).
|
|
- * @return the argument passed to the bootstrap method, with the given index.
|
|
- */
|
|
- public Object getBootstrapMethodArgument(final int index) {
|
|
- return bootstrapMethodArguments[index];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the arguments to pass to the bootstrap method, in order to compute the value of this
|
|
- * constant. WARNING: this array must not be modified, and must not be returned to the user.
|
|
- *
|
|
- * @return the arguments to pass to the bootstrap method, in order to compute the value of this
|
|
- * constant.
|
|
- */
|
|
- Object[] getBootstrapMethodArgumentsUnsafe() {
|
|
- return bootstrapMethodArguments;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of this constant.
|
|
- *
|
|
- * @return the size of this constant, i.e., 2 for {@code long} and {@code double}, 1 otherwise.
|
|
- */
|
|
- public int getSize() {
|
|
- char firstCharOfDescriptor = descriptor.charAt(0);
|
|
- return (firstCharOfDescriptor == 'J' || firstCharOfDescriptor == 'D') ? 2 : 1;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean equals(final Object object) {
|
|
- if (object == this) {
|
|
- return true;
|
|
- }
|
|
- if (!(object instanceof ConstantDynamic)) {
|
|
- return false;
|
|
- }
|
|
- ConstantDynamic constantDynamic = (ConstantDynamic) object;
|
|
- return name.equals(constantDynamic.name)
|
|
- && descriptor.equals(constantDynamic.descriptor)
|
|
- && bootstrapMethod.equals(constantDynamic.bootstrapMethod)
|
|
- && Arrays.equals(bootstrapMethodArguments, constantDynamic.bootstrapMethodArguments);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int hashCode() {
|
|
- return name.hashCode()
|
|
- ^ Integer.rotateLeft(descriptor.hashCode(), 8)
|
|
- ^ Integer.rotateLeft(bootstrapMethod.hashCode(), 16)
|
|
- ^ Integer.rotateLeft(Arrays.hashCode(bootstrapMethodArguments), 24);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String toString() {
|
|
- return name
|
|
- + " : "
|
|
- + descriptor
|
|
- + ' '
|
|
- + bootstrapMethod
|
|
- + ' '
|
|
- + Arrays.toString(bootstrapMethodArguments);
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Constants.java b/src/main/java/org/mvel2/asm/Constants.java
|
|
deleted file mode 100644
|
|
index d8c82a0..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Constants.java
|
|
+++ /dev/null
|
|
@@ -1,177 +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;
|
|
-
|
|
-/**
|
|
- * Defines additional JVM opcodes, access flags and constants which are not part of the ASM public
|
|
- * API.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html">JVMS 6</a>
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class Constants implements Opcodes {
|
|
-
|
|
- // The ClassFile attribute names, in the order they are defined in
|
|
- // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
|
|
-
|
|
- static final String CONSTANT_VALUE = "ConstantValue";
|
|
- static final String CODE = "Code";
|
|
- static final String STACK_MAP_TABLE = "StackMapTable";
|
|
- static final String EXCEPTIONS = "Exceptions";
|
|
- static final String INNER_CLASSES = "InnerClasses";
|
|
- static final String ENCLOSING_METHOD = "EnclosingMethod";
|
|
- static final String SYNTHETIC = "Synthetic";
|
|
- static final String SIGNATURE = "Signature";
|
|
- static final String SOURCE_FILE = "SourceFile";
|
|
- static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
|
|
- static final String LINE_NUMBER_TABLE = "LineNumberTable";
|
|
- static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable";
|
|
- static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable";
|
|
- static final String DEPRECATED = "Deprecated";
|
|
- static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
|
|
- static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
|
|
- static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
|
|
- static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
|
|
- "RuntimeInvisibleParameterAnnotations";
|
|
- static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
|
|
- static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations";
|
|
- static final String ANNOTATION_DEFAULT = "AnnotationDefault";
|
|
- static final String BOOTSTRAP_METHODS = "BootstrapMethods";
|
|
- static final String METHOD_PARAMETERS = "MethodParameters";
|
|
- static final String MODULE = "Module";
|
|
- static final String MODULE_PACKAGES = "ModulePackages";
|
|
- static final String MODULE_MAIN_CLASS = "ModuleMainClass";
|
|
- static final String NEST_HOST = "NestHost";
|
|
- static final String NEST_MEMBERS = "NestMembers";
|
|
-
|
|
- // ASM specific access flags.
|
|
- // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard
|
|
- // access flags, and also to make sure that these flags are automatically filtered out when
|
|
- // written in class files (because access flags are stored using 16 bits only).
|
|
-
|
|
- static final int ACC_CONSTRUCTOR = 0x40000; // method access flag.
|
|
-
|
|
- // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}.
|
|
-
|
|
- /**
|
|
- * A frame inserted between already existing frames. This internal stack map frame type (in
|
|
- * addition to the ones declared in {@link Opcodes}) can only be used if the frame content can be
|
|
- * computed from the previous existing frame and from the instructions between this existing frame
|
|
- * and the inserted one, without any knowledge of the type hierarchy. This kind of frame is only
|
|
- * used when an unconditional jump is inserted in a method while expanding an ASM specific
|
|
- * instruction. Keep in sync with Opcodes.java.
|
|
- */
|
|
- static final int F_INSERT = 256;
|
|
-
|
|
- // The JVM opcode values which are not part of the ASM public API.
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html.
|
|
-
|
|
- static final int LDC_W = 19;
|
|
- static final int LDC2_W = 20;
|
|
- static final int ILOAD_0 = 26;
|
|
- static final int ILOAD_1 = 27;
|
|
- static final int ILOAD_2 = 28;
|
|
- static final int ILOAD_3 = 29;
|
|
- static final int LLOAD_0 = 30;
|
|
- static final int LLOAD_1 = 31;
|
|
- static final int LLOAD_2 = 32;
|
|
- static final int LLOAD_3 = 33;
|
|
- static final int FLOAD_0 = 34;
|
|
- static final int FLOAD_1 = 35;
|
|
- static final int FLOAD_2 = 36;
|
|
- static final int FLOAD_3 = 37;
|
|
- static final int DLOAD_0 = 38;
|
|
- static final int DLOAD_1 = 39;
|
|
- static final int DLOAD_2 = 40;
|
|
- static final int DLOAD_3 = 41;
|
|
- static final int ALOAD_0 = 42;
|
|
- static final int ALOAD_1 = 43;
|
|
- static final int ALOAD_2 = 44;
|
|
- static final int ALOAD_3 = 45;
|
|
- static final int ISTORE_0 = 59;
|
|
- static final int ISTORE_1 = 60;
|
|
- static final int ISTORE_2 = 61;
|
|
- static final int ISTORE_3 = 62;
|
|
- static final int LSTORE_0 = 63;
|
|
- static final int LSTORE_1 = 64;
|
|
- static final int LSTORE_2 = 65;
|
|
- static final int LSTORE_3 = 66;
|
|
- static final int FSTORE_0 = 67;
|
|
- static final int FSTORE_1 = 68;
|
|
- static final int FSTORE_2 = 69;
|
|
- static final int FSTORE_3 = 70;
|
|
- static final int DSTORE_0 = 71;
|
|
- static final int DSTORE_1 = 72;
|
|
- static final int DSTORE_2 = 73;
|
|
- static final int DSTORE_3 = 74;
|
|
- static final int ASTORE_0 = 75;
|
|
- static final int ASTORE_1 = 76;
|
|
- static final int ASTORE_2 = 77;
|
|
- static final int ASTORE_3 = 78;
|
|
- static final int WIDE = 196;
|
|
- static final int GOTO_W = 200;
|
|
- static final int JSR_W = 201;
|
|
-
|
|
- // Constants to convert between normal and wide jump instructions.
|
|
-
|
|
- // The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP.
|
|
- static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO;
|
|
-
|
|
- // Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa.
|
|
-
|
|
- // The delta between the ASM_IFEQ, ..., ASM_IF_ACMPNE, ASM_GOTO and ASM_JSR opcodes
|
|
- // and IFEQ, ..., IF_ACMPNE, GOTO and JSR.
|
|
- static final int ASM_OPCODE_DELTA = 49;
|
|
-
|
|
- // The delta between the ASM_IFNULL and ASM_IFNONNULL opcodes and IFNULL and IFNONNULL.
|
|
- static final int ASM_IFNULL_OPCODE_DELTA = 20;
|
|
-
|
|
- // ASM specific opcodes, used for long forward jump instructions.
|
|
-
|
|
- static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA;
|
|
- static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA;
|
|
- static final int ASM_JSR = JSR + ASM_OPCODE_DELTA;
|
|
- static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
|
|
- static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
|
|
- static final int ASM_GOTO_W = 220;
|
|
-
|
|
- private Constants() {}
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Context.java b/src/main/java/org/mvel2/asm/Context.java
|
|
deleted file mode 100644
|
|
index acff45e..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Context.java
|
|
+++ /dev/null
|
|
@@ -1,137 +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;
|
|
-
|
|
-/**
|
|
- * Information about a class being parsed in a {@link ClassReader}.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class Context {
|
|
-
|
|
- /** The prototypes of the attributes that must be parsed in this class. */
|
|
- Attribute[] attributePrototypes;
|
|
-
|
|
- /**
|
|
- * The options used to parse this class. One or more of {@link ClassReader#SKIP_CODE}, {@link
|
|
- * ClassReader#SKIP_DEBUG}, {@link ClassReader#SKIP_FRAMES}, {@link ClassReader#EXPAND_FRAMES} or
|
|
- * {@link ClassReader#EXPAND_ASM_INSNS}.
|
|
- */
|
|
- int parsingOptions;
|
|
-
|
|
- /** The buffer used to read strings in the constant pool. */
|
|
- char[] charBuffer;
|
|
-
|
|
- // Information about the current method, i.e. the one read in the current (or latest) call
|
|
- // to {@link ClassReader#readMethod()}.
|
|
-
|
|
- /** The access flags of the current method. */
|
|
- int currentMethodAccessFlags;
|
|
-
|
|
- /** The name of the current method. */
|
|
- String currentMethodName;
|
|
-
|
|
- /** The descriptor of the current method. */
|
|
- String currentMethodDescriptor;
|
|
-
|
|
- /**
|
|
- * The labels of the current method, indexed by bytecode offset (only bytecode offsets for which a
|
|
- * label is needed have a non null associated Label).
|
|
- */
|
|
- Label[] currentMethodLabels;
|
|
-
|
|
- // Information about the current type annotation target, i.e. the one read in the current
|
|
- // (or latest) call to {@link ClassReader#readAnnotationTarget()}.
|
|
-
|
|
- /**
|
|
- * The target_type and target_info of the current type annotation target, encoded as described in
|
|
- * {@link TypeReference}.
|
|
- */
|
|
- int currentTypeAnnotationTarget;
|
|
-
|
|
- /** The target_path of the current type annotation target. */
|
|
- TypePath currentTypeAnnotationTargetPath;
|
|
-
|
|
- /** The start of each local variable range in the current local variable annotation. */
|
|
- Label[] currentLocalVariableAnnotationRangeStarts;
|
|
-
|
|
- /** The end of each local variable range in the current local variable annotation. */
|
|
- Label[] currentLocalVariableAnnotationRangeEnds;
|
|
-
|
|
- /**
|
|
- * The local variable index of each local variable range in the current local variable annotation.
|
|
- */
|
|
- int[] currentLocalVariableAnnotationRangeIndices;
|
|
-
|
|
- // Information about the current stack map frame, i.e. the one read in the current (or latest)
|
|
- // call to {@link ClassReader#readFrame()}.
|
|
-
|
|
- /** The bytecode offset of the current stack map frame. */
|
|
- int currentFrameOffset;
|
|
-
|
|
- /**
|
|
- * The type of the current stack map frame. One of {@link Opcodes#F_FULL}, {@link
|
|
- * Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or {@link Opcodes#F_SAME1}.
|
|
- */
|
|
- int currentFrameType;
|
|
-
|
|
- /**
|
|
- * The number of local variable types in the current stack map frame. Each type is represented
|
|
- * with a single array element (even long and double).
|
|
- */
|
|
- int currentFrameLocalCount;
|
|
-
|
|
- /**
|
|
- * The delta number of local variable types in the current stack map frame (each type is
|
|
- * represented with a single array element - even long and double). This is the number of local
|
|
- * variable types in this frame, minus the number of local variable types in the previous frame.
|
|
- */
|
|
- int currentFrameLocalCountDelta;
|
|
-
|
|
- /**
|
|
- * The types of the local variables in the current stack map frame. Each type is represented with
|
|
- * a single array element (even long and double), using the format described in {@link
|
|
- * MethodVisitor#visitFrame}. Depending on {@link #currentFrameType}, this contains the types of
|
|
- * all the local variables, or only those of the additional ones (compared to the previous frame).
|
|
- */
|
|
- Object[] currentFrameLocalTypes;
|
|
-
|
|
- /**
|
|
- * The number stack element types in the current stack map frame. Each type is represented with a
|
|
- * single array element (even long and double).
|
|
- */
|
|
- int currentFrameStackCount;
|
|
-
|
|
- /**
|
|
- * The types of the stack elements in the current stack map frame. Each type is represented with a
|
|
- * single array element (even long and double), using the format described in {@link
|
|
- * MethodVisitor#visitFrame}.
|
|
- */
|
|
- Object[] currentFrameStackTypes;
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/CurrentFrame.java b/src/main/java/org/mvel2/asm/CurrentFrame.java
|
|
deleted file mode 100644
|
|
index f37d3ec..0000000
|
|
--- a/src/main/java/org/mvel2/asm/CurrentFrame.java
|
|
+++ /dev/null
|
|
@@ -1,56 +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;
|
|
-
|
|
-/**
|
|
- * Information about the input stack map frame at the "current" instruction of a method. This is
|
|
- * implemented as a Frame subclass for a "basic block" containing only one instruction.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class CurrentFrame extends Frame {
|
|
-
|
|
- CurrentFrame(final Label owner) {
|
|
- super(owner);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets this CurrentFrame to the input stack map frame of the next "current" instruction, i.e. the
|
|
- * instruction just after the given one. It is assumed that the value of this object when this
|
|
- * method is called is the stack map frame status just before the given instruction is executed.
|
|
- */
|
|
- @Override
|
|
- void execute(
|
|
- final int opcode, final int arg, final Symbol symbolArg, final SymbolTable symbolTable) {
|
|
- super.execute(opcode, arg, symbolArg, symbolTable);
|
|
- Frame successor = new Frame(null);
|
|
- merge(symbolTable, successor, 0);
|
|
- copyFrom(successor);
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Edge.java b/src/main/java/org/mvel2/asm/Edge.java
|
|
deleted file mode 100644
|
|
index 16c53eb..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Edge.java
|
|
+++ /dev/null
|
|
@@ -1,91 +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 edge in the control flow graph of a method. Each node of this graph is a basic block,
|
|
- * represented with the Label corresponding to its first instruction. Each edge goes from one node
|
|
- * to another, i.e. from one basic block to another (called the predecessor and successor blocks,
|
|
- * respectively). An edge corresponds either to a jump or ret instruction or to an exception
|
|
- * handler.
|
|
- *
|
|
- * @see Label
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class Edge {
|
|
-
|
|
- /**
|
|
- * A control flow graph edge corresponding to a jump or ret instruction. Only used with {@link
|
|
- * ClassWriter#COMPUTE_FRAMES}.
|
|
- */
|
|
- static final int JUMP = 0;
|
|
-
|
|
- /**
|
|
- * A control flow graph edge corresponding to an exception handler. Only used with {@link
|
|
- * ClassWriter#COMPUTE_MAXS}.
|
|
- */
|
|
- static final int EXCEPTION = 0x7FFFFFFF;
|
|
-
|
|
- /**
|
|
- * Information about this control flow graph edge.
|
|
- *
|
|
- * <ul>
|
|
- * <li>If {@link ClassWriter#COMPUTE_MAXS} is used, this field contains either a stack size
|
|
- * delta (for an edge corresponding to a jump instruction), or the value EXCEPTION (for an
|
|
- * edge corresponding to an exception handler). The stack size delta is the stack size just
|
|
- * after the jump instruction, minus the stack size at the beginning of the predecessor
|
|
- * basic block, i.e. the one containing the jump instruction.
|
|
- * <li>If {@link ClassWriter#COMPUTE_FRAMES} is used, this field contains either the value JUMP
|
|
- * (for an edge corresponding to a jump instruction), or the index, in the {@link
|
|
- * ClassWriter} type table, of the exception type that is handled (for an edge corresponding
|
|
- * to an exception handler).
|
|
- * </ul>
|
|
- */
|
|
- final int info;
|
|
-
|
|
- /** The successor block of this control flow graph edge. */
|
|
- final Label successor;
|
|
-
|
|
- /**
|
|
- * The next edge in the list of outgoing edges of a basic block. See {@link Label#outgoingEdges}.
|
|
- */
|
|
- Edge nextEdge;
|
|
-
|
|
- /**
|
|
- * Constructs a new Edge.
|
|
- *
|
|
- * @param info see {@link #info}.
|
|
- * @param successor see {@link #successor}.
|
|
- * @param nextEdge see {@link #nextEdge}.
|
|
- */
|
|
- Edge(final int info, final Label successor, final Edge nextEdge) {
|
|
- this.info = info;
|
|
- this.successor = successor;
|
|
- this.nextEdge = nextEdge;
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/FieldVisitor.java b/src/main/java/org/mvel2/asm/FieldVisitor.java
|
|
deleted file mode 100644
|
|
index 6afe4ee..0000000
|
|
--- a/src/main/java/org/mvel2/asm/FieldVisitor.java
|
|
+++ /dev/null
|
|
@@ -1,133 +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 field. The methods of this class must be called in the following order:
|
|
- * ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code
|
|
- * visitEnd}.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public abstract class FieldVisitor {
|
|
-
|
|
- /**
|
|
- * 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 field visitor to which this visitor must delegate method calls. May be null. */
|
|
- protected FieldVisitor fv;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link FieldVisitor}.
|
|
- *
|
|
- * @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 FieldVisitor(final int api) {
|
|
- this(api, null);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link FieldVisitor}.
|
|
- *
|
|
- * @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 fieldVisitor the field visitor to which this visitor must delegate method calls. May be
|
|
- * null.
|
|
- */
|
|
- public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- this.api = api;
|
|
- this.fv = fieldVisitor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation of the field.
|
|
- *
|
|
- * @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 (fv != null) {
|
|
- return fv.visitAnnotation(descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation on the type of the field.
|
|
- *
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
|
- * {@link TypeReference#FIELD}. 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 (fv != null) {
|
|
- return fv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a non standard attribute of the field.
|
|
- *
|
|
- * @param attribute an attribute.
|
|
- */
|
|
- public void visitAttribute(final Attribute attribute) {
|
|
- if (fv != null) {
|
|
- fv.visitAttribute(attribute);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the end of the field. This method, which is the last one to be called, is used to inform
|
|
- * the visitor that all the annotations and attributes of the field have been visited.
|
|
- */
|
|
- public void visitEnd() {
|
|
- if (fv != null) {
|
|
- fv.visitEnd();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/FieldWriter.java b/src/main/java/org/mvel2/asm/FieldWriter.java
|
|
deleted file mode 100644
|
|
index 196fb9b..0000000
|
|
--- a/src/main/java/org/mvel2/asm/FieldWriter.java
|
|
+++ /dev/null
|
|
@@ -1,346 +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 FieldVisitor} that generates a corresponding 'field_info' structure, 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.5">JVMS
|
|
- * 4.5</a>
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class FieldWriter extends FieldVisitor {
|
|
-
|
|
- /** Where the constants used in this FieldWriter must be stored. */
|
|
- private final SymbolTable symbolTable;
|
|
-
|
|
- // Note: fields are ordered as in the field_info structure, and those related to attributes are
|
|
- // ordered as in Section 4.7 of the JVMS.
|
|
-
|
|
- /**
|
|
- * The access_flags field of the field_info JVMS structure. This field can contain ASM specific
|
|
- * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
|
|
- * ClassFile structure.
|
|
- */
|
|
- private final int accessFlags;
|
|
-
|
|
- /** The name_index field of the field_info JVMS structure. */
|
|
- private final int nameIndex;
|
|
-
|
|
- /** The descriptor_index field of the field_info JVMS structure. */
|
|
- private final int descriptorIndex;
|
|
-
|
|
- /**
|
|
- * The signature_index field of the Signature attribute of this field_info, or 0 if there is no
|
|
- * Signature attribute.
|
|
- */
|
|
- private int signatureIndex;
|
|
-
|
|
- /**
|
|
- * The constantvalue_index field of the ConstantValue attribute of this field_info, or 0 if there
|
|
- * is no ConstantValue attribute.
|
|
- */
|
|
- private int constantValueIndex;
|
|
-
|
|
- /**
|
|
- * The last runtime visible annotation of this field. 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 field. 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 field. 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 field. The previous ones can be accessed
|
|
- * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
|
|
-
|
|
- /**
|
|
- * The first non standard attribute of this field. 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
|
|
- * #putFieldInfo} method writes the attributes in the order defined by this list, i.e. in the
|
|
- * reverse order specified by the user.
|
|
- */
|
|
- private Attribute firstAttribute;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Constructor
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link FieldWriter}.
|
|
- *
|
|
- * @param symbolTable where the constants used in this FieldWriter must be stored.
|
|
- * @param access the field's access flags (see {@link Opcodes}).
|
|
- * @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}.
|
|
- * @param constantValue the field's constant value. May be {@literal null}.
|
|
- */
|
|
- FieldWriter(
|
|
- final SymbolTable symbolTable,
|
|
- final int access,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final String signature,
|
|
- final Object constantValue) {
|
|
- super(Opcodes.ASM7);
|
|
- this.symbolTable = symbolTable;
|
|
- this.accessFlags = access;
|
|
- this.nameIndex = symbolTable.addConstantUtf8(name);
|
|
- this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
|
|
- if (signature != null) {
|
|
- this.signatureIndex = symbolTable.addConstantUtf8(signature);
|
|
- }
|
|
- if (constantValue != null) {
|
|
- this.constantValueIndex = symbolTable.addConstant(constantValue).index;
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Implementation of the FieldVisitor abstract class
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- @Override
|
|
- public 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 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 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 visitEnd() {
|
|
- // Nothing to do.
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Utility methods
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the size of the field_info JVMS structure generated by this FieldWriter. Also adds the
|
|
- * names of the attributes of this field in the constant pool.
|
|
- *
|
|
- * @return the size in bytes of the field_info JVMS structure.
|
|
- */
|
|
- int computeFieldInfoSize() {
|
|
- // The access_flags, name_index, descriptor_index and attributes_count fields use 8 bytes.
|
|
- int size = 8;
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- if (constantValueIndex != 0) {
|
|
- // ConstantValue attributes always use 8 bytes.
|
|
- symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE);
|
|
- size += 8;
|
|
- }
|
|
- // Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
|
|
- && symbolTable.getMajorVersion() < Opcodes.V1_5) {
|
|
- // Synthetic attributes always use 6 bytes.
|
|
- symbolTable.addConstantUtf8(Constants.SYNTHETIC);
|
|
- size += 6;
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- // Signature attributes always use 8 bytes.
|
|
- symbolTable.addConstantUtf8(Constants.SIGNATURE);
|
|
- size += 8;
|
|
- }
|
|
- // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- // Deprecated attributes always use 6 bytes.
|
|
- symbolTable.addConstantUtf8(Constants.DEPRECATED);
|
|
- size += 6;
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeVisibleAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- size += firstAttribute.computeAttributesSize(symbolTable);
|
|
- }
|
|
- return size;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the content of the field_info JVMS structure generated by this FieldWriter into the given
|
|
- * ByteVector.
|
|
- *
|
|
- * @param output where the field_info structure must be put.
|
|
- */
|
|
- void putFieldInfo(final ByteVector output) {
|
|
- boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
|
|
- // Put the access_flags, name_index and descriptor_index fields.
|
|
- int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0;
|
|
- output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
|
|
- // Compute and put the attributes_count field.
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- int attributesCount = 0;
|
|
- if (constantValueIndex != 0) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- ++attributesCount;
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- attributesCount += firstAttribute.getAttributeCount();
|
|
- }
|
|
- output.putShort(attributesCount);
|
|
- // Put the field_info attributes.
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- if (constantValueIndex != 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE))
|
|
- .putInt(2)
|
|
- .putShort(constantValueIndex);
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
|
- output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
|
|
- .putInt(2)
|
|
- .putShort(signatureIndex);
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- lastRuntimeVisibleAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- lastRuntimeInvisibleAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- lastRuntimeVisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- lastRuntimeInvisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- firstAttribute.putAttributes(symbolTable, output);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Collects the attributes of this field into the given set of attribute prototypes.
|
|
- *
|
|
- * @param attributePrototypes a set of attribute prototypes.
|
|
- */
|
|
- final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
|
|
- attributePrototypes.addAttributes(firstAttribute);
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Frame.java b/src/main/java/org/mvel2/asm/Frame.java
|
|
deleted file mode 100644
|
|
index 8efe358..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Frame.java
|
|
+++ /dev/null
|
|
@@ -1,1468 +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;
|
|
-
|
|
-/**
|
|
- * The input and output stack map frames of a basic block.
|
|
- *
|
|
- * <p>Stack map frames are computed in two steps:
|
|
- *
|
|
- * <ul>
|
|
- * <li>During the visit of each instruction in MethodWriter, the state of the frame at the end of
|
|
- * the current basic block is updated by simulating the action of the instruction on the
|
|
- * previous state of this so called "output frame".
|
|
- * <li>After all instructions have been visited, a fix point algorithm is used in MethodWriter to
|
|
- * compute the "input frame" of each basic block (i.e. the stack map frame at the beginning of
|
|
- * the basic block). See {@link MethodWriter#computeAllFrames}.
|
|
- * </ul>
|
|
- *
|
|
- * <p>Output stack map frames are computed relatively to the input frame of the basic block, which
|
|
- * is not yet known when output frames are computed. It is therefore necessary to be able to
|
|
- * represent abstract types such as "the type at position x in the input frame locals" or "the type
|
|
- * at position x from the top of the input frame stack" or even "the type at position x in the input
|
|
- * frame, with y more (or less) array dimensions". This explains the rather complicated type format
|
|
- * used in this class, explained below.
|
|
- *
|
|
- * <p>The local variables and the operand stack of input and output frames contain values called
|
|
- * "abstract types" hereafter. An abstract type is represented with 4 fields named DIM, KIND, FLAGS
|
|
- * and VALUE, packed in a single int value for better performance and memory efficiency:
|
|
- *
|
|
- * <pre>
|
|
- * =====================================
|
|
- * |.DIM|KIND|FLAG|...............VALUE|
|
|
- * =====================================
|
|
- * </pre>
|
|
- *
|
|
- * <ul>
|
|
- * <li>the DIM field, stored in the 4 most significant bits, is a signed number of array
|
|
- * dimensions (from -8 to 7, included). It can be retrieved with {@link #DIM_MASK} and a right
|
|
- * shift of {@link #DIM_SHIFT}.
|
|
- * <li>the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be
|
|
- * retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link
|
|
- * #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND}
|
|
- * or {@link #STACK_KIND}.
|
|
- * <li>the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag
|
|
- * is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
|
|
- * <li>the VALUE field, stored in the remaining 20 bits, contains either
|
|
- * <ul>
|
|
- * <li>one of the constants {@link #ITEM_TOP}, {@link #ITEM_ASM_BOOLEAN}, {@link
|
|
- * #ITEM_ASM_BYTE}, {@link #ITEM_ASM_CHAR} or {@link #ITEM_ASM_SHORT}, {@link
|
|
- * #ITEM_INTEGER}, {@link #ITEM_FLOAT}, {@link #ITEM_LONG}, {@link #ITEM_DOUBLE}, {@link
|
|
- * #ITEM_NULL} or {@link #ITEM_UNINITIALIZED_THIS}, if KIND is equal to {@link
|
|
- * #CONSTANT_KIND}.
|
|
- * <li>the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link
|
|
- * SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}.
|
|
- * <li>the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type
|
|
- * table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}.
|
|
- * <li>the index of a local variable in the input stack frame, if KIND is equal to {@link
|
|
- * #LOCAL_KIND}.
|
|
- * <li>a position relatively to the top of the stack of the input stack frame, if KIND is
|
|
- * equal to {@link #STACK_KIND},
|
|
- * </ul>
|
|
- * </ul>
|
|
- *
|
|
- * <p>Output frames can contain abstract types of any kind and with a positive or negative array
|
|
- * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid
|
|
- * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or
|
|
- * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type
|
|
- * table contains only internal type names (array type descriptors are forbidden - array dimensions
|
|
- * must be represented through the DIM field).
|
|
- *
|
|
- * <p>The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE +
|
|
- * TOP), for local variables as well as in the operand stack. This is necessary to be able to
|
|
- * simulate DUPx_y instructions, whose effect would be dependent on the concrete types represented
|
|
- * by the abstract types in the stack (which are not always known).
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-class Frame {
|
|
-
|
|
- // Constants used in the StackMapTable attribute.
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4.
|
|
-
|
|
- static final int SAME_FRAME = 0;
|
|
- static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64;
|
|
- static final int RESERVED = 128;
|
|
- static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247;
|
|
- static final int CHOP_FRAME = 248;
|
|
- static final int SAME_FRAME_EXTENDED = 251;
|
|
- static final int APPEND_FRAME = 252;
|
|
- static final int FULL_FRAME = 255;
|
|
-
|
|
- static final int ITEM_TOP = 0;
|
|
- static final int ITEM_INTEGER = 1;
|
|
- static final int ITEM_FLOAT = 2;
|
|
- static final int ITEM_DOUBLE = 3;
|
|
- static final int ITEM_LONG = 4;
|
|
- static final int ITEM_NULL = 5;
|
|
- static final int ITEM_UNINITIALIZED_THIS = 6;
|
|
- static final int ITEM_OBJECT = 7;
|
|
- static final int ITEM_UNINITIALIZED = 8;
|
|
- // Additional, ASM specific constants used in abstract types below.
|
|
- private static final int ITEM_ASM_BOOLEAN = 9;
|
|
- private static final int ITEM_ASM_BYTE = 10;
|
|
- private static final int ITEM_ASM_CHAR = 11;
|
|
- private static final int ITEM_ASM_SHORT = 12;
|
|
-
|
|
- // Bitmasks to get each field of an abstract type.
|
|
-
|
|
- private static final int DIM_MASK = 0xF0000000;
|
|
- private static final int KIND_MASK = 0x0F000000;
|
|
- private static final int FLAGS_MASK = 0x00F00000;
|
|
- private static final int VALUE_MASK = 0x000FFFFF;
|
|
-
|
|
- // Constants to manipulate the DIM field of an abstract type.
|
|
-
|
|
- /** The number of right shift bits to use to get the array dimensions of an abstract type. */
|
|
- private static final int DIM_SHIFT = 28;
|
|
-
|
|
- /** The constant to be added to an abstract type to get one with one more array dimension. */
|
|
- private static final int ARRAY_OF = +1 << DIM_SHIFT;
|
|
-
|
|
- /** The constant to be added to an abstract type to get one with one less array dimension. */
|
|
- private static final int ELEMENT_OF = -1 << DIM_SHIFT;
|
|
-
|
|
- // Possible values for the KIND field of an abstract type.
|
|
-
|
|
- private static final int CONSTANT_KIND = 0x01000000;
|
|
- private static final int REFERENCE_KIND = 0x02000000;
|
|
- private static final int UNINITIALIZED_KIND = 0x03000000;
|
|
- private static final int LOCAL_KIND = 0x04000000;
|
|
- private static final int STACK_KIND = 0x05000000;
|
|
-
|
|
- // Possible flags for the FLAGS field of an abstract type.
|
|
-
|
|
- /**
|
|
- * A flag used for LOCAL_KIND and STACK_KIND abstract types, indicating that if the resolved,
|
|
- * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been
|
|
- * partially overridden with an xSTORE instruction).
|
|
- */
|
|
- private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK;
|
|
-
|
|
- // Useful predefined abstract types (all the possible CONSTANT_KIND types).
|
|
-
|
|
- private static final int TOP = CONSTANT_KIND | ITEM_TOP;
|
|
- private static final int BOOLEAN = CONSTANT_KIND | ITEM_ASM_BOOLEAN;
|
|
- private static final int BYTE = CONSTANT_KIND | ITEM_ASM_BYTE;
|
|
- private static final int CHAR = CONSTANT_KIND | ITEM_ASM_CHAR;
|
|
- private static final int SHORT = CONSTANT_KIND | ITEM_ASM_SHORT;
|
|
- private static final int INTEGER = CONSTANT_KIND | ITEM_INTEGER;
|
|
- private static final int FLOAT = CONSTANT_KIND | ITEM_FLOAT;
|
|
- private static final int LONG = CONSTANT_KIND | ITEM_LONG;
|
|
- private static final int DOUBLE = CONSTANT_KIND | ITEM_DOUBLE;
|
|
- private static final int NULL = CONSTANT_KIND | ITEM_NULL;
|
|
- private static final int UNINITIALIZED_THIS = CONSTANT_KIND | ITEM_UNINITIALIZED_THIS;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Instance fields
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /** The basic block to which these input and output stack map frames correspond. */
|
|
- Label owner;
|
|
-
|
|
- /** The input stack map frame locals. This is an array of abstract types. */
|
|
- private int[] inputLocals;
|
|
-
|
|
- /** The input stack map frame stack. This is an array of abstract types. */
|
|
- private int[] inputStack;
|
|
-
|
|
- /** The output stack map frame locals. This is an array of abstract types. */
|
|
- private int[] outputLocals;
|
|
-
|
|
- /** The output stack map frame stack. This is an array of abstract types. */
|
|
- private int[] outputStack;
|
|
-
|
|
- /**
|
|
- * The start of the output stack, relatively to the input stack. This offset is always negative or
|
|
- * null. A null offset means that the output stack must be appended to the input stack. A -n
|
|
- * offset means that the first n output stack elements must replace the top n input stack
|
|
- * elements, and that the other elements must be appended to the input stack.
|
|
- */
|
|
- private short outputStackStart;
|
|
-
|
|
- /** The index of the top stack element in {@link #outputStack}. */
|
|
- private short outputStackTop;
|
|
-
|
|
- /** The number of types that are initialized in the basic block. See {@link #initializations}. */
|
|
- private int initializationCount;
|
|
-
|
|
- /**
|
|
- * The abstract types that are initialized in the basic block. A constructor invocation on an
|
|
- * UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace <i>every occurrence</i> of this
|
|
- * type in the local variables and in the operand stack. This cannot be done during the first step
|
|
- * of the algorithm since, during this step, the local variables and the operand stack types are
|
|
- * still abstract. It is therefore necessary to store the abstract types of the constructors which
|
|
- * are invoked in the basic block, in order to do this replacement during the second step of the
|
|
- * algorithm, where the frames are fully computed. Note that this array can contain abstract types
|
|
- * that are relative to the input locals or to the input stack.
|
|
- */
|
|
- private int[] initializations;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Constructor
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Constructs a new Frame.
|
|
- *
|
|
- * @param owner the basic block to which these input and output stack map frames correspond.
|
|
- */
|
|
- Frame(final Label owner) {
|
|
- this.owner = owner;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets this frame to the value of the given frame.
|
|
- *
|
|
- * <p>WARNING: after this method is called the two frames share the same data structures. It is
|
|
- * recommended to discard the given frame to avoid unexpected side effects.
|
|
- *
|
|
- * @param frame The new frame value.
|
|
- */
|
|
- final void copyFrom(final Frame frame) {
|
|
- inputLocals = frame.inputLocals;
|
|
- inputStack = frame.inputStack;
|
|
- outputStackStart = 0;
|
|
- outputLocals = frame.outputLocals;
|
|
- outputStack = frame.outputStack;
|
|
- outputStackTop = frame.outputStackTop;
|
|
- initializationCount = frame.initializationCount;
|
|
- initializations = frame.initializations;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Static methods to get abstract types from other type formats
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the abstract type corresponding to the given public API frame element type.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param type a frame element type described using the same format as in {@link
|
|
- * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
|
- * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or
|
|
- * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating
|
|
- * a NEW instruction (for uninitialized types).
|
|
- * @return the abstract type corresponding to the given frame element type.
|
|
- */
|
|
- static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Object type) {
|
|
- if (type instanceof Integer) {
|
|
- return CONSTANT_KIND | ((Integer) type).intValue();
|
|
- } else if (type instanceof String) {
|
|
- String descriptor = Type.getObjectType((String) type).getDescriptor();
|
|
- return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0);
|
|
- } else {
|
|
- return UNINITIALIZED_KIND
|
|
- | symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the abstract type corresponding to the internal name of a class.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param internalName the internal name of a class. This must <i>not</i> be an array type
|
|
- * descriptor.
|
|
- * @return the abstract type value corresponding to the given internal name.
|
|
- */
|
|
- static int getAbstractTypeFromInternalName(
|
|
- final SymbolTable symbolTable, final String internalName) {
|
|
- return REFERENCE_KIND | symbolTable.addType(internalName);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the abstract type corresponding to the given type descriptor.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param buffer a string ending with a type descriptor.
|
|
- * @param offset the start offset of the type descriptor in buffer.
|
|
- * @return the abstract type corresponding to the given type descriptor.
|
|
- */
|
|
- private static int getAbstractTypeFromDescriptor(
|
|
- final SymbolTable symbolTable, final String buffer, final int offset) {
|
|
- String internalName;
|
|
- switch (buffer.charAt(offset)) {
|
|
- case 'V':
|
|
- return 0;
|
|
- case 'Z':
|
|
- case 'C':
|
|
- case 'B':
|
|
- case 'S':
|
|
- case 'I':
|
|
- return INTEGER;
|
|
- case 'F':
|
|
- return FLOAT;
|
|
- case 'J':
|
|
- return LONG;
|
|
- case 'D':
|
|
- return DOUBLE;
|
|
- case 'L':
|
|
- internalName = buffer.substring(offset + 1, buffer.length() - 1);
|
|
- return REFERENCE_KIND | symbolTable.addType(internalName);
|
|
- case '[':
|
|
- int elementDescriptorOffset = offset + 1;
|
|
- while (buffer.charAt(elementDescriptorOffset) == '[') {
|
|
- ++elementDescriptorOffset;
|
|
- }
|
|
- int typeValue;
|
|
- switch (buffer.charAt(elementDescriptorOffset)) {
|
|
- case 'Z':
|
|
- typeValue = BOOLEAN;
|
|
- break;
|
|
- case 'C':
|
|
- typeValue = CHAR;
|
|
- break;
|
|
- case 'B':
|
|
- typeValue = BYTE;
|
|
- break;
|
|
- case 'S':
|
|
- typeValue = SHORT;
|
|
- break;
|
|
- case 'I':
|
|
- typeValue = INTEGER;
|
|
- break;
|
|
- case 'F':
|
|
- typeValue = FLOAT;
|
|
- break;
|
|
- case 'J':
|
|
- typeValue = LONG;
|
|
- break;
|
|
- case 'D':
|
|
- typeValue = DOUBLE;
|
|
- break;
|
|
- case 'L':
|
|
- internalName = buffer.substring(elementDescriptorOffset + 1, buffer.length() - 1);
|
|
- typeValue = REFERENCE_KIND | symbolTable.addType(internalName);
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- return ((elementDescriptorOffset - offset) << DIM_SHIFT) | typeValue;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods related to the input frame
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Sets the input frame from the given method description. This method is used to initialize the
|
|
- * first frame of a method, which is implicit (i.e. not stored explicitly in the StackMapTable
|
|
- * attribute).
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param access the method's access flags.
|
|
- * @param descriptor the method descriptor.
|
|
- * @param maxLocals the maximum number of local variables of the method.
|
|
- */
|
|
- final void setInputFrameFromDescriptor(
|
|
- final SymbolTable symbolTable,
|
|
- final int access,
|
|
- final String descriptor,
|
|
- final int maxLocals) {
|
|
- inputLocals = new int[maxLocals];
|
|
- inputStack = new int[0];
|
|
- int inputLocalIndex = 0;
|
|
- if ((access & Opcodes.ACC_STATIC) == 0) {
|
|
- if ((access & Constants.ACC_CONSTRUCTOR) == 0) {
|
|
- inputLocals[inputLocalIndex++] =
|
|
- REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName());
|
|
- } else {
|
|
- inputLocals[inputLocalIndex++] = UNINITIALIZED_THIS;
|
|
- }
|
|
- }
|
|
- for (Type argumentType : Type.getArgumentTypes(descriptor)) {
|
|
- int abstractType =
|
|
- getAbstractTypeFromDescriptor(symbolTable, argumentType.getDescriptor(), 0);
|
|
- inputLocals[inputLocalIndex++] = abstractType;
|
|
- if (abstractType == LONG || abstractType == DOUBLE) {
|
|
- inputLocals[inputLocalIndex++] = TOP;
|
|
- }
|
|
- }
|
|
- while (inputLocalIndex < maxLocals) {
|
|
- inputLocals[inputLocalIndex++] = TOP;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the input frame from the given public API frame description.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param numLocal the number of local variables.
|
|
- * @param local the local variable types, described using the same format as in {@link
|
|
- * MethodVisitor#visitFrame}.
|
|
- * @param numStack the number of operand stack elements.
|
|
- * @param stack the operand stack types, described using the same format as in {@link
|
|
- * MethodVisitor#visitFrame}.
|
|
- */
|
|
- final void setInputFrameFromApiFormat(
|
|
- final SymbolTable symbolTable,
|
|
- final int numLocal,
|
|
- final Object[] local,
|
|
- final int numStack,
|
|
- final Object[] stack) {
|
|
- int inputLocalIndex = 0;
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- inputLocals[inputLocalIndex++] = getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
|
- if (local[i] == Opcodes.LONG || local[i] == Opcodes.DOUBLE) {
|
|
- inputLocals[inputLocalIndex++] = TOP;
|
|
- }
|
|
- }
|
|
- while (inputLocalIndex < inputLocals.length) {
|
|
- inputLocals[inputLocalIndex++] = TOP;
|
|
- }
|
|
- int numStackTop = 0;
|
|
- for (int i = 0; i < numStack; ++i) {
|
|
- if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
|
- ++numStackTop;
|
|
- }
|
|
- }
|
|
- inputStack = new int[numStack + numStackTop];
|
|
- int inputStackIndex = 0;
|
|
- for (int i = 0; i < numStack; ++i) {
|
|
- inputStack[inputStackIndex++] = getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
|
- if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
|
- inputStack[inputStackIndex++] = TOP;
|
|
- }
|
|
- }
|
|
- outputStackTop = 0;
|
|
- initializationCount = 0;
|
|
- }
|
|
-
|
|
- final int getInputStackSize() {
|
|
- return inputStack.length;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods related to the output frame
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the abstract type stored at the given local variable index in the output frame.
|
|
- *
|
|
- * @param localIndex the index of the local variable whose value must be returned.
|
|
- * @return the abstract type stored at the given local variable index in the output frame.
|
|
- */
|
|
- private int getLocal(final int localIndex) {
|
|
- if (outputLocals == null || localIndex >= outputLocals.length) {
|
|
- // If this local has never been assigned in this basic block, it is still equal to its value
|
|
- // in the input frame.
|
|
- return LOCAL_KIND | localIndex;
|
|
- } else {
|
|
- int abstractType = outputLocals[localIndex];
|
|
- if (abstractType == 0) {
|
|
- // If this local has never been assigned in this basic block, so it is still equal to its
|
|
- // value in the input frame.
|
|
- abstractType = outputLocals[localIndex] = LOCAL_KIND | localIndex;
|
|
- }
|
|
- return abstractType;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Replaces the abstract type stored at the given local variable index in the output frame.
|
|
- *
|
|
- * @param localIndex the index of the output frame local variable that must be set.
|
|
- * @param abstractType the value that must be set.
|
|
- */
|
|
- private void setLocal(final int localIndex, final int abstractType) {
|
|
- // Create and/or resize the output local variables array if necessary.
|
|
- if (outputLocals == null) {
|
|
- outputLocals = new int[10];
|
|
- }
|
|
- int outputLocalsLength = outputLocals.length;
|
|
- if (localIndex >= outputLocalsLength) {
|
|
- int[] newOutputLocals = new int[Math.max(localIndex + 1, 2 * outputLocalsLength)];
|
|
- System.arraycopy(outputLocals, 0, newOutputLocals, 0, outputLocalsLength);
|
|
- outputLocals = newOutputLocals;
|
|
- }
|
|
- // Set the local variable.
|
|
- outputLocals[localIndex] = abstractType;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Pushes the given abstract type on the output frame stack.
|
|
- *
|
|
- * @param abstractType an abstract type.
|
|
- */
|
|
- private void push(final int abstractType) {
|
|
- // Create and/or resize the output stack array if necessary.
|
|
- if (outputStack == null) {
|
|
- outputStack = new int[10];
|
|
- }
|
|
- int outputStackLength = outputStack.length;
|
|
- if (outputStackTop >= outputStackLength) {
|
|
- int[] newOutputStack = new int[Math.max(outputStackTop + 1, 2 * outputStackLength)];
|
|
- System.arraycopy(outputStack, 0, newOutputStack, 0, outputStackLength);
|
|
- outputStack = newOutputStack;
|
|
- }
|
|
- // Pushes the abstract type on the output stack.
|
|
- outputStack[outputStackTop++] = abstractType;
|
|
- // Updates the maximum size reached by the output stack, if needed (note that this size is
|
|
- // relative to the input stack size, which is not known yet).
|
|
- short outputStackSize = (short) (outputStackStart + outputStackTop);
|
|
- if (outputStackSize > owner.outputStackMax) {
|
|
- owner.outputStackMax = outputStackSize;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Pushes the abstract type corresponding to the given descriptor on the output frame stack.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param descriptor a type or method descriptor (in which case its return type is pushed).
|
|
- */
|
|
- private void push(final SymbolTable symbolTable, final String descriptor) {
|
|
- int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0;
|
|
- int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset);
|
|
- if (abstractType != 0) {
|
|
- push(abstractType);
|
|
- if (abstractType == LONG || abstractType == DOUBLE) {
|
|
- push(TOP);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Pops an abstract type from the output frame stack and returns its value.
|
|
- *
|
|
- * @return the abstract type that has been popped from the output frame stack.
|
|
- */
|
|
- private int pop() {
|
|
- if (outputStackTop > 0) {
|
|
- return outputStack[--outputStackTop];
|
|
- } else {
|
|
- // If the output frame stack is empty, pop from the input stack.
|
|
- return STACK_KIND | -(--outputStackStart);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Pops the given number of abstract types from the output frame stack.
|
|
- *
|
|
- * @param elements the number of abstract types that must be popped.
|
|
- */
|
|
- private void pop(final int elements) {
|
|
- if (outputStackTop >= elements) {
|
|
- outputStackTop -= elements;
|
|
- } else {
|
|
- // If the number of elements to be popped is greater than the number of elements in the output
|
|
- // stack, clear it, and pop the remaining elements from the input stack.
|
|
- outputStackStart -= elements - outputStackTop;
|
|
- outputStackTop = 0;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Pops as many abstract types from the output frame stack as described by the given descriptor.
|
|
- *
|
|
- * @param descriptor a type or method descriptor (in which case its argument types are popped).
|
|
- */
|
|
- private void pop(final String descriptor) {
|
|
- char firstDescriptorChar = descriptor.charAt(0);
|
|
- if (firstDescriptorChar == '(') {
|
|
- pop((Type.getArgumentsAndReturnSizes(descriptor) >> 2) - 1);
|
|
- } else if (firstDescriptorChar == 'J' || firstDescriptorChar == 'D') {
|
|
- pop(2);
|
|
- } else {
|
|
- pop(1);
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to handle uninitialized types
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Adds an abstract type to the list of types on which a constructor is invoked in the basic
|
|
- * block.
|
|
- *
|
|
- * @param abstractType an abstract type on a which a constructor is invoked.
|
|
- */
|
|
- private void addInitializedType(final int abstractType) {
|
|
- // Create and/or resize the initializations array if necessary.
|
|
- if (initializations == null) {
|
|
- initializations = new int[2];
|
|
- }
|
|
- int initializationsLength = initializations.length;
|
|
- if (initializationCount >= initializationsLength) {
|
|
- int[] newInitializations =
|
|
- new int[Math.max(initializationCount + 1, 2 * initializationsLength)];
|
|
- System.arraycopy(initializations, 0, newInitializations, 0, initializationsLength);
|
|
- initializations = newInitializations;
|
|
- }
|
|
- // Store the abstract type.
|
|
- initializations[initializationCount++] = abstractType;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the "initialized" abstract type corresponding to the given abstract type.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param abstractType an abstract type.
|
|
- * @return the REFERENCE_KIND abstract type corresponding to abstractType if it is
|
|
- * UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a
|
|
- * constructor is invoked in the basic block. Otherwise returns abstractType.
|
|
- */
|
|
- private int getInitializedType(final SymbolTable symbolTable, final int abstractType) {
|
|
- if (abstractType == UNINITIALIZED_THIS
|
|
- || (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) {
|
|
- for (int i = 0; i < initializationCount; ++i) {
|
|
- int initializedType = initializations[i];
|
|
- int dim = initializedType & DIM_MASK;
|
|
- int kind = initializedType & KIND_MASK;
|
|
- int value = initializedType & VALUE_MASK;
|
|
- if (kind == LOCAL_KIND) {
|
|
- initializedType = dim + inputLocals[value];
|
|
- } else if (kind == STACK_KIND) {
|
|
- initializedType = dim + inputStack[inputStack.length - value];
|
|
- }
|
|
- if (abstractType == initializedType) {
|
|
- if (abstractType == UNINITIALIZED_THIS) {
|
|
- return REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName());
|
|
- } else {
|
|
- return REFERENCE_KIND
|
|
- | symbolTable.addType(symbolTable.getType(abstractType & VALUE_MASK).value);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
- return abstractType;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Main method, to simulate the execution of each instruction on the output frame
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Simulates the action of the given instruction on the output stack frame.
|
|
- *
|
|
- * @param opcode the opcode of the instruction.
|
|
- * @param arg the numeric operand of the instruction, if any.
|
|
- * @param argSymbol the Symbol operand of the instruction, if any.
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- */
|
|
- void execute(
|
|
- final int opcode, final int arg, final Symbol argSymbol, final SymbolTable symbolTable) {
|
|
- // Abstract types popped from the stack or read from local variables.
|
|
- int abstractType1;
|
|
- int abstractType2;
|
|
- int abstractType3;
|
|
- int abstractType4;
|
|
- switch (opcode) {
|
|
- case Opcodes.NOP:
|
|
- case Opcodes.INEG:
|
|
- case Opcodes.LNEG:
|
|
- case Opcodes.FNEG:
|
|
- case Opcodes.DNEG:
|
|
- case Opcodes.I2B:
|
|
- case Opcodes.I2C:
|
|
- case Opcodes.I2S:
|
|
- case Opcodes.GOTO:
|
|
- case Opcodes.RETURN:
|
|
- break;
|
|
- case Opcodes.ACONST_NULL:
|
|
- push(NULL);
|
|
- break;
|
|
- case Opcodes.ICONST_M1:
|
|
- case Opcodes.ICONST_0:
|
|
- case Opcodes.ICONST_1:
|
|
- case Opcodes.ICONST_2:
|
|
- case Opcodes.ICONST_3:
|
|
- case Opcodes.ICONST_4:
|
|
- case Opcodes.ICONST_5:
|
|
- case Opcodes.BIPUSH:
|
|
- case Opcodes.SIPUSH:
|
|
- case Opcodes.ILOAD:
|
|
- push(INTEGER);
|
|
- break;
|
|
- case Opcodes.LCONST_0:
|
|
- case Opcodes.LCONST_1:
|
|
- case Opcodes.LLOAD:
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.FCONST_0:
|
|
- case Opcodes.FCONST_1:
|
|
- case Opcodes.FCONST_2:
|
|
- case Opcodes.FLOAD:
|
|
- push(FLOAT);
|
|
- break;
|
|
- case Opcodes.DCONST_0:
|
|
- case Opcodes.DCONST_1:
|
|
- case Opcodes.DLOAD:
|
|
- push(DOUBLE);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.LDC:
|
|
- switch (argSymbol.tag) {
|
|
- case Symbol.CONSTANT_INTEGER_TAG:
|
|
- push(INTEGER);
|
|
- break;
|
|
- case Symbol.CONSTANT_LONG_TAG:
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Symbol.CONSTANT_FLOAT_TAG:
|
|
- push(FLOAT);
|
|
- break;
|
|
- case Symbol.CONSTANT_DOUBLE_TAG:
|
|
- push(DOUBLE);
|
|
- push(TOP);
|
|
- break;
|
|
- case Symbol.CONSTANT_CLASS_TAG:
|
|
- push(REFERENCE_KIND | symbolTable.addType("java/lang/Class"));
|
|
- break;
|
|
- case Symbol.CONSTANT_STRING_TAG:
|
|
- push(REFERENCE_KIND | symbolTable.addType("java/lang/String"));
|
|
- break;
|
|
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
|
|
- push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodType"));
|
|
- break;
|
|
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
|
- push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodHandle"));
|
|
- break;
|
|
- case Symbol.CONSTANT_DYNAMIC_TAG:
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- break;
|
|
- case Opcodes.ALOAD:
|
|
- push(getLocal(arg));
|
|
- break;
|
|
- case Opcodes.LALOAD:
|
|
- case Opcodes.D2L:
|
|
- pop(2);
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.DALOAD:
|
|
- case Opcodes.L2D:
|
|
- pop(2);
|
|
- push(DOUBLE);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.AALOAD:
|
|
- pop(1);
|
|
- abstractType1 = pop();
|
|
- push(abstractType1 == NULL ? abstractType1 : ELEMENT_OF + abstractType1);
|
|
- break;
|
|
- case Opcodes.ISTORE:
|
|
- case Opcodes.FSTORE:
|
|
- case Opcodes.ASTORE:
|
|
- abstractType1 = pop();
|
|
- setLocal(arg, abstractType1);
|
|
- if (arg > 0) {
|
|
- int previousLocalType = getLocal(arg - 1);
|
|
- if (previousLocalType == LONG || previousLocalType == DOUBLE) {
|
|
- setLocal(arg - 1, TOP);
|
|
- } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND
|
|
- || (previousLocalType & KIND_MASK) == STACK_KIND) {
|
|
- // The type of the previous local variable is not known yet, but if it later appears
|
|
- // to be LONG or DOUBLE, we should then use TOP instead.
|
|
- setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG);
|
|
- }
|
|
- }
|
|
- break;
|
|
- case Opcodes.LSTORE:
|
|
- case Opcodes.DSTORE:
|
|
- pop(1);
|
|
- abstractType1 = pop();
|
|
- setLocal(arg, abstractType1);
|
|
- setLocal(arg + 1, TOP);
|
|
- if (arg > 0) {
|
|
- int previousLocalType = getLocal(arg - 1);
|
|
- if (previousLocalType == LONG || previousLocalType == DOUBLE) {
|
|
- setLocal(arg - 1, TOP);
|
|
- } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND
|
|
- || (previousLocalType & KIND_MASK) == STACK_KIND) {
|
|
- // The type of the previous local variable is not known yet, but if it later appears
|
|
- // to be LONG or DOUBLE, we should then use TOP instead.
|
|
- setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG);
|
|
- }
|
|
- }
|
|
- break;
|
|
- case Opcodes.IASTORE:
|
|
- case Opcodes.BASTORE:
|
|
- case Opcodes.CASTORE:
|
|
- case Opcodes.SASTORE:
|
|
- case Opcodes.FASTORE:
|
|
- case Opcodes.AASTORE:
|
|
- pop(3);
|
|
- break;
|
|
- case Opcodes.LASTORE:
|
|
- case Opcodes.DASTORE:
|
|
- pop(4);
|
|
- break;
|
|
- case Opcodes.POP:
|
|
- case Opcodes.IFEQ:
|
|
- case Opcodes.IFNE:
|
|
- case Opcodes.IFLT:
|
|
- case Opcodes.IFGE:
|
|
- case Opcodes.IFGT:
|
|
- case Opcodes.IFLE:
|
|
- case Opcodes.IRETURN:
|
|
- case Opcodes.FRETURN:
|
|
- case Opcodes.ARETURN:
|
|
- case Opcodes.TABLESWITCH:
|
|
- case Opcodes.LOOKUPSWITCH:
|
|
- case Opcodes.ATHROW:
|
|
- case Opcodes.MONITORENTER:
|
|
- case Opcodes.MONITOREXIT:
|
|
- case Opcodes.IFNULL:
|
|
- case Opcodes.IFNONNULL:
|
|
- pop(1);
|
|
- break;
|
|
- case Opcodes.POP2:
|
|
- case Opcodes.IF_ICMPEQ:
|
|
- case Opcodes.IF_ICMPNE:
|
|
- case Opcodes.IF_ICMPLT:
|
|
- case Opcodes.IF_ICMPGE:
|
|
- case Opcodes.IF_ICMPGT:
|
|
- case Opcodes.IF_ICMPLE:
|
|
- case Opcodes.IF_ACMPEQ:
|
|
- case Opcodes.IF_ACMPNE:
|
|
- case Opcodes.LRETURN:
|
|
- case Opcodes.DRETURN:
|
|
- pop(2);
|
|
- break;
|
|
- case Opcodes.DUP:
|
|
- abstractType1 = pop();
|
|
- push(abstractType1);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.DUP_X1:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- push(abstractType1);
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.DUP_X2:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- abstractType3 = pop();
|
|
- push(abstractType1);
|
|
- push(abstractType3);
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.DUP2:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.DUP2_X1:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- abstractType3 = pop();
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- push(abstractType3);
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.DUP2_X2:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- abstractType3 = pop();
|
|
- abstractType4 = pop();
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- push(abstractType4);
|
|
- push(abstractType3);
|
|
- push(abstractType2);
|
|
- push(abstractType1);
|
|
- break;
|
|
- case Opcodes.SWAP:
|
|
- abstractType1 = pop();
|
|
- abstractType2 = pop();
|
|
- push(abstractType1);
|
|
- push(abstractType2);
|
|
- break;
|
|
- case Opcodes.IALOAD:
|
|
- case Opcodes.BALOAD:
|
|
- case Opcodes.CALOAD:
|
|
- case Opcodes.SALOAD:
|
|
- case Opcodes.IADD:
|
|
- case Opcodes.ISUB:
|
|
- case Opcodes.IMUL:
|
|
- case Opcodes.IDIV:
|
|
- case Opcodes.IREM:
|
|
- case Opcodes.IAND:
|
|
- case Opcodes.IOR:
|
|
- case Opcodes.IXOR:
|
|
- case Opcodes.ISHL:
|
|
- case Opcodes.ISHR:
|
|
- case Opcodes.IUSHR:
|
|
- case Opcodes.L2I:
|
|
- case Opcodes.D2I:
|
|
- case Opcodes.FCMPL:
|
|
- case Opcodes.FCMPG:
|
|
- pop(2);
|
|
- push(INTEGER);
|
|
- break;
|
|
- case Opcodes.LADD:
|
|
- case Opcodes.LSUB:
|
|
- case Opcodes.LMUL:
|
|
- case Opcodes.LDIV:
|
|
- case Opcodes.LREM:
|
|
- case Opcodes.LAND:
|
|
- case Opcodes.LOR:
|
|
- case Opcodes.LXOR:
|
|
- pop(4);
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.FALOAD:
|
|
- case Opcodes.FADD:
|
|
- case Opcodes.FSUB:
|
|
- case Opcodes.FMUL:
|
|
- case Opcodes.FDIV:
|
|
- case Opcodes.FREM:
|
|
- case Opcodes.L2F:
|
|
- case Opcodes.D2F:
|
|
- pop(2);
|
|
- push(FLOAT);
|
|
- break;
|
|
- case Opcodes.DADD:
|
|
- case Opcodes.DSUB:
|
|
- case Opcodes.DMUL:
|
|
- case Opcodes.DDIV:
|
|
- case Opcodes.DREM:
|
|
- pop(4);
|
|
- push(DOUBLE);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.LSHL:
|
|
- case Opcodes.LSHR:
|
|
- case Opcodes.LUSHR:
|
|
- pop(3);
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.IINC:
|
|
- setLocal(arg, INTEGER);
|
|
- break;
|
|
- case Opcodes.I2L:
|
|
- case Opcodes.F2L:
|
|
- pop(1);
|
|
- push(LONG);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.I2F:
|
|
- pop(1);
|
|
- push(FLOAT);
|
|
- break;
|
|
- case Opcodes.I2D:
|
|
- case Opcodes.F2D:
|
|
- pop(1);
|
|
- push(DOUBLE);
|
|
- push(TOP);
|
|
- break;
|
|
- case Opcodes.F2I:
|
|
- case Opcodes.ARRAYLENGTH:
|
|
- case Opcodes.INSTANCEOF:
|
|
- pop(1);
|
|
- push(INTEGER);
|
|
- break;
|
|
- case Opcodes.LCMP:
|
|
- case Opcodes.DCMPL:
|
|
- case Opcodes.DCMPG:
|
|
- pop(4);
|
|
- push(INTEGER);
|
|
- break;
|
|
- case Opcodes.JSR:
|
|
- case Opcodes.RET:
|
|
- throw new IllegalArgumentException("JSR/RET are not supported with computeFrames option");
|
|
- case Opcodes.GETSTATIC:
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- case Opcodes.PUTSTATIC:
|
|
- pop(argSymbol.value);
|
|
- break;
|
|
- case Opcodes.GETFIELD:
|
|
- pop(1);
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- case Opcodes.PUTFIELD:
|
|
- pop(argSymbol.value);
|
|
- pop();
|
|
- break;
|
|
- case Opcodes.INVOKEVIRTUAL:
|
|
- case Opcodes.INVOKESPECIAL:
|
|
- case Opcodes.INVOKESTATIC:
|
|
- case Opcodes.INVOKEINTERFACE:
|
|
- pop(argSymbol.value);
|
|
- if (opcode != Opcodes.INVOKESTATIC) {
|
|
- abstractType1 = pop();
|
|
- if (opcode == Opcodes.INVOKESPECIAL && argSymbol.name.charAt(0) == '<') {
|
|
- addInitializedType(abstractType1);
|
|
- }
|
|
- }
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- case Opcodes.INVOKEDYNAMIC:
|
|
- pop(argSymbol.value);
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- case Opcodes.NEW:
|
|
- push(UNINITIALIZED_KIND | symbolTable.addUninitializedType(argSymbol.value, arg));
|
|
- break;
|
|
- case Opcodes.NEWARRAY:
|
|
- pop();
|
|
- switch (arg) {
|
|
- case Opcodes.T_BOOLEAN:
|
|
- push(ARRAY_OF | BOOLEAN);
|
|
- break;
|
|
- case Opcodes.T_CHAR:
|
|
- push(ARRAY_OF | CHAR);
|
|
- break;
|
|
- case Opcodes.T_BYTE:
|
|
- push(ARRAY_OF | BYTE);
|
|
- break;
|
|
- case Opcodes.T_SHORT:
|
|
- push(ARRAY_OF | SHORT);
|
|
- break;
|
|
- case Opcodes.T_INT:
|
|
- push(ARRAY_OF | INTEGER);
|
|
- break;
|
|
- case Opcodes.T_FLOAT:
|
|
- push(ARRAY_OF | FLOAT);
|
|
- break;
|
|
- case Opcodes.T_DOUBLE:
|
|
- push(ARRAY_OF | DOUBLE);
|
|
- break;
|
|
- case Opcodes.T_LONG:
|
|
- push(ARRAY_OF | LONG);
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- break;
|
|
- case Opcodes.ANEWARRAY:
|
|
- String arrayElementType = argSymbol.value;
|
|
- pop();
|
|
- if (arrayElementType.charAt(0) == '[') {
|
|
- push(symbolTable, '[' + arrayElementType);
|
|
- } else {
|
|
- push(ARRAY_OF | REFERENCE_KIND | symbolTable.addType(arrayElementType));
|
|
- }
|
|
- break;
|
|
- case Opcodes.CHECKCAST:
|
|
- String castType = argSymbol.value;
|
|
- pop();
|
|
- if (castType.charAt(0) == '[') {
|
|
- push(symbolTable, castType);
|
|
- } else {
|
|
- push(REFERENCE_KIND | symbolTable.addType(castType));
|
|
- }
|
|
- break;
|
|
- case Opcodes.MULTIANEWARRAY:
|
|
- pop(arg);
|
|
- push(symbolTable, argSymbol.value);
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Frame merging methods, used in the second step of the stack map frame computation algorithm
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Merges the input frame of the given {@link Frame} with the input and output frames of this
|
|
- * {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation
|
|
- * (the input and output frames of this {@link Frame} are never changed).
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param dstFrame the {@link Frame} whose input frame must be updated. This should be the frame
|
|
- * of a successor, in the control flow graph, of the basic block corresponding to this frame.
|
|
- * @param catchTypeIndex if 'frame' corresponds to an exception handler basic block, the type
|
|
- * table index of the caught exception type, otherwise 0.
|
|
- * @return {@literal true} if the input frame of 'frame' has been changed by this operation.
|
|
- */
|
|
- final boolean merge(
|
|
- final SymbolTable symbolTable, final Frame dstFrame, final int catchTypeIndex) {
|
|
- boolean frameChanged = false;
|
|
-
|
|
- // Compute the concrete types of the local variables at the end of the basic block corresponding
|
|
- // to this frame, by resolving its abstract output types, and merge these concrete types with
|
|
- // those of the local variables in the input frame of dstFrame.
|
|
- int numLocal = inputLocals.length;
|
|
- int numStack = inputStack.length;
|
|
- if (dstFrame.inputLocals == null) {
|
|
- dstFrame.inputLocals = new int[numLocal];
|
|
- frameChanged = true;
|
|
- }
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- int concreteOutputType;
|
|
- if (outputLocals != null && i < outputLocals.length) {
|
|
- int abstractOutputType = outputLocals[i];
|
|
- if (abstractOutputType == 0) {
|
|
- // If the local variable has never been assigned in this basic block, it is equal to its
|
|
- // value at the beginning of the block.
|
|
- concreteOutputType = inputLocals[i];
|
|
- } else {
|
|
- int dim = abstractOutputType & DIM_MASK;
|
|
- int kind = abstractOutputType & KIND_MASK;
|
|
- if (kind == LOCAL_KIND) {
|
|
- // By definition, a LOCAL_KIND type designates the concrete type of a local variable at
|
|
- // the beginning of the basic block corresponding to this frame (which is known when
|
|
- // this method is called, but was not when the abstract type was computed).
|
|
- concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
|
|
- if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
|
- && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
|
- concreteOutputType = TOP;
|
|
- }
|
|
- } else if (kind == STACK_KIND) {
|
|
- // By definition, a STACK_KIND type designates the concrete type of a local variable at
|
|
- // the beginning of the basic block corresponding to this frame (which is known when
|
|
- // this method is called, but was not when the abstract type was computed).
|
|
- concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
|
|
- if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
|
- && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
|
- concreteOutputType = TOP;
|
|
- }
|
|
- } else {
|
|
- concreteOutputType = abstractOutputType;
|
|
- }
|
|
- }
|
|
- } else {
|
|
- // If the local variable has never been assigned in this basic block, it is equal to its
|
|
- // value at the beginning of the block.
|
|
- concreteOutputType = inputLocals[i];
|
|
- }
|
|
- // concreteOutputType might be an uninitialized type from the input locals or from the input
|
|
- // stack. However, if a constructor has been called for this class type in the basic block,
|
|
- // then this type is no longer uninitialized at the end of basic block.
|
|
- if (initializations != null) {
|
|
- concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
|
- }
|
|
- frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputLocals, i);
|
|
- }
|
|
-
|
|
- // If dstFrame is an exception handler block, it can be reached from any instruction of the
|
|
- // basic block corresponding to this frame, in particular from the first one. Therefore, the
|
|
- // input locals of dstFrame should be compatible (i.e. merged) with the input locals of this
|
|
- // frame (and the input stack of dstFrame should be compatible, i.e. merged, with a one
|
|
- // element stack containing the caught exception type).
|
|
- if (catchTypeIndex > 0) {
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- frameChanged |= merge(symbolTable, inputLocals[i], dstFrame.inputLocals, i);
|
|
- }
|
|
- if (dstFrame.inputStack == null) {
|
|
- dstFrame.inputStack = new int[1];
|
|
- frameChanged = true;
|
|
- }
|
|
- frameChanged |= merge(symbolTable, catchTypeIndex, dstFrame.inputStack, 0);
|
|
- return frameChanged;
|
|
- }
|
|
-
|
|
- // Compute the concrete types of the stack operands at the end of the basic block corresponding
|
|
- // to this frame, by resolving its abstract output types, and merge these concrete types with
|
|
- // those of the stack operands in the input frame of dstFrame.
|
|
- int numInputStack = inputStack.length + outputStackStart;
|
|
- if (dstFrame.inputStack == null) {
|
|
- dstFrame.inputStack = new int[numInputStack + outputStackTop];
|
|
- frameChanged = true;
|
|
- }
|
|
- // First, do this for the stack operands that have not been popped in the basic block
|
|
- // corresponding to this frame, and which are therefore equal to their value in the input
|
|
- // frame (except for uninitialized types, which may have been initialized).
|
|
- for (int i = 0; i < numInputStack; ++i) {
|
|
- int concreteOutputType = inputStack[i];
|
|
- if (initializations != null) {
|
|
- concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
|
- }
|
|
- frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputStack, i);
|
|
- }
|
|
- // Then, do this for the stack operands that have pushed in the basic block (this code is the
|
|
- // same as the one above for local variables).
|
|
- for (int i = 0; i < outputStackTop; ++i) {
|
|
- int concreteOutputType;
|
|
- int abstractOutputType = outputStack[i];
|
|
- int dim = abstractOutputType & DIM_MASK;
|
|
- int kind = abstractOutputType & KIND_MASK;
|
|
- if (kind == LOCAL_KIND) {
|
|
- concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK];
|
|
- if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
|
- && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
|
- concreteOutputType = TOP;
|
|
- }
|
|
- } else if (kind == STACK_KIND) {
|
|
- concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)];
|
|
- if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0
|
|
- && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) {
|
|
- concreteOutputType = TOP;
|
|
- }
|
|
- } else {
|
|
- concreteOutputType = abstractOutputType;
|
|
- }
|
|
- if (initializations != null) {
|
|
- concreteOutputType = getInitializedType(symbolTable, concreteOutputType);
|
|
- }
|
|
- frameChanged |=
|
|
- merge(symbolTable, concreteOutputType, dstFrame.inputStack, numInputStack + i);
|
|
- }
|
|
- return frameChanged;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Merges the type at the given index in the given abstract type array with the given type.
|
|
- * Returns {@literal true} if the type array has been modified by this operation.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param sourceType the abstract type with which the abstract type array element must be merged.
|
|
- * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link
|
|
- * #UNINITIALIZED_KIND} kind, with positive or null array dimensions.
|
|
- * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND},
|
|
- * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array
|
|
- * dimensions.
|
|
- * @param dstIndex the index of the type that must be merged in dstTypes.
|
|
- * @return {@literal true} if the type array has been modified by this operation.
|
|
- */
|
|
- private static boolean merge(
|
|
- final SymbolTable symbolTable,
|
|
- final int sourceType,
|
|
- final int[] dstTypes,
|
|
- final int dstIndex) {
|
|
- int dstType = dstTypes[dstIndex];
|
|
- if (dstType == sourceType) {
|
|
- // If the types are equal, merge(sourceType, dstType) = dstType, so there is no change.
|
|
- return false;
|
|
- }
|
|
- int srcType = sourceType;
|
|
- if ((sourceType & ~DIM_MASK) == NULL) {
|
|
- if (dstType == NULL) {
|
|
- return false;
|
|
- }
|
|
- srcType = NULL;
|
|
- }
|
|
- if (dstType == 0) {
|
|
- // If dstTypes[dstIndex] has never been assigned, merge(srcType, dstType) = srcType.
|
|
- dstTypes[dstIndex] = srcType;
|
|
- return true;
|
|
- }
|
|
- int mergedType;
|
|
- if ((dstType & DIM_MASK) != 0 || (dstType & KIND_MASK) == REFERENCE_KIND) {
|
|
- // If dstType is a reference type of any array dimension.
|
|
- if (srcType == NULL) {
|
|
- // If srcType is the NULL type, merge(srcType, dstType) = dstType, so there is no change.
|
|
- return false;
|
|
- } else if ((srcType & (DIM_MASK | KIND_MASK)) == (dstType & (DIM_MASK | KIND_MASK))) {
|
|
- // If srcType has the same array dimension and the same kind as dstType.
|
|
- if ((dstType & KIND_MASK) == REFERENCE_KIND) {
|
|
- // If srcType and dstType are reference types with the same array dimension,
|
|
- // merge(srcType, dstType) = dim(srcType) | common super class of srcType and dstType.
|
|
- mergedType =
|
|
- (srcType & DIM_MASK)
|
|
- | REFERENCE_KIND
|
|
- | symbolTable.addMergedType(srcType & VALUE_MASK, dstType & VALUE_MASK);
|
|
- } else {
|
|
- // If srcType and dstType are array types of equal dimension but different element types,
|
|
- // merge(srcType, dstType) = dim(srcType) - 1 | java/lang/Object.
|
|
- int mergedDim = ELEMENT_OF + (srcType & DIM_MASK);
|
|
- mergedType = mergedDim | REFERENCE_KIND | symbolTable.addType("java/lang/Object");
|
|
- }
|
|
- } else if ((srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND) {
|
|
- // If srcType is any other reference or array type,
|
|
- // merge(srcType, dstType) = min(srcDdim, dstDim) | java/lang/Object
|
|
- // where srcDim is the array dimension of srcType, minus 1 if srcType is an array type
|
|
- // with a non reference element type (and similarly for dstDim).
|
|
- int srcDim = srcType & DIM_MASK;
|
|
- if (srcDim != 0 && (srcType & KIND_MASK) != REFERENCE_KIND) {
|
|
- srcDim = ELEMENT_OF + srcDim;
|
|
- }
|
|
- int dstDim = dstType & DIM_MASK;
|
|
- if (dstDim != 0 && (dstType & KIND_MASK) != REFERENCE_KIND) {
|
|
- dstDim = ELEMENT_OF + dstDim;
|
|
- }
|
|
- mergedType =
|
|
- Math.min(srcDim, dstDim) | REFERENCE_KIND | symbolTable.addType("java/lang/Object");
|
|
- } else {
|
|
- // If srcType is any other type, merge(srcType, dstType) = TOP.
|
|
- mergedType = TOP;
|
|
- }
|
|
- } else if (dstType == NULL) {
|
|
- // If dstType is the NULL type, merge(srcType, dstType) = srcType, or TOP if srcType is not a
|
|
- // an array type or a reference type.
|
|
- mergedType =
|
|
- (srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND ? srcType : TOP;
|
|
- } else {
|
|
- // If dstType is any other type, merge(srcType, dstType) = TOP whatever srcType.
|
|
- mergedType = TOP;
|
|
- }
|
|
- if (mergedType != dstType) {
|
|
- dstTypes[dstIndex] = mergedType;
|
|
- return true;
|
|
- }
|
|
- return false;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Frame output methods, to generate StackMapFrame attributes
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Makes the given {@link MethodWriter} visit the input frame of this {@link Frame}. The visit is
|
|
- * done with the {@link MethodWriter#visitFrameStart}, {@link MethodWriter#visitAbstractType} and
|
|
- * {@link MethodWriter#visitFrameEnd} methods.
|
|
- *
|
|
- * @param methodWriter the {@link MethodWriter} that should visit the input frame of this {@link
|
|
- * Frame}.
|
|
- */
|
|
- final void accept(final MethodWriter methodWriter) {
|
|
- // Compute the number of locals, ignoring TOP types that are just after a LONG or a DOUBLE, and
|
|
- // all trailing TOP types.
|
|
- int[] localTypes = inputLocals;
|
|
- int numLocal = 0;
|
|
- int numTrailingTop = 0;
|
|
- int i = 0;
|
|
- while (i < localTypes.length) {
|
|
- int localType = localTypes[i];
|
|
- i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
|
- if (localType == TOP) {
|
|
- numTrailingTop++;
|
|
- } else {
|
|
- numLocal += numTrailingTop + 1;
|
|
- numTrailingTop = 0;
|
|
- }
|
|
- }
|
|
- // Compute the stack size, ignoring TOP types that are just after a LONG or a DOUBLE.
|
|
- int[] stackTypes = inputStack;
|
|
- int numStack = 0;
|
|
- i = 0;
|
|
- while (i < stackTypes.length) {
|
|
- int stackType = stackTypes[i];
|
|
- i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
|
- numStack++;
|
|
- }
|
|
- // Visit the frame and its content.
|
|
- int frameIndex = methodWriter.visitFrameStart(owner.bytecodeOffset, numLocal, numStack);
|
|
- i = 0;
|
|
- while (numLocal-- > 0) {
|
|
- int localType = localTypes[i];
|
|
- i += (localType == LONG || localType == DOUBLE) ? 2 : 1;
|
|
- methodWriter.visitAbstractType(frameIndex++, localType);
|
|
- }
|
|
- i = 0;
|
|
- while (numStack-- > 0) {
|
|
- int stackType = stackTypes[i];
|
|
- i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1;
|
|
- methodWriter.visitAbstractType(frameIndex++, stackType);
|
|
- }
|
|
- methodWriter.visitFrameEnd();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Put the given abstract type in the given ByteVector, using the JVMS verification_type_info
|
|
- * format used in StackMapTable attributes.
|
|
- *
|
|
- * @param symbolTable the type table to use to lookup and store type {@link Symbol}.
|
|
- * @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link
|
|
- * Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types.
|
|
- * @param output where the abstract type must be put.
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4">JVMS
|
|
- * 4.7.4</a>
|
|
- */
|
|
- static void putAbstractType(
|
|
- final SymbolTable symbolTable, final int abstractType, final ByteVector output) {
|
|
- int arrayDimensions = (abstractType & Frame.DIM_MASK) >> DIM_SHIFT;
|
|
- if (arrayDimensions == 0) {
|
|
- int typeValue = abstractType & VALUE_MASK;
|
|
- switch (abstractType & KIND_MASK) {
|
|
- case CONSTANT_KIND:
|
|
- output.putByte(typeValue);
|
|
- break;
|
|
- case REFERENCE_KIND:
|
|
- output
|
|
- .putByte(ITEM_OBJECT)
|
|
- .putShort(symbolTable.addConstantClass(symbolTable.getType(typeValue).value).index);
|
|
- break;
|
|
- case UNINITIALIZED_KIND:
|
|
- output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data);
|
|
- break;
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- } else {
|
|
- // Case of an array type, we need to build its descriptor first.
|
|
- StringBuilder typeDescriptor = new StringBuilder();
|
|
- while (arrayDimensions-- > 0) {
|
|
- typeDescriptor.append('[');
|
|
- }
|
|
- if ((abstractType & KIND_MASK) == REFERENCE_KIND) {
|
|
- typeDescriptor
|
|
- .append('L')
|
|
- .append(symbolTable.getType(abstractType & VALUE_MASK).value)
|
|
- .append(';');
|
|
- } else {
|
|
- switch (abstractType & VALUE_MASK) {
|
|
- case Frame.ITEM_ASM_BOOLEAN:
|
|
- typeDescriptor.append('Z');
|
|
- break;
|
|
- case Frame.ITEM_ASM_BYTE:
|
|
- typeDescriptor.append('B');
|
|
- break;
|
|
- case Frame.ITEM_ASM_CHAR:
|
|
- typeDescriptor.append('C');
|
|
- break;
|
|
- case Frame.ITEM_ASM_SHORT:
|
|
- typeDescriptor.append('S');
|
|
- break;
|
|
- case Frame.ITEM_INTEGER:
|
|
- typeDescriptor.append('I');
|
|
- break;
|
|
- case Frame.ITEM_FLOAT:
|
|
- typeDescriptor.append('F');
|
|
- break;
|
|
- case Frame.ITEM_LONG:
|
|
- typeDescriptor.append('J');
|
|
- break;
|
|
- case Frame.ITEM_DOUBLE:
|
|
- typeDescriptor.append('D');
|
|
- break;
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- }
|
|
- output
|
|
- .putByte(ITEM_OBJECT)
|
|
- .putShort(symbolTable.addConstantClass(typeDescriptor.toString()).index);
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Handle.java b/src/main/java/org/mvel2/asm/Handle.java
|
|
deleted file mode 100644
|
|
index 57aec52..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Handle.java
|
|
+++ /dev/null
|
|
@@ -1,189 +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 reference to a field or a method.
|
|
- *
|
|
- * @author Remi Forax
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public final class Handle {
|
|
-
|
|
- /**
|
|
- * The kind of field or method designated by this Handle. Should 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}.
|
|
- */
|
|
- private final int tag;
|
|
-
|
|
- /** The internal name of the class that owns the field or method designated by this handle. */
|
|
- private final String owner;
|
|
-
|
|
- /** The name of the field or method designated by this handle. */
|
|
- private final String name;
|
|
-
|
|
- /** The descriptor of the field or method designated by this handle. */
|
|
- private final String descriptor;
|
|
-
|
|
- /** Whether the owner is an interface or not. */
|
|
- private final boolean isInterface;
|
|
-
|
|
- /**
|
|
- * Constructs a new field or method handle.
|
|
- *
|
|
- * @param tag the kind of field or method designated by 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 class that owns the field or method designated by this
|
|
- * handle.
|
|
- * @param name the name of the field or method designated by this handle.
|
|
- * @param descriptor the descriptor of the field or method designated by this handle.
|
|
- * @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String,
|
|
- * boolean)}.
|
|
- */
|
|
- @Deprecated
|
|
- public Handle(final int tag, final String owner, final String name, final String descriptor) {
|
|
- this(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new field or method handle.
|
|
- *
|
|
- * @param tag the kind of field or method designated by 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 class that owns the field or method designated by this
|
|
- * handle.
|
|
- * @param name the name of the field or method designated by this handle.
|
|
- * @param descriptor the descriptor of the field or method designated by this handle.
|
|
- * @param isInterface whether the owner is an interface or not.
|
|
- */
|
|
- public Handle(
|
|
- final int tag,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final boolean isInterface) {
|
|
- this.tag = tag;
|
|
- this.owner = owner;
|
|
- this.name = name;
|
|
- this.descriptor = descriptor;
|
|
- this.isInterface = isInterface;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the kind of field or method designated by this handle.
|
|
- *
|
|
- * @return {@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}.
|
|
- */
|
|
- public int getTag() {
|
|
- return tag;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the internal name of the class that owns the field or method designated by this handle.
|
|
- *
|
|
- * @return the internal name of the class that owns the field or method designated by this handle.
|
|
- */
|
|
- public String getOwner() {
|
|
- return owner;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the name of the field or method designated by this handle.
|
|
- *
|
|
- * @return the name of the field or method designated by this handle.
|
|
- */
|
|
- public String getName() {
|
|
- return name;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor of the field or method designated by this handle.
|
|
- *
|
|
- * @return the descriptor of the field or method designated by this handle.
|
|
- */
|
|
- public String getDesc() {
|
|
- return descriptor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns true if the owner of the field or method designated by this handle is an interface.
|
|
- *
|
|
- * @return true if the owner of the field or method designated by this handle is an interface.
|
|
- */
|
|
- public boolean isInterface() {
|
|
- return isInterface;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean equals(final Object object) {
|
|
- if (object == this) {
|
|
- return true;
|
|
- }
|
|
- if (!(object instanceof Handle)) {
|
|
- return false;
|
|
- }
|
|
- Handle handle = (Handle) object;
|
|
- return tag == handle.tag
|
|
- && isInterface == handle.isInterface
|
|
- && owner.equals(handle.owner)
|
|
- && name.equals(handle.name)
|
|
- && descriptor.equals(handle.descriptor);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int hashCode() {
|
|
- return tag
|
|
- + (isInterface ? 64 : 0)
|
|
- + owner.hashCode() * name.hashCode() * descriptor.hashCode();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the textual representation of this handle. The textual representation is:
|
|
- *
|
|
- * <ul>
|
|
- * <li>for a reference to a class: owner "." name descriptor " (" tag ")",
|
|
- * <li>for a reference to an interface: owner "." name descriptor " (" tag " itf)".
|
|
- * </ul>
|
|
- */
|
|
- @Override
|
|
- public String toString() {
|
|
- return owner + '.' + name + descriptor + " (" + tag + (isInterface ? " itf" : "") + ')';
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Handler.java b/src/main/java/org/mvel2/asm/Handler.java
|
|
deleted file mode 100644
|
|
index a506efd..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Handler.java
|
|
+++ /dev/null
|
|
@@ -1,198 +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;
|
|
-
|
|
-/**
|
|
- * Information about an exception handler. Corresponds to an element of the exception_table array of
|
|
- * a Code attribute, as defined in the Java Virtual Machine Specification (JVMS). Handler instances
|
|
- * can be chained together, with their {@link #nextHandler} field, to describe a full JVMS
|
|
- * exception_table array.
|
|
- *
|
|
- * @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
|
|
- */
|
|
-final class Handler {
|
|
-
|
|
- /**
|
|
- * The start_pc field of this JVMS exception_table entry. Corresponds to the beginning of the
|
|
- * exception handler's scope (inclusive).
|
|
- */
|
|
- final Label startPc;
|
|
-
|
|
- /**
|
|
- * The end_pc field of this JVMS exception_table entry. Corresponds to the end of the exception
|
|
- * handler's scope (exclusive).
|
|
- */
|
|
- final Label endPc;
|
|
-
|
|
- /**
|
|
- * The handler_pc field of this JVMS exception_table entry. Corresponding to the beginning of the
|
|
- * exception handler's code.
|
|
- */
|
|
- final Label handlerPc;
|
|
-
|
|
- /**
|
|
- * The catch_type field of this JVMS exception_table entry. This is the constant pool index of the
|
|
- * internal name of the type of exceptions handled by this handler, or 0 to catch any exceptions.
|
|
- */
|
|
- final int catchType;
|
|
-
|
|
- /**
|
|
- * The internal name of the type of exceptions handled by this handler, or {@literal null} to
|
|
- * catch any exceptions.
|
|
- */
|
|
- final String catchTypeDescriptor;
|
|
-
|
|
- /** The next exception handler. */
|
|
- Handler nextHandler;
|
|
-
|
|
- /**
|
|
- * Constructs a new Handler.
|
|
- *
|
|
- * @param startPc the start_pc field of this JVMS exception_table entry.
|
|
- * @param endPc the end_pc field of this JVMS exception_table entry.
|
|
- * @param handlerPc the handler_pc field of this JVMS exception_table entry.
|
|
- * @param catchType The catch_type field of this JVMS exception_table entry.
|
|
- * @param catchTypeDescriptor The internal name of the type of exceptions handled by this handler,
|
|
- * or {@literal null} to catch any exceptions.
|
|
- */
|
|
- Handler(
|
|
- final Label startPc,
|
|
- final Label endPc,
|
|
- final Label handlerPc,
|
|
- final int catchType,
|
|
- final String catchTypeDescriptor) {
|
|
- this.startPc = startPc;
|
|
- this.endPc = endPc;
|
|
- this.handlerPc = handlerPc;
|
|
- this.catchType = catchType;
|
|
- this.catchTypeDescriptor = catchTypeDescriptor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new Handler from the given one, with a different scope.
|
|
- *
|
|
- * @param handler an existing Handler.
|
|
- * @param startPc the start_pc field of this JVMS exception_table entry.
|
|
- * @param endPc the end_pc field of this JVMS exception_table entry.
|
|
- */
|
|
- Handler(final Handler handler, final Label startPc, final Label endPc) {
|
|
- this(startPc, endPc, handler.handlerPc, handler.catchType, handler.catchTypeDescriptor);
|
|
- this.nextHandler = handler.nextHandler;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Removes the range between start and end from the Handler list that begins with the given
|
|
- * element.
|
|
- *
|
|
- * @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
|
- * @param start the start of the range to be removed.
|
|
- * @param end the end of the range to be removed. Maybe {@literal null}.
|
|
- * @return the exception handler list with the start-end range removed.
|
|
- */
|
|
- static Handler removeRange(final Handler firstHandler, final Label start, final Label end) {
|
|
- if (firstHandler == null) {
|
|
- return null;
|
|
- } else {
|
|
- firstHandler.nextHandler = removeRange(firstHandler.nextHandler, start, end);
|
|
- }
|
|
- int handlerStart = firstHandler.startPc.bytecodeOffset;
|
|
- int handlerEnd = firstHandler.endPc.bytecodeOffset;
|
|
- int rangeStart = start.bytecodeOffset;
|
|
- int rangeEnd = end == null ? Integer.MAX_VALUE : end.bytecodeOffset;
|
|
- // Return early if [handlerStart,handlerEnd[ and [rangeStart,rangeEnd[ don't intersect.
|
|
- if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) {
|
|
- return firstHandler;
|
|
- }
|
|
- if (rangeStart <= handlerStart) {
|
|
- if (rangeEnd >= handlerEnd) {
|
|
- // If [handlerStart,handlerEnd[ is included in [rangeStart,rangeEnd[, remove firstHandler.
|
|
- return firstHandler.nextHandler;
|
|
- } else {
|
|
- // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [rangeEnd,handlerEnd[
|
|
- return new Handler(firstHandler, end, firstHandler.endPc);
|
|
- }
|
|
- } else if (rangeEnd >= handlerEnd) {
|
|
- // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [handlerStart,rangeStart[
|
|
- return new Handler(firstHandler, firstHandler.startPc, start);
|
|
- } else {
|
|
- // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ =
|
|
- // [handlerStart,rangeStart[ + [rangeEnd,handerEnd[
|
|
- firstHandler.nextHandler = new Handler(firstHandler, end, firstHandler.endPc);
|
|
- return new Handler(firstHandler, firstHandler.startPc, start);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of elements of the Handler list that begins with the given element.
|
|
- *
|
|
- * @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
|
- * @return the number of elements of the Handler list that begins with 'handler'.
|
|
- */
|
|
- static int getExceptionTableLength(final Handler firstHandler) {
|
|
- int length = 0;
|
|
- Handler handler = firstHandler;
|
|
- while (handler != null) {
|
|
- length++;
|
|
- handler = handler.nextHandler;
|
|
- }
|
|
- return length;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size in bytes of the JVMS exception_table corresponding to the Handler list that
|
|
- * begins with the given element. <i>This includes the exception_table_length field.</i>
|
|
- *
|
|
- * @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
|
- * @return the size in bytes of the exception_table_length and exception_table structures.
|
|
- */
|
|
- static int getExceptionTableSize(final Handler firstHandler) {
|
|
- return 2 + 8 * getExceptionTableLength(firstHandler);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the JVMS exception_table corresponding to the Handler list that begins with the given
|
|
- * element. <i>This includes the exception_table_length field.</i>
|
|
- *
|
|
- * @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
|
- * @param output where the exception_table_length and exception_table structures must be put.
|
|
- */
|
|
- static void putExceptionTable(final Handler firstHandler, final ByteVector output) {
|
|
- output.putShort(getExceptionTableLength(firstHandler));
|
|
- Handler handler = firstHandler;
|
|
- while (handler != null) {
|
|
- output
|
|
- .putShort(handler.startPc.bytecodeOffset)
|
|
- .putShort(handler.endPc.bytecodeOffset)
|
|
- .putShort(handler.handlerPc.bytecodeOffset)
|
|
- .putShort(handler.catchType);
|
|
- handler = handler.nextHandler;
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Label.java b/src/main/java/org/mvel2/asm/Label.java
|
|
deleted file mode 100644
|
|
index 54ac261..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Label.java
|
|
+++ /dev/null
|
|
@@ -1,621 +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 position in the bytecode of a method. Labels are used for jump, goto, and switch instructions,
|
|
- * and for try catch blocks. A label designates the <i>instruction</i> that is just after. Note
|
|
- * however that there can be other elements between a label and the instruction it designates (such
|
|
- * as other labels, stack map frames, line numbers, etc.).
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public class Label {
|
|
-
|
|
- /**
|
|
- * A flag indicating that a label is only used for debug attributes. Such a label is not the start
|
|
- * of a basic block, the target of a jump instruction, or an exception handler. It can be safely
|
|
- * ignored in control flow graph analysis algorithms (for optimization purposes).
|
|
- */
|
|
- static final int FLAG_DEBUG_ONLY = 1;
|
|
-
|
|
- /**
|
|
- * A flag indicating that a label is the target of a jump instruction, or the start of an
|
|
- * exception handler.
|
|
- */
|
|
- static final int FLAG_JUMP_TARGET = 2;
|
|
-
|
|
- /** A flag indicating that the bytecode offset of a label is known. */
|
|
- static final int FLAG_RESOLVED = 4;
|
|
-
|
|
- /** A flag indicating that a label corresponds to a reachable basic block. */
|
|
- static final int FLAG_REACHABLE = 8;
|
|
-
|
|
- /**
|
|
- * A flag indicating that the basic block corresponding to a label ends with a subroutine call. By
|
|
- * construction in {@link MethodWriter#visitJumpInsn}, labels with this flag set have at least two
|
|
- * outgoing edges:
|
|
- *
|
|
- * <ul>
|
|
- * <li>the first one corresponds to the instruction that follows the jsr instruction in the
|
|
- * bytecode, i.e. where execution continues when it returns from the jsr call. This is a
|
|
- * virtual control flow edge, since execution never goes directly from the jsr to the next
|
|
- * instruction. Instead, it goes to the subroutine and eventually returns to the instruction
|
|
- * following the jsr. This virtual edge is used to compute the real outgoing edges of the
|
|
- * basic blocks ending with a ret instruction, in {@link #addSubroutineRetSuccessors}.
|
|
- * <li>the second one corresponds to the target of the jsr instruction,
|
|
- * </ul>
|
|
- */
|
|
- static final int FLAG_SUBROUTINE_CALLER = 16;
|
|
-
|
|
- /**
|
|
- * A flag indicating that the basic block corresponding to a label is the start of a subroutine.
|
|
- */
|
|
- static final int FLAG_SUBROUTINE_START = 32;
|
|
-
|
|
- /** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */
|
|
- static final int FLAG_SUBROUTINE_END = 64;
|
|
-
|
|
- /**
|
|
- * The number of elements to add to the {@link #otherLineNumbers} array when it needs to be
|
|
- * resized to store a new source line number.
|
|
- */
|
|
- static final int LINE_NUMBERS_CAPACITY_INCREMENT = 4;
|
|
-
|
|
- /**
|
|
- * The number of elements to add to the {@link #forwardReferences} array when it needs to be
|
|
- * resized to store a new forward reference.
|
|
- */
|
|
- static final int FORWARD_REFERENCES_CAPACITY_INCREMENT = 6;
|
|
-
|
|
- /**
|
|
- * The bit mask to extract the type of a forward reference to this label. The extracted type is
|
|
- * either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}.
|
|
- *
|
|
- * @see #forwardReferences
|
|
- */
|
|
- static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000;
|
|
-
|
|
- /**
|
|
- * The type of forward references stored with two bytes in the bytecode. This is the case, for
|
|
- * instance, of a forward reference from an ifnull instruction.
|
|
- */
|
|
- static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000;
|
|
-
|
|
- /**
|
|
- * The type of forward references stored in four bytes in the bytecode. This is the case, for
|
|
- * instance, of a forward reference from a lookupswitch instruction.
|
|
- */
|
|
- static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
|
|
-
|
|
- /**
|
|
- * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
|
|
- * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
|
|
- * as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}).
|
|
- *
|
|
- * @see #forwardReferences
|
|
- */
|
|
- static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF;
|
|
-
|
|
- /**
|
|
- * A sentinel element used to indicate the end of a list of labels.
|
|
- *
|
|
- * @see #nextListElement
|
|
- */
|
|
- static final Label EMPTY_LIST = new Label();
|
|
-
|
|
- /**
|
|
- * A user managed state associated with this label. Warning: this field is used by the ASM tree
|
|
- * package. In order to use it with the ASM tree package you must override the getLabelNode method
|
|
- * in MethodNode.
|
|
- */
|
|
- public Object info;
|
|
-
|
|
- /**
|
|
- * The type and status of this label or its corresponding basic block. Must be zero or more of
|
|
- * {@link #FLAG_DEBUG_ONLY}, {@link #FLAG_JUMP_TARGET}, {@link #FLAG_RESOLVED}, {@link
|
|
- * #FLAG_REACHABLE}, {@link #FLAG_SUBROUTINE_CALLER}, {@link #FLAG_SUBROUTINE_START}, {@link
|
|
- * #FLAG_SUBROUTINE_END}.
|
|
- */
|
|
- short flags;
|
|
-
|
|
- /**
|
|
- * The source line number corresponding to this label, or 0. If there are several source line
|
|
- * numbers corresponding to this label, the first one is stored in this field, and the remaining
|
|
- * ones are stored in {@link #otherLineNumbers}.
|
|
- */
|
|
- private short lineNumber;
|
|
-
|
|
- /**
|
|
- * The source line numbers corresponding to this label, in addition to {@link #lineNumber}, or
|
|
- * null. The first element of this array is the number n of source line numbers it contains, which
|
|
- * are stored between indices 1 and n (inclusive).
|
|
- */
|
|
- private int[] otherLineNumbers;
|
|
-
|
|
- /**
|
|
- * The offset of this label in the bytecode of its method, in bytes. This value is set if and only
|
|
- * if the {@link #FLAG_RESOLVED} flag is set.
|
|
- */
|
|
- int bytecodeOffset;
|
|
-
|
|
- /**
|
|
- * The forward references to this label. The first element is the number of forward references,
|
|
- * times 2 (this corresponds to the index of the last element actually used in this array). Then,
|
|
- * each forward reference is described with two consecutive integers noted
|
|
- * 'sourceInsnBytecodeOffset' and 'reference':
|
|
- *
|
|
- * <ul>
|
|
- * <li>'sourceInsnBytecodeOffset' is the bytecode offset of the instruction that contains the
|
|
- * forward reference,
|
|
- * <li>'reference' contains the type and the offset in the bytecode where the forward reference
|
|
- * value must be stored, which can be extracted with {@link #FORWARD_REFERENCE_TYPE_MASK}
|
|
- * and {@link #FORWARD_REFERENCE_HANDLE_MASK}.
|
|
- * </ul>
|
|
- *
|
|
- * <p>For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is
|
|
- * equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1
|
|
- * (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after
|
|
- * the start of the instruction itself). For the default case of a lookupswitch instruction at
|
|
- * bytecode offset x, 'sourceInsnBytecodeOffset' is equal to x, and 'reference' is of type {@link
|
|
- * #FORWARD_REFERENCE_TYPE_WIDE} with value between x + 1 and x + 4 (because the lookupswitch
|
|
- * instruction uses a 4 bytes bytecode offset operand stored one to four bytes after the start of
|
|
- * the instruction itself).
|
|
- */
|
|
- private int[] forwardReferences;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- // Fields for the control flow and data flow graph analysis algorithms (used to compute the
|
|
- // maximum stack size or the stack map frames). A control flow graph contains one node per "basic
|
|
- // block", and one edge per "jump" from one basic block to another. Each node (i.e., each basic
|
|
- // block) is represented with the Label object that corresponds to the first instruction of this
|
|
- // basic block. Each node also stores the list of its successors in the graph, as a linked list of
|
|
- // Edge objects.
|
|
- //
|
|
- // The control flow analysis algorithms used to compute the maximum stack size or the stack map
|
|
- // frames are similar and use two steps. The first step, during the visit of each instruction,
|
|
- // builds information about the state of the local variables and the operand stack at the end of
|
|
- // each basic block, called the "output frame", <i>relatively</i> to the frame state at the
|
|
- // beginning of the basic block, which is called the "input frame", and which is <i>unknown</i>
|
|
- // during this step. The second step, in {@link MethodWriter#computeAllFrames} and {@link
|
|
- // MethodWriter#computeMaxStackAndLocal}, is a fix point algorithm
|
|
- // that computes information about the input frame of each basic block, from the input state of
|
|
- // the first basic block (known from the method signature), and by the using the previously
|
|
- // computed relative output frames.
|
|
- //
|
|
- // The algorithm used to compute the maximum stack size only computes the relative output and
|
|
- // absolute input stack heights, while the algorithm used to compute stack map frames computes
|
|
- // relative output frames and absolute input frames.
|
|
-
|
|
- /**
|
|
- * The number of elements in the input stack of the basic block corresponding to this label. This
|
|
- * field is computed in {@link MethodWriter#computeMaxStackAndLocal}.
|
|
- */
|
|
- short inputStackSize;
|
|
-
|
|
- /**
|
|
- * The number of elements in the output stack, at the end of the basic block corresponding to this
|
|
- * label. This field is only computed for basic blocks that end with a RET instruction.
|
|
- */
|
|
- short outputStackSize;
|
|
-
|
|
- /**
|
|
- * The maximum height reached by the output stack, relatively to the top of the input stack, in
|
|
- * the basic block corresponding to this label. This maximum is always positive or null.
|
|
- */
|
|
- short outputStackMax;
|
|
-
|
|
- /**
|
|
- * The id of the subroutine to which this basic block belongs, or 0. If the basic block belongs to
|
|
- * several subroutines, this is the id of the "oldest" subroutine that contains it (with the
|
|
- * convention that a subroutine calling another one is "older" than the callee). This field is
|
|
- * computed in {@link MethodWriter#computeMaxStackAndLocal}, if the method contains JSR
|
|
- * instructions.
|
|
- */
|
|
- short subroutineId;
|
|
-
|
|
- /**
|
|
- * The input and output stack map frames of the basic block corresponding to this label. This
|
|
- * field is only used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} or {@link
|
|
- * MethodWriter#COMPUTE_INSERTED_FRAMES} option is used.
|
|
- */
|
|
- Frame frame;
|
|
-
|
|
- /**
|
|
- * The successor of this label, in the order they are visited in {@link MethodVisitor#visitLabel}.
|
|
- * This linked list does not include labels used for debug info only. If the {@link
|
|
- * MethodWriter#COMPUTE_ALL_FRAMES} or {@link MethodWriter#COMPUTE_INSERTED_FRAMES} option is used
|
|
- * then it does not contain either successive labels that denote the same bytecode offset (in this
|
|
- * case only the first label appears in this list).
|
|
- */
|
|
- Label nextBasicBlock;
|
|
-
|
|
- /**
|
|
- * The outgoing edges of the basic block corresponding to this label, in the control flow graph of
|
|
- * its method. These edges are stored in a linked list of {@link Edge} objects, linked to each
|
|
- * other by their {@link Edge#nextEdge} field.
|
|
- */
|
|
- Edge outgoingEdges;
|
|
-
|
|
- /**
|
|
- * The next element in the list of labels to which this label belongs, or null if it does not
|
|
- * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in
|
|
- * order to ensure that this field is null if and only if this label does not belong to a list of
|
|
- * labels. Note that there can be several lists of labels at the same time, but that a label can
|
|
- * belong to at most one list at a time (unless some lists share a common tail, but this is not
|
|
- * used in practice).
|
|
- *
|
|
- * <p>List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
|
|
- * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
|
|
- * respectively, as well as in {@link #markSubroutine} and {@link #addSubroutineRetSuccessors} to
|
|
- * compute the basic blocks belonging to subroutines and their outgoing edges. Outside of these
|
|
- * methods, this field should be null (this property is a precondition and a postcondition of
|
|
- * these methods).
|
|
- */
|
|
- Label nextListElement;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Constructor and accessors
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /** Constructs a new label. */
|
|
- public Label() {
|
|
- // Nothing to do.
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the bytecode offset corresponding to this label. This offset is computed from the start
|
|
- * of the method's bytecode. <i>This method is intended for {@link Attribute} sub classes, and is
|
|
- * normally not needed by class generators or adapters.</i>
|
|
- *
|
|
- * @return the bytecode offset corresponding to this label.
|
|
- * @throws IllegalStateException if this label is not resolved yet.
|
|
- */
|
|
- public int getOffset() {
|
|
- if ((flags & FLAG_RESOLVED) == 0) {
|
|
- throw new IllegalStateException("Label offset position has not been resolved yet");
|
|
- }
|
|
- return bytecodeOffset;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the "canonical" {@link Label} instance corresponding to this label's bytecode offset,
|
|
- * if known, otherwise the label itself. The canonical instance is the first label (in the order
|
|
- * of their visit by {@link MethodVisitor#visitLabel}) corresponding to this bytecode offset. It
|
|
- * cannot be known for labels which have not been visited yet.
|
|
- *
|
|
- * <p><i>This method should only be used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} option
|
|
- * is used.</i>
|
|
- *
|
|
- * @return the label itself if {@link #frame} is null, otherwise the Label's frame owner. This
|
|
- * corresponds to the "canonical" label instance described above thanks to the way the label
|
|
- * frame is set in {@link MethodWriter#visitLabel}.
|
|
- */
|
|
- final Label getCanonicalInstance() {
|
|
- return frame == null ? this : frame.owner;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to manage line numbers
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Adds a source line number corresponding to this label.
|
|
- *
|
|
- * @param lineNumber a source line number (which should be strictly positive).
|
|
- */
|
|
- final void addLineNumber(final int lineNumber) {
|
|
- if (this.lineNumber == 0) {
|
|
- this.lineNumber = (short) lineNumber;
|
|
- } else {
|
|
- if (otherLineNumbers == null) {
|
|
- otherLineNumbers = new int[LINE_NUMBERS_CAPACITY_INCREMENT];
|
|
- }
|
|
- int otherLineNumberIndex = ++otherLineNumbers[0];
|
|
- if (otherLineNumberIndex >= otherLineNumbers.length) {
|
|
- int[] newLineNumbers = new int[otherLineNumbers.length + LINE_NUMBERS_CAPACITY_INCREMENT];
|
|
- System.arraycopy(otherLineNumbers, 0, newLineNumbers, 0, otherLineNumbers.length);
|
|
- otherLineNumbers = newLineNumbers;
|
|
- }
|
|
- otherLineNumbers[otherLineNumberIndex] = lineNumber;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Makes the given visitor visit this label and its source line numbers, if applicable.
|
|
- *
|
|
- * @param methodVisitor a method visitor.
|
|
- * @param visitLineNumbers whether to visit of the label's source line numbers, if any.
|
|
- */
|
|
- final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) {
|
|
- methodVisitor.visitLabel(this);
|
|
- if (visitLineNumbers && lineNumber != 0) {
|
|
- methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this);
|
|
- if (otherLineNumbers != null) {
|
|
- for (int i = 1; i <= otherLineNumbers[0]; ++i) {
|
|
- methodVisitor.visitLineNumber(otherLineNumbers[i], this);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to compute offsets and to manage forward references
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label
|
|
- * is known, the relative bytecode offset between the label and the instruction referencing it is
|
|
- * computed and written directly. Otherwise, a null relative offset is written and a new forward
|
|
- * reference is declared for this label.
|
|
- *
|
|
- * @param code the bytecode of the method. This is where the reference is appended.
|
|
- * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
|
|
- * reference to be appended.
|
|
- * @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes).
|
|
- */
|
|
- final void put(
|
|
- final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) {
|
|
- if ((flags & FLAG_RESOLVED) == 0) {
|
|
- if (wideReference) {
|
|
- addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length);
|
|
- code.putInt(-1);
|
|
- } else {
|
|
- addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length);
|
|
- code.putShort(-1);
|
|
- }
|
|
- } else {
|
|
- if (wideReference) {
|
|
- code.putInt(bytecodeOffset - sourceInsnBytecodeOffset);
|
|
- } else {
|
|
- code.putShort(bytecodeOffset - sourceInsnBytecodeOffset);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a forward reference to this label. This method must be called only for a true forward
|
|
- * reference, i.e. only if this label is not resolved yet. For backward references, the relative
|
|
- * bytecode offset of the reference can be, and must be, computed and stored directly.
|
|
- *
|
|
- * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
|
|
- * reference stored at referenceHandle.
|
|
- * @param referenceType either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link
|
|
- * #FORWARD_REFERENCE_TYPE_WIDE}.
|
|
- * @param referenceHandle the offset in the bytecode where the forward reference value must be
|
|
- * stored.
|
|
- */
|
|
- private void addForwardReference(
|
|
- final int sourceInsnBytecodeOffset, final int referenceType, final int referenceHandle) {
|
|
- if (forwardReferences == null) {
|
|
- forwardReferences = new int[FORWARD_REFERENCES_CAPACITY_INCREMENT];
|
|
- }
|
|
- int lastElementIndex = forwardReferences[0];
|
|
- if (lastElementIndex + 2 >= forwardReferences.length) {
|
|
- int[] newValues = new int[forwardReferences.length + FORWARD_REFERENCES_CAPACITY_INCREMENT];
|
|
- System.arraycopy(forwardReferences, 0, newValues, 0, forwardReferences.length);
|
|
- forwardReferences = newValues;
|
|
- }
|
|
- forwardReferences[++lastElementIndex] = sourceInsnBytecodeOffset;
|
|
- forwardReferences[++lastElementIndex] = referenceType | referenceHandle;
|
|
- forwardReferences[0] = lastElementIndex;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the bytecode offset of this label to the given value and resolves the forward references
|
|
- * to this label, if any. This method must be called when this label is added to the bytecode of
|
|
- * the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
|
|
- * where left in the bytecode by each forward reference previously added to this label.
|
|
- *
|
|
- * @param code the bytecode of the method.
|
|
- * @param bytecodeOffset the bytecode offset of this label.
|
|
- * @return {@literal true} if a blank that was left for this label was too small to store the
|
|
- * offset. In such a case the corresponding jump instruction is replaced with an equivalent
|
|
- * ASM specific instruction using an unsigned two bytes offset. These ASM specific
|
|
- * instructions are later replaced with standard bytecode instructions with wider offsets (4
|
|
- * bytes instead of 2), in ClassReader.
|
|
- */
|
|
- final boolean resolve(final byte[] code, final int bytecodeOffset) {
|
|
- this.flags |= FLAG_RESOLVED;
|
|
- this.bytecodeOffset = bytecodeOffset;
|
|
- if (forwardReferences == null) {
|
|
- return false;
|
|
- }
|
|
- boolean hasAsmInstructions = false;
|
|
- for (int i = forwardReferences[0]; i > 0; i -= 2) {
|
|
- final int sourceInsnBytecodeOffset = forwardReferences[i - 1];
|
|
- final int reference = forwardReferences[i];
|
|
- final int relativeOffset = bytecodeOffset - sourceInsnBytecodeOffset;
|
|
- int handle = reference & FORWARD_REFERENCE_HANDLE_MASK;
|
|
- if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) {
|
|
- if (relativeOffset < Short.MIN_VALUE || relativeOffset > Short.MAX_VALUE) {
|
|
- // Change the opcode of the jump instruction, in order to be able to find it later in
|
|
- // ClassReader. These ASM specific opcodes are similar to jump instruction opcodes, except
|
|
- // that the 2 bytes offset is unsigned (and can therefore represent values from 0 to
|
|
- // 65535, which is sufficient since the size of a method is limited to 65535 bytes).
|
|
- int opcode = code[sourceInsnBytecodeOffset] & 0xFF;
|
|
- if (opcode < Opcodes.IFNULL) {
|
|
- // Change IFEQ ... JSR to ASM_IFEQ ... ASM_JSR.
|
|
- code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_OPCODE_DELTA);
|
|
- } else {
|
|
- // Change IFNULL and IFNONNULL to ASM_IFNULL and ASM_IFNONNULL.
|
|
- code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_IFNULL_OPCODE_DELTA);
|
|
- }
|
|
- hasAsmInstructions = true;
|
|
- }
|
|
- code[handle++] = (byte) (relativeOffset >>> 8);
|
|
- code[handle] = (byte) relativeOffset;
|
|
- } else {
|
|
- code[handle++] = (byte) (relativeOffset >>> 24);
|
|
- code[handle++] = (byte) (relativeOffset >>> 16);
|
|
- code[handle++] = (byte) (relativeOffset >>> 8);
|
|
- code[handle] = (byte) relativeOffset;
|
|
- }
|
|
- }
|
|
- return hasAsmInstructions;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods related to subroutines
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Finds the basic blocks that belong to the subroutine starting with the basic block
|
|
- * corresponding to this label, and marks these blocks as belonging to this subroutine. This
|
|
- * method follows the control flow graph to find all the blocks that are reachable from the
|
|
- * current basic block WITHOUT following any jsr target.
|
|
- *
|
|
- * <p>Note: a precondition and postcondition of this method is that all labels must have a null
|
|
- * {@link #nextListElement}.
|
|
- *
|
|
- * @param subroutineId the id of the subroutine starting with the basic block corresponding to
|
|
- * this label.
|
|
- */
|
|
- final void markSubroutine(final short subroutineId) {
|
|
- // Data flow algorithm: put this basic block in a list of blocks to process (which are blocks
|
|
- // belonging to subroutine subroutineId) and, while there are blocks to process, remove one from
|
|
- // the list, mark it as belonging to the subroutine, and add its successor basic blocks in the
|
|
- // control flow graph to the list of blocks to process (if not already done).
|
|
- Label listOfBlocksToProcess = this;
|
|
- listOfBlocksToProcess.nextListElement = EMPTY_LIST;
|
|
- while (listOfBlocksToProcess != EMPTY_LIST) {
|
|
- // Remove a basic block from the list of blocks to process.
|
|
- Label basicBlock = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
|
|
- basicBlock.nextListElement = null;
|
|
-
|
|
- // If it is not already marked as belonging to a subroutine, mark it as belonging to
|
|
- // subroutineId and add its successors to the list of blocks to process (unless already done).
|
|
- if (basicBlock.subroutineId == 0) {
|
|
- basicBlock.subroutineId = subroutineId;
|
|
- listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Finds the basic blocks that end a subroutine starting with the basic block corresponding to
|
|
- * this label and, for each one of them, adds an outgoing edge to the basic block following the
|
|
- * given subroutine call. In other words, completes the control flow graph by adding the edges
|
|
- * corresponding to the return from this subroutine, when called from the given caller basic
|
|
- * block.
|
|
- *
|
|
- * <p>Note: a precondition and postcondition of this method is that all labels must have a null
|
|
- * {@link #nextListElement}.
|
|
- *
|
|
- * @param subroutineCaller a basic block that ends with a jsr to the basic block corresponding to
|
|
- * this label. This label is supposed to correspond to the start of a subroutine.
|
|
- */
|
|
- final void addSubroutineRetSuccessors(final Label subroutineCaller) {
|
|
- // Data flow algorithm: put this basic block in a list blocks to process (which are blocks
|
|
- // belonging to a subroutine starting with this label) and, while there are blocks to process,
|
|
- // remove one from the list, put it in a list of blocks that have been processed, add a return
|
|
- // edge to the successor of subroutineCaller if applicable, and add its successor basic blocks
|
|
- // in the control flow graph to the list of blocks to process (if not already done).
|
|
- Label listOfProcessedBlocks = EMPTY_LIST;
|
|
- Label listOfBlocksToProcess = this;
|
|
- listOfBlocksToProcess.nextListElement = EMPTY_LIST;
|
|
- while (listOfBlocksToProcess != EMPTY_LIST) {
|
|
- // Move a basic block from the list of blocks to process to the list of processed blocks.
|
|
- Label basicBlock = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = basicBlock.nextListElement;
|
|
- basicBlock.nextListElement = listOfProcessedBlocks;
|
|
- listOfProcessedBlocks = basicBlock;
|
|
-
|
|
- // Add an edge from this block to the successor of the caller basic block, if this block is
|
|
- // the end of a subroutine and if this block and subroutineCaller do not belong to the same
|
|
- // subroutine.
|
|
- if ((basicBlock.flags & FLAG_SUBROUTINE_END) != 0
|
|
- && basicBlock.subroutineId != subroutineCaller.subroutineId) {
|
|
- basicBlock.outgoingEdges =
|
|
- new Edge(
|
|
- basicBlock.outputStackSize,
|
|
- // By construction, the first outgoing edge of a basic block that ends with a jsr
|
|
- // instruction leads to the jsr continuation block, i.e. where execution continues
|
|
- // when ret is called (see {@link #FLAG_SUBROUTINE_CALLER}).
|
|
- subroutineCaller.outgoingEdges.successor,
|
|
- basicBlock.outgoingEdges);
|
|
- }
|
|
- // Add its successors to the list of blocks to process. Note that {@link #pushSuccessors} does
|
|
- // not push basic blocks which are already in a list. Here this means either in the list of
|
|
- // blocks to process, or in the list of already processed blocks. This second list is
|
|
- // important to make sure we don't reprocess an already processed block.
|
|
- listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
|
|
- }
|
|
- // Reset the {@link #nextListElement} of all the basic blocks that have been processed to null,
|
|
- // so that this method can be called again with a different subroutine or subroutine caller.
|
|
- while (listOfProcessedBlocks != EMPTY_LIST) {
|
|
- Label newListOfProcessedBlocks = listOfProcessedBlocks.nextListElement;
|
|
- listOfProcessedBlocks.nextListElement = null;
|
|
- listOfProcessedBlocks = newListOfProcessedBlocks;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds the successors of this label in the method's control flow graph (except those
|
|
- * corresponding to a jsr target, and those already in a list of labels) to the given list of
|
|
- * blocks to process, and returns the new list.
|
|
- *
|
|
- * @param listOfLabelsToProcess a list of basic blocks to process, linked together with their
|
|
- * {@link #nextListElement} field.
|
|
- * @return the new list of blocks to process.
|
|
- */
|
|
- private Label pushSuccessors(final Label listOfLabelsToProcess) {
|
|
- Label newListOfLabelsToProcess = listOfLabelsToProcess;
|
|
- Edge outgoingEdge = outgoingEdges;
|
|
- while (outgoingEdge != null) {
|
|
- // By construction, the second outgoing edge of a basic block that ends with a jsr instruction
|
|
- // leads to the jsr target (see {@link #FLAG_SUBROUTINE_CALLER}).
|
|
- boolean isJsrTarget =
|
|
- (flags & Label.FLAG_SUBROUTINE_CALLER) != 0 && outgoingEdge == outgoingEdges.nextEdge;
|
|
- if (!isJsrTarget && outgoingEdge.successor.nextListElement == null) {
|
|
- // Add this successor to the list of blocks to process, if it does not already belong to a
|
|
- // list of labels.
|
|
- outgoingEdge.successor.nextListElement = newListOfLabelsToProcess;
|
|
- newListOfLabelsToProcess = outgoingEdge.successor;
|
|
- }
|
|
- outgoingEdge = outgoingEdge.nextEdge;
|
|
- }
|
|
- return newListOfLabelsToProcess;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Overridden Object methods
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns a string representation of this label.
|
|
- *
|
|
- * @return a string representation of this label.
|
|
- */
|
|
- @Override
|
|
- public String toString() {
|
|
- return "L" + System.identityHashCode(this);
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/MethodTooLargeException.java b/src/main/java/org/mvel2/asm/MethodTooLargeException.java
|
|
deleted file mode 100644
|
|
index be9c718..0000000
|
|
--- a/src/main/java/org/mvel2/asm/MethodTooLargeException.java
|
|
+++ /dev/null
|
|
@@ -1,99 +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 Code attribute of a method produced by a {@link ClassWriter} is too
|
|
- * large.
|
|
- *
|
|
- * @author Jason Zaugg
|
|
- */
|
|
-public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
|
- private static final long serialVersionUID = 6807380416709738314L;
|
|
-
|
|
- private final String className;
|
|
- private final String methodName;
|
|
- private final String descriptor;
|
|
- private final int codeSize;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link MethodTooLargeException}.
|
|
- *
|
|
- * @param className the internal name of the owner class.
|
|
- * @param methodName the name of the method.
|
|
- * @param descriptor the descriptor of the method.
|
|
- * @param codeSize the size of the method's Code attribute, in bytes.
|
|
- */
|
|
- public MethodTooLargeException(
|
|
- final String className,
|
|
- final String methodName,
|
|
- final String descriptor,
|
|
- final int codeSize) {
|
|
- super("Method too large: " + className + "." + methodName + " " + descriptor);
|
|
- this.className = className;
|
|
- this.methodName = methodName;
|
|
- this.descriptor = descriptor;
|
|
- this.codeSize = codeSize;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the internal name of the owner class.
|
|
- *
|
|
- * @return the internal name of the owner class.
|
|
- */
|
|
- public String getClassName() {
|
|
- return className;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the name of the method.
|
|
- *
|
|
- * @return the name of the method.
|
|
- */
|
|
- public String getMethodName() {
|
|
- return methodName;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor of the method.
|
|
- *
|
|
- * @return the descriptor of the method.
|
|
- */
|
|
- public String getDescriptor() {
|
|
- return descriptor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of the method's Code attribute, in bytes.
|
|
- *
|
|
- * @return the size of the method's Code attribute, in bytes.
|
|
- */
|
|
- public int getCodeSize() {
|
|
- return codeSize;
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/MethodVisitor.java b/src/main/java/org/mvel2/asm/MethodVisitor.java
|
|
deleted file mode 100644
|
|
index a1dc457..0000000
|
|
--- a/src/main/java/org/mvel2/asm/MethodVisitor.java
|
|
+++ /dev/null
|
|
@@ -1,781 +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 method. The methods of this class must be called in the following
|
|
- * order: ( {@code visitParameter} )* [ {@code visitAnnotationDefault} ] ( {@code visitAnnotation} |
|
|
- * {@code visitAnnotableParameterCount} | {@code visitParameterAnnotation} {@code
|
|
- * visitTypeAnnotation} | {@code visitAttribute} )* [ {@code visitCode} ( {@code visitFrame} |
|
|
- * {@code visit<i>X</i>Insn} | {@code visitLabel} | {@code visitInsnAnnotation} | {@code
|
|
- * visitTryCatchBlock} | {@code visitTryCatchAnnotation} | {@code visitLocalVariable} | {@code
|
|
- * visitLocalVariableAnnotation} | {@code visitLineNumber} )* {@code visitMaxs} ] {@code visitEnd}.
|
|
- * In addition, the {@code visit<i>X</i>Insn} and {@code visitLabel} methods must be called in the
|
|
- * sequential order of the bytecode instructions of the visited code, {@code visitInsnAnnotation}
|
|
- * must be called <i>after</i> the annotated instruction, {@code visitTryCatchBlock} must be called
|
|
- * <i>before</i> the labels passed as arguments have been visited, {@code
|
|
- * visitTryCatchBlockAnnotation} must be called <i>after</i> the corresponding try catch block has
|
|
- * been visited, and the {@code visitLocalVariable}, {@code visitLocalVariableAnnotation} and {@code
|
|
- * visitLineNumber} methods must be called <i>after</i> the labels passed as arguments have been
|
|
- * visited.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public abstract class MethodVisitor {
|
|
-
|
|
- private static final String REQUIRES_ASM5 = "This feature requires ASM5";
|
|
-
|
|
- /**
|
|
- * 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 method visitor to which this visitor must delegate method calls. May be null. */
|
|
- protected MethodVisitor mv;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link MethodVisitor}.
|
|
- *
|
|
- * @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 MethodVisitor(final int api) {
|
|
- this(api, null);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link MethodVisitor}.
|
|
- *
|
|
- * @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 methodVisitor the method visitor to which this visitor must delegate method calls. May
|
|
- * be null.
|
|
- */
|
|
- public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- this.api = api;
|
|
- this.mv = methodVisitor;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Parameters, annotations and non standard attributes
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Visits a parameter of this method.
|
|
- *
|
|
- * @param name parameter name or null if none is provided.
|
|
- * @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
|
|
- * or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}).
|
|
- */
|
|
- public void visitParameter(final String name, final int access) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- mv.visitParameter(name, access);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the default value of this annotation interface method.
|
|
- *
|
|
- * @return a visitor to the visit the actual default value of this annotation interface method, or
|
|
- * {@literal null} if this visitor is not interested in visiting this default value. The
|
|
- * 'name' parameters passed to the methods of this annotation visitor are ignored. Moreover,
|
|
- * exacly one visit method must be called on this annotation visitor, followed by visitEnd.
|
|
- */
|
|
- public AnnotationVisitor visitAnnotationDefault() {
|
|
- if (mv != null) {
|
|
- return mv.visitAnnotationDefault();
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation of this method.
|
|
- *
|
|
- * @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 (mv != null) {
|
|
- return mv.visitAnnotation(descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation on a type in the method signature.
|
|
- *
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
|
- * {@link TypeReference#METHOD_TYPE_PARAMETER}, {@link
|
|
- * TypeReference#METHOD_TYPE_PARAMETER_BOUND}, {@link TypeReference#METHOD_RETURN}, {@link
|
|
- * TypeReference#METHOD_RECEIVER}, {@link TypeReference#METHOD_FORMAL_PARAMETER} or {@link
|
|
- * TypeReference#THROWS}. 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(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- return mv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the number of method parameters that can have annotations. By default (i.e. when this
|
|
- * method is not called), all the method parameters defined by the method descriptor can have
|
|
- * annotations.
|
|
- *
|
|
- * @param parameterCount the number of method parameters than can have annotations. This number
|
|
- * must be less or equal than the number of parameter types in the method descriptor. It can
|
|
- * be strictly less when a method has synthetic parameters and when these parameters are
|
|
- * ignored when computing parameter indices for the purpose of parameter annotations (see
|
|
- * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18).
|
|
- * @param visible {@literal true} to define the number of method parameters that can have
|
|
- * annotations visible at runtime, {@literal false} to define the number of method parameters
|
|
- * that can have annotations invisible at runtime.
|
|
- */
|
|
- public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) {
|
|
- if (mv != null) {
|
|
- mv.visitAnnotableParameterCount(parameterCount, visible);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation of a parameter this method.
|
|
- *
|
|
- * @param parameter the parameter index. This index must be strictly smaller than the number of
|
|
- * parameters in the method descriptor, and strictly smaller than the parameter count
|
|
- * specified in {@link #visitAnnotableParameterCount}. Important note: <i>a parameter index i
|
|
- * is not required to correspond to the i'th parameter descriptor in the method
|
|
- * descriptor</i>, in particular in case of synthetic parameters (see
|
|
- * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18).
|
|
- * @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 visitParameterAnnotation(
|
|
- final int parameter, final String descriptor, final boolean visible) {
|
|
- if (mv != null) {
|
|
- return mv.visitParameterAnnotation(parameter, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a non standard attribute of this method.
|
|
- *
|
|
- * @param attribute an attribute.
|
|
- */
|
|
- public void visitAttribute(final Attribute attribute) {
|
|
- if (mv != null) {
|
|
- mv.visitAttribute(attribute);
|
|
- }
|
|
- }
|
|
-
|
|
- /** Starts the visit of the method's code, if any (i.e. non abstract method). */
|
|
- public void visitCode() {
|
|
- if (mv != null) {
|
|
- mv.visitCode();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the current state of the local variables and operand stack elements. This method must(*)
|
|
- * be called <i>just before</i> any instruction <b>i</b> that follows an unconditional branch
|
|
- * instruction such as GOTO or THROW, that is the target of a jump instruction, or that starts an
|
|
- * exception handler block. The visited types must describe the values of the local variables and
|
|
- * of the operand stack elements <i>just before</i> <b>i</b> is executed.<br>
|
|
- * <br>
|
|
- * (*) this is mandatory only for classes whose version is greater than or equal to {@link
|
|
- * Opcodes#V1_6}. <br>
|
|
- * <br>
|
|
- * The frames of a method must be given either in expanded form, or in compressed form (all frames
|
|
- * must use the same format, i.e. you must not mix expanded and compressed frames within a single
|
|
- * method):
|
|
- *
|
|
- * <ul>
|
|
- * <li>In expanded form, all frames must have the F_NEW type.
|
|
- * <li>In compressed form, frames are basically "deltas" from the state of the previous frame:
|
|
- * <ul>
|
|
- * <li>{@link Opcodes#F_SAME} representing frame with exactly the same locals as the
|
|
- * previous frame and with the empty stack.
|
|
- * <li>{@link Opcodes#F_SAME1} representing frame with exactly the same locals as the
|
|
- * previous frame and with single value on the stack ( <code>numStack</code> is 1 and
|
|
- * <code>stack[0]</code> contains value for the type of the stack item).
|
|
- * <li>{@link Opcodes#F_APPEND} representing frame with current locals are the same as the
|
|
- * locals in the previous frame, except that additional locals are defined (<code>
|
|
- * numLocal</code> is 1, 2 or 3 and <code>local</code> elements contains values
|
|
- * representing added types).
|
|
- * <li>{@link Opcodes#F_CHOP} representing frame with current locals are the same as the
|
|
- * locals in the previous frame, except that the last 1-3 locals are absent and with
|
|
- * the empty stack (<code>numLocal</code> is 1, 2 or 3).
|
|
- * <li>{@link Opcodes#F_FULL} representing complete frame data.
|
|
- * </ul>
|
|
- * </ul>
|
|
- *
|
|
- * <br>
|
|
- * In both cases the first frame, corresponding to the method's parameters and access flags, is
|
|
- * implicit and must not be visited. Also, it is illegal to visit two or more frames for the same
|
|
- * code location (i.e., at least one instruction must be visited between two calls to visitFrame).
|
|
- *
|
|
- * @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
|
|
- * frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
|
|
- * Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
|
|
- * @param numLocal the number of local variables in the visited frame.
|
|
- * @param local the local variable types in this frame. This array must not be modified. Primitive
|
|
- * types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
|
- * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or
|
|
- * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a single element).
|
|
- * Reference types are represented by String objects (representing internal names), and
|
|
- * uninitialized types by Label objects (this label designates the NEW instruction that
|
|
- * created this uninitialized value).
|
|
- * @param numStack the number of operand stack elements in the visited frame.
|
|
- * @param stack the operand stack types in this frame. This array must not be modified. Its
|
|
- * content has the same format as the "local" array.
|
|
- * @throws IllegalStateException if a frame is visited just after another one, without any
|
|
- * instruction between the two (unless this frame is a Opcodes#F_SAME frame, in which case it
|
|
- * is silently ignored).
|
|
- */
|
|
- public void visitFrame(
|
|
- final int type,
|
|
- final int numLocal,
|
|
- final Object[] local,
|
|
- final int numStack,
|
|
- final Object[] stack) {
|
|
- if (mv != null) {
|
|
- mv.visitFrame(type, numLocal, local, numStack, stack);
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Normal instructions
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Visits a zero operand instruction.
|
|
- *
|
|
- * @param opcode the opcode of the instruction to be visited. This opcode is either NOP,
|
|
- * ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5,
|
|
- * LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD,
|
|
- * FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE,
|
|
- * AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2,
|
|
- * SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV,
|
|
- * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR,
|
|
- * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I,
|
|
- * D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN,
|
|
- * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT.
|
|
- */
|
|
- public void visitInsn(final int opcode) {
|
|
- if (mv != null) {
|
|
- mv.visitInsn(opcode);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an instruction with a single int operand.
|
|
- *
|
|
- * @param opcode the opcode of the instruction to be visited. This opcode is either BIPUSH, SIPUSH
|
|
- * or NEWARRAY.
|
|
- * @param operand the operand of the instruction to be visited.<br>
|
|
- * When opcode is BIPUSH, operand value should be between Byte.MIN_VALUE and Byte.MAX_VALUE.
|
|
- * <br>
|
|
- * When opcode is SIPUSH, operand value should be between Short.MIN_VALUE and Short.MAX_VALUE.
|
|
- * <br>
|
|
- * When opcode is NEWARRAY, operand value should be one of {@link Opcodes#T_BOOLEAN}, {@link
|
|
- * Opcodes#T_CHAR}, {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, {@link Opcodes#T_BYTE},
|
|
- * {@link Opcodes#T_SHORT}, {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
|
|
- */
|
|
- public void visitIntInsn(final int opcode, final int operand) {
|
|
- if (mv != null) {
|
|
- mv.visitIntInsn(opcode, operand);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a local variable instruction. A local variable instruction is an instruction that loads
|
|
- * or stores the value of a local variable.
|
|
- *
|
|
- * @param opcode the opcode of the local variable instruction to be visited. This opcode is either
|
|
- * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
|
|
- * @param var the operand of the instruction to be visited. This operand is the index of a local
|
|
- * variable.
|
|
- */
|
|
- public void visitVarInsn(final int opcode, final int var) {
|
|
- if (mv != null) {
|
|
- mv.visitVarInsn(opcode, var);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a type instruction. A type instruction is an instruction that takes the internal name of
|
|
- * a class as parameter.
|
|
- *
|
|
- * @param opcode the opcode of the type instruction to be visited. This opcode is either NEW,
|
|
- * ANEWARRAY, CHECKCAST or INSTANCEOF.
|
|
- * @param type the operand of the instruction to be visited. This operand must be the internal
|
|
- * name of an object or array class (see {@link Type#getInternalName()}).
|
|
- */
|
|
- public void visitTypeInsn(final int opcode, final String type) {
|
|
- if (mv != null) {
|
|
- mv.visitTypeInsn(opcode, type);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a field instruction. A field instruction is an instruction that loads or stores the
|
|
- * value of a field of an object.
|
|
- *
|
|
- * @param opcode the opcode of the type instruction to be visited. This opcode is either
|
|
- * GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
|
|
- * @param owner the internal name of the field's owner class (see {@link Type#getInternalName()}).
|
|
- * @param name the field's name.
|
|
- * @param descriptor the field's descriptor (see {@link Type}).
|
|
- */
|
|
- public void visitFieldInsn(
|
|
- final int opcode, final String owner, final String name, final String descriptor) {
|
|
- if (mv != null) {
|
|
- mv.visitFieldInsn(opcode, owner, name, descriptor);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a method instruction. A method instruction is an instruction that invokes a method.
|
|
- *
|
|
- * @param opcode the opcode of the type instruction to be visited. This opcode is either
|
|
- * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
|
|
- * @param owner the internal name of the method's owner class (see {@link
|
|
- * Type#getInternalName()}).
|
|
- * @param name the method's name.
|
|
- * @param descriptor the method's descriptor (see {@link Type}).
|
|
- * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead.
|
|
- */
|
|
- @Deprecated
|
|
- public void visitMethodInsn(
|
|
- final int opcode, final String owner, final String name, final String descriptor) {
|
|
- if (api >= Opcodes.ASM5) {
|
|
- boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
|
|
- visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
|
- return;
|
|
- }
|
|
- if (mv != null) {
|
|
- mv.visitMethodInsn(opcode, owner, name, descriptor);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a method instruction. A method instruction is an instruction that invokes a method.
|
|
- *
|
|
- * @param opcode the opcode of the type instruction to be visited. This opcode is either
|
|
- * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
|
|
- * @param owner the internal name of the method's owner class (see {@link
|
|
- * Type#getInternalName()}).
|
|
- * @param name the method's name.
|
|
- * @param descriptor the method's descriptor (see {@link Type}).
|
|
- * @param isInterface if the method's owner class is an interface.
|
|
- */
|
|
- public void visitMethodInsn(
|
|
- final int opcode,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final boolean isInterface) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) {
|
|
- throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5");
|
|
- }
|
|
- visitMethodInsn(opcode, owner, name, descriptor);
|
|
- return;
|
|
- }
|
|
- if (mv != null) {
|
|
- mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an invokedynamic instruction.
|
|
- *
|
|
- * @param name the method's name.
|
|
- * @param descriptor the method's descriptor (see {@link Type}).
|
|
- * @param bootstrapMethodHandle the bootstrap method.
|
|
- * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be
|
|
- * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link
|
|
- * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify
|
|
- * the content of the array so a caller should expect that this array may change.
|
|
- */
|
|
- public void visitInvokeDynamicInsn(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final Handle bootstrapMethodHandle,
|
|
- final Object... bootstrapMethodArguments) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a jump instruction. A jump instruction is an instruction that may jump to another
|
|
- * instruction.
|
|
- *
|
|
- * @param opcode the opcode of the type instruction to be visited. This opcode is either IFEQ,
|
|
- * IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT,
|
|
- * IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
|
|
- * @param label the operand of the instruction to be visited. This operand is a label that
|
|
- * designates the instruction to which the jump instruction may jump.
|
|
- */
|
|
- public void visitJumpInsn(final int opcode, final Label label) {
|
|
- if (mv != null) {
|
|
- mv.visitJumpInsn(opcode, label);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a label. A label designates the instruction that will be visited just after it.
|
|
- *
|
|
- * @param label a {@link Label} object.
|
|
- */
|
|
- public void visitLabel(final Label label) {
|
|
- if (mv != null) {
|
|
- mv.visitLabel(label);
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Special instructions
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Visits a LDC instruction. Note that new constant types may be added in future versions of the
|
|
- * Java Virtual Machine. To easily detect new constant types, implementations of this method
|
|
- * should check for unexpected constant types, like this:
|
|
- *
|
|
- * <pre>
|
|
- * if (cst instanceof Integer) {
|
|
- * // ...
|
|
- * } else if (cst instanceof Float) {
|
|
- * // ...
|
|
- * } else if (cst instanceof Long) {
|
|
- * // ...
|
|
- * } else if (cst instanceof Double) {
|
|
- * // ...
|
|
- * } else if (cst instanceof String) {
|
|
- * // ...
|
|
- * } else if (cst instanceof Type) {
|
|
- * int sort = ((Type) cst).getSort();
|
|
- * if (sort == Type.OBJECT) {
|
|
- * // ...
|
|
- * } else if (sort == Type.ARRAY) {
|
|
- * // ...
|
|
- * } else if (sort == Type.METHOD) {
|
|
- * // ...
|
|
- * } else {
|
|
- * // throw an exception
|
|
- * }
|
|
- * } else if (cst instanceof Handle) {
|
|
- * // ...
|
|
- * } else if (cst instanceof ConstantDynamic) {
|
|
- * // ...
|
|
- * } else {
|
|
- * // throw an exception
|
|
- * }
|
|
- * </pre>
|
|
- *
|
|
- * @param value the constant to be loaded on the stack. This parameter must be a non null {@link
|
|
- * Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link
|
|
- * Type} of OBJECT or ARRAY sort for {@code .class} constants, for classes whose version is
|
|
- * 49, a {@link Type} of METHOD sort for MethodType, a {@link Handle} for MethodHandle
|
|
- * constants, for classes whose version is 51 or a {@link ConstantDynamic} for a constant
|
|
- * dynamic for classes whose version is 55.
|
|
- */
|
|
- public void visitLdcInsn(final Object value) {
|
|
- if (api < Opcodes.ASM5
|
|
- && (value instanceof Handle
|
|
- || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
|
|
- throw new UnsupportedOperationException("This feature requires ASM7");
|
|
- }
|
|
- if (mv != null) {
|
|
- mv.visitLdcInsn(value);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an IINC instruction.
|
|
- *
|
|
- * @param var index of the local variable to be incremented.
|
|
- * @param increment amount to increment the local variable by.
|
|
- */
|
|
- public void visitIincInsn(final int var, final int increment) {
|
|
- if (mv != null) {
|
|
- mv.visitIincInsn(var, increment);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a TABLESWITCH instruction.
|
|
- *
|
|
- * @param min the minimum key value.
|
|
- * @param max the maximum key value.
|
|
- * @param dflt beginning of the default handler block.
|
|
- * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the
|
|
- * handler block for the {@code min + i} key.
|
|
- */
|
|
- public void visitTableSwitchInsn(
|
|
- final int min, final int max, final Label dflt, final Label... labels) {
|
|
- if (mv != null) {
|
|
- mv.visitTableSwitchInsn(min, max, dflt, labels);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a LOOKUPSWITCH instruction.
|
|
- *
|
|
- * @param dflt beginning of the default handler block.
|
|
- * @param keys the values of the keys.
|
|
- * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the
|
|
- * handler block for the {@code keys[i]} key.
|
|
- */
|
|
- public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
|
|
- if (mv != null) {
|
|
- mv.visitLookupSwitchInsn(dflt, keys, labels);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a MULTIANEWARRAY instruction.
|
|
- *
|
|
- * @param descriptor an array type descriptor (see {@link Type}).
|
|
- * @param numDimensions the number of dimensions of the array to allocate.
|
|
- */
|
|
- public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) {
|
|
- if (mv != null) {
|
|
- mv.visitMultiANewArrayInsn(descriptor, numDimensions);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation on an instruction. This method must be called just <i>after</i> the
|
|
- * annotated instruction. It can be called several times for the same instruction.
|
|
- *
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
|
- * {@link TypeReference#INSTANCEOF}, {@link TypeReference#NEW}, {@link
|
|
- * TypeReference#CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE}, {@link
|
|
- * TypeReference#CAST}, {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link
|
|
- * TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT}, {@link
|
|
- * TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link
|
|
- * TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT}. 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 visitInsnAnnotation(
|
|
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- return mv.visitInsnAnnotation(typeRef, typePath, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Exceptions table entries, debug information, max stack and max locals
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Visits a try catch block.
|
|
- *
|
|
- * @param start the beginning of the exception handler's scope (inclusive).
|
|
- * @param end the end of the exception handler's scope (exclusive).
|
|
- * @param handler the beginning of the exception handler's code.
|
|
- * @param type the internal name of the type of exceptions handled by the handler, or {@literal
|
|
- * null} to catch any exceptions (for "finally" blocks).
|
|
- * @throws IllegalArgumentException if one of the labels has already been visited by this visitor
|
|
- * (by the {@link #visitLabel} method).
|
|
- */
|
|
- public void visitTryCatchBlock(
|
|
- final Label start, final Label end, final Label handler, final String type) {
|
|
- if (mv != null) {
|
|
- mv.visitTryCatchBlock(start, end, handler, type);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation on an exception handler type. This method must be called <i>after</i> the
|
|
- * {@link #visitTryCatchBlock} for the annotated exception handler. It can be called several times
|
|
- * for the same exception handler.
|
|
- *
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
|
- * {@link TypeReference#EXCEPTION_PARAMETER}. 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 visitTryCatchAnnotation(
|
|
- final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- return mv.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a local variable declaration.
|
|
- *
|
|
- * @param name the name of a local variable.
|
|
- * @param descriptor the type descriptor of this local variable.
|
|
- * @param signature the type signature of this local variable. May be {@literal null} if the local
|
|
- * variable type does not use generic types.
|
|
- * @param start the first instruction corresponding to the scope of this local variable
|
|
- * (inclusive).
|
|
- * @param end the last instruction corresponding to the scope of this local variable (exclusive).
|
|
- * @param index the local variable's index.
|
|
- * @throws IllegalArgumentException if one of the labels has not already been visited by this
|
|
- * visitor (by the {@link #visitLabel} method).
|
|
- */
|
|
- public void visitLocalVariable(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final String signature,
|
|
- final Label start,
|
|
- final Label end,
|
|
- final int index) {
|
|
- if (mv != null) {
|
|
- mv.visitLocalVariable(name, descriptor, signature, start, end, index);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an annotation on a local variable type.
|
|
- *
|
|
- * @param typeRef a reference to the annotated type. The sort of this type reference must be
|
|
- * {@link TypeReference#LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE}. 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 start the fist instructions corresponding to the continuous ranges that make the scope
|
|
- * of this local variable (inclusive).
|
|
- * @param end the last instructions corresponding to the continuous ranges that make the scope of
|
|
- * this local variable (exclusive). This array must have the same size as the 'start' array.
|
|
- * @param index the local variable's index in each range. This array must have the same size as
|
|
- * the 'start' array.
|
|
- * @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 visitLocalVariableAnnotation(
|
|
- final int typeRef,
|
|
- final TypePath typePath,
|
|
- final Label[] start,
|
|
- final Label[] end,
|
|
- final int[] index,
|
|
- final String descriptor,
|
|
- final boolean visible) {
|
|
- if (api < Opcodes.ASM5) {
|
|
- throw new UnsupportedOperationException(REQUIRES_ASM5);
|
|
- }
|
|
- if (mv != null) {
|
|
- return mv.visitLocalVariableAnnotation(
|
|
- typeRef, typePath, start, end, index, descriptor, visible);
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a line number declaration.
|
|
- *
|
|
- * @param line a line number. This number refers to the source file from which the class was
|
|
- * compiled.
|
|
- * @param start the first instruction corresponding to this line number.
|
|
- * @throws IllegalArgumentException if {@code start} has not already been visited by this visitor
|
|
- * (by the {@link #visitLabel} method).
|
|
- */
|
|
- public void visitLineNumber(final int line, final Label start) {
|
|
- if (mv != null) {
|
|
- mv.visitLineNumber(line, start);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the maximum stack size and the maximum number of local variables of the method.
|
|
- *
|
|
- * @param maxStack maximum stack size of the method.
|
|
- * @param maxLocals maximum number of local variables for the method.
|
|
- */
|
|
- public void visitMaxs(final int maxStack, final int maxLocals) {
|
|
- if (mv != null) {
|
|
- mv.visitMaxs(maxStack, maxLocals);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the end of the method. This method, which is the last one to be called, is used to
|
|
- * inform the visitor that all the annotations and attributes of the method have been visited.
|
|
- */
|
|
- public void visitEnd() {
|
|
- if (mv != null) {
|
|
- mv.visitEnd();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/MethodWriter.java b/src/main/java/org/mvel2/asm/MethodWriter.java
|
|
deleted file mode 100644
|
|
index 08f1d76..0000000
|
|
--- a/src/main/java/org/mvel2/asm/MethodWriter.java
|
|
+++ /dev/null
|
|
@@ -1,2441 +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 MethodVisitor} that generates a corresponding 'method_info' structure, 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.6">JVMS
|
|
- * 4.6</a>
|
|
- * @author Eric Bruneton
|
|
- * @author Eugene Kuleshov
|
|
- */
|
|
-final class MethodWriter extends MethodVisitor {
|
|
-
|
|
- /** Indicates that nothing must be computed. */
|
|
- static final int COMPUTE_NOTHING = 0;
|
|
-
|
|
- /**
|
|
- * Indicates that the maximum stack size and the maximum number of local variables must be
|
|
- * computed, from scratch.
|
|
- */
|
|
- static final int COMPUTE_MAX_STACK_AND_LOCAL = 1;
|
|
-
|
|
- /**
|
|
- * Indicates that the maximum stack size and the maximum number of local variables must be
|
|
- * computed, from the existing stack map frames. This can be done more efficiently than with the
|
|
- * control flow graph algorithm used for {@link #COMPUTE_MAX_STACK_AND_LOCAL}, by using a linear
|
|
- * scan of the bytecode instructions.
|
|
- */
|
|
- static final int COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES = 2;
|
|
-
|
|
- /**
|
|
- * Indicates that the stack map frames of type F_INSERT must be computed. The other frames are not
|
|
- * computed. They should all be of type F_NEW and should be sufficient to compute the content of
|
|
- * the F_INSERT frames, together with the bytecode instructions between a F_NEW and a F_INSERT
|
|
- * frame - and without any knowledge of the type hierarchy (by definition of F_INSERT).
|
|
- */
|
|
- static final int COMPUTE_INSERTED_FRAMES = 3;
|
|
-
|
|
- /**
|
|
- * Indicates that all the stack map frames must be computed. In this case the maximum stack size
|
|
- * and the maximum number of local variables is also computed.
|
|
- */
|
|
- static final int COMPUTE_ALL_FRAMES = 4;
|
|
-
|
|
- /** Indicates that {@link #STACK_SIZE_DELTA} is not applicable (not constant or never used). */
|
|
- private static final int NA = 0;
|
|
-
|
|
- /**
|
|
- * The stack size variation corresponding to each JVM opcode. The stack size variation for opcode
|
|
- * 'o' is given by the array element at index 'o'.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html">JVMS 6</a>
|
|
- */
|
|
- private static final int[] STACK_SIZE_DELTA = {
|
|
- 0, // nop = 0 (0x0)
|
|
- 1, // aconst_null = 1 (0x1)
|
|
- 1, // iconst_m1 = 2 (0x2)
|
|
- 1, // iconst_0 = 3 (0x3)
|
|
- 1, // iconst_1 = 4 (0x4)
|
|
- 1, // iconst_2 = 5 (0x5)
|
|
- 1, // iconst_3 = 6 (0x6)
|
|
- 1, // iconst_4 = 7 (0x7)
|
|
- 1, // iconst_5 = 8 (0x8)
|
|
- 2, // lconst_0 = 9 (0x9)
|
|
- 2, // lconst_1 = 10 (0xa)
|
|
- 1, // fconst_0 = 11 (0xb)
|
|
- 1, // fconst_1 = 12 (0xc)
|
|
- 1, // fconst_2 = 13 (0xd)
|
|
- 2, // dconst_0 = 14 (0xe)
|
|
- 2, // dconst_1 = 15 (0xf)
|
|
- 1, // bipush = 16 (0x10)
|
|
- 1, // sipush = 17 (0x11)
|
|
- 1, // ldc = 18 (0x12)
|
|
- NA, // ldc_w = 19 (0x13)
|
|
- NA, // ldc2_w = 20 (0x14)
|
|
- 1, // iload = 21 (0x15)
|
|
- 2, // lload = 22 (0x16)
|
|
- 1, // fload = 23 (0x17)
|
|
- 2, // dload = 24 (0x18)
|
|
- 1, // aload = 25 (0x19)
|
|
- NA, // iload_0 = 26 (0x1a)
|
|
- NA, // iload_1 = 27 (0x1b)
|
|
- NA, // iload_2 = 28 (0x1c)
|
|
- NA, // iload_3 = 29 (0x1d)
|
|
- NA, // lload_0 = 30 (0x1e)
|
|
- NA, // lload_1 = 31 (0x1f)
|
|
- NA, // lload_2 = 32 (0x20)
|
|
- NA, // lload_3 = 33 (0x21)
|
|
- NA, // fload_0 = 34 (0x22)
|
|
- NA, // fload_1 = 35 (0x23)
|
|
- NA, // fload_2 = 36 (0x24)
|
|
- NA, // fload_3 = 37 (0x25)
|
|
- NA, // dload_0 = 38 (0x26)
|
|
- NA, // dload_1 = 39 (0x27)
|
|
- NA, // dload_2 = 40 (0x28)
|
|
- NA, // dload_3 = 41 (0x29)
|
|
- NA, // aload_0 = 42 (0x2a)
|
|
- NA, // aload_1 = 43 (0x2b)
|
|
- NA, // aload_2 = 44 (0x2c)
|
|
- NA, // aload_3 = 45 (0x2d)
|
|
- -1, // iaload = 46 (0x2e)
|
|
- 0, // laload = 47 (0x2f)
|
|
- -1, // faload = 48 (0x30)
|
|
- 0, // daload = 49 (0x31)
|
|
- -1, // aaload = 50 (0x32)
|
|
- -1, // baload = 51 (0x33)
|
|
- -1, // caload = 52 (0x34)
|
|
- -1, // saload = 53 (0x35)
|
|
- -1, // istore = 54 (0x36)
|
|
- -2, // lstore = 55 (0x37)
|
|
- -1, // fstore = 56 (0x38)
|
|
- -2, // dstore = 57 (0x39)
|
|
- -1, // astore = 58 (0x3a)
|
|
- NA, // istore_0 = 59 (0x3b)
|
|
- NA, // istore_1 = 60 (0x3c)
|
|
- NA, // istore_2 = 61 (0x3d)
|
|
- NA, // istore_3 = 62 (0x3e)
|
|
- NA, // lstore_0 = 63 (0x3f)
|
|
- NA, // lstore_1 = 64 (0x40)
|
|
- NA, // lstore_2 = 65 (0x41)
|
|
- NA, // lstore_3 = 66 (0x42)
|
|
- NA, // fstore_0 = 67 (0x43)
|
|
- NA, // fstore_1 = 68 (0x44)
|
|
- NA, // fstore_2 = 69 (0x45)
|
|
- NA, // fstore_3 = 70 (0x46)
|
|
- NA, // dstore_0 = 71 (0x47)
|
|
- NA, // dstore_1 = 72 (0x48)
|
|
- NA, // dstore_2 = 73 (0x49)
|
|
- NA, // dstore_3 = 74 (0x4a)
|
|
- NA, // astore_0 = 75 (0x4b)
|
|
- NA, // astore_1 = 76 (0x4c)
|
|
- NA, // astore_2 = 77 (0x4d)
|
|
- NA, // astore_3 = 78 (0x4e)
|
|
- -3, // iastore = 79 (0x4f)
|
|
- -4, // lastore = 80 (0x50)
|
|
- -3, // fastore = 81 (0x51)
|
|
- -4, // dastore = 82 (0x52)
|
|
- -3, // aastore = 83 (0x53)
|
|
- -3, // bastore = 84 (0x54)
|
|
- -3, // castore = 85 (0x55)
|
|
- -3, // sastore = 86 (0x56)
|
|
- -1, // pop = 87 (0x57)
|
|
- -2, // pop2 = 88 (0x58)
|
|
- 1, // dup = 89 (0x59)
|
|
- 1, // dup_x1 = 90 (0x5a)
|
|
- 1, // dup_x2 = 91 (0x5b)
|
|
- 2, // dup2 = 92 (0x5c)
|
|
- 2, // dup2_x1 = 93 (0x5d)
|
|
- 2, // dup2_x2 = 94 (0x5e)
|
|
- 0, // swap = 95 (0x5f)
|
|
- -1, // iadd = 96 (0x60)
|
|
- -2, // ladd = 97 (0x61)
|
|
- -1, // fadd = 98 (0x62)
|
|
- -2, // dadd = 99 (0x63)
|
|
- -1, // isub = 100 (0x64)
|
|
- -2, // lsub = 101 (0x65)
|
|
- -1, // fsub = 102 (0x66)
|
|
- -2, // dsub = 103 (0x67)
|
|
- -1, // imul = 104 (0x68)
|
|
- -2, // lmul = 105 (0x69)
|
|
- -1, // fmul = 106 (0x6a)
|
|
- -2, // dmul = 107 (0x6b)
|
|
- -1, // idiv = 108 (0x6c)
|
|
- -2, // ldiv = 109 (0x6d)
|
|
- -1, // fdiv = 110 (0x6e)
|
|
- -2, // ddiv = 111 (0x6f)
|
|
- -1, // irem = 112 (0x70)
|
|
- -2, // lrem = 113 (0x71)
|
|
- -1, // frem = 114 (0x72)
|
|
- -2, // drem = 115 (0x73)
|
|
- 0, // ineg = 116 (0x74)
|
|
- 0, // lneg = 117 (0x75)
|
|
- 0, // fneg = 118 (0x76)
|
|
- 0, // dneg = 119 (0x77)
|
|
- -1, // ishl = 120 (0x78)
|
|
- -1, // lshl = 121 (0x79)
|
|
- -1, // ishr = 122 (0x7a)
|
|
- -1, // lshr = 123 (0x7b)
|
|
- -1, // iushr = 124 (0x7c)
|
|
- -1, // lushr = 125 (0x7d)
|
|
- -1, // iand = 126 (0x7e)
|
|
- -2, // land = 127 (0x7f)
|
|
- -1, // ior = 128 (0x80)
|
|
- -2, // lor = 129 (0x81)
|
|
- -1, // ixor = 130 (0x82)
|
|
- -2, // lxor = 131 (0x83)
|
|
- 0, // iinc = 132 (0x84)
|
|
- 1, // i2l = 133 (0x85)
|
|
- 0, // i2f = 134 (0x86)
|
|
- 1, // i2d = 135 (0x87)
|
|
- -1, // l2i = 136 (0x88)
|
|
- -1, // l2f = 137 (0x89)
|
|
- 0, // l2d = 138 (0x8a)
|
|
- 0, // f2i = 139 (0x8b)
|
|
- 1, // f2l = 140 (0x8c)
|
|
- 1, // f2d = 141 (0x8d)
|
|
- -1, // d2i = 142 (0x8e)
|
|
- 0, // d2l = 143 (0x8f)
|
|
- -1, // d2f = 144 (0x90)
|
|
- 0, // i2b = 145 (0x91)
|
|
- 0, // i2c = 146 (0x92)
|
|
- 0, // i2s = 147 (0x93)
|
|
- -3, // lcmp = 148 (0x94)
|
|
- -1, // fcmpl = 149 (0x95)
|
|
- -1, // fcmpg = 150 (0x96)
|
|
- -3, // dcmpl = 151 (0x97)
|
|
- -3, // dcmpg = 152 (0x98)
|
|
- -1, // ifeq = 153 (0x99)
|
|
- -1, // ifne = 154 (0x9a)
|
|
- -1, // iflt = 155 (0x9b)
|
|
- -1, // ifge = 156 (0x9c)
|
|
- -1, // ifgt = 157 (0x9d)
|
|
- -1, // ifle = 158 (0x9e)
|
|
- -2, // if_icmpeq = 159 (0x9f)
|
|
- -2, // if_icmpne = 160 (0xa0)
|
|
- -2, // if_icmplt = 161 (0xa1)
|
|
- -2, // if_icmpge = 162 (0xa2)
|
|
- -2, // if_icmpgt = 163 (0xa3)
|
|
- -2, // if_icmple = 164 (0xa4)
|
|
- -2, // if_acmpeq = 165 (0xa5)
|
|
- -2, // if_acmpne = 166 (0xa6)
|
|
- 0, // goto = 167 (0xa7)
|
|
- 1, // jsr = 168 (0xa8)
|
|
- 0, // ret = 169 (0xa9)
|
|
- -1, // tableswitch = 170 (0xaa)
|
|
- -1, // lookupswitch = 171 (0xab)
|
|
- -1, // ireturn = 172 (0xac)
|
|
- -2, // lreturn = 173 (0xad)
|
|
- -1, // freturn = 174 (0xae)
|
|
- -2, // dreturn = 175 (0xaf)
|
|
- -1, // areturn = 176 (0xb0)
|
|
- 0, // return = 177 (0xb1)
|
|
- NA, // getstatic = 178 (0xb2)
|
|
- NA, // putstatic = 179 (0xb3)
|
|
- NA, // getfield = 180 (0xb4)
|
|
- NA, // putfield = 181 (0xb5)
|
|
- NA, // invokevirtual = 182 (0xb6)
|
|
- NA, // invokespecial = 183 (0xb7)
|
|
- NA, // invokestatic = 184 (0xb8)
|
|
- NA, // invokeinterface = 185 (0xb9)
|
|
- NA, // invokedynamic = 186 (0xba)
|
|
- 1, // new = 187 (0xbb)
|
|
- 0, // newarray = 188 (0xbc)
|
|
- 0, // anewarray = 189 (0xbd)
|
|
- 0, // arraylength = 190 (0xbe)
|
|
- NA, // athrow = 191 (0xbf)
|
|
- 0, // checkcast = 192 (0xc0)
|
|
- 0, // instanceof = 193 (0xc1)
|
|
- -1, // monitorenter = 194 (0xc2)
|
|
- -1, // monitorexit = 195 (0xc3)
|
|
- NA, // wide = 196 (0xc4)
|
|
- NA, // multianewarray = 197 (0xc5)
|
|
- -1, // ifnull = 198 (0xc6)
|
|
- -1, // ifnonnull = 199 (0xc7)
|
|
- NA, // goto_w = 200 (0xc8)
|
|
- NA // jsr_w = 201 (0xc9)
|
|
- };
|
|
-
|
|
- /** Where the constants used in this MethodWriter must be stored. */
|
|
- private final SymbolTable symbolTable;
|
|
-
|
|
- // Note: fields are ordered as in the method_info structure, and those related to attributes are
|
|
- // ordered as in Section 4.7 of the JVMS.
|
|
-
|
|
- /**
|
|
- * The access_flags field of the method_info JVMS structure. This field can contain ASM specific
|
|
- * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
|
|
- * ClassFile structure.
|
|
- */
|
|
- private final int accessFlags;
|
|
-
|
|
- /** The name_index field of the method_info JVMS structure. */
|
|
- private final int nameIndex;
|
|
-
|
|
- /** The name of this method. */
|
|
- private final String name;
|
|
-
|
|
- /** The descriptor_index field of the method_info JVMS structure. */
|
|
- private final int descriptorIndex;
|
|
-
|
|
- /** The descriptor of this method. */
|
|
- private final String descriptor;
|
|
-
|
|
- // Code attribute fields and sub attributes:
|
|
-
|
|
- /** The max_stack field of the Code attribute. */
|
|
- private int maxStack;
|
|
-
|
|
- /** The max_locals field of the Code attribute. */
|
|
- private int maxLocals;
|
|
-
|
|
- /** The 'code' field of the Code attribute. */
|
|
- private final ByteVector code = new ByteVector();
|
|
-
|
|
- /**
|
|
- * The first element in the exception handler list (used to generate the exception_table of the
|
|
- * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May
|
|
- * be {@literal null}.
|
|
- */
|
|
- private Handler firstHandler;
|
|
-
|
|
- /**
|
|
- * The last element in the exception handler list (used to generate the exception_table of the
|
|
- * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May
|
|
- * be {@literal null}.
|
|
- */
|
|
- private Handler lastHandler;
|
|
-
|
|
- /** The line_number_table_length field of the LineNumberTable code attribute. */
|
|
- private int lineNumberTableLength;
|
|
-
|
|
- /** The line_number_table array of the LineNumberTable code attribute, or {@literal null}. */
|
|
- private ByteVector lineNumberTable;
|
|
-
|
|
- /** The local_variable_table_length field of the LocalVariableTable code attribute. */
|
|
- private int localVariableTableLength;
|
|
-
|
|
- /**
|
|
- * The local_variable_table array of the LocalVariableTable code attribute, or {@literal null}.
|
|
- */
|
|
- private ByteVector localVariableTable;
|
|
-
|
|
- /** The local_variable_type_table_length field of the LocalVariableTypeTable code attribute. */
|
|
- private int localVariableTypeTableLength;
|
|
-
|
|
- /**
|
|
- * The local_variable_type_table array of the LocalVariableTypeTable code attribute, or {@literal
|
|
- * null}.
|
|
- */
|
|
- private ByteVector localVariableTypeTable;
|
|
-
|
|
- /** The number_of_entries field of the StackMapTable code attribute. */
|
|
- private int stackMapTableNumberOfEntries;
|
|
-
|
|
- /** The 'entries' array of the StackMapTable code attribute. */
|
|
- private ByteVector stackMapTableEntries;
|
|
-
|
|
- /**
|
|
- * The last runtime visible type annotation of the Code attribute. The previous ones can be
|
|
- * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter lastCodeRuntimeVisibleTypeAnnotation;
|
|
-
|
|
- /**
|
|
- * The last runtime invisible type annotation of the Code attribute. The previous ones can be
|
|
- * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter lastCodeRuntimeInvisibleTypeAnnotation;
|
|
-
|
|
- /**
|
|
- * The first non standard attribute of the Code attribute. 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
|
|
- * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the
|
|
- * reverse order specified by the user.
|
|
- */
|
|
- private Attribute firstCodeAttribute;
|
|
-
|
|
- // Other method_info attributes:
|
|
-
|
|
- /** The number_of_exceptions field of the Exceptions attribute. */
|
|
- private final int numberOfExceptions;
|
|
-
|
|
- /** The exception_index_table array of the Exceptions attribute, or {@literal null}. */
|
|
- private final int[] exceptionIndexTable;
|
|
-
|
|
- /** The signature_index field of the Signature attribute. */
|
|
- private final int signatureIndex;
|
|
-
|
|
- /**
|
|
- * The last runtime visible annotation of this method. 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 method. The previous ones can be accessed with
|
|
- * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter lastRuntimeInvisibleAnnotation;
|
|
-
|
|
- /** The number of method parameters that can have runtime visible annotations, or 0. */
|
|
- private int visibleAnnotableParameterCount;
|
|
-
|
|
- /**
|
|
- * The runtime visible parameter annotations of this method. Each array element contains the last
|
|
- * annotation of a parameter (which can be {@literal null} - the previous ones can be accessed
|
|
- * with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter[] lastRuntimeVisibleParameterAnnotations;
|
|
-
|
|
- /** The number of method parameters that can have runtime visible annotations, or 0. */
|
|
- private int invisibleAnnotableParameterCount;
|
|
-
|
|
- /**
|
|
- * The runtime invisible parameter annotations of this method. Each array element contains the
|
|
- * last annotation of a parameter (which can be {@literal null} - the previous ones can be
|
|
- * accessed with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter[] lastRuntimeInvisibleParameterAnnotations;
|
|
-
|
|
- /**
|
|
- * The last runtime visible type annotation of this method. 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 method. The previous ones can be accessed
|
|
- * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
|
- */
|
|
- private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
|
|
-
|
|
- /** The default_value field of the AnnotationDefault attribute, or {@literal null}. */
|
|
- private ByteVector defaultValue;
|
|
-
|
|
- /** The parameters_count field of the MethodParameters attribute. */
|
|
- private int parametersCount;
|
|
-
|
|
- /** The 'parameters' array of the MethodParameters attribute, or {@literal null}. */
|
|
- private ByteVector parameters;
|
|
-
|
|
- /**
|
|
- * The first non standard attribute of this method. 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
|
|
- * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the
|
|
- * reverse order specified by the user.
|
|
- */
|
|
- private Attribute firstAttribute;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Fields used to compute the maximum stack size and number of locals, and the stack map frames
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Indicates what must be computed. Must be one of {@link #COMPUTE_ALL_FRAMES}, {@link
|
|
- * #COMPUTE_INSERTED_FRAMES}, {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}.
|
|
- */
|
|
- private final int compute;
|
|
-
|
|
- /**
|
|
- * The first basic block of the method. The next ones (in bytecode offset order) can be accessed
|
|
- * with the {@link Label#nextBasicBlock} field.
|
|
- */
|
|
- private Label firstBasicBlock;
|
|
-
|
|
- /**
|
|
- * The last basic block of the method (in bytecode offset order). This field is updated each time
|
|
- * a basic block is encountered, and is used to append it at the end of the basic block list.
|
|
- */
|
|
- private Label lastBasicBlock;
|
|
-
|
|
- /**
|
|
- * The current basic block, i.e. the basic block of the last visited instruction. When {@link
|
|
- * #compute} is equal to {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_ALL_FRAMES}, this
|
|
- * field is {@literal null} for unreachable code. When {@link #compute} is equal to {@link
|
|
- * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES} or {@link #COMPUTE_INSERTED_FRAMES}, this field stays
|
|
- * unchanged throughout the whole method (i.e. the whole code is seen as a single basic block;
|
|
- * indeed, the existing frames are sufficient by hypothesis to compute any intermediate frame -
|
|
- * and the maximum stack size as well - without using any control flow graph).
|
|
- */
|
|
- private Label currentBasicBlock;
|
|
-
|
|
- /**
|
|
- * The relative stack size after the last visited instruction. This size is relative to the
|
|
- * beginning of {@link #currentBasicBlock}, i.e. the true stack size after the last visited
|
|
- * instruction is equal to the {@link Label#inputStackSize} of the current basic block plus {@link
|
|
- * #relativeStackSize}. When {@link #compute} is equal to {@link
|
|
- * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of
|
|
- * the method, so this relative size is also equal to the absolute stack size after the last
|
|
- * visited instruction.
|
|
- */
|
|
- private int relativeStackSize;
|
|
-
|
|
- /**
|
|
- * The maximum relative stack size after the last visited instruction. This size is relative to
|
|
- * the beginning of {@link #currentBasicBlock}, i.e. the true maximum stack size after the last
|
|
- * visited instruction is equal to the {@link Label#inputStackSize} of the current basic block
|
|
- * plus {@link #maxRelativeStackSize}.When {@link #compute} is equal to {@link
|
|
- * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of
|
|
- * the method, so this relative size is also equal to the absolute maximum stack size after the
|
|
- * last visited instruction.
|
|
- */
|
|
- private int maxRelativeStackSize;
|
|
-
|
|
- /** The number of local variables in the last visited stack map frame. */
|
|
- private int currentLocals;
|
|
-
|
|
- /** The bytecode offset of the last frame that was written in {@link #stackMapTableEntries}. */
|
|
- private int previousFrameOffset;
|
|
-
|
|
- /**
|
|
- * The last frame that was written in {@link #stackMapTableEntries}. This field has the same
|
|
- * format as {@link #currentFrame}.
|
|
- */
|
|
- private int[] previousFrame;
|
|
-
|
|
- /**
|
|
- * The current stack map frame. The first element contains the bytecode offset of the instruction
|
|
- * to which the frame corresponds, the second element is the number of locals and the third one is
|
|
- * the number of stack elements. The local variables start at index 3 and are followed by the
|
|
- * operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack.
|
|
- * Local variables and operand stack entries contain abstract types, as defined in {@link Frame},
|
|
- * but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link
|
|
- * Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry.
|
|
- */
|
|
- private int[] currentFrame;
|
|
-
|
|
- /** Whether this method contains subroutines. */
|
|
- private boolean hasSubroutines;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Other miscellaneous status fields
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /** Whether the bytecode of this method contains ASM specific instructions. */
|
|
- private boolean hasAsmInstructions;
|
|
-
|
|
- /**
|
|
- * The start offset of the last visited instruction. Used to set the offset field of type
|
|
- * annotations of type 'offset_target' (see <a
|
|
- * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.1">JVMS
|
|
- * 4.7.20.1</a>).
|
|
- */
|
|
- private int lastBytecodeOffset;
|
|
-
|
|
- /**
|
|
- * The offset in bytes in {@link SymbolTable#getSource} from which the method_info for this method
|
|
- * (excluding its first 6 bytes) must be copied, or 0.
|
|
- */
|
|
- private int sourceOffset;
|
|
-
|
|
- /**
|
|
- * The length in bytes in {@link SymbolTable#getSource} which must be copied to get the
|
|
- * method_info for this method (excluding its first 6 bytes for access_flags, name_index and
|
|
- * descriptor_index).
|
|
- */
|
|
- private int sourceLength;
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Constructor and accessors
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link MethodWriter}.
|
|
- *
|
|
- * @param symbolTable where the constants used in this AnnotationWriter must be stored.
|
|
- * @param access the method's access flags (see {@link Opcodes}).
|
|
- * @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}.
|
|
- * @param exceptions the internal names of the method's exceptions. May be {@literal null}.
|
|
- * @param compute indicates what must be computed (see #compute).
|
|
- */
|
|
- MethodWriter(
|
|
- final SymbolTable symbolTable,
|
|
- final int access,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final String signature,
|
|
- final String[] exceptions,
|
|
- final int compute) {
|
|
- super(Opcodes.ASM7);
|
|
- this.symbolTable = symbolTable;
|
|
- this.accessFlags = "<init>".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access;
|
|
- this.nameIndex = symbolTable.addConstantUtf8(name);
|
|
- this.name = name;
|
|
- this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
|
|
- this.descriptor = descriptor;
|
|
- this.signatureIndex = signature == null ? 0 : symbolTable.addConstantUtf8(signature);
|
|
- if (exceptions != null && exceptions.length > 0) {
|
|
- numberOfExceptions = exceptions.length;
|
|
- this.exceptionIndexTable = new int[numberOfExceptions];
|
|
- for (int i = 0; i < numberOfExceptions; ++i) {
|
|
- this.exceptionIndexTable[i] = symbolTable.addConstantClass(exceptions[i]).index;
|
|
- }
|
|
- } else {
|
|
- numberOfExceptions = 0;
|
|
- this.exceptionIndexTable = null;
|
|
- }
|
|
- this.compute = compute;
|
|
- if (compute != COMPUTE_NOTHING) {
|
|
- // Update maxLocals and currentLocals.
|
|
- int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
|
|
- if ((access & Opcodes.ACC_STATIC) != 0) {
|
|
- --argumentsSize;
|
|
- }
|
|
- maxLocals = argumentsSize;
|
|
- currentLocals = argumentsSize;
|
|
- // Create and visit the label for the first basic block.
|
|
- firstBasicBlock = new Label();
|
|
- visitLabel(firstBasicBlock);
|
|
- }
|
|
- }
|
|
-
|
|
- boolean hasFrames() {
|
|
- return stackMapTableNumberOfEntries > 0;
|
|
- }
|
|
-
|
|
- boolean hasAsmInstructions() {
|
|
- return hasAsmInstructions;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Implementation of the MethodVisitor abstract class
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- @Override
|
|
- public void visitParameter(final String name, final int access) {
|
|
- if (parameters == null) {
|
|
- parameters = new ByteVector();
|
|
- }
|
|
- ++parametersCount;
|
|
- parameters.putShort((name == null) ? 0 : symbolTable.addConstantUtf8(name)).putShort(access);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public AnnotationVisitor visitAnnotationDefault() {
|
|
- defaultValue = new ByteVector();
|
|
- return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, defaultValue, null);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public 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 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 void visitAnnotableParameterCount(final int parameterCount, final boolean visible) {
|
|
- if (visible) {
|
|
- visibleAnnotableParameterCount = parameterCount;
|
|
- } else {
|
|
- invisibleAnnotableParameterCount = parameterCount;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public AnnotationVisitor visitParameterAnnotation(
|
|
- final int parameter, final String annotationDescriptor, 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(annotationDescriptor)).putShort(0);
|
|
- if (visible) {
|
|
- if (lastRuntimeVisibleParameterAnnotations == null) {
|
|
- lastRuntimeVisibleParameterAnnotations =
|
|
- new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
|
|
- }
|
|
- return lastRuntimeVisibleParameterAnnotations[parameter] =
|
|
- new AnnotationWriter(
|
|
- symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]);
|
|
- } else {
|
|
- if (lastRuntimeInvisibleParameterAnnotations == null) {
|
|
- lastRuntimeInvisibleParameterAnnotations =
|
|
- new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
|
|
- }
|
|
- return lastRuntimeInvisibleParameterAnnotations[parameter] =
|
|
- new AnnotationWriter(
|
|
- symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]);
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitAttribute(final Attribute attribute) {
|
|
- // Store the attributes in the <i>reverse</i> order of their visit by this method.
|
|
- if (attribute.isCodeAttribute()) {
|
|
- attribute.nextAttribute = firstCodeAttribute;
|
|
- firstCodeAttribute = attribute;
|
|
- } else {
|
|
- attribute.nextAttribute = firstAttribute;
|
|
- firstAttribute = attribute;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitCode() {
|
|
- // Nothing to do.
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitFrame(
|
|
- final int type,
|
|
- final int numLocal,
|
|
- final Object[] local,
|
|
- final int numStack,
|
|
- final Object[] stack) {
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (compute == COMPUTE_INSERTED_FRAMES) {
|
|
- if (currentBasicBlock.frame == null) {
|
|
- // This should happen only once, for the implicit first frame (which is explicitly visited
|
|
- // in ClassReader if the EXPAND_ASM_INSNS option is used - and COMPUTE_INSERTED_FRAMES
|
|
- // can't be set if EXPAND_ASM_INSNS is not used).
|
|
- currentBasicBlock.frame = new CurrentFrame(currentBasicBlock);
|
|
- currentBasicBlock.frame.setInputFrameFromDescriptor(
|
|
- symbolTable, accessFlags, descriptor, numLocal);
|
|
- currentBasicBlock.frame.accept(this);
|
|
- } else {
|
|
- if (type == Opcodes.F_NEW) {
|
|
- currentBasicBlock.frame.setInputFrameFromApiFormat(
|
|
- symbolTable, numLocal, local, numStack, stack);
|
|
- }
|
|
- // If type is not F_NEW then it is F_INSERT by hypothesis, and currentBlock.frame contains
|
|
- // the stack map frame at the current instruction, computed from the last F_NEW frame and
|
|
- // the bytecode instructions in between (via calls to CurrentFrame#execute).
|
|
- currentBasicBlock.frame.accept(this);
|
|
- }
|
|
- } else if (type == Opcodes.F_NEW) {
|
|
- if (previousFrame == null) {
|
|
- int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
|
|
- Frame implicitFirstFrame = new Frame(new Label());
|
|
- implicitFirstFrame.setInputFrameFromDescriptor(
|
|
- symbolTable, accessFlags, descriptor, argumentsSize);
|
|
- implicitFirstFrame.accept(this);
|
|
- }
|
|
- currentLocals = numLocal;
|
|
- int frameIndex = visitFrameStart(code.length, numLocal, numStack);
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]);
|
|
- }
|
|
- for (int i = 0; i < numStack; ++i) {
|
|
- currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]);
|
|
- }
|
|
- visitFrameEnd();
|
|
- } else {
|
|
- int offsetDelta;
|
|
- if (stackMapTableEntries == null) {
|
|
- stackMapTableEntries = new ByteVector();
|
|
- offsetDelta = code.length;
|
|
- } else {
|
|
- offsetDelta = code.length - previousFrameOffset - 1;
|
|
- if (offsetDelta < 0) {
|
|
- if (type == Opcodes.F_SAME) {
|
|
- return;
|
|
- } else {
|
|
- throw new IllegalStateException();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- switch (type) {
|
|
- case Opcodes.F_FULL:
|
|
- currentLocals = numLocal;
|
|
- stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal);
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- putFrameType(local[i]);
|
|
- }
|
|
- stackMapTableEntries.putShort(numStack);
|
|
- for (int i = 0; i < numStack; ++i) {
|
|
- putFrameType(stack[i]);
|
|
- }
|
|
- break;
|
|
- case Opcodes.F_APPEND:
|
|
- currentLocals += numLocal;
|
|
- stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + numLocal).putShort(offsetDelta);
|
|
- for (int i = 0; i < numLocal; ++i) {
|
|
- putFrameType(local[i]);
|
|
- }
|
|
- break;
|
|
- case Opcodes.F_CHOP:
|
|
- currentLocals -= numLocal;
|
|
- stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - numLocal).putShort(offsetDelta);
|
|
- break;
|
|
- case Opcodes.F_SAME:
|
|
- if (offsetDelta < 64) {
|
|
- stackMapTableEntries.putByte(offsetDelta);
|
|
- } else {
|
|
- stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta);
|
|
- }
|
|
- break;
|
|
- case Opcodes.F_SAME1:
|
|
- if (offsetDelta < 64) {
|
|
- stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta);
|
|
- } else {
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
|
- .putShort(offsetDelta);
|
|
- }
|
|
- putFrameType(stack[0]);
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
-
|
|
- previousFrameOffset = code.length;
|
|
- ++stackMapTableNumberOfEntries;
|
|
- }
|
|
-
|
|
- if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
|
|
- relativeStackSize = numStack;
|
|
- for (int i = 0; i < numStack; ++i) {
|
|
- if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) {
|
|
- relativeStackSize++;
|
|
- }
|
|
- }
|
|
- if (relativeStackSize > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = relativeStackSize;
|
|
- }
|
|
- }
|
|
-
|
|
- maxStack = Math.max(maxStack, numStack);
|
|
- maxLocals = Math.max(maxLocals, currentLocals);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitInsn(final int opcode) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- code.putByte(opcode);
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, 0, null, null);
|
|
- } else {
|
|
- int size = relativeStackSize + STACK_SIZE_DELTA[opcode];
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
|
|
- endCurrentBasicBlockWithNoSuccessor();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitIntInsn(final int opcode, final int operand) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- if (opcode == Opcodes.SIPUSH) {
|
|
- code.put12(opcode, operand);
|
|
- } else { // BIPUSH or NEWARRAY
|
|
- code.put11(opcode, operand);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, operand, null, null);
|
|
- } else if (opcode != Opcodes.NEWARRAY) {
|
|
- // The stack size delta is 1 for BIPUSH or SIPUSH, and 0 for NEWARRAY.
|
|
- int size = relativeStackSize + 1;
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitVarInsn(final int opcode, final int var) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- if (var < 4 && opcode != Opcodes.RET) {
|
|
- int optimizedOpcode;
|
|
- if (opcode < Opcodes.ISTORE) {
|
|
- optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + var;
|
|
- } else {
|
|
- optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + var;
|
|
- }
|
|
- code.putByte(optimizedOpcode);
|
|
- } else if (var >= 256) {
|
|
- code.putByte(Constants.WIDE).put12(opcode, var);
|
|
- } else {
|
|
- code.put11(opcode, var);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, var, null, null);
|
|
- } else {
|
|
- if (opcode == Opcodes.RET) {
|
|
- // No stack size delta.
|
|
- currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_END;
|
|
- currentBasicBlock.outputStackSize = (short) relativeStackSize;
|
|
- endCurrentBasicBlockWithNoSuccessor();
|
|
- } else { // xLOAD or xSTORE
|
|
- int size = relativeStackSize + STACK_SIZE_DELTA[opcode];
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
- if (compute != COMPUTE_NOTHING) {
|
|
- int currentMaxLocals;
|
|
- if (opcode == Opcodes.LLOAD
|
|
- || opcode == Opcodes.DLOAD
|
|
- || opcode == Opcodes.LSTORE
|
|
- || opcode == Opcodes.DSTORE) {
|
|
- currentMaxLocals = var + 2;
|
|
- } else {
|
|
- currentMaxLocals = var + 1;
|
|
- }
|
|
- if (currentMaxLocals > maxLocals) {
|
|
- maxLocals = currentMaxLocals;
|
|
- }
|
|
- }
|
|
- if (opcode >= Opcodes.ISTORE && compute == COMPUTE_ALL_FRAMES && firstHandler != null) {
|
|
- // If there are exception handler blocks, each instruction within a handler range is, in
|
|
- // theory, a basic block (since execution can jump from this instruction to the exception
|
|
- // handler). As a consequence, the local variable types at the beginning of the handler
|
|
- // block should be the merge of the local variable types at all the instructions within the
|
|
- // handler range. However, instead of creating a basic block for each instruction, we can
|
|
- // get the same result in a more efficient way. Namely, by starting a new basic block after
|
|
- // each xSTORE instruction, which is what we do here.
|
|
- visitLabel(new Label());
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitTypeInsn(final int opcode, final String type) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol typeSymbol = symbolTable.addConstantClass(type);
|
|
- code.put12(opcode, typeSymbol.index);
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, lastBytecodeOffset, typeSymbol, symbolTable);
|
|
- } else if (opcode == Opcodes.NEW) {
|
|
- // The stack size delta is 1 for NEW, and 0 for ANEWARRAY, CHECKCAST, or INSTANCEOF.
|
|
- int size = relativeStackSize + 1;
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitFieldInsn(
|
|
- final int opcode, final String owner, final String name, final String descriptor) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol fieldrefSymbol = symbolTable.addConstantFieldref(owner, name, descriptor);
|
|
- code.put12(opcode, fieldrefSymbol.index);
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, 0, fieldrefSymbol, symbolTable);
|
|
- } else {
|
|
- int size;
|
|
- char firstDescChar = descriptor.charAt(0);
|
|
- switch (opcode) {
|
|
- case Opcodes.GETSTATIC:
|
|
- size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 2 : 1);
|
|
- break;
|
|
- case Opcodes.PUTSTATIC:
|
|
- size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -2 : -1);
|
|
- break;
|
|
- case Opcodes.GETFIELD:
|
|
- size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 1 : 0);
|
|
- break;
|
|
- case Opcodes.PUTFIELD:
|
|
- default:
|
|
- size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -3 : -2);
|
|
- break;
|
|
- }
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitMethodInsn(
|
|
- final int opcode,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final boolean isInterface) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol methodrefSymbol = symbolTable.addConstantMethodref(owner, name, descriptor, isInterface);
|
|
- if (opcode == Opcodes.INVOKEINTERFACE) {
|
|
- code.put12(Opcodes.INVOKEINTERFACE, methodrefSymbol.index)
|
|
- .put11(methodrefSymbol.getArgumentsAndReturnSizes() >> 2, 0);
|
|
- } else {
|
|
- code.put12(opcode, methodrefSymbol.index);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(opcode, 0, methodrefSymbol, symbolTable);
|
|
- } else {
|
|
- int argumentsAndReturnSize = methodrefSymbol.getArgumentsAndReturnSizes();
|
|
- int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2);
|
|
- int size;
|
|
- if (opcode == Opcodes.INVOKESTATIC) {
|
|
- size = relativeStackSize + stackSizeDelta + 1;
|
|
- } else {
|
|
- size = relativeStackSize + stackSizeDelta;
|
|
- }
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitInvokeDynamicInsn(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final Handle bootstrapMethodHandle,
|
|
- final Object... bootstrapMethodArguments) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol invokeDynamicSymbol =
|
|
- symbolTable.addConstantInvokeDynamic(
|
|
- name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
|
|
- code.put12(Opcodes.INVOKEDYNAMIC, invokeDynamicSymbol.index);
|
|
- code.putShort(0);
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, invokeDynamicSymbol, symbolTable);
|
|
- } else {
|
|
- int argumentsAndReturnSize = invokeDynamicSymbol.getArgumentsAndReturnSizes();
|
|
- int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2) + 1;
|
|
- int size = relativeStackSize + stackSizeDelta;
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitJumpInsn(final int opcode, final Label label) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- // Compute the 'base' opcode, i.e. GOTO or JSR if opcode is GOTO_W or JSR_W, otherwise opcode.
|
|
- int baseOpcode =
|
|
- opcode >= Constants.GOTO_W ? opcode - Constants.WIDE_JUMP_OPCODE_DELTA : opcode;
|
|
- boolean nextInsnIsJumpTarget = false;
|
|
- if ((label.flags & Label.FLAG_RESOLVED) != 0
|
|
- && label.bytecodeOffset - code.length < Short.MIN_VALUE) {
|
|
- // Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO
|
|
- // with GOTO_W, JSR with JSR_W and IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:..., where
|
|
- // IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where <L> designates
|
|
- // the instruction just after the GOTO_W.
|
|
- if (baseOpcode == Opcodes.GOTO) {
|
|
- code.putByte(Constants.GOTO_W);
|
|
- } else if (baseOpcode == Opcodes.JSR) {
|
|
- code.putByte(Constants.JSR_W);
|
|
- } else {
|
|
- // Put the "opposite" opcode of baseOpcode. 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). The jump offset is 8 bytes (3 for IFNOTxxx, 5 for GOTO_W).
|
|
- code.putByte(baseOpcode >= Opcodes.IFNULL ? baseOpcode ^ 1 : ((baseOpcode + 1) ^ 1) - 1);
|
|
- code.putShort(8);
|
|
- // Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this
|
|
- // method or another one, and if the class has frames, we will need to insert a frame after
|
|
- // this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM
|
|
- // specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W
|
|
- // here, which has the unfortunate effect of forcing this additional round trip (which in
|
|
- // some case would not have been really necessary, but we can't know this at this point).
|
|
- code.putByte(Constants.ASM_GOTO_W);
|
|
- hasAsmInstructions = true;
|
|
- // The instruction after the GOTO_W becomes the target of the IFNOT instruction.
|
|
- nextInsnIsJumpTarget = true;
|
|
- }
|
|
- label.put(code, code.length - 1, true);
|
|
- } else if (baseOpcode != opcode) {
|
|
- // Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove
|
|
- // ASM specific instructions). In this case we keep the original instruction.
|
|
- code.putByte(opcode);
|
|
- label.put(code, code.length - 1, true);
|
|
- } else {
|
|
- // Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these
|
|
- // cases we store the offset in 2 bytes (which will be increased via a ClassReader ->
|
|
- // ClassWriter round trip if it turns out that 2 bytes are not sufficient).
|
|
- code.putByte(baseOpcode);
|
|
- label.put(code, code.length - 1, false);
|
|
- }
|
|
-
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- Label nextBasicBlock = null;
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- currentBasicBlock.frame.execute(baseOpcode, 0, null, null);
|
|
- // Record the fact that 'label' is the target of a jump instruction.
|
|
- label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET;
|
|
- // Add 'label' as a successor of the current basic block.
|
|
- addSuccessorToCurrentBasicBlock(Edge.JUMP, label);
|
|
- if (baseOpcode != Opcodes.GOTO) {
|
|
- // The next instruction starts a new basic block (except for GOTO: by default the code
|
|
- // following a goto is unreachable - unless there is an explicit label for it - and we
|
|
- // should not compute stack frame types for its instructions).
|
|
- nextBasicBlock = new Label();
|
|
- }
|
|
- } else if (compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(baseOpcode, 0, null, null);
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
|
|
- // No need to update maxRelativeStackSize (the stack size delta is always negative).
|
|
- relativeStackSize += STACK_SIZE_DELTA[baseOpcode];
|
|
- } else {
|
|
- if (baseOpcode == Opcodes.JSR) {
|
|
- // Record the fact that 'label' designates a subroutine, if not already done.
|
|
- if ((label.flags & Label.FLAG_SUBROUTINE_START) == 0) {
|
|
- label.flags |= Label.FLAG_SUBROUTINE_START;
|
|
- hasSubroutines = true;
|
|
- }
|
|
- currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_CALLER;
|
|
- // Note that, by construction in this method, a block which calls a subroutine has at
|
|
- // least two successors in the control flow graph: the first one (added below) leads to
|
|
- // the instruction after the JSR, while the second one (added here) leads to the JSR
|
|
- // target. Note that the first successor is virtual (it does not correspond to a possible
|
|
- // execution path): it is only used to compute the successors of the basic blocks ending
|
|
- // with a ret, in {@link Label#addSubroutineRetSuccessors}.
|
|
- addSuccessorToCurrentBasicBlock(relativeStackSize + 1, label);
|
|
- // The instruction after the JSR starts a new basic block.
|
|
- nextBasicBlock = new Label();
|
|
- } else {
|
|
- // No need to update maxRelativeStackSize (the stack size delta is always negative).
|
|
- relativeStackSize += STACK_SIZE_DELTA[baseOpcode];
|
|
- addSuccessorToCurrentBasicBlock(relativeStackSize, label);
|
|
- }
|
|
- }
|
|
- // If the next instruction starts a new basic block, call visitLabel to add the label of this
|
|
- // instruction as a successor of the current block, and to start a new basic block.
|
|
- if (nextBasicBlock != null) {
|
|
- if (nextInsnIsJumpTarget) {
|
|
- nextBasicBlock.flags |= Label.FLAG_JUMP_TARGET;
|
|
- }
|
|
- visitLabel(nextBasicBlock);
|
|
- }
|
|
- if (baseOpcode == Opcodes.GOTO) {
|
|
- endCurrentBasicBlockWithNoSuccessor();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitLabel(final Label label) {
|
|
- // Resolve the forward references to this label, if any.
|
|
- hasAsmInstructions |= label.resolve(code.data, code.length);
|
|
- // visitLabel starts a new basic block (except for debug only labels), so we need to update the
|
|
- // previous and current block references and list of successors.
|
|
- if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) {
|
|
- return;
|
|
- }
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- if (currentBasicBlock != null) {
|
|
- if (label.bytecodeOffset == currentBasicBlock.bytecodeOffset) {
|
|
- // We use {@link Label#getCanonicalInstance} to store the state of a basic block in only
|
|
- // one place, but this does not work for labels which have not been visited yet.
|
|
- // Therefore, when we detect here two labels having the same bytecode offset, we need to
|
|
- // - consolidate the state scattered in these two instances into the canonical instance:
|
|
- currentBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET);
|
|
- // - make sure the two instances share the same Frame instance (the implementation of
|
|
- // {@link Label#getCanonicalInstance} relies on this property; here label.frame should be
|
|
- // null):
|
|
- label.frame = currentBasicBlock.frame;
|
|
- // - and make sure to NOT assign 'label' into 'currentBasicBlock' or 'lastBasicBlock', so
|
|
- // that they still refer to the canonical instance for this bytecode offset.
|
|
- return;
|
|
- }
|
|
- // End the current basic block (with one new successor).
|
|
- addSuccessorToCurrentBasicBlock(Edge.JUMP, label);
|
|
- }
|
|
- // Append 'label' at the end of the basic block list.
|
|
- if (lastBasicBlock != null) {
|
|
- if (label.bytecodeOffset == lastBasicBlock.bytecodeOffset) {
|
|
- // Same comment as above.
|
|
- lastBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET);
|
|
- // Here label.frame should be null.
|
|
- label.frame = lastBasicBlock.frame;
|
|
- currentBasicBlock = lastBasicBlock;
|
|
- return;
|
|
- }
|
|
- lastBasicBlock.nextBasicBlock = label;
|
|
- }
|
|
- lastBasicBlock = label;
|
|
- // Make it the new current basic block.
|
|
- currentBasicBlock = label;
|
|
- // Here label.frame should be null.
|
|
- label.frame = new Frame(label);
|
|
- } else if (compute == COMPUTE_INSERTED_FRAMES) {
|
|
- if (currentBasicBlock == null) {
|
|
- // This case should happen only once, for the visitLabel call in the constructor. Indeed, if
|
|
- // compute is equal to COMPUTE_INSERTED_FRAMES, currentBasicBlock stays unchanged.
|
|
- currentBasicBlock = label;
|
|
- } else {
|
|
- // Update the frame owner so that a correct frame offset is computed in Frame.accept().
|
|
- currentBasicBlock.frame.owner = label;
|
|
- }
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) {
|
|
- if (currentBasicBlock != null) {
|
|
- // End the current basic block (with one new successor).
|
|
- currentBasicBlock.outputStackMax = (short) maxRelativeStackSize;
|
|
- addSuccessorToCurrentBasicBlock(relativeStackSize, label);
|
|
- }
|
|
- // Start a new current basic block, and reset the current and maximum relative stack sizes.
|
|
- currentBasicBlock = label;
|
|
- relativeStackSize = 0;
|
|
- maxRelativeStackSize = 0;
|
|
- // Append the new basic block at the end of the basic block list.
|
|
- if (lastBasicBlock != null) {
|
|
- lastBasicBlock.nextBasicBlock = label;
|
|
- }
|
|
- lastBasicBlock = label;
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES && currentBasicBlock == null) {
|
|
- // This case should happen only once, for the visitLabel call in the constructor. Indeed, if
|
|
- // compute is equal to COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES, currentBasicBlock stays
|
|
- // unchanged.
|
|
- currentBasicBlock = label;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitLdcInsn(final Object value) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol constantSymbol = symbolTable.addConstant(value);
|
|
- int constantIndex = constantSymbol.index;
|
|
- char firstDescriptorChar;
|
|
- boolean isLongOrDouble =
|
|
- constantSymbol.tag == Symbol.CONSTANT_LONG_TAG
|
|
- || constantSymbol.tag == Symbol.CONSTANT_DOUBLE_TAG
|
|
- || (constantSymbol.tag == Symbol.CONSTANT_DYNAMIC_TAG
|
|
- && ((firstDescriptorChar = constantSymbol.value.charAt(0)) == 'J'
|
|
- || firstDescriptorChar == 'D'));
|
|
- if (isLongOrDouble) {
|
|
- code.put12(Constants.LDC2_W, constantIndex);
|
|
- } else if (constantIndex >= 256) {
|
|
- code.put12(Constants.LDC_W, constantIndex);
|
|
- } else {
|
|
- code.put11(Opcodes.LDC, constantIndex);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(Opcodes.LDC, 0, constantSymbol, symbolTable);
|
|
- } else {
|
|
- int size = relativeStackSize + (isLongOrDouble ? 2 : 1);
|
|
- if (size > maxRelativeStackSize) {
|
|
- maxRelativeStackSize = size;
|
|
- }
|
|
- relativeStackSize = size;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitIincInsn(final int var, final int increment) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- if ((var > 255) || (increment > 127) || (increment < -128)) {
|
|
- code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment);
|
|
- } else {
|
|
- code.putByte(Opcodes.IINC).put11(var, increment);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null
|
|
- && (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) {
|
|
- currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null);
|
|
- }
|
|
- if (compute != COMPUTE_NOTHING) {
|
|
- int currentMaxLocals = var + 1;
|
|
- if (currentMaxLocals > maxLocals) {
|
|
- maxLocals = currentMaxLocals;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitTableSwitchInsn(
|
|
- final int min, final int max, final Label dflt, final Label... labels) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- code.putByte(Opcodes.TABLESWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4);
|
|
- dflt.put(code, lastBytecodeOffset, true);
|
|
- code.putInt(min).putInt(max);
|
|
- for (Label label : labels) {
|
|
- label.put(code, lastBytecodeOffset, true);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- visitSwitchInsn(dflt, labels);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- code.putByte(Opcodes.LOOKUPSWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4);
|
|
- dflt.put(code, lastBytecodeOffset, true);
|
|
- code.putInt(labels.length);
|
|
- for (int i = 0; i < labels.length; ++i) {
|
|
- code.putInt(keys[i]);
|
|
- labels[i].put(code, lastBytecodeOffset, true);
|
|
- }
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- visitSwitchInsn(dflt, labels);
|
|
- }
|
|
-
|
|
- private void visitSwitchInsn(final Label dflt, final Label[] labels) {
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- currentBasicBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
|
|
- // Add all the labels as successors of the current basic block.
|
|
- addSuccessorToCurrentBasicBlock(Edge.JUMP, dflt);
|
|
- dflt.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET;
|
|
- for (Label label : labels) {
|
|
- addSuccessorToCurrentBasicBlock(Edge.JUMP, label);
|
|
- label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET;
|
|
- }
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) {
|
|
- // No need to update maxRelativeStackSize (the stack size delta is always negative).
|
|
- --relativeStackSize;
|
|
- // Add all the labels as successors of the current basic block.
|
|
- addSuccessorToCurrentBasicBlock(relativeStackSize, dflt);
|
|
- for (Label label : labels) {
|
|
- addSuccessorToCurrentBasicBlock(relativeStackSize, label);
|
|
- }
|
|
- }
|
|
- // End the current basic block.
|
|
- endCurrentBasicBlockWithNoSuccessor();
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) {
|
|
- lastBytecodeOffset = code.length;
|
|
- // Add the instruction to the bytecode of the method.
|
|
- Symbol descSymbol = symbolTable.addConstantClass(descriptor);
|
|
- code.put12(Opcodes.MULTIANEWARRAY, descSymbol.index).putByte(numDimensions);
|
|
- // If needed, update the maximum stack size and number of locals, and stack map frames.
|
|
- if (currentBasicBlock != null) {
|
|
- if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) {
|
|
- currentBasicBlock.frame.execute(
|
|
- Opcodes.MULTIANEWARRAY, numDimensions, descSymbol, symbolTable);
|
|
- } else {
|
|
- // No need to update maxRelativeStackSize (the stack size delta is always negative).
|
|
- relativeStackSize += 1 - numDimensions;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public AnnotationVisitor visitInsnAnnotation(
|
|
- 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 & 0xFF0000FF) | (lastBytecodeOffset << 8), 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 lastCodeRuntimeVisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
|
|
- } else {
|
|
- return lastCodeRuntimeInvisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitTryCatchBlock(
|
|
- final Label start, final Label end, final Label handler, final String type) {
|
|
- Handler newHandler =
|
|
- new Handler(
|
|
- start, end, handler, type != null ? symbolTable.addConstantClass(type).index : 0, type);
|
|
- if (firstHandler == null) {
|
|
- firstHandler = newHandler;
|
|
- } else {
|
|
- lastHandler.nextHandler = newHandler;
|
|
- }
|
|
- lastHandler = newHandler;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public AnnotationVisitor visitTryCatchAnnotation(
|
|
- 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 lastCodeRuntimeVisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
|
|
- } else {
|
|
- return lastCodeRuntimeInvisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitLocalVariable(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final String signature,
|
|
- final Label start,
|
|
- final Label end,
|
|
- final int index) {
|
|
- if (signature != null) {
|
|
- if (localVariableTypeTable == null) {
|
|
- localVariableTypeTable = new ByteVector();
|
|
- }
|
|
- ++localVariableTypeTableLength;
|
|
- localVariableTypeTable
|
|
- .putShort(start.bytecodeOffset)
|
|
- .putShort(end.bytecodeOffset - start.bytecodeOffset)
|
|
- .putShort(symbolTable.addConstantUtf8(name))
|
|
- .putShort(symbolTable.addConstantUtf8(signature))
|
|
- .putShort(index);
|
|
- }
|
|
- if (localVariableTable == null) {
|
|
- localVariableTable = new ByteVector();
|
|
- }
|
|
- ++localVariableTableLength;
|
|
- localVariableTable
|
|
- .putShort(start.bytecodeOffset)
|
|
- .putShort(end.bytecodeOffset - start.bytecodeOffset)
|
|
- .putShort(symbolTable.addConstantUtf8(name))
|
|
- .putShort(symbolTable.addConstantUtf8(descriptor))
|
|
- .putShort(index);
|
|
- if (compute != COMPUTE_NOTHING) {
|
|
- char firstDescChar = descriptor.charAt(0);
|
|
- int currentMaxLocals = index + (firstDescChar == 'J' || firstDescChar == 'D' ? 2 : 1);
|
|
- if (currentMaxLocals > maxLocals) {
|
|
- maxLocals = currentMaxLocals;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public AnnotationVisitor visitLocalVariableAnnotation(
|
|
- final int typeRef,
|
|
- final TypePath typePath,
|
|
- final Label[] start,
|
|
- final Label[] end,
|
|
- final int[] index,
|
|
- final String descriptor,
|
|
- final boolean visible) {
|
|
- // 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.
|
|
- typeAnnotation.putByte(typeRef >>> 24).putShort(start.length);
|
|
- for (int i = 0; i < start.length; ++i) {
|
|
- typeAnnotation
|
|
- .putShort(start[i].bytecodeOffset)
|
|
- .putShort(end[i].bytecodeOffset - start[i].bytecodeOffset)
|
|
- .putShort(index[i]);
|
|
- }
|
|
- 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 lastCodeRuntimeVisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation);
|
|
- } else {
|
|
- return lastCodeRuntimeInvisibleTypeAnnotation =
|
|
- new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation);
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitLineNumber(final int line, final Label start) {
|
|
- if (lineNumberTable == null) {
|
|
- lineNumberTable = new ByteVector();
|
|
- }
|
|
- ++lineNumberTableLength;
|
|
- lineNumberTable.putShort(start.bytecodeOffset);
|
|
- lineNumberTable.putShort(line);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitMaxs(final int maxStack, final int maxLocals) {
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- computeAllFrames();
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) {
|
|
- computeMaxStackAndLocal();
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
|
|
- this.maxStack = maxRelativeStackSize;
|
|
- } else {
|
|
- this.maxStack = maxStack;
|
|
- this.maxLocals = maxLocals;
|
|
- }
|
|
- }
|
|
-
|
|
- /** Computes all the stack map frames of the method, from scratch. */
|
|
- private void computeAllFrames() {
|
|
- // Complete the control flow graph with exception handler blocks.
|
|
- Handler handler = firstHandler;
|
|
- while (handler != null) {
|
|
- String catchTypeDescriptor =
|
|
- handler.catchTypeDescriptor == null ? "java/lang/Throwable" : handler.catchTypeDescriptor;
|
|
- int catchType = Frame.getAbstractTypeFromInternalName(symbolTable, catchTypeDescriptor);
|
|
- // Mark handlerBlock as an exception handler.
|
|
- Label handlerBlock = handler.handlerPc.getCanonicalInstance();
|
|
- handlerBlock.flags |= Label.FLAG_JUMP_TARGET;
|
|
- // Add handlerBlock as a successor of all the basic blocks in the exception handler range.
|
|
- Label handlerRangeBlock = handler.startPc.getCanonicalInstance();
|
|
- Label handlerRangeEnd = handler.endPc.getCanonicalInstance();
|
|
- while (handlerRangeBlock != handlerRangeEnd) {
|
|
- handlerRangeBlock.outgoingEdges =
|
|
- new Edge(catchType, handlerBlock, handlerRangeBlock.outgoingEdges);
|
|
- handlerRangeBlock = handlerRangeBlock.nextBasicBlock;
|
|
- }
|
|
- handler = handler.nextHandler;
|
|
- }
|
|
-
|
|
- // Create and visit the first (implicit) frame.
|
|
- Frame firstFrame = firstBasicBlock.frame;
|
|
- firstFrame.setInputFrameFromDescriptor(symbolTable, accessFlags, descriptor, this.maxLocals);
|
|
- firstFrame.accept(this);
|
|
-
|
|
- // Fix point algorithm: add the first basic block to a list of blocks to process (i.e. blocks
|
|
- // whose stack map frame has changed) and, while there are blocks to process, remove one from
|
|
- // the list and update the stack map frames of its successor blocks in the control flow graph
|
|
- // (which might change them, in which case these blocks must be processed too, and are thus
|
|
- // added to the list of blocks to process). Also compute the maximum stack size of the method,
|
|
- // as a by-product.
|
|
- Label listOfBlocksToProcess = firstBasicBlock;
|
|
- listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST;
|
|
- int maxStackSize = 0;
|
|
- while (listOfBlocksToProcess != Label.EMPTY_LIST) {
|
|
- // Remove a basic block from the list of blocks to process.
|
|
- Label basicBlock = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
|
|
- basicBlock.nextListElement = null;
|
|
- // By definition, basicBlock is reachable.
|
|
- basicBlock.flags |= Label.FLAG_REACHABLE;
|
|
- // Update the (absolute) maximum stack size.
|
|
- int maxBlockStackSize = basicBlock.frame.getInputStackSize() + basicBlock.outputStackMax;
|
|
- if (maxBlockStackSize > maxStackSize) {
|
|
- maxStackSize = maxBlockStackSize;
|
|
- }
|
|
- // Update the successor blocks of basicBlock in the control flow graph.
|
|
- Edge outgoingEdge = basicBlock.outgoingEdges;
|
|
- while (outgoingEdge != null) {
|
|
- Label successorBlock = outgoingEdge.successor.getCanonicalInstance();
|
|
- boolean successorBlockChanged =
|
|
- basicBlock.frame.merge(symbolTable, successorBlock.frame, outgoingEdge.info);
|
|
- if (successorBlockChanged && successorBlock.nextListElement == null) {
|
|
- // If successorBlock has changed it must be processed. Thus, if it is not already in the
|
|
- // list of blocks to process, add it to this list.
|
|
- successorBlock.nextListElement = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = successorBlock;
|
|
- }
|
|
- outgoingEdge = outgoingEdge.nextEdge;
|
|
- }
|
|
- }
|
|
-
|
|
- // Loop over all the basic blocks and visit the stack map frames that must be stored in the
|
|
- // StackMapTable attribute. Also replace unreachable code with NOP* ATHROW, and remove it from
|
|
- // exception handler ranges.
|
|
- Label basicBlock = firstBasicBlock;
|
|
- while (basicBlock != null) {
|
|
- if ((basicBlock.flags & (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE))
|
|
- == (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) {
|
|
- basicBlock.frame.accept(this);
|
|
- }
|
|
- if ((basicBlock.flags & Label.FLAG_REACHABLE) == 0) {
|
|
- // Find the start and end bytecode offsets of this unreachable block.
|
|
- Label nextBasicBlock = basicBlock.nextBasicBlock;
|
|
- int startOffset = basicBlock.bytecodeOffset;
|
|
- int endOffset = (nextBasicBlock == null ? code.length : nextBasicBlock.bytecodeOffset) - 1;
|
|
- if (endOffset >= startOffset) {
|
|
- // Replace its instructions with NOP ... NOP ATHROW.
|
|
- for (int i = startOffset; i < endOffset; ++i) {
|
|
- code.data[i] = Opcodes.NOP;
|
|
- }
|
|
- code.data[endOffset] = (byte) Opcodes.ATHROW;
|
|
- // Emit a frame for this unreachable block, with no local and a Throwable on the stack
|
|
- // (so that the ATHROW could consume this Throwable if it were reachable).
|
|
- int frameIndex = visitFrameStart(startOffset, /* numLocal = */ 0, /* numStack = */ 1);
|
|
- currentFrame[frameIndex] =
|
|
- Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable");
|
|
- visitFrameEnd();
|
|
- // Remove this unreachable basic block from the exception handler ranges.
|
|
- firstHandler = Handler.removeRange(firstHandler, basicBlock, nextBasicBlock);
|
|
- // The maximum stack size is now at least one, because of the Throwable declared above.
|
|
- maxStackSize = Math.max(maxStackSize, 1);
|
|
- }
|
|
- }
|
|
- basicBlock = basicBlock.nextBasicBlock;
|
|
- }
|
|
-
|
|
- this.maxStack = maxStackSize;
|
|
- }
|
|
-
|
|
- /** Computes the maximum stack size of the method. */
|
|
- private void computeMaxStackAndLocal() {
|
|
- // Complete the control flow graph with exception handler blocks.
|
|
- Handler handler = firstHandler;
|
|
- while (handler != null) {
|
|
- Label handlerBlock = handler.handlerPc;
|
|
- Label handlerRangeBlock = handler.startPc;
|
|
- Label handlerRangeEnd = handler.endPc;
|
|
- // Add handlerBlock as a successor of all the basic blocks in the exception handler range.
|
|
- while (handlerRangeBlock != handlerRangeEnd) {
|
|
- if ((handlerRangeBlock.flags & Label.FLAG_SUBROUTINE_CALLER) == 0) {
|
|
- handlerRangeBlock.outgoingEdges =
|
|
- new Edge(Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges);
|
|
- } else {
|
|
- // If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing
|
|
- // edges to preserve the hypothesis about JSR block successors order (see
|
|
- // {@link #visitJumpInsn}).
|
|
- handlerRangeBlock.outgoingEdges.nextEdge.nextEdge =
|
|
- new Edge(
|
|
- Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges.nextEdge.nextEdge);
|
|
- }
|
|
- handlerRangeBlock = handlerRangeBlock.nextBasicBlock;
|
|
- }
|
|
- handler = handler.nextHandler;
|
|
- }
|
|
-
|
|
- // Complete the control flow graph with the successor blocks of subroutines, if needed.
|
|
- if (hasSubroutines) {
|
|
- // First step: find the subroutines. This step determines, for each basic block, to which
|
|
- // subroutine(s) it belongs. Start with the main "subroutine":
|
|
- short numSubroutines = 1;
|
|
- firstBasicBlock.markSubroutine(numSubroutines);
|
|
- // Then, mark the subroutines called by the main subroutine, then the subroutines called by
|
|
- // those called by the main subroutine, etc.
|
|
- for (short currentSubroutine = 1; currentSubroutine <= numSubroutines; ++currentSubroutine) {
|
|
- Label basicBlock = firstBasicBlock;
|
|
- while (basicBlock != null) {
|
|
- if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0
|
|
- && basicBlock.subroutineId == currentSubroutine) {
|
|
- Label jsrTarget = basicBlock.outgoingEdges.nextEdge.successor;
|
|
- if (jsrTarget.subroutineId == 0) {
|
|
- // If this subroutine has not been marked yet, find its basic blocks.
|
|
- jsrTarget.markSubroutine(++numSubroutines);
|
|
- }
|
|
- }
|
|
- basicBlock = basicBlock.nextBasicBlock;
|
|
- }
|
|
- }
|
|
- // Second step: find the successors in the control flow graph of each subroutine basic block
|
|
- // 'r' ending with a RET instruction. These successors are the virtual successors of the basic
|
|
- // blocks ending with JSR instructions (see {@link #visitJumpInsn)} that can reach 'r'.
|
|
- Label basicBlock = firstBasicBlock;
|
|
- while (basicBlock != null) {
|
|
- if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
|
|
- // By construction, jsr targets are stored in the second outgoing edge of basic blocks
|
|
- // that ends with a jsr instruction (see {@link #FLAG_SUBROUTINE_CALLER}).
|
|
- Label subroutine = basicBlock.outgoingEdges.nextEdge.successor;
|
|
- subroutine.addSubroutineRetSuccessors(basicBlock);
|
|
- }
|
|
- basicBlock = basicBlock.nextBasicBlock;
|
|
- }
|
|
- }
|
|
-
|
|
- // Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks
|
|
- // whose input stack size has changed) and, while there are blocks to process, remove one
|
|
- // from the list, update the input stack size of its successor blocks in the control flow
|
|
- // graph, and add these blocks to the list of blocks to process (if not already done).
|
|
- Label listOfBlocksToProcess = firstBasicBlock;
|
|
- listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST;
|
|
- int maxStackSize = maxStack;
|
|
- while (listOfBlocksToProcess != Label.EMPTY_LIST) {
|
|
- // Remove a basic block from the list of blocks to process. Note that we don't reset
|
|
- // basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already
|
|
- // processed basic blocks.
|
|
- Label basicBlock = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
|
|
- // Compute the (absolute) input stack size and maximum stack size of this block.
|
|
- int inputStackTop = basicBlock.inputStackSize;
|
|
- int maxBlockStackSize = inputStackTop + basicBlock.outputStackMax;
|
|
- // Update the absolute maximum stack size of the method.
|
|
- if (maxBlockStackSize > maxStackSize) {
|
|
- maxStackSize = maxBlockStackSize;
|
|
- }
|
|
- // Update the input stack size of the successor blocks of basicBlock in the control flow
|
|
- // graph, and add these blocks to the list of blocks to process, if not already done.
|
|
- Edge outgoingEdge = basicBlock.outgoingEdges;
|
|
- if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
|
|
- // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual
|
|
- // edges which lead to the instruction just after the jsr, and do not correspond to a
|
|
- // possible execution path (see {@link #visitJumpInsn} and
|
|
- // {@link Label#FLAG_SUBROUTINE_CALLER}).
|
|
- outgoingEdge = outgoingEdge.nextEdge;
|
|
- }
|
|
- while (outgoingEdge != null) {
|
|
- Label successorBlock = outgoingEdge.successor;
|
|
- if (successorBlock.nextListElement == null) {
|
|
- successorBlock.inputStackSize =
|
|
- (short) (outgoingEdge.info == Edge.EXCEPTION ? 1 : inputStackTop + outgoingEdge.info);
|
|
- successorBlock.nextListElement = listOfBlocksToProcess;
|
|
- listOfBlocksToProcess = successorBlock;
|
|
- }
|
|
- outgoingEdge = outgoingEdge.nextEdge;
|
|
- }
|
|
- }
|
|
- this.maxStack = maxStackSize;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitEnd() {
|
|
- // Nothing to do.
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Utility methods: control flow analysis algorithm
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Adds a successor to {@link #currentBasicBlock} in the control flow graph.
|
|
- *
|
|
- * @param info information about the control flow edge to be added.
|
|
- * @param successor the successor block to be added to the current basic block.
|
|
- */
|
|
- private void addSuccessorToCurrentBasicBlock(final int info, final Label successor) {
|
|
- currentBasicBlock.outgoingEdges = new Edge(info, successor, currentBasicBlock.outgoingEdges);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Ends the current basic block. This method must be used in the case where the current basic
|
|
- * block does not have any successor.
|
|
- *
|
|
- * <p>WARNING: this method must be called after the currently visited instruction has been put in
|
|
- * {@link #code} (if frames are computed, this method inserts a new Label to start a new basic
|
|
- * block after the current instruction).
|
|
- */
|
|
- private void endCurrentBasicBlockWithNoSuccessor() {
|
|
- if (compute == COMPUTE_ALL_FRAMES) {
|
|
- Label nextBasicBlock = new Label();
|
|
- nextBasicBlock.frame = new Frame(nextBasicBlock);
|
|
- nextBasicBlock.resolve(code.data, code.length);
|
|
- lastBasicBlock.nextBasicBlock = nextBasicBlock;
|
|
- lastBasicBlock = nextBasicBlock;
|
|
- currentBasicBlock = null;
|
|
- } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) {
|
|
- currentBasicBlock.outputStackMax = (short) maxRelativeStackSize;
|
|
- currentBasicBlock = null;
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Utility methods: stack map frames
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Starts the visit of a new stack map frame, stored in {@link #currentFrame}.
|
|
- *
|
|
- * @param offset the bytecode offset of the instruction to which the frame corresponds.
|
|
- * @param numLocal the number of local variables in the frame.
|
|
- * @param numStack the number of stack elements in the frame.
|
|
- * @return the index of the next element to be written in this frame.
|
|
- */
|
|
- int visitFrameStart(final int offset, final int numLocal, final int numStack) {
|
|
- int frameLength = 3 + numLocal + numStack;
|
|
- if (currentFrame == null || currentFrame.length < frameLength) {
|
|
- currentFrame = new int[frameLength];
|
|
- }
|
|
- currentFrame[0] = offset;
|
|
- currentFrame[1] = numLocal;
|
|
- currentFrame[2] = numStack;
|
|
- return 3;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets an abstract type in {@link #currentFrame}.
|
|
- *
|
|
- * @param frameIndex the index of the element to be set in {@link #currentFrame}.
|
|
- * @param abstractType an abstract type.
|
|
- */
|
|
- void visitAbstractType(final int frameIndex, final int abstractType) {
|
|
- currentFrame[frameIndex] = abstractType;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Ends the visit of {@link #currentFrame} by writing it in the StackMapTable entries and by
|
|
- * updating the StackMapTable number_of_entries (except if the current frame is the first one,
|
|
- * which is implicit in StackMapTable). Then resets {@link #currentFrame} to {@literal null}.
|
|
- */
|
|
- void visitFrameEnd() {
|
|
- if (previousFrame != null) {
|
|
- if (stackMapTableEntries == null) {
|
|
- stackMapTableEntries = new ByteVector();
|
|
- }
|
|
- putFrame();
|
|
- ++stackMapTableNumberOfEntries;
|
|
- }
|
|
- previousFrame = currentFrame;
|
|
- currentFrame = null;
|
|
- }
|
|
-
|
|
- /** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */
|
|
- private void putFrame() {
|
|
- final int numLocal = currentFrame[1];
|
|
- final int numStack = currentFrame[2];
|
|
- if (symbolTable.getMajorVersion() < Opcodes.V1_6) {
|
|
- // Generate a StackMap attribute entry, which are always uncompressed.
|
|
- stackMapTableEntries.putShort(currentFrame[0]).putShort(numLocal);
|
|
- putAbstractTypes(3, 3 + numLocal);
|
|
- stackMapTableEntries.putShort(numStack);
|
|
- putAbstractTypes(3 + numLocal, 3 + numLocal + numStack);
|
|
- return;
|
|
- }
|
|
- final int offsetDelta =
|
|
- stackMapTableNumberOfEntries == 0
|
|
- ? currentFrame[0]
|
|
- : currentFrame[0] - previousFrame[0] - 1;
|
|
- final int previousNumlocal = previousFrame[1];
|
|
- final int numLocalDelta = numLocal - previousNumlocal;
|
|
- int type = Frame.FULL_FRAME;
|
|
- if (numStack == 0) {
|
|
- switch (numLocalDelta) {
|
|
- case -3:
|
|
- case -2:
|
|
- case -1:
|
|
- type = Frame.CHOP_FRAME;
|
|
- break;
|
|
- case 0:
|
|
- type = offsetDelta < 64 ? Frame.SAME_FRAME : Frame.SAME_FRAME_EXTENDED;
|
|
- break;
|
|
- case 1:
|
|
- case 2:
|
|
- case 3:
|
|
- type = Frame.APPEND_FRAME;
|
|
- break;
|
|
- default:
|
|
- // Keep the FULL_FRAME type.
|
|
- break;
|
|
- }
|
|
- } else if (numLocalDelta == 0 && numStack == 1) {
|
|
- type =
|
|
- offsetDelta < 63
|
|
- ? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME
|
|
- : Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
|
|
- }
|
|
- if (type != Frame.FULL_FRAME) {
|
|
- // Verify if locals are the same as in the previous frame.
|
|
- int frameIndex = 3;
|
|
- for (int i = 0; i < previousNumlocal && i < numLocal; i++) {
|
|
- if (currentFrame[frameIndex] != previousFrame[frameIndex]) {
|
|
- type = Frame.FULL_FRAME;
|
|
- break;
|
|
- }
|
|
- frameIndex++;
|
|
- }
|
|
- }
|
|
- switch (type) {
|
|
- case Frame.SAME_FRAME:
|
|
- stackMapTableEntries.putByte(offsetDelta);
|
|
- break;
|
|
- case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME:
|
|
- stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta);
|
|
- putAbstractTypes(3 + numLocal, 4 + numLocal);
|
|
- break;
|
|
- case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
|
- .putShort(offsetDelta);
|
|
- putAbstractTypes(3 + numLocal, 4 + numLocal);
|
|
- break;
|
|
- case Frame.SAME_FRAME_EXTENDED:
|
|
- stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta);
|
|
- break;
|
|
- case Frame.CHOP_FRAME:
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta)
|
|
- .putShort(offsetDelta);
|
|
- break;
|
|
- case Frame.APPEND_FRAME:
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta)
|
|
- .putShort(offsetDelta);
|
|
- putAbstractTypes(3 + previousNumlocal, 3 + numLocal);
|
|
- break;
|
|
- case Frame.FULL_FRAME:
|
|
- default:
|
|
- stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal);
|
|
- putAbstractTypes(3, 3 + numLocal);
|
|
- stackMapTableEntries.putShort(numStack);
|
|
- putAbstractTypes(3 + numLocal, 3 + numLocal + numStack);
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts some abstract types of {@link #currentFrame} in {@link #stackMapTableEntries} , using the
|
|
- * JVMS verification_type_info format used in StackMapTable attributes.
|
|
- *
|
|
- * @param start index of the first type in {@link #currentFrame} to write.
|
|
- * @param end index of last type in {@link #currentFrame} to write (exclusive).
|
|
- */
|
|
- private void putAbstractTypes(final int start, final int end) {
|
|
- for (int i = start; i < end; ++i) {
|
|
- Frame.putAbstractType(symbolTable, currentFrame[i], stackMapTableEntries);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the given public API frame element type in {@link #stackMapTableEntries} , using the JVMS
|
|
- * verification_type_info format used in StackMapTable attributes.
|
|
- *
|
|
- * @param type a frame element type described using the same format as in {@link
|
|
- * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
|
- * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or
|
|
- * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating
|
|
- * a NEW instruction (for uninitialized types).
|
|
- */
|
|
- private void putFrameType(final Object type) {
|
|
- if (type instanceof Integer) {
|
|
- stackMapTableEntries.putByte(((Integer) type).intValue());
|
|
- } else if (type instanceof String) {
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.ITEM_OBJECT)
|
|
- .putShort(symbolTable.addConstantClass((String) type).index);
|
|
- } else {
|
|
- stackMapTableEntries
|
|
- .putByte(Frame.ITEM_UNINITIALIZED)
|
|
- .putShort(((Label) type).bytecodeOffset);
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Utility methods
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns whether the attributes of this method can be copied from the attributes of the given
|
|
- * method (assuming there is no method visitor between the given ClassReader and this
|
|
- * MethodWriter). This method should only be called just after this MethodWriter has been created,
|
|
- * and before any content is visited. It returns true if the attributes corresponding to the
|
|
- * constructor arguments (at most a Signature, an Exception, a Deprecated and a Synthetic
|
|
- * attribute) are the same as the corresponding attributes in the given method.
|
|
- *
|
|
- * @param source the source ClassReader from which the attributes of this method might be copied.
|
|
- * @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which
|
|
- * the attributes of this method might be copied.
|
|
- * @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which
|
|
- * the attributes of this method might be copied.
|
|
- * @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes
|
|
- * of this method might be copied contains a Synthetic attribute.
|
|
- * @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes
|
|
- * of this method might be copied contains a Deprecated attribute.
|
|
- * @param descriptorIndex the descriptor_index field of the method_info JVMS structure from which
|
|
- * the attributes of this method might be copied.
|
|
- * @param signatureIndex the constant pool index contained in the Signature attribute of the
|
|
- * method_info JVMS structure from which the attributes of this method might be copied, or 0.
|
|
- * @param exceptionsOffset the offset in 'source.b' of the Exceptions attribute of the method_info
|
|
- * JVMS structure from which the attributes of this method might be copied, or 0.
|
|
- * @return whether the attributes of this method can be copied from the attributes of the
|
|
- * method_info JVMS structure in 'source.b', between 'methodInfoOffset' and 'methodInfoOffset'
|
|
- * + 'methodInfoLength'.
|
|
- */
|
|
- boolean canCopyMethodAttributes(
|
|
- final ClassReader source,
|
|
- final int methodInfoOffset,
|
|
- final int methodInfoLength,
|
|
- final boolean hasSyntheticAttribute,
|
|
- final boolean hasDeprecatedAttribute,
|
|
- final int descriptorIndex,
|
|
- final int signatureIndex,
|
|
- final int exceptionsOffset) {
|
|
- // If the method descriptor has changed, with more locals than the max_locals field of the
|
|
- // original Code attribute, if any, then the original method attributes can't be copied. A
|
|
- // conservative check on the descriptor changes alone ensures this (being more precise is not
|
|
- // worth the additional complexity, because these cases should be rare -- if a transform changes
|
|
- // a method descriptor, most of the time it needs to change the method's code too).
|
|
- if (source != symbolTable.getSource()
|
|
- || descriptorIndex != this.descriptorIndex
|
|
- || signatureIndex != this.signatureIndex
|
|
- || hasDeprecatedAttribute != ((accessFlags & Opcodes.ACC_DEPRECATED) != 0)) {
|
|
- return false;
|
|
- }
|
|
- boolean needSyntheticAttribute =
|
|
- symbolTable.getMajorVersion() < Opcodes.V1_5 && (accessFlags & Opcodes.ACC_SYNTHETIC) != 0;
|
|
- if (hasSyntheticAttribute != needSyntheticAttribute) {
|
|
- return false;
|
|
- }
|
|
- if (exceptionsOffset == 0) {
|
|
- if (numberOfExceptions != 0) {
|
|
- return false;
|
|
- }
|
|
- } else if (source.readUnsignedShort(exceptionsOffset) == numberOfExceptions) {
|
|
- int currentExceptionOffset = exceptionsOffset + 2;
|
|
- for (int i = 0; i < numberOfExceptions; ++i) {
|
|
- if (source.readUnsignedShort(currentExceptionOffset) != exceptionIndexTable[i]) {
|
|
- return false;
|
|
- }
|
|
- currentExceptionOffset += 2;
|
|
- }
|
|
- }
|
|
- // Don't copy the attributes yet, instead store their location in the source class reader so
|
|
- // they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes
|
|
- // of the method_info JVMS structure.
|
|
- this.sourceOffset = methodInfoOffset + 6;
|
|
- this.sourceLength = methodInfoLength - 6;
|
|
- return true;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of the method_info JVMS structure generated by this MethodWriter. Also add the
|
|
- * names of the attributes of this method in the constant pool.
|
|
- *
|
|
- * @return the size in bytes of the method_info JVMS structure.
|
|
- */
|
|
- int computeMethodInfoSize() {
|
|
- // If this method_info must be copied from an existing one, the size computation is trivial.
|
|
- if (sourceOffset != 0) {
|
|
- // sourceLength excludes the first 6 bytes for access_flags, name_index and descriptor_index.
|
|
- return 6 + sourceLength;
|
|
- }
|
|
- // 2 bytes each for access_flags, name_index, descriptor_index and attributes_count.
|
|
- int size = 8;
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- if (code.length > 0) {
|
|
- if (code.length > 65535) {
|
|
- throw new MethodTooLargeException(
|
|
- symbolTable.getClassName(), name, descriptor, code.length);
|
|
- }
|
|
- symbolTable.addConstantUtf8(Constants.CODE);
|
|
- // The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack,
|
|
- // max_locals, code_length and attributes_count, plus the bytecode and the exception table.
|
|
- size += 16 + code.length + Handler.getExceptionTableSize(firstHandler);
|
|
- if (stackMapTableEntries != null) {
|
|
- boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6;
|
|
- symbolTable.addConstantUtf8(useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap");
|
|
- // 6 header bytes and 2 bytes for number_of_entries.
|
|
- size += 8 + stackMapTableEntries.length;
|
|
- }
|
|
- if (lineNumberTable != null) {
|
|
- symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE);
|
|
- // 6 header bytes and 2 bytes for line_number_table_length.
|
|
- size += 8 + lineNumberTable.length;
|
|
- }
|
|
- if (localVariableTable != null) {
|
|
- symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE);
|
|
- // 6 header bytes and 2 bytes for local_variable_table_length.
|
|
- size += 8 + localVariableTable.length;
|
|
- }
|
|
- if (localVariableTypeTable != null) {
|
|
- symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE);
|
|
- // 6 header bytes and 2 bytes for local_variable_type_table_length.
|
|
- size += 8 + localVariableTypeTable.length;
|
|
- }
|
|
- if (lastCodeRuntimeVisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (lastCodeRuntimeInvisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (firstCodeAttribute != null) {
|
|
- size +=
|
|
- firstCodeAttribute.computeAttributesSize(
|
|
- symbolTable, code.data, code.length, maxStack, maxLocals);
|
|
- }
|
|
- }
|
|
- if (numberOfExceptions > 0) {
|
|
- symbolTable.addConstantUtf8(Constants.EXCEPTIONS);
|
|
- size += 8 + 2 * numberOfExceptions;
|
|
- }
|
|
- boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
|
- symbolTable.addConstantUtf8(Constants.SYNTHETIC);
|
|
- size += 6;
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- symbolTable.addConstantUtf8(Constants.SIGNATURE);
|
|
- size += 8;
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- symbolTable.addConstantUtf8(Constants.DEPRECATED);
|
|
- size += 6;
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeVisibleAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeVisibleParameterAnnotations != null) {
|
|
- size +=
|
|
- AnnotationWriter.computeParameterAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
|
|
- lastRuntimeVisibleParameterAnnotations,
|
|
- visibleAnnotableParameterCount == 0
|
|
- ? lastRuntimeVisibleParameterAnnotations.length
|
|
- : visibleAnnotableParameterCount);
|
|
- }
|
|
- if (lastRuntimeInvisibleParameterAnnotations != null) {
|
|
- size +=
|
|
- AnnotationWriter.computeParameterAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS,
|
|
- lastRuntimeInvisibleParameterAnnotations,
|
|
- invisibleAnnotableParameterCount == 0
|
|
- ? lastRuntimeInvisibleParameterAnnotations.length
|
|
- : invisibleAnnotableParameterCount);
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
|
- }
|
|
- if (defaultValue != null) {
|
|
- symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT);
|
|
- size += 6 + defaultValue.length;
|
|
- }
|
|
- if (parameters != null) {
|
|
- symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS);
|
|
- // 6 header bytes and 1 byte for parameters_count.
|
|
- size += 7 + parameters.length;
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- size += firstAttribute.computeAttributesSize(symbolTable);
|
|
- }
|
|
- return size;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the content of the method_info JVMS structure generated by this MethodWriter into the
|
|
- * given ByteVector.
|
|
- *
|
|
- * @param output where the method_info structure must be put.
|
|
- */
|
|
- void putMethodInfo(final ByteVector output) {
|
|
- boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
|
|
- int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0;
|
|
- output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
|
|
- // If this method_info must be copied from an existing one, copy it now and return early.
|
|
- if (sourceOffset != 0) {
|
|
- output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength);
|
|
- return;
|
|
- }
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- int attributeCount = 0;
|
|
- if (code.length > 0) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (numberOfExceptions > 0) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeVisibleParameterAnnotations != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeInvisibleParameterAnnotations != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (defaultValue != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (parameters != null) {
|
|
- ++attributeCount;
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- attributeCount += firstAttribute.getAttributeCount();
|
|
- }
|
|
- // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
|
- output.putShort(attributeCount);
|
|
- if (code.length > 0) {
|
|
- // 2, 2, 4 and 2 bytes respectively for max_stack, max_locals, code_length and
|
|
- // attributes_count, plus the bytecode and the exception table.
|
|
- int size = 10 + code.length + Handler.getExceptionTableSize(firstHandler);
|
|
- int codeAttributeCount = 0;
|
|
- if (stackMapTableEntries != null) {
|
|
- // 6 header bytes and 2 bytes for number_of_entries.
|
|
- size += 8 + stackMapTableEntries.length;
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (lineNumberTable != null) {
|
|
- // 6 header bytes and 2 bytes for line_number_table_length.
|
|
- size += 8 + lineNumberTable.length;
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (localVariableTable != null) {
|
|
- // 6 header bytes and 2 bytes for local_variable_table_length.
|
|
- size += 8 + localVariableTable.length;
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (localVariableTypeTable != null) {
|
|
- // 6 header bytes and 2 bytes for local_variable_type_table_length.
|
|
- size += 8 + localVariableTypeTable.length;
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (lastCodeRuntimeVisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (lastCodeRuntimeInvisibleTypeAnnotation != null) {
|
|
- size +=
|
|
- lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
|
- Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
|
- ++codeAttributeCount;
|
|
- }
|
|
- if (firstCodeAttribute != null) {
|
|
- size +=
|
|
- firstCodeAttribute.computeAttributesSize(
|
|
- symbolTable, code.data, code.length, maxStack, maxLocals);
|
|
- codeAttributeCount += firstCodeAttribute.getAttributeCount();
|
|
- }
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.CODE))
|
|
- .putInt(size)
|
|
- .putShort(maxStack)
|
|
- .putShort(maxLocals)
|
|
- .putInt(code.length)
|
|
- .putByteArray(code.data, 0, code.length);
|
|
- Handler.putExceptionTable(firstHandler, output);
|
|
- output.putShort(codeAttributeCount);
|
|
- if (stackMapTableEntries != null) {
|
|
- boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6;
|
|
- output
|
|
- .putShort(
|
|
- symbolTable.addConstantUtf8(
|
|
- useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap"))
|
|
- .putInt(2 + stackMapTableEntries.length)
|
|
- .putShort(stackMapTableNumberOfEntries)
|
|
- .putByteArray(stackMapTableEntries.data, 0, stackMapTableEntries.length);
|
|
- }
|
|
- if (lineNumberTable != null) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE))
|
|
- .putInt(2 + lineNumberTable.length)
|
|
- .putShort(lineNumberTableLength)
|
|
- .putByteArray(lineNumberTable.data, 0, lineNumberTable.length);
|
|
- }
|
|
- if (localVariableTable != null) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE))
|
|
- .putInt(2 + localVariableTable.length)
|
|
- .putShort(localVariableTableLength)
|
|
- .putByteArray(localVariableTable.data, 0, localVariableTable.length);
|
|
- }
|
|
- if (localVariableTypeTable != null) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE))
|
|
- .putInt(2 + localVariableTypeTable.length)
|
|
- .putShort(localVariableTypeTableLength)
|
|
- .putByteArray(localVariableTypeTable.data, 0, localVariableTypeTable.length);
|
|
- }
|
|
- if (lastCodeRuntimeVisibleTypeAnnotation != null) {
|
|
- lastCodeRuntimeVisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastCodeRuntimeInvisibleTypeAnnotation != null) {
|
|
- lastCodeRuntimeInvisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (firstCodeAttribute != null) {
|
|
- firstCodeAttribute.putAttributes(
|
|
- symbolTable, code.data, code.length, maxStack, maxLocals, output);
|
|
- }
|
|
- }
|
|
- if (numberOfExceptions > 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.EXCEPTIONS))
|
|
- .putInt(2 + 2 * numberOfExceptions)
|
|
- .putShort(numberOfExceptions);
|
|
- for (int exceptionIndex : exceptionIndexTable) {
|
|
- output.putShort(exceptionIndex);
|
|
- }
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
|
- output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
|
|
- }
|
|
- if (signatureIndex != 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
|
|
- .putInt(2)
|
|
- .putShort(signatureIndex);
|
|
- }
|
|
- if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
|
- output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
|
|
- }
|
|
- if (lastRuntimeVisibleAnnotation != null) {
|
|
- lastRuntimeVisibleAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeInvisibleAnnotation != null) {
|
|
- lastRuntimeInvisibleAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeVisibleParameterAnnotations != null) {
|
|
- AnnotationWriter.putParameterAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
|
|
- lastRuntimeVisibleParameterAnnotations,
|
|
- visibleAnnotableParameterCount == 0
|
|
- ? lastRuntimeVisibleParameterAnnotations.length
|
|
- : visibleAnnotableParameterCount,
|
|
- output);
|
|
- }
|
|
- if (lastRuntimeInvisibleParameterAnnotations != null) {
|
|
- AnnotationWriter.putParameterAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS),
|
|
- lastRuntimeInvisibleParameterAnnotations,
|
|
- invisibleAnnotableParameterCount == 0
|
|
- ? lastRuntimeInvisibleParameterAnnotations.length
|
|
- : invisibleAnnotableParameterCount,
|
|
- output);
|
|
- }
|
|
- if (lastRuntimeVisibleTypeAnnotation != null) {
|
|
- lastRuntimeVisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (lastRuntimeInvisibleTypeAnnotation != null) {
|
|
- lastRuntimeInvisibleTypeAnnotation.putAnnotations(
|
|
- symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
|
|
- }
|
|
- if (defaultValue != null) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT))
|
|
- .putInt(defaultValue.length)
|
|
- .putByteArray(defaultValue.data, 0, defaultValue.length);
|
|
- }
|
|
- if (parameters != null) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS))
|
|
- .putInt(1 + parameters.length)
|
|
- .putByte(parametersCount)
|
|
- .putByteArray(parameters.data, 0, parameters.length);
|
|
- }
|
|
- if (firstAttribute != null) {
|
|
- firstAttribute.putAttributes(symbolTable, output);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Collects the attributes of this method into the given set of attribute prototypes.
|
|
- *
|
|
- * @param attributePrototypes a set of attribute prototypes.
|
|
- */
|
|
- final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
|
|
- attributePrototypes.addAttributes(firstAttribute);
|
|
- attributePrototypes.addAttributes(firstCodeAttribute);
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/ModuleVisitor.java b/src/main/java/org/mvel2/asm/ModuleVisitor.java
|
|
deleted file mode 100644
|
|
index a674ec9..0000000
|
|
--- a/src/main/java/org/mvel2/asm/ModuleVisitor.java
|
|
+++ /dev/null
|
|
@@ -1,174 +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 module. The methods of this class must be called in the following
|
|
- * order: ( {@code visitMainClass} | ( {@code visitPackage} | {@code visitRequire} | {@code
|
|
- * visitExport} | {@code visitOpen} | {@code visitUse} | {@code visitProvide} )* ) {@code visitEnd}.
|
|
- *
|
|
- * @author Remi Forax
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public abstract class ModuleVisitor {
|
|
- /**
|
|
- * The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
|
- * Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
|
- */
|
|
- protected final int api;
|
|
-
|
|
- /** The module visitor to which this visitor must delegate method calls. May be null. */
|
|
- protected ModuleVisitor mv;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link ModuleVisitor}.
|
|
- *
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6}
|
|
- * or {@link Opcodes#ASM7}.
|
|
- */
|
|
- public ModuleVisitor(final int api) {
|
|
- this(api, null);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link ModuleVisitor}.
|
|
- *
|
|
- * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6}
|
|
- * or {@link Opcodes#ASM7}.
|
|
- * @param moduleVisitor the module visitor to which this visitor must delegate method calls. May
|
|
- * be null.
|
|
- */
|
|
- public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) {
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM7) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- this.api = api;
|
|
- this.mv = moduleVisitor;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit the main class of the current module.
|
|
- *
|
|
- * @param mainClass the internal name of the main class of the current module.
|
|
- */
|
|
- public void visitMainClass(final String mainClass) {
|
|
- if (mv != null) {
|
|
- mv.visitMainClass(mainClass);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit a package of the current module.
|
|
- *
|
|
- * @param packaze the internal name of a package.
|
|
- */
|
|
- public void visitPackage(final String packaze) {
|
|
- if (mv != null) {
|
|
- mv.visitPackage(packaze);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a dependence of the current module.
|
|
- *
|
|
- * @param module the fully qualified name (using dots) of the dependence.
|
|
- * @param access the access flag of the dependence among {@code ACC_TRANSITIVE}, {@code
|
|
- * ACC_STATIC_PHASE}, {@code ACC_SYNTHETIC} and {@code ACC_MANDATED}.
|
|
- * @param version the module version at compile time, or {@literal null}.
|
|
- */
|
|
- public void visitRequire(final String module, final int access, final String version) {
|
|
- if (mv != null) {
|
|
- mv.visitRequire(module, access, version);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit an exported package of the current module.
|
|
- *
|
|
- * @param packaze the internal name of the exported package.
|
|
- * @param access the access flag of the exported package, valid values are among {@code
|
|
- * ACC_SYNTHETIC} and {@code ACC_MANDATED}.
|
|
- * @param modules the fully qualified names (using dots) of the modules that can access the public
|
|
- * classes of the exported package, or {@literal null}.
|
|
- */
|
|
- public void visitExport(final String packaze, final int access, final String... modules) {
|
|
- if (mv != null) {
|
|
- mv.visitExport(packaze, access, modules);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit an open package of the current module.
|
|
- *
|
|
- * @param packaze the internal name of the opened package.
|
|
- * @param access the access flag of the opened package, valid values are among {@code
|
|
- * ACC_SYNTHETIC} and {@code ACC_MANDATED}.
|
|
- * @param modules the fully qualified names (using dots) of the modules that can use deep
|
|
- * reflection to the classes of the open package, or {@literal null}.
|
|
- */
|
|
- public void visitOpen(final String packaze, final int access, final String... modules) {
|
|
- if (mv != null) {
|
|
- mv.visitOpen(packaze, access, modules);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit a service used by the current module. The name must be the internal name of an interface
|
|
- * or a class.
|
|
- *
|
|
- * @param service the internal name of the service.
|
|
- */
|
|
- public void visitUse(final String service) {
|
|
- if (mv != null) {
|
|
- mv.visitUse(service);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visit an implementation of a service.
|
|
- *
|
|
- * @param service the internal name of the service.
|
|
- * @param providers the internal names of the implementations of the service (there is at least
|
|
- * one provider).
|
|
- */
|
|
- public void visitProvide(final String service, final String... providers) {
|
|
- if (mv != null) {
|
|
- mv.visitProvide(service, providers);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the end of the module. This method, which is the last one to be called, is used to
|
|
- * inform the visitor that everything have been visited.
|
|
- */
|
|
- public void visitEnd() {
|
|
- if (mv != null) {
|
|
- mv.visitEnd();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/ModuleWriter.java b/src/main/java/org/mvel2/asm/ModuleWriter.java
|
|
deleted file mode 100644
|
|
index 65daa30..0000000
|
|
--- a/src/main/java/org/mvel2/asm/ModuleWriter.java
|
|
+++ /dev/null
|
|
@@ -1,253 +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 ModuleVisitor} that generates the corresponding Module, ModulePackages and
|
|
- * ModuleMainClass attributes, 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.25">JVMS
|
|
- * 4.7.25</a>
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.26">JVMS
|
|
- * 4.7.26</a>
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.27">JVMS
|
|
- * 4.7.27</a>
|
|
- * @author Remi Forax
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class ModuleWriter extends ModuleVisitor {
|
|
-
|
|
- /** Where the constants used in this AnnotationWriter must be stored. */
|
|
- private final SymbolTable symbolTable;
|
|
-
|
|
- /** The module_name_index field of the JVMS Module attribute. */
|
|
- private final int moduleNameIndex;
|
|
-
|
|
- /** The module_flags field of the JVMS Module attribute. */
|
|
- private final int moduleFlags;
|
|
-
|
|
- /** The module_version_index field of the JVMS Module attribute. */
|
|
- private final int moduleVersionIndex;
|
|
-
|
|
- /** The requires_count field of the JVMS Module attribute. */
|
|
- private int requiresCount;
|
|
-
|
|
- /** The binary content of the 'requires' array of the JVMS Module attribute. */
|
|
- private final ByteVector requires;
|
|
-
|
|
- /** The exports_count field of the JVMS Module attribute. */
|
|
- private int exportsCount;
|
|
-
|
|
- /** The binary content of the 'exports' array of the JVMS Module attribute. */
|
|
- private final ByteVector exports;
|
|
-
|
|
- /** The opens_count field of the JVMS Module attribute. */
|
|
- private int opensCount;
|
|
-
|
|
- /** The binary content of the 'opens' array of the JVMS Module attribute. */
|
|
- private final ByteVector opens;
|
|
-
|
|
- /** The uses_count field of the JVMS Module attribute. */
|
|
- private int usesCount;
|
|
-
|
|
- /** The binary content of the 'uses_index' array of the JVMS Module attribute. */
|
|
- private final ByteVector usesIndex;
|
|
-
|
|
- /** The provides_count field of the JVMS Module attribute. */
|
|
- private int providesCount;
|
|
-
|
|
- /** The binary content of the 'provides' array of the JVMS Module attribute. */
|
|
- private final ByteVector provides;
|
|
-
|
|
- /** The provides_count field of the JVMS ModulePackages attribute. */
|
|
- private int packageCount;
|
|
-
|
|
- /** The binary content of the 'package_index' array of the JVMS ModulePackages attribute. */
|
|
- private final ByteVector packageIndex;
|
|
-
|
|
- /** The main_class_index field of the JVMS ModuleMainClass attribute, or 0. */
|
|
- private int mainClassIndex;
|
|
-
|
|
- ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) {
|
|
- super(Opcodes.ASM7);
|
|
- this.symbolTable = symbolTable;
|
|
- this.moduleNameIndex = name;
|
|
- this.moduleFlags = access;
|
|
- this.moduleVersionIndex = version;
|
|
- this.requires = new ByteVector();
|
|
- this.exports = new ByteVector();
|
|
- this.opens = new ByteVector();
|
|
- this.usesIndex = new ByteVector();
|
|
- this.provides = new ByteVector();
|
|
- this.packageIndex = new ByteVector();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitMainClass(final String mainClass) {
|
|
- this.mainClassIndex = symbolTable.addConstantClass(mainClass).index;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitPackage(final String packaze) {
|
|
- packageIndex.putShort(symbolTable.addConstantPackage(packaze).index);
|
|
- packageCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitRequire(final String module, final int access, final String version) {
|
|
- requires
|
|
- .putShort(symbolTable.addConstantModule(module).index)
|
|
- .putShort(access)
|
|
- .putShort(version == null ? 0 : symbolTable.addConstantUtf8(version));
|
|
- requiresCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitExport(final String packaze, final int access, final String... modules) {
|
|
- exports.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access);
|
|
- if (modules == null) {
|
|
- exports.putShort(0);
|
|
- } else {
|
|
- exports.putShort(modules.length);
|
|
- for (String module : modules) {
|
|
- exports.putShort(symbolTable.addConstantModule(module).index);
|
|
- }
|
|
- }
|
|
- exportsCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitOpen(final String packaze, final int access, final String... modules) {
|
|
- opens.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access);
|
|
- if (modules == null) {
|
|
- opens.putShort(0);
|
|
- } else {
|
|
- opens.putShort(modules.length);
|
|
- for (String module : modules) {
|
|
- opens.putShort(symbolTable.addConstantModule(module).index);
|
|
- }
|
|
- }
|
|
- opensCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitUse(final String service) {
|
|
- usesIndex.putShort(symbolTable.addConstantClass(service).index);
|
|
- usesCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitProvide(final String service, final String... providers) {
|
|
- provides.putShort(symbolTable.addConstantClass(service).index);
|
|
- provides.putShort(providers.length);
|
|
- for (String provider : providers) {
|
|
- provides.putShort(symbolTable.addConstantClass(provider).index);
|
|
- }
|
|
- providesCount++;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitEnd() {
|
|
- // Nothing to do.
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of Module, ModulePackages and ModuleMainClass attributes generated by this
|
|
- * ModuleWriter.
|
|
- *
|
|
- * @return the number of Module, ModulePackages and ModuleMainClass attributes (between 1 and 3).
|
|
- */
|
|
- int getAttributeCount() {
|
|
- return 1 + (packageCount > 0 ? 1 : 0) + (mainClassIndex > 0 ? 1 : 0);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of the Module, ModulePackages and ModuleMainClass attributes generated by this
|
|
- * ModuleWriter. Also add the names of these attributes in the constant pool.
|
|
- *
|
|
- * @return the size in bytes of the Module, ModulePackages and ModuleMainClass attributes.
|
|
- */
|
|
- int computeAttributesSize() {
|
|
- symbolTable.addConstantUtf8(Constants.MODULE);
|
|
- // 6 attribute header bytes, 6 bytes for name, flags and version, and 5 * 2 bytes for counts.
|
|
- int size =
|
|
- 22 + requires.length + exports.length + opens.length + usesIndex.length + provides.length;
|
|
- if (packageCount > 0) {
|
|
- symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES);
|
|
- // 6 attribute header bytes, and 2 bytes for package_count.
|
|
- size += 8 + packageIndex.length;
|
|
- }
|
|
- if (mainClassIndex > 0) {
|
|
- symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS);
|
|
- // 6 attribute header bytes, and 2 bytes for main_class_index.
|
|
- size += 8;
|
|
- }
|
|
- return size;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the Module, ModulePackages and ModuleMainClass attributes generated by this ModuleWriter
|
|
- * in the given ByteVector.
|
|
- *
|
|
- * @param output where the attributes must be put.
|
|
- */
|
|
- void putAttributes(final ByteVector output) {
|
|
- // 6 bytes for name, flags and version, and 5 * 2 bytes for counts.
|
|
- int moduleAttributeLength =
|
|
- 16 + requires.length + exports.length + opens.length + usesIndex.length + provides.length;
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.MODULE))
|
|
- .putInt(moduleAttributeLength)
|
|
- .putShort(moduleNameIndex)
|
|
- .putShort(moduleFlags)
|
|
- .putShort(moduleVersionIndex)
|
|
- .putShort(requiresCount)
|
|
- .putByteArray(requires.data, 0, requires.length)
|
|
- .putShort(exportsCount)
|
|
- .putByteArray(exports.data, 0, exports.length)
|
|
- .putShort(opensCount)
|
|
- .putByteArray(opens.data, 0, opens.length)
|
|
- .putShort(usesCount)
|
|
- .putByteArray(usesIndex.data, 0, usesIndex.length)
|
|
- .putShort(providesCount)
|
|
- .putByteArray(provides.data, 0, provides.length);
|
|
- if (packageCount > 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES))
|
|
- .putInt(2 + packageIndex.length)
|
|
- .putShort(packageCount)
|
|
- .putByteArray(packageIndex.data, 0, packageIndex.length);
|
|
- }
|
|
- if (mainClassIndex > 0) {
|
|
- output
|
|
- .putShort(symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS))
|
|
- .putInt(2)
|
|
- .putShort(mainClassIndex);
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Opcodes.java b/src/main/java/org/mvel2/asm/Opcodes.java
|
|
deleted file mode 100644
|
|
index d1c1b3c..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Opcodes.java
|
|
+++ /dev/null
|
|
@@ -1,340 +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;
|
|
-
|
|
-/**
|
|
- * The JVM opcodes, access flags and array type codes. This interface does not define all the JVM
|
|
- * opcodes because some opcodes are automatically handled. For example, the xLOAD and xSTORE opcodes
|
|
- * are automatically replaced by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and
|
|
- * xSTORE_n opcodes are therefore not defined in this interface. Likewise for LDC, automatically
|
|
- * replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and JSR_W.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html">JVMS 6</a>
|
|
- * @author Eric Bruneton
|
|
- * @author Eugene Kuleshov
|
|
- */
|
|
-// DontCheck(InterfaceIsType): can't be fixed (for backward binary compatibility).
|
|
-public interface Opcodes {
|
|
-
|
|
- // ASM API versions.
|
|
-
|
|
- int ASM4 = 4 << 16 | 0 << 8;
|
|
- int ASM5 = 5 << 16 | 0 << 8;
|
|
- int ASM6 = 6 << 16 | 0 << 8;
|
|
- int ASM7 = 7 << 16 | 0 << 8;
|
|
-
|
|
- // Java ClassFile versions (the minor version is stored in the 16 most
|
|
- // significant bits, and the
|
|
- // major version in the 16 least significant bits).
|
|
-
|
|
- int V1_1 = 3 << 16 | 45;
|
|
- int V1_2 = 0 << 16 | 46;
|
|
- int V1_3 = 0 << 16 | 47;
|
|
- int V1_4 = 0 << 16 | 48;
|
|
- int V1_5 = 0 << 16 | 49;
|
|
- int V1_6 = 0 << 16 | 50;
|
|
- int V1_7 = 0 << 16 | 51;
|
|
- int V1_8 = 0 << 16 | 52;
|
|
- int V9 = 0 << 16 | 53;
|
|
- int V10 = 0 << 16 | 54;
|
|
- int V11 = 0 << 16 | 55;
|
|
- int V12 = 0 << 16 | 56;
|
|
-
|
|
- /**
|
|
- * Version flag indicating that the class is using 'preview' features.
|
|
- *
|
|
- * <p>{@code version & V_PREVIEW == V_PREVIEW} tests if a version is flagged with {@code
|
|
- * V_PREVIEW}.
|
|
- */
|
|
- int V_PREVIEW = 0xFFFF0000;
|
|
-
|
|
- // Access flags values, defined in
|
|
- // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1
|
|
- // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5-200-A.1
|
|
- // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6-200-A.1
|
|
- // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25
|
|
-
|
|
- int ACC_PUBLIC = 0x0001; // class, field, method
|
|
- int ACC_PRIVATE = 0x0002; // class, field, method
|
|
- int ACC_PROTECTED = 0x0004; // class, field, method
|
|
- int ACC_STATIC = 0x0008; // field, method
|
|
- int ACC_FINAL = 0x0010; // class, field, method, parameter
|
|
- int ACC_SUPER = 0x0020; // class
|
|
- int ACC_SYNCHRONIZED = 0x0020; // method
|
|
- int ACC_OPEN = 0x0020; // module
|
|
- int ACC_TRANSITIVE = 0x0020; // module requires
|
|
- int ACC_VOLATILE = 0x0040; // field
|
|
- int ACC_BRIDGE = 0x0040; // method
|
|
- int ACC_STATIC_PHASE = 0x0040; // module requires
|
|
- int ACC_VARARGS = 0x0080; // method
|
|
- int ACC_TRANSIENT = 0x0080; // field
|
|
- int ACC_NATIVE = 0x0100; // method
|
|
- int ACC_INTERFACE = 0x0200; // class
|
|
- int ACC_ABSTRACT = 0x0400; // class, method
|
|
- int ACC_STRICT = 0x0800; // method
|
|
- int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module *
|
|
- int ACC_ANNOTATION = 0x2000; // class
|
|
- int ACC_ENUM = 0x4000; // class(?) field inner
|
|
- int ACC_MANDATED = 0x8000; // parameter, module, module *
|
|
- int ACC_MODULE = 0x8000; // class
|
|
-
|
|
- // ASM specific access flags.
|
|
- // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard
|
|
- // access flags, and also to make sure that these flags are automatically filtered out when
|
|
- // written in class files (because access flags are stored using 16 bits only).
|
|
-
|
|
- int ACC_DEPRECATED = 0x20000; // class, field, method
|
|
-
|
|
- // Possible values for the type operand of the NEWARRAY instruction.
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray.
|
|
-
|
|
- int T_BOOLEAN = 4;
|
|
- int T_CHAR = 5;
|
|
- int T_FLOAT = 6;
|
|
- int T_DOUBLE = 7;
|
|
- int T_BYTE = 8;
|
|
- int T_SHORT = 9;
|
|
- int T_INT = 10;
|
|
- int T_LONG = 11;
|
|
-
|
|
- // Possible values for the reference_kind field of CONSTANT_MethodHandle_info structures.
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.8.
|
|
-
|
|
- int H_GETFIELD = 1;
|
|
- int H_GETSTATIC = 2;
|
|
- int H_PUTFIELD = 3;
|
|
- int H_PUTSTATIC = 4;
|
|
- int H_INVOKEVIRTUAL = 5;
|
|
- int H_INVOKESTATIC = 6;
|
|
- int H_INVOKESPECIAL = 7;
|
|
- int H_NEWINVOKESPECIAL = 8;
|
|
- int H_INVOKEINTERFACE = 9;
|
|
-
|
|
- // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}.
|
|
-
|
|
- /** An expanded frame. See {@link ClassReader#EXPAND_FRAMES}. */
|
|
- int F_NEW = -1;
|
|
-
|
|
- /** A compressed frame with complete frame data. */
|
|
- int F_FULL = 0;
|
|
-
|
|
- /**
|
|
- * A compressed frame where locals are the same as the locals in the previous frame, except that
|
|
- * additional 1-3 locals are defined, and with an empty stack.
|
|
- */
|
|
- int F_APPEND = 1;
|
|
-
|
|
- /**
|
|
- * A compressed frame where locals are the same as the locals in the previous frame, except that
|
|
- * the last 1-3 locals are absent and with an empty stack.
|
|
- */
|
|
- int F_CHOP = 2;
|
|
-
|
|
- /**
|
|
- * A compressed frame with exactly the same locals as the previous frame and with an empty stack.
|
|
- */
|
|
- int F_SAME = 3;
|
|
-
|
|
- /**
|
|
- * A compressed frame with exactly the same locals as the previous frame and with a single value
|
|
- * on the stack.
|
|
- */
|
|
- int F_SAME1 = 4;
|
|
-
|
|
- // Standard stack map frame element types, used in {@link ClassVisitor#visitFrame}.
|
|
-
|
|
- Integer TOP = Frame.ITEM_TOP;
|
|
- Integer INTEGER = Frame.ITEM_INTEGER;
|
|
- Integer FLOAT = Frame.ITEM_FLOAT;
|
|
- Integer DOUBLE = Frame.ITEM_DOUBLE;
|
|
- Integer LONG = Frame.ITEM_LONG;
|
|
- Integer NULL = Frame.ITEM_NULL;
|
|
- Integer UNINITIALIZED_THIS = Frame.ITEM_UNINITIALIZED_THIS;
|
|
-
|
|
- // The JVM opcode values (with the MethodVisitor method name used to visit them in comment, and
|
|
- // where '-' means 'same method name as on the previous line').
|
|
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html.
|
|
-
|
|
- int NOP = 0; // visitInsn
|
|
- int ACONST_NULL = 1; // -
|
|
- int ICONST_M1 = 2; // -
|
|
- int ICONST_0 = 3; // -
|
|
- int ICONST_1 = 4; // -
|
|
- int ICONST_2 = 5; // -
|
|
- int ICONST_3 = 6; // -
|
|
- int ICONST_4 = 7; // -
|
|
- int ICONST_5 = 8; // -
|
|
- int LCONST_0 = 9; // -
|
|
- int LCONST_1 = 10; // -
|
|
- int FCONST_0 = 11; // -
|
|
- int FCONST_1 = 12; // -
|
|
- int FCONST_2 = 13; // -
|
|
- int DCONST_0 = 14; // -
|
|
- int DCONST_1 = 15; // -
|
|
- int BIPUSH = 16; // visitIntInsn
|
|
- int SIPUSH = 17; // -
|
|
- int LDC = 18; // visitLdcInsn
|
|
- int ILOAD = 21; // visitVarInsn
|
|
- int LLOAD = 22; // -
|
|
- int FLOAD = 23; // -
|
|
- int DLOAD = 24; // -
|
|
- int ALOAD = 25; // -
|
|
- int IALOAD = 46; // visitInsn
|
|
- int LALOAD = 47; // -
|
|
- int FALOAD = 48; // -
|
|
- int DALOAD = 49; // -
|
|
- int AALOAD = 50; // -
|
|
- int BALOAD = 51; // -
|
|
- int CALOAD = 52; // -
|
|
- int SALOAD = 53; // -
|
|
- int ISTORE = 54; // visitVarInsn
|
|
- int LSTORE = 55; // -
|
|
- int FSTORE = 56; // -
|
|
- int DSTORE = 57; // -
|
|
- int ASTORE = 58; // -
|
|
- int IASTORE = 79; // visitInsn
|
|
- int LASTORE = 80; // -
|
|
- int FASTORE = 81; // -
|
|
- int DASTORE = 82; // -
|
|
- int AASTORE = 83; // -
|
|
- int BASTORE = 84; // -
|
|
- int CASTORE = 85; // -
|
|
- int SASTORE = 86; // -
|
|
- int POP = 87; // -
|
|
- int POP2 = 88; // -
|
|
- int DUP = 89; // -
|
|
- int DUP_X1 = 90; // -
|
|
- int DUP_X2 = 91; // -
|
|
- int DUP2 = 92; // -
|
|
- int DUP2_X1 = 93; // -
|
|
- int DUP2_X2 = 94; // -
|
|
- int SWAP = 95; // -
|
|
- int IADD = 96; // -
|
|
- int LADD = 97; // -
|
|
- int FADD = 98; // -
|
|
- int DADD = 99; // -
|
|
- int ISUB = 100; // -
|
|
- int LSUB = 101; // -
|
|
- int FSUB = 102; // -
|
|
- int DSUB = 103; // -
|
|
- int IMUL = 104; // -
|
|
- int LMUL = 105; // -
|
|
- int FMUL = 106; // -
|
|
- int DMUL = 107; // -
|
|
- int IDIV = 108; // -
|
|
- int LDIV = 109; // -
|
|
- int FDIV = 110; // -
|
|
- int DDIV = 111; // -
|
|
- int IREM = 112; // -
|
|
- int LREM = 113; // -
|
|
- int FREM = 114; // -
|
|
- int DREM = 115; // -
|
|
- int INEG = 116; // -
|
|
- int LNEG = 117; // -
|
|
- int FNEG = 118; // -
|
|
- int DNEG = 119; // -
|
|
- int ISHL = 120; // -
|
|
- int LSHL = 121; // -
|
|
- int ISHR = 122; // -
|
|
- int LSHR = 123; // -
|
|
- int IUSHR = 124; // -
|
|
- int LUSHR = 125; // -
|
|
- int IAND = 126; // -
|
|
- int LAND = 127; // -
|
|
- int IOR = 128; // -
|
|
- int LOR = 129; // -
|
|
- int IXOR = 130; // -
|
|
- int LXOR = 131; // -
|
|
- int IINC = 132; // visitIincInsn
|
|
- int I2L = 133; // visitInsn
|
|
- int I2F = 134; // -
|
|
- int I2D = 135; // -
|
|
- int L2I = 136; // -
|
|
- int L2F = 137; // -
|
|
- int L2D = 138; // -
|
|
- int F2I = 139; // -
|
|
- int F2L = 140; // -
|
|
- int F2D = 141; // -
|
|
- int D2I = 142; // -
|
|
- int D2L = 143; // -
|
|
- int D2F = 144; // -
|
|
- int I2B = 145; // -
|
|
- int I2C = 146; // -
|
|
- int I2S = 147; // -
|
|
- int LCMP = 148; // -
|
|
- int FCMPL = 149; // -
|
|
- int FCMPG = 150; // -
|
|
- int DCMPL = 151; // -
|
|
- int DCMPG = 152; // -
|
|
- int IFEQ = 153; // visitJumpInsn
|
|
- int IFNE = 154; // -
|
|
- int IFLT = 155; // -
|
|
- int IFGE = 156; // -
|
|
- int IFGT = 157; // -
|
|
- int IFLE = 158; // -
|
|
- int IF_ICMPEQ = 159; // -
|
|
- int IF_ICMPNE = 160; // -
|
|
- int IF_ICMPLT = 161; // -
|
|
- int IF_ICMPGE = 162; // -
|
|
- int IF_ICMPGT = 163; // -
|
|
- int IF_ICMPLE = 164; // -
|
|
- int IF_ACMPEQ = 165; // -
|
|
- int IF_ACMPNE = 166; // -
|
|
- int GOTO = 167; // -
|
|
- int JSR = 168; // -
|
|
- int RET = 169; // visitVarInsn
|
|
- int TABLESWITCH = 170; // visiTableSwitchInsn
|
|
- int LOOKUPSWITCH = 171; // visitLookupSwitch
|
|
- int IRETURN = 172; // visitInsn
|
|
- int LRETURN = 173; // -
|
|
- int FRETURN = 174; // -
|
|
- int DRETURN = 175; // -
|
|
- int ARETURN = 176; // -
|
|
- int RETURN = 177; // -
|
|
- int GETSTATIC = 178; // visitFieldInsn
|
|
- int PUTSTATIC = 179; // -
|
|
- int GETFIELD = 180; // -
|
|
- int PUTFIELD = 181; // -
|
|
- int INVOKEVIRTUAL = 182; // visitMethodInsn
|
|
- int INVOKESPECIAL = 183; // -
|
|
- int INVOKESTATIC = 184; // -
|
|
- int INVOKEINTERFACE = 185; // -
|
|
- int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
|
|
- int NEW = 187; // visitTypeInsn
|
|
- int NEWARRAY = 188; // visitIntInsn
|
|
- int ANEWARRAY = 189; // visitTypeInsn
|
|
- int ARRAYLENGTH = 190; // visitInsn
|
|
- int ATHROW = 191; // -
|
|
- int CHECKCAST = 192; // visitTypeInsn
|
|
- int INSTANCEOF = 193; // -
|
|
- int MONITORENTER = 194; // visitInsn
|
|
- int MONITOREXIT = 195; // -
|
|
- int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
|
|
- int IFNULL = 198; // visitJumpInsn
|
|
- int IFNONNULL = 199; // -
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Symbol.java b/src/main/java/org/mvel2/asm/Symbol.java
|
|
deleted file mode 100644
|
|
index 80a653c..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Symbol.java
|
|
+++ /dev/null
|
|
@@ -1,243 +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 entry of the constant pool, of the BootstrapMethods attribute, or of the (ASM specific) type
|
|
- * table of a class.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVMS
|
|
- * 4.4</a>
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
|
|
- * 4.7.23</a>
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-abstract class Symbol {
|
|
-
|
|
- // Tag values for the constant pool entries (using the same order as in the JVMS).
|
|
-
|
|
- /** The tag value of CONSTANT_Class_info JVMS structures. */
|
|
- static final int CONSTANT_CLASS_TAG = 7;
|
|
-
|
|
- /** The tag value of CONSTANT_Fieldref_info JVMS structures. */
|
|
- static final int CONSTANT_FIELDREF_TAG = 9;
|
|
-
|
|
- /** The tag value of CONSTANT_Methodref_info JVMS structures. */
|
|
- static final int CONSTANT_METHODREF_TAG = 10;
|
|
-
|
|
- /** The tag value of CONSTANT_InterfaceMethodref_info JVMS structures. */
|
|
- static final int CONSTANT_INTERFACE_METHODREF_TAG = 11;
|
|
-
|
|
- /** The tag value of CONSTANT_String_info JVMS structures. */
|
|
- static final int CONSTANT_STRING_TAG = 8;
|
|
-
|
|
- /** The tag value of CONSTANT_Integer_info JVMS structures. */
|
|
- static final int CONSTANT_INTEGER_TAG = 3;
|
|
-
|
|
- /** The tag value of CONSTANT_Float_info JVMS structures. */
|
|
- static final int CONSTANT_FLOAT_TAG = 4;
|
|
-
|
|
- /** The tag value of CONSTANT_Long_info JVMS structures. */
|
|
- static final int CONSTANT_LONG_TAG = 5;
|
|
-
|
|
- /** The tag value of CONSTANT_Double_info JVMS structures. */
|
|
- static final int CONSTANT_DOUBLE_TAG = 6;
|
|
-
|
|
- /** The tag value of CONSTANT_NameAndType_info JVMS structures. */
|
|
- static final int CONSTANT_NAME_AND_TYPE_TAG = 12;
|
|
-
|
|
- /** The tag value of CONSTANT_Utf8_info JVMS structures. */
|
|
- static final int CONSTANT_UTF8_TAG = 1;
|
|
-
|
|
- /** The tag value of CONSTANT_MethodHandle_info JVMS structures. */
|
|
- static final int CONSTANT_METHOD_HANDLE_TAG = 15;
|
|
-
|
|
- /** The tag value of CONSTANT_MethodType_info JVMS structures. */
|
|
- static final int CONSTANT_METHOD_TYPE_TAG = 16;
|
|
-
|
|
- /** The tag value of CONSTANT_Dynamic_info JVMS structures. */
|
|
- static final int CONSTANT_DYNAMIC_TAG = 17;
|
|
-
|
|
- /** The tag value of CONSTANT_InvokeDynamic_info JVMS structures. */
|
|
- static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18;
|
|
-
|
|
- /** The tag value of CONSTANT_Module_info JVMS structures. */
|
|
- static final int CONSTANT_MODULE_TAG = 19;
|
|
-
|
|
- /** The tag value of CONSTANT_Package_info JVMS structures. */
|
|
- static final int CONSTANT_PACKAGE_TAG = 20;
|
|
-
|
|
- // Tag values for the BootstrapMethods attribute entries (ASM specific tag).
|
|
-
|
|
- /** The tag value of the BootstrapMethods attribute entries. */
|
|
- static final int BOOTSTRAP_METHOD_TAG = 64;
|
|
-
|
|
- // Tag values for the type table entries (ASM specific tags).
|
|
-
|
|
- /** The tag value of a normal type entry in the (ASM specific) type table of a class. */
|
|
- static final int TYPE_TAG = 128;
|
|
-
|
|
- /**
|
|
- * The tag value of an {@link Frame#ITEM_UNINITIALIZED} type entry in the type table of a class.
|
|
- */
|
|
- static final int UNINITIALIZED_TYPE_TAG = 129;
|
|
-
|
|
- /** The tag value of a merged type entry in the (ASM specific) type table of a class. */
|
|
- static final int MERGED_TYPE_TAG = 130;
|
|
-
|
|
- // Instance fields.
|
|
-
|
|
- /**
|
|
- * The index of this symbol in the constant pool, in the BootstrapMethods attribute, or in the
|
|
- * (ASM specific) type table of a class (depending on the {@link #tag} value).
|
|
- */
|
|
- final int index;
|
|
-
|
|
- /**
|
|
- * A tag indicating the type of this symbol. Must be one of the static tag values defined in this
|
|
- * class.
|
|
- */
|
|
- final int tag;
|
|
-
|
|
- /**
|
|
- * The internal name of the owner class of this symbol. Only used for {@link
|
|
- * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link
|
|
- * #CONSTANT_INTERFACE_METHODREF_TAG}, and {@link #CONSTANT_METHOD_HANDLE_TAG} symbols.
|
|
- */
|
|
- final String owner;
|
|
-
|
|
- /**
|
|
- * The name of the class field or method corresponding to this symbol. Only used for {@link
|
|
- * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link
|
|
- * #CONSTANT_INTERFACE_METHODREF_TAG}, {@link #CONSTANT_NAME_AND_TYPE_TAG}, {@link
|
|
- * #CONSTANT_METHOD_HANDLE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link
|
|
- * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols.
|
|
- */
|
|
- final String name;
|
|
-
|
|
- /**
|
|
- * The string value of this symbol. This is:
|
|
- *
|
|
- * <ul>
|
|
- * <li>a field or method descriptor for {@link #CONSTANT_FIELDREF_TAG}, {@link
|
|
- * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG}, {@link
|
|
- * #CONSTANT_NAME_AND_TYPE_TAG}, {@link #CONSTANT_METHOD_HANDLE_TAG}, {@link
|
|
- * #CONSTANT_METHOD_TYPE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link
|
|
- * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
|
|
- * <li>an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG}
|
|
- * symbols,
|
|
- * <li>an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link
|
|
- * #UNINITIALIZED_TYPE_TAG} symbols,
|
|
- * <li>{@literal null} for the other types of symbol.
|
|
- * </ul>
|
|
- */
|
|
- final String value;
|
|
-
|
|
- /**
|
|
- * The numeric value of this symbol. This is:
|
|
- *
|
|
- * <ul>
|
|
- * <li>the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link
|
|
- * #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG},
|
|
- * <li>the CONSTANT_MethodHandle_info reference_kind field value for {@link
|
|
- * #CONSTANT_METHOD_HANDLE_TAG} symbols,
|
|
- * <li>the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link
|
|
- * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
|
|
- * <li>the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for
|
|
- * {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols,
|
|
- * <li>the bytecode offset of the NEW instruction that created an {@link
|
|
- * Frame#ITEM_UNINITIALIZED} type for {@link #UNINITIALIZED_TYPE_TAG} symbols,
|
|
- * <li>the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link
|
|
- * #MERGED_TYPE_TAG} symbols,
|
|
- * <li>0 for the other types of symbol.
|
|
- * </ul>
|
|
- */
|
|
- final long data;
|
|
-
|
|
- /**
|
|
- * Additional information about this symbol, generally computed lazily. <i>Warning: the value of
|
|
- * this field is ignored when comparing Symbol instances</i> (to avoid duplicate entries in a
|
|
- * SymbolTable). Therefore, this field should only contain data that can be computed from the
|
|
- * other fields of this class. It contains:
|
|
- *
|
|
- * <ul>
|
|
- * <li>the {@link Type#getArgumentsAndReturnSizes} of the symbol's method descriptor for {@link
|
|
- * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link
|
|
- * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
|
|
- * <li>the index in the InnerClasses_attribute 'classes' array (plus one) corresponding to this
|
|
- * class, for {@link #CONSTANT_CLASS_TAG} symbols,
|
|
- * <li>the index (in the class' type table) of the merged type of the two source types for
|
|
- * {@link #MERGED_TYPE_TAG} symbols,
|
|
- * <li>0 for the other types of symbol, or if this field has not been computed yet.
|
|
- * </ul>
|
|
- */
|
|
- int info;
|
|
-
|
|
- /**
|
|
- * Constructs a new Symbol. This constructor can't be used directly because the Symbol class is
|
|
- * abstract. Instead, use the factory methods of the {@link SymbolTable} class.
|
|
- *
|
|
- * @param index the symbol index in the constant pool, in the BootstrapMethods attribute, or in
|
|
- * the (ASM specific) type table of a class (depending on 'tag').
|
|
- * @param tag the symbol type. Must be one of the static tag values defined in this class.
|
|
- * @param owner The internal name of the symbol's owner class. Maybe {@literal null}.
|
|
- * @param name The name of the symbol's corresponding class field or method. Maybe {@literal
|
|
- * null}.
|
|
- * @param value The string value of this symbol. Maybe {@literal null}.
|
|
- * @param data The numeric value of this symbol.
|
|
- */
|
|
- Symbol(
|
|
- final int index,
|
|
- final int tag,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String value,
|
|
- final long data) {
|
|
- this.index = index;
|
|
- this.tag = tag;
|
|
- this.owner = owner;
|
|
- this.name = name;
|
|
- this.value = value;
|
|
- this.data = data;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the result {@link Type#getArgumentsAndReturnSizes} on {@link #value}.
|
|
- *
|
|
- * @return the result {@link Type#getArgumentsAndReturnSizes} on {@link #value} (memoized in
|
|
- * {@link #info} for efficiency). This should only be used for {@link
|
|
- * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link
|
|
- * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols.
|
|
- */
|
|
- int getArgumentsAndReturnSizes() {
|
|
- if (info == 0) {
|
|
- info = Type.getArgumentsAndReturnSizes(value);
|
|
- }
|
|
- return info;
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/SymbolTable.java b/src/main/java/org/mvel2/asm/SymbolTable.java
|
|
deleted file mode 100644
|
|
index 0d0ef63..0000000
|
|
--- a/src/main/java/org/mvel2/asm/SymbolTable.java
|
|
+++ /dev/null
|
|
@@ -1,1320 +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 org.mvel2.optimizers.OptimizationNotSupported;
|
|
-
|
|
-/**
|
|
- * The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type
|
|
- * table entries of a class.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVMS
|
|
- * 4.4</a>
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
|
|
- * 4.7.23</a>
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-final class SymbolTable {
|
|
-
|
|
- /**
|
|
- * The ClassWriter to which this SymbolTable belongs. This is only used to get access to {@link
|
|
- * ClassWriter#getCommonSuperClass} and to serialize custom attributes with {@link
|
|
- * Attribute#write}.
|
|
- */
|
|
- final ClassWriter classWriter;
|
|
-
|
|
- /**
|
|
- * The ClassReader from which this SymbolTable was constructed, or {@literal null} if it was
|
|
- * constructed from scratch.
|
|
- */
|
|
- private final ClassReader sourceClassReader;
|
|
-
|
|
- /** The major version number of the class to which this symbol table belongs. */
|
|
- private int majorVersion;
|
|
-
|
|
- /** The internal name of the class to which this symbol table belongs. */
|
|
- private String className;
|
|
-
|
|
- /**
|
|
- * The total number of {@link Entry} instances in {@link #entries}. This includes entries that are
|
|
- * accessible (recursively) via {@link Entry#next}.
|
|
- */
|
|
- private int entryCount;
|
|
-
|
|
- /**
|
|
- * A hash set of all the entries in this SymbolTable (this includes the constant pool entries, the
|
|
- * bootstrap method entries and the type table entries). Each {@link Entry} instance is stored at
|
|
- * the array index given by its hash code modulo the array size. If several entries must be stored
|
|
- * at the same array index, they are linked together via their {@link Entry#next} field. The
|
|
- * factory methods of this class make sure that this table does not contain duplicated entries.
|
|
- */
|
|
- private Entry[] entries;
|
|
-
|
|
- /**
|
|
- * The number of constant pool items in {@link #constantPool}, plus 1. The first constant pool
|
|
- * item has index 1, and long and double items count for two items.
|
|
- */
|
|
- private int constantPoolCount;
|
|
-
|
|
- /**
|
|
- * The content of the ClassFile's constant_pool JVMS structure corresponding to this SymbolTable.
|
|
- * The ClassFile's constant_pool_count field is <i>not</i> included.
|
|
- */
|
|
- private ByteVector constantPool;
|
|
-
|
|
- /**
|
|
- * The number of bootstrap methods in {@link #bootstrapMethods}. Corresponds to the
|
|
- * BootstrapMethods_attribute's num_bootstrap_methods field value.
|
|
- */
|
|
- private int bootstrapMethodCount;
|
|
-
|
|
- /**
|
|
- * The content of the BootstrapMethods attribute 'bootstrap_methods' array corresponding to this
|
|
- * SymbolTable. Note that the first 6 bytes of the BootstrapMethods_attribute, and its
|
|
- * num_bootstrap_methods field, are <i>not</i> included.
|
|
- */
|
|
- private ByteVector bootstrapMethods;
|
|
-
|
|
- /**
|
|
- * The actual number of elements in {@link #typeTable}. These elements are stored from index 0 to
|
|
- * typeCount (excluded). The other array entries are empty.
|
|
- */
|
|
- private int typeCount;
|
|
-
|
|
- /**
|
|
- * An ASM specific type table used to temporarily store internal names that will not necessarily
|
|
- * be stored in the constant pool. This type table is used by the control flow and data flow
|
|
- * analysis algorithm used to compute stack map frames from scratch. This array stores {@link
|
|
- * Symbol#TYPE_TAG} and {@link Symbol#UNINITIALIZED_TYPE_TAG}) Symbol. The type symbol at index
|
|
- * {@code i} has its {@link Symbol#index} equal to {@code i} (and vice versa).
|
|
- */
|
|
- private Entry[] typeTable;
|
|
-
|
|
- /**
|
|
- * Constructs a new, empty SymbolTable for the given ClassWriter.
|
|
- *
|
|
- * @param classWriter a ClassWriter.
|
|
- */
|
|
- SymbolTable(final ClassWriter classWriter) {
|
|
- this.classWriter = classWriter;
|
|
- this.sourceClassReader = null;
|
|
- this.entries = new Entry[256];
|
|
- this.constantPoolCount = 1;
|
|
- this.constantPool = new ByteVector();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Constructs a new SymbolTable for the given ClassWriter, initialized with the constant pool and
|
|
- * bootstrap methods of the given ClassReader.
|
|
- *
|
|
- * @param classWriter a ClassWriter.
|
|
- * @param classReader the ClassReader whose constant pool and bootstrap methods must be copied to
|
|
- * initialize the SymbolTable.
|
|
- */
|
|
- SymbolTable(final ClassWriter classWriter, final ClassReader classReader) {
|
|
- this.classWriter = classWriter;
|
|
- this.sourceClassReader = classReader;
|
|
-
|
|
- // Copy the constant pool binary content.
|
|
- byte[] inputBytes = classReader.b;
|
|
- int constantPoolOffset = classReader.getItem(1) - 1;
|
|
- int constantPoolLength = classReader.header - constantPoolOffset;
|
|
- constantPoolCount = classReader.getItemCount();
|
|
- constantPool = new ByteVector(constantPoolLength);
|
|
- constantPool.putByteArray(inputBytes, constantPoolOffset, constantPoolLength);
|
|
-
|
|
- // Add the constant pool items in the symbol table entries. Reserve enough space in 'entries' to
|
|
- // avoid too many hash set collisions (entries is not dynamically resized by the addConstant*
|
|
- // method calls below), and to account for bootstrap method entries.
|
|
- entries = new Entry[constantPoolCount * 2];
|
|
- char[] charBuffer = new char[classReader.getMaxStringLength()];
|
|
- boolean hasBootstrapMethods = false;
|
|
- int itemIndex = 1;
|
|
- while (itemIndex < constantPoolCount) {
|
|
- int itemOffset = classReader.getItem(itemIndex);
|
|
- int itemTag = inputBytes[itemOffset - 1];
|
|
- int nameAndTypeItemOffset;
|
|
- switch (itemTag) {
|
|
- case Symbol.CONSTANT_FIELDREF_TAG:
|
|
- case Symbol.CONSTANT_METHODREF_TAG:
|
|
- case Symbol.CONSTANT_INTERFACE_METHODREF_TAG:
|
|
- nameAndTypeItemOffset =
|
|
- classReader.getItem(classReader.readUnsignedShort(itemOffset + 2));
|
|
- addConstantMemberReference(
|
|
- itemIndex,
|
|
- itemTag,
|
|
- classReader.readClass(itemOffset, charBuffer),
|
|
- classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
|
|
- classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer));
|
|
- break;
|
|
- case Symbol.CONSTANT_INTEGER_TAG:
|
|
- case Symbol.CONSTANT_FLOAT_TAG:
|
|
- addConstantIntegerOrFloat(itemIndex, itemTag, classReader.readInt(itemOffset));
|
|
- break;
|
|
- case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
|
|
- addConstantNameAndType(
|
|
- itemIndex,
|
|
- classReader.readUTF8(itemOffset, charBuffer),
|
|
- classReader.readUTF8(itemOffset + 2, charBuffer));
|
|
- break;
|
|
- case Symbol.CONSTANT_LONG_TAG:
|
|
- case Symbol.CONSTANT_DOUBLE_TAG:
|
|
- addConstantLongOrDouble(itemIndex, itemTag, classReader.readLong(itemOffset));
|
|
- break;
|
|
- case Symbol.CONSTANT_UTF8_TAG:
|
|
- addConstantUtf8(itemIndex, classReader.readUtf(itemIndex, charBuffer));
|
|
- break;
|
|
- case Symbol.CONSTANT_METHOD_HANDLE_TAG:
|
|
- int memberRefItemOffset =
|
|
- classReader.getItem(classReader.readUnsignedShort(itemOffset + 1));
|
|
- nameAndTypeItemOffset =
|
|
- classReader.getItem(classReader.readUnsignedShort(memberRefItemOffset + 2));
|
|
- addConstantMethodHandle(
|
|
- itemIndex,
|
|
- classReader.readByte(itemOffset),
|
|
- classReader.readClass(memberRefItemOffset, charBuffer),
|
|
- classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
|
|
- classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer));
|
|
- break;
|
|
- case Symbol.CONSTANT_DYNAMIC_TAG:
|
|
- case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
|
|
- hasBootstrapMethods = true;
|
|
- nameAndTypeItemOffset =
|
|
- classReader.getItem(classReader.readUnsignedShort(itemOffset + 2));
|
|
- addConstantDynamicOrInvokeDynamicReference(
|
|
- itemTag,
|
|
- itemIndex,
|
|
- classReader.readUTF8(nameAndTypeItemOffset, charBuffer),
|
|
- classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer),
|
|
- classReader.readUnsignedShort(itemOffset));
|
|
- break;
|
|
- case Symbol.CONSTANT_STRING_TAG:
|
|
- case Symbol.CONSTANT_CLASS_TAG:
|
|
- case Symbol.CONSTANT_METHOD_TYPE_TAG:
|
|
- case Symbol.CONSTANT_MODULE_TAG:
|
|
- case Symbol.CONSTANT_PACKAGE_TAG:
|
|
- addConstantUtf8Reference(
|
|
- itemIndex, itemTag, classReader.readUTF8(itemOffset, charBuffer));
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- itemIndex +=
|
|
- (itemTag == Symbol.CONSTANT_LONG_TAG || itemTag == Symbol.CONSTANT_DOUBLE_TAG) ? 2 : 1;
|
|
- }
|
|
-
|
|
- // Copy the BootstrapMethods, if any.
|
|
- if (hasBootstrapMethods) {
|
|
- copyBootstrapMethods(classReader, charBuffer);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Read the BootstrapMethods 'bootstrap_methods' array binary content and add them as entries of
|
|
- * the SymbolTable.
|
|
- *
|
|
- * @param classReader the ClassReader whose bootstrap methods must be copied to initialize the
|
|
- * SymbolTable.
|
|
- * @param charBuffer a buffer used to read strings in the constant pool.
|
|
- */
|
|
- private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
|
|
- // Find attributOffset of the 'bootstrap_methods' array.
|
|
- byte[] inputBytes = classReader.b;
|
|
- int currentAttributeOffset = classReader.getFirstAttributeOffset();
|
|
- for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
|
|
- String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
|
|
- if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
|
|
- bootstrapMethodCount = classReader.readUnsignedShort(currentAttributeOffset + 6);
|
|
- break;
|
|
- }
|
|
- currentAttributeOffset += 6 + classReader.readInt(currentAttributeOffset + 2);
|
|
- }
|
|
- if (bootstrapMethodCount > 0) {
|
|
- // Compute the offset and the length of the BootstrapMethods 'bootstrap_methods' array.
|
|
- int bootstrapMethodsOffset = currentAttributeOffset + 8;
|
|
- int bootstrapMethodsLength = classReader.readInt(currentAttributeOffset + 2) - 2;
|
|
- bootstrapMethods = new ByteVector(bootstrapMethodsLength);
|
|
- bootstrapMethods.putByteArray(inputBytes, bootstrapMethodsOffset, bootstrapMethodsLength);
|
|
-
|
|
- // Add each bootstrap method in the symbol table entries.
|
|
- int currentOffset = bootstrapMethodsOffset;
|
|
- for (int i = 0; i < bootstrapMethodCount; i++) {
|
|
- int offset = currentOffset - bootstrapMethodsOffset;
|
|
- int bootstrapMethodRef = classReader.readUnsignedShort(currentOffset);
|
|
- currentOffset += 2;
|
|
- int numBootstrapArguments = classReader.readUnsignedShort(currentOffset);
|
|
- currentOffset += 2;
|
|
- int hashCode = classReader.readConst(bootstrapMethodRef, charBuffer).hashCode();
|
|
- while (numBootstrapArguments-- > 0) {
|
|
- int bootstrapArgument = classReader.readUnsignedShort(currentOffset);
|
|
- currentOffset += 2;
|
|
- hashCode ^= classReader.readConst(bootstrapArgument, charBuffer).hashCode();
|
|
- }
|
|
- add(new Entry(i, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode & 0x7FFFFFFF));
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the ClassReader from which this SymbolTable was constructed.
|
|
- *
|
|
- * @return the ClassReader from which this SymbolTable was constructed, or {@literal null} if it
|
|
- * was constructed from scratch.
|
|
- */
|
|
- ClassReader getSource() {
|
|
- return sourceClassReader;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the major version of the class to which this symbol table belongs.
|
|
- *
|
|
- * @return the major version of the class to which this symbol table belongs.
|
|
- */
|
|
- int getMajorVersion() {
|
|
- return majorVersion;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the internal name of the class to which this symbol table belongs.
|
|
- *
|
|
- * @return the internal name of the class to which this symbol table belongs.
|
|
- */
|
|
- String getClassName() {
|
|
- return className;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the major version and the name of the class to which this symbol table belongs. Also adds
|
|
- * the class name to the constant pool.
|
|
- *
|
|
- * @param majorVersion a major ClassFile version number.
|
|
- * @param className an internal class name.
|
|
- * @return the constant pool index of a new or already existing Symbol with the given class name.
|
|
- */
|
|
- int setMajorVersionAndClassName(final int majorVersion, final String className) {
|
|
- this.majorVersion = majorVersion;
|
|
- this.className = className;
|
|
- return addConstantClass(className).index;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of items in this symbol table's constant_pool array (plus 1).
|
|
- *
|
|
- * @return the number of items in this symbol table's constant_pool array (plus 1).
|
|
- */
|
|
- int getConstantPoolCount() {
|
|
- return constantPoolCount;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the length in bytes of this symbol table's constant_pool array.
|
|
- *
|
|
- * @return the length in bytes of this symbol table's constant_pool array.
|
|
- */
|
|
- int getConstantPoolLength() {
|
|
- return constantPool.length;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts this symbol table's constant_pool array in the given ByteVector, preceded by the
|
|
- * constant_pool_count value.
|
|
- *
|
|
- * @param output where the JVMS ClassFile's constant_pool array must be put.
|
|
- */
|
|
- void putConstantPool(final ByteVector output) {
|
|
- output.putShort(constantPoolCount).putByteArray(constantPool.data, 0, constantPool.length);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size in bytes of this symbol table's BootstrapMethods attribute. Also adds the
|
|
- * attribute name in the constant pool.
|
|
- *
|
|
- * @return the size in bytes of this symbol table's BootstrapMethods attribute.
|
|
- */
|
|
- int computeBootstrapMethodsSize() {
|
|
- if (bootstrapMethods != null) {
|
|
- addConstantUtf8(Constants.BOOTSTRAP_METHODS);
|
|
- return 8 + bootstrapMethods.length;
|
|
- } else {
|
|
- return 0;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts this symbol table's BootstrapMethods attribute in the given ByteVector. This includes the
|
|
- * 6 attribute header bytes and the num_bootstrap_methods value.
|
|
- *
|
|
- * @param output where the JVMS BootstrapMethods attribute must be put.
|
|
- */
|
|
- void putBootstrapMethods(final ByteVector output) {
|
|
- if (bootstrapMethods != null) {
|
|
- output
|
|
- .putShort(addConstantUtf8(Constants.BOOTSTRAP_METHODS))
|
|
- .putInt(bootstrapMethods.length + 2)
|
|
- .putShort(bootstrapMethodCount)
|
|
- .putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length);
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Generic symbol table entries management.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the list of entries which can potentially have the given hash code.
|
|
- *
|
|
- * @param hashCode a {@link Entry#hashCode} value.
|
|
- * @return the list of entries which can potentially have the given hash code. The list is stored
|
|
- * via the {@link Entry#next} field.
|
|
- */
|
|
- private Entry get(final int hashCode) {
|
|
- return entries[hashCode % entries.length];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the given entry in the {@link #entries} hash set. This method does <i>not</i> check
|
|
- * whether {@link #entries} already contains a similar entry or not. {@link #entries} is resized
|
|
- * if necessary to avoid hash collisions (multiple entries needing to be stored at the same {@link
|
|
- * #entries} array index) as much as possible, with reasonable memory usage.
|
|
- *
|
|
- * @param entry an Entry (which must not already be contained in {@link #entries}).
|
|
- * @return the given entry
|
|
- */
|
|
- private Entry put(final Entry entry) {
|
|
- if (entryCount > (entries.length * 3) / 4) {
|
|
- int currentCapacity = entries.length;
|
|
- int newCapacity = currentCapacity * 2 + 1;
|
|
- Entry[] newEntries = new Entry[newCapacity];
|
|
- for (int i = currentCapacity - 1; i >= 0; --i) {
|
|
- Entry currentEntry = entries[i];
|
|
- while (currentEntry != null) {
|
|
- int newCurrentEntryIndex = currentEntry.hashCode % newCapacity;
|
|
- Entry nextEntry = currentEntry.next;
|
|
- currentEntry.next = newEntries[newCurrentEntryIndex];
|
|
- newEntries[newCurrentEntryIndex] = currentEntry;
|
|
- currentEntry = nextEntry;
|
|
- }
|
|
- }
|
|
- entries = newEntries;
|
|
- }
|
|
- entryCount++;
|
|
- int index = entry.hashCode % entries.length;
|
|
- entry.next = entries[index];
|
|
- return entries[index] = entry;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds the given entry in the {@link #entries} hash set. This method does <i>not</i> check
|
|
- * whether {@link #entries} already contains a similar entry or not, and does <i>not</i> resize
|
|
- * {@link #entries} if necessary.
|
|
- *
|
|
- * @param entry an Entry (which must not already be contained in {@link #entries}).
|
|
- */
|
|
- private void add(final Entry entry) {
|
|
- entryCount++;
|
|
- int index = entry.hashCode % entries.length;
|
|
- entry.next = entries[index];
|
|
- entries[index] = entry;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Constant pool entries management.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Adds a number or string constant to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value the value of the constant to be added to the constant pool. This parameter must be
|
|
- * an {@link Integer}, {@link Byte}, {@link Character}, {@link Short}, {@link Boolean}, {@link
|
|
- * Float}, {@link Long}, {@link Double}, {@link String}, {@link Type} or {@link Handle}.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstant(final Object value) {
|
|
- if (value instanceof Integer) {
|
|
- return addConstantInteger(((Integer) value).intValue());
|
|
- } else if (value instanceof Byte) {
|
|
- return addConstantInteger(((Byte) value).intValue());
|
|
- } else if (value instanceof Character) {
|
|
- return addConstantInteger(((Character) value).charValue());
|
|
- } else if (value instanceof Short) {
|
|
- return addConstantInteger(((Short) value).intValue());
|
|
- } else if (value instanceof Boolean) {
|
|
- return addConstantInteger(((Boolean) value).booleanValue() ? 1 : 0);
|
|
- } else if (value instanceof Float) {
|
|
- return addConstantFloat(((Float) value).floatValue());
|
|
- } else if (value instanceof Long) {
|
|
- return addConstantLong(((Long) value).longValue());
|
|
- } else if (value instanceof Double) {
|
|
- return addConstantDouble(((Double) value).doubleValue());
|
|
- } else if (value instanceof String) {
|
|
- return addConstantString((String) value);
|
|
- } else if (value instanceof Type) {
|
|
- Type type = (Type) value;
|
|
- int typeSort = type.getSort();
|
|
- if (typeSort == Type.OBJECT) {
|
|
- return addConstantClass(type.getInternalName());
|
|
- } else if (typeSort == Type.METHOD) {
|
|
- return addConstantMethodType(type.getDescriptor());
|
|
- } else { // type is a primitive or array type.
|
|
- return addConstantClass(type.getDescriptor());
|
|
- }
|
|
- } else if (value instanceof Handle) {
|
|
- Handle handle = (Handle) value;
|
|
- return addConstantMethodHandle(
|
|
- handle.getTag(),
|
|
- handle.getOwner(),
|
|
- handle.getName(),
|
|
- handle.getDesc(),
|
|
- handle.isInterface());
|
|
- } else if (value instanceof ConstantDynamic) {
|
|
- ConstantDynamic constantDynamic = (ConstantDynamic) value;
|
|
- return addConstantDynamic(
|
|
- constantDynamic.getName(),
|
|
- constantDynamic.getDescriptor(),
|
|
- constantDynamic.getBootstrapMethod(),
|
|
- constantDynamic.getBootstrapMethodArgumentsUnsafe());
|
|
- } else {
|
|
- throw new OptimizationNotSupported();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Class_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value the internal name of a class.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantClass(final String value) {
|
|
- return addConstantUtf8Reference(Symbol.CONSTANT_CLASS_TAG, value);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Fieldref_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param owner the internal name of a class.
|
|
- * @param name a field name.
|
|
- * @param descriptor a field descriptor.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantFieldref(final String owner, final String name, final String descriptor) {
|
|
- return addConstantMemberReference(Symbol.CONSTANT_FIELDREF_TAG, owner, name, descriptor);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to the constant pool of this
|
|
- * symbol table. Does nothing if the constant pool already contains a similar item.
|
|
- *
|
|
- * @param owner the internal name of a class.
|
|
- * @param name a method name.
|
|
- * @param descriptor a method descriptor.
|
|
- * @param isInterface whether owner is an interface or not.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantMethodref(
|
|
- final String owner, final String name, final String descriptor, final boolean isInterface) {
|
|
- int tag = isInterface ? Symbol.CONSTANT_INTERFACE_METHODREF_TAG : Symbol.CONSTANT_METHODREF_TAG;
|
|
- return addConstantMemberReference(tag, owner, name, descriptor);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to
|
|
- * the constant pool of this symbol table. Does nothing if the constant pool already contains a
|
|
- * similar item.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG}
|
|
- * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}.
|
|
- * @param owner the internal name of a class.
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- private Entry addConstantMemberReference(
|
|
- final int tag, final String owner, final String name, final String descriptor) {
|
|
- int hashCode = hash(tag, owner, name, descriptor);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag
|
|
- && entry.hashCode == hashCode
|
|
- && entry.owner.equals(owner)
|
|
- && entry.name.equals(name)
|
|
- && entry.value.equals(descriptor)) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.put122(
|
|
- tag, addConstantClass(owner).index, addConstantNameAndType(name, descriptor));
|
|
- return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, 0, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info
|
|
- * to the constant pool of this symbol table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG}
|
|
- * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}.
|
|
- * @param owner the internal name of a class.
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- */
|
|
- private void addConstantMemberReference(
|
|
- final int index,
|
|
- final int tag,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor) {
|
|
- add(new Entry(index, tag, owner, name, descriptor, 0, hash(tag, owner, name, descriptor)));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_String_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value a string.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantString(final String value) {
|
|
- return addConstantUtf8Reference(Symbol.CONSTANT_STRING_TAG, value);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Integer_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value an int.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantInteger(final int value) {
|
|
- return addConstantIntegerOrFloat(Symbol.CONSTANT_INTEGER_TAG, value);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Float_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value a float.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantFloat(final float value) {
|
|
- return addConstantIntegerOrFloat(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol table.
|
|
- * Does nothing if the constant pool already contains a similar item.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}.
|
|
- * @param value an int or float.
|
|
- * @return a constant pool constant with the given tag and primitive values.
|
|
- */
|
|
- private Symbol addConstantIntegerOrFloat(final int tag, final int value) {
|
|
- int hashCode = hash(tag, value);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.putByte(tag).putInt(value);
|
|
- return put(new Entry(constantPoolCount++, tag, value, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol
|
|
- * table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}.
|
|
- * @param value an int or float.
|
|
- */
|
|
- private void addConstantIntegerOrFloat(final int index, final int tag, final int value) {
|
|
- add(new Entry(index, tag, value, hash(tag, value)));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Long_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value a long.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantLong(final long value) {
|
|
- return addConstantLongOrDouble(Symbol.CONSTANT_LONG_TAG, value);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Double_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value a double.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantDouble(final double value) {
|
|
- return addConstantLongOrDouble(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol table.
|
|
- * Does nothing if the constant pool already contains a similar item.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}.
|
|
- * @param value a long or double.
|
|
- * @return a constant pool constant with the given tag and primitive values.
|
|
- */
|
|
- private Symbol addConstantLongOrDouble(final int tag, final long value) {
|
|
- int hashCode = hash(tag, value);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- int index = constantPoolCount;
|
|
- constantPool.putByte(tag).putLong(value);
|
|
- constantPoolCount += 2;
|
|
- return put(new Entry(index, tag, value, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol
|
|
- * table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}.
|
|
- * @param value a long or double.
|
|
- */
|
|
- private void addConstantLongOrDouble(final int index, final int tag, final long value) {
|
|
- add(new Entry(index, tag, value, hash(tag, value)));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_NameAndType_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- int addConstantNameAndType(final String name, final String descriptor) {
|
|
- final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG;
|
|
- int hashCode = hash(tag, name, descriptor);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag
|
|
- && entry.hashCode == hashCode
|
|
- && entry.name.equals(name)
|
|
- && entry.value.equals(descriptor)) {
|
|
- return entry.index;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.put122(tag, addConstantUtf8(name), addConstantUtf8(descriptor));
|
|
- return put(new Entry(constantPoolCount++, tag, name, descriptor, hashCode)).index;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_NameAndType_info to the constant pool of this symbol table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- */
|
|
- private void addConstantNameAndType(final int index, final String name, final String descriptor) {
|
|
- final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG;
|
|
- add(new Entry(index, tag, name, descriptor, hash(tag, name, descriptor)));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Utf8_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param value a string.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- int addConstantUtf8(final String value) {
|
|
- int hashCode = hash(Symbol.CONSTANT_UTF8_TAG, value);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == Symbol.CONSTANT_UTF8_TAG
|
|
- && entry.hashCode == hashCode
|
|
- && entry.value.equals(value)) {
|
|
- return entry.index;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.putByte(Symbol.CONSTANT_UTF8_TAG).putUTF8(value);
|
|
- return put(new Entry(constantPoolCount++, Symbol.CONSTANT_UTF8_TAG, value, hashCode)).index;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_String_info to the constant pool of this symbol table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param value a string.
|
|
- */
|
|
- private void addConstantUtf8(final int index, final String value) {
|
|
- add(new Entry(index, Symbol.CONSTANT_UTF8_TAG, value, hash(Symbol.CONSTANT_UTF8_TAG, value)));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_MethodHandle_info to the constant pool of this symbol table. Does nothing if
|
|
- * the constant pool already contains a similar item.
|
|
- *
|
|
- * @param referenceKind one of {@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 a class of interface.
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- * @param isInterface whether owner is an interface or not.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantMethodHandle(
|
|
- final int referenceKind,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final boolean isInterface) {
|
|
- final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG;
|
|
- // Note that we don't need to include isInterface in the hash computation, because it is
|
|
- // redundant with owner (we can't have the same owner with different isInterface values).
|
|
- int hashCode = hash(tag, owner, name, descriptor, referenceKind);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag
|
|
- && entry.hashCode == hashCode
|
|
- && entry.data == referenceKind
|
|
- && entry.owner.equals(owner)
|
|
- && entry.name.equals(name)
|
|
- && entry.value.equals(descriptor)) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- if (referenceKind <= Opcodes.H_PUTSTATIC) {
|
|
- constantPool.put112(tag, referenceKind, addConstantFieldref(owner, name, descriptor).index);
|
|
- } else {
|
|
- constantPool.put112(
|
|
- tag, referenceKind, addConstantMethodref(owner, name, descriptor, isInterface).index);
|
|
- }
|
|
- return put(
|
|
- new Entry(constantPoolCount++, tag, owner, name, descriptor, referenceKind, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_MethodHandle_info to the constant pool of this symbol table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param referenceKind one of {@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 a class of interface.
|
|
- * @param name a field or method name.
|
|
- * @param descriptor a field or method descriptor.
|
|
- */
|
|
- private void addConstantMethodHandle(
|
|
- final int index,
|
|
- final int referenceKind,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String descriptor) {
|
|
- final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG;
|
|
- int hashCode = hash(tag, owner, name, descriptor, referenceKind);
|
|
- add(new Entry(index, tag, owner, name, descriptor, referenceKind, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_MethodType_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param methodDescriptor a method descriptor.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantMethodType(final String methodDescriptor) {
|
|
- return addConstantUtf8Reference(Symbol.CONSTANT_METHOD_TYPE_TAG, methodDescriptor);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Dynamic_info to the constant pool of this symbol table. Also adds the related
|
|
- * bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the constant
|
|
- * pool already contains a similar item.
|
|
- *
|
|
- * @param name a method name.
|
|
- * @param descriptor a field descriptor.
|
|
- * @param bootstrapMethodHandle a bootstrap method handle.
|
|
- * @param bootstrapMethodArguments the bootstrap method arguments.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantDynamic(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final Handle bootstrapMethodHandle,
|
|
- final Object... bootstrapMethodArguments) {
|
|
- Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments);
|
|
- return addConstantDynamicOrInvokeDynamicReference(
|
|
- Symbol.CONSTANT_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Also adds the
|
|
- * related bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param name a method name.
|
|
- * @param descriptor a method descriptor.
|
|
- * @param bootstrapMethodHandle a bootstrap method handle.
|
|
- * @param bootstrapMethodArguments the bootstrap method arguments.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantInvokeDynamic(
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final Handle bootstrapMethodHandle,
|
|
- final Object... bootstrapMethodArguments) {
|
|
- Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments);
|
|
- return addConstantDynamicOrInvokeDynamicReference(
|
|
- Symbol.CONSTANT_INVOKE_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Dynamic or a CONSTANT_InvokeDynamic_info to the constant pool of this symbol
|
|
- * table. Does nothing if the constant pool already contains a similar item.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link
|
|
- * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}.
|
|
- * @param name a method name.
|
|
- * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for
|
|
- * CONSTANT_INVOKE_DYNAMIC_TAG.
|
|
- * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- private Symbol addConstantDynamicOrInvokeDynamicReference(
|
|
- final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) {
|
|
- int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag
|
|
- && entry.hashCode == hashCode
|
|
- && entry.data == bootstrapMethodIndex
|
|
- && entry.name.equals(name)
|
|
- && entry.value.equals(descriptor)) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.put122(tag, bootstrapMethodIndex, addConstantNameAndType(name, descriptor));
|
|
- return put(
|
|
- new Entry(
|
|
- constantPoolCount++, tag, null, name, descriptor, bootstrapMethodIndex, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_Dynamic_info or CONSTANT_InvokeDynamic_info to the constant pool of this
|
|
- * symbol table.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link
|
|
- * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}.
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param name a method name.
|
|
- * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG or a method descriptor for
|
|
- * CONSTANT_INVOKE_DYNAMIC_TAG.
|
|
- * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute.
|
|
- */
|
|
- private void addConstantDynamicOrInvokeDynamicReference(
|
|
- final int tag,
|
|
- final int index,
|
|
- final String name,
|
|
- final String descriptor,
|
|
- final int bootstrapMethodIndex) {
|
|
- int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex);
|
|
- add(new Entry(index, tag, null, name, descriptor, bootstrapMethodIndex, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Module_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param moduleName a fully qualified name (using dots) of a module.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantModule(final String moduleName) {
|
|
- return addConstantUtf8Reference(Symbol.CONSTANT_MODULE_TAG, moduleName);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Package_info to the constant pool of this symbol table. Does nothing if the
|
|
- * constant pool already contains a similar item.
|
|
- *
|
|
- * @param packageName the internal name of a package.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addConstantPackage(final String packageName) {
|
|
- return addConstantUtf8Reference(Symbol.CONSTANT_PACKAGE_TAG, packageName);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info,
|
|
- * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. Does
|
|
- * nothing if the constant pool already contains a similar item.
|
|
- *
|
|
- * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link
|
|
- * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link
|
|
- * Symbol#CONSTANT_PACKAGE_TAG}.
|
|
- * @param value an internal class name, an arbitrary string, a method descriptor, a module or a
|
|
- * package name, depending on tag.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- private Symbol addConstantUtf8Reference(final int tag, final String value) {
|
|
- int hashCode = hash(tag, value);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == tag && entry.hashCode == hashCode && entry.value.equals(value)) {
|
|
- return entry;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- constantPool.put12(tag, addConstantUtf8(value));
|
|
- return put(new Entry(constantPoolCount++, tag, value, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a new CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info,
|
|
- * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table.
|
|
- *
|
|
- * @param index the constant pool index of the new Symbol.
|
|
- * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link
|
|
- * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link
|
|
- * Symbol#CONSTANT_PACKAGE_TAG}.
|
|
- * @param value an internal class name, an arbitrary string, a method descriptor, a module or a
|
|
- * package name, depending on tag.
|
|
- */
|
|
- private void addConstantUtf8Reference(final int index, final int tag, final String value) {
|
|
- add(new Entry(index, tag, value, hash(tag, value)));
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Bootstrap method entries management.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if
|
|
- * the BootstrapMethods already contains a similar bootstrap method.
|
|
- *
|
|
- * @param bootstrapMethodHandle a bootstrap method handle.
|
|
- * @param bootstrapMethodArguments the bootstrap method arguments.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- Symbol addBootstrapMethod(
|
|
- final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) {
|
|
- ByteVector bootstrapMethodsAttribute = bootstrapMethods;
|
|
- if (bootstrapMethodsAttribute == null) {
|
|
- bootstrapMethodsAttribute = bootstrapMethods = new ByteVector();
|
|
- }
|
|
-
|
|
- // The bootstrap method arguments can be Constant_Dynamic values, which reference other
|
|
- // bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool
|
|
- // and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified
|
|
- // while adding the given bootstrap method to it, in the rest of this method.
|
|
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
|
|
- addConstant(bootstrapMethodArgument);
|
|
- }
|
|
-
|
|
- // Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
|
|
- // compare it with existing ones, and will be reverted below if there is already a similar
|
|
- // bootstrap method.
|
|
- int bootstrapMethodOffset = bootstrapMethodsAttribute.length;
|
|
- bootstrapMethodsAttribute.putShort(
|
|
- addConstantMethodHandle(
|
|
- bootstrapMethodHandle.getTag(),
|
|
- bootstrapMethodHandle.getOwner(),
|
|
- bootstrapMethodHandle.getName(),
|
|
- bootstrapMethodHandle.getDesc(),
|
|
- bootstrapMethodHandle.isInterface())
|
|
- .index);
|
|
- int numBootstrapArguments = bootstrapMethodArguments.length;
|
|
- bootstrapMethodsAttribute.putShort(numBootstrapArguments);
|
|
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
|
|
- bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
|
|
- }
|
|
-
|
|
- // Compute the length and the hash code of the bootstrap method.
|
|
- int bootstrapMethodlength = bootstrapMethodsAttribute.length - bootstrapMethodOffset;
|
|
- int hashCode = bootstrapMethodHandle.hashCode();
|
|
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
|
|
- hashCode ^= bootstrapMethodArgument.hashCode();
|
|
- }
|
|
- hashCode &= 0x7FFFFFFF;
|
|
-
|
|
- // Add the bootstrap method to the symbol table or revert the above changes.
|
|
- return addBootstrapMethod(bootstrapMethodOffset, bootstrapMethodlength, hashCode);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if
|
|
- * the BootstrapMethods already contains a similar bootstrap method (more precisely, reverts the
|
|
- * content of {@link #bootstrapMethods} to remove the last, duplicate bootstrap method).
|
|
- *
|
|
- * @param offset the offset of the last bootstrap method in {@link #bootstrapMethods}, in bytes.
|
|
- * @param length the length of this bootstrap method in {@link #bootstrapMethods}, in bytes.
|
|
- * @param hashCode the hash code of this bootstrap method.
|
|
- * @return a new or already existing Symbol with the given value.
|
|
- */
|
|
- private Symbol addBootstrapMethod(final int offset, final int length, final int hashCode) {
|
|
- final byte[] bootstrapMethodsData = bootstrapMethods.data;
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == Symbol.BOOTSTRAP_METHOD_TAG && entry.hashCode == hashCode) {
|
|
- int otherOffset = (int) entry.data;
|
|
- boolean isSameBootstrapMethod = true;
|
|
- for (int i = 0; i < length; ++i) {
|
|
- if (bootstrapMethodsData[offset + i] != bootstrapMethodsData[otherOffset + i]) {
|
|
- isSameBootstrapMethod = false;
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (isSameBootstrapMethod) {
|
|
- bootstrapMethods.length = offset; // Revert to old position.
|
|
- return entry;
|
|
- }
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- return put(new Entry(bootstrapMethodCount++, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode));
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Type table entries management.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the type table element whose index is given.
|
|
- *
|
|
- * @param typeIndex a type table index.
|
|
- * @return the type table element whose index is given.
|
|
- */
|
|
- Symbol getType(final int typeIndex) {
|
|
- return typeTable[typeIndex];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a type in the type table of this symbol table. Does nothing if the type table already
|
|
- * contains a similar type.
|
|
- *
|
|
- * @param value an internal class name.
|
|
- * @return the index of a new or already existing type Symbol with the given value.
|
|
- */
|
|
- int addType(final String value) {
|
|
- int hashCode = hash(Symbol.TYPE_TAG, value);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == Symbol.TYPE_TAG && entry.hashCode == hashCode && entry.value.equals(value)) {
|
|
- return entry.index;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- return addTypeInternal(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds an {@link Frame#ITEM_UNINITIALIZED} type in the type table of this symbol table. Does
|
|
- * nothing if the type table already contains a similar type.
|
|
- *
|
|
- * @param value an internal class name.
|
|
- * @param bytecodeOffset the bytecode offset of the NEW instruction that created this {@link
|
|
- * Frame#ITEM_UNINITIALIZED} type value.
|
|
- * @return the index of a new or already existing type Symbol with the given value.
|
|
- */
|
|
- int addUninitializedType(final String value, final int bytecodeOffset) {
|
|
- int hashCode = hash(Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == Symbol.UNINITIALIZED_TYPE_TAG
|
|
- && entry.hashCode == hashCode
|
|
- && entry.data == bytecodeOffset
|
|
- && entry.value.equals(value)) {
|
|
- return entry.index;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- return addTypeInternal(
|
|
- new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds a merged type in the type table of this symbol table. Does nothing if the type table
|
|
- * already contains a similar type.
|
|
- *
|
|
- * @param typeTableIndex1 a {@link Symbol#TYPE_TAG} type, specified by its index in the type
|
|
- * table.
|
|
- * @param typeTableIndex2 another {@link Symbol#TYPE_TAG} type, specified by its index in the type
|
|
- * table.
|
|
- * @return the index of a new or already existing {@link Symbol#TYPE_TAG} type Symbol,
|
|
- * corresponding to the common super class of the given types.
|
|
- */
|
|
- int addMergedType(final int typeTableIndex1, final int typeTableIndex2) {
|
|
- // TODO sort the arguments? The merge result should be independent of their order.
|
|
- long data = typeTableIndex1 | (((long) typeTableIndex2) << 32);
|
|
- int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2);
|
|
- Entry entry = get(hashCode);
|
|
- while (entry != null) {
|
|
- if (entry.tag == Symbol.MERGED_TYPE_TAG && entry.hashCode == hashCode && entry.data == data) {
|
|
- return entry.info;
|
|
- }
|
|
- entry = entry.next;
|
|
- }
|
|
- String type1 = typeTable[typeTableIndex1].value;
|
|
- String type2 = typeTable[typeTableIndex2].value;
|
|
- int commonSuperTypeIndex = addType(classWriter.getCommonSuperClass(type1, type2));
|
|
- put(new Entry(typeCount, Symbol.MERGED_TYPE_TAG, data, hashCode)).info = commonSuperTypeIndex;
|
|
- return commonSuperTypeIndex;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Adds the given type Symbol to {@link #typeTable}.
|
|
- *
|
|
- * @param entry a {@link Symbol#TYPE_TAG} or {@link Symbol#UNINITIALIZED_TYPE_TAG} type symbol.
|
|
- * The index of this Symbol must be equal to the current value of {@link #typeCount}.
|
|
- * @return the index in {@link #typeTable} where the given type was added, which is also equal to
|
|
- * entry's index by hypothesis.
|
|
- */
|
|
- private int addTypeInternal(final Entry entry) {
|
|
- if (typeTable == null) {
|
|
- typeTable = new Entry[16];
|
|
- }
|
|
- if (typeCount == typeTable.length) {
|
|
- Entry[] newTypeTable = new Entry[2 * typeTable.length];
|
|
- System.arraycopy(typeTable, 0, newTypeTable, 0, typeTable.length);
|
|
- typeTable = newTypeTable;
|
|
- }
|
|
- typeTable[typeCount++] = entry;
|
|
- return put(entry).index;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Static helper methods to compute hash codes.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- private static int hash(final int tag, final int value) {
|
|
- return 0x7FFFFFFF & (tag + value);
|
|
- }
|
|
-
|
|
- private static int hash(final int tag, final long value) {
|
|
- return 0x7FFFFFFF & (tag + (int) value + (int) (value >>> 32));
|
|
- }
|
|
-
|
|
- private static int hash(final int tag, final String value) {
|
|
- return 0x7FFFFFFF & (tag + value.hashCode());
|
|
- }
|
|
-
|
|
- private static int hash(final int tag, final String value1, final int value2) {
|
|
- return 0x7FFFFFFF & (tag + value1.hashCode() + value2);
|
|
- }
|
|
-
|
|
- private static int hash(final int tag, final String value1, final String value2) {
|
|
- return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode());
|
|
- }
|
|
-
|
|
- private static int hash(
|
|
- final int tag, final String value1, final String value2, final int value3) {
|
|
- return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * (value3 + 1));
|
|
- }
|
|
-
|
|
- private static int hash(
|
|
- final int tag, final String value1, final String value2, final String value3) {
|
|
- return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode());
|
|
- }
|
|
-
|
|
- private static int hash(
|
|
- final int tag,
|
|
- final String value1,
|
|
- final String value2,
|
|
- final String value3,
|
|
- final int value4) {
|
|
- return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode() * value4);
|
|
- }
|
|
-
|
|
- /**
|
|
- * An entry of a SymbolTable. This concrete and private subclass of {@link Symbol} adds two fields
|
|
- * which are only used inside SymbolTable, to implement hash sets of symbols (in order to avoid
|
|
- * duplicate symbols). See {@link #entries}.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
- private static class Entry extends Symbol {
|
|
-
|
|
- /** The hash code of this entry. */
|
|
- final int hashCode;
|
|
-
|
|
- /**
|
|
- * Another entry (and so on recursively) having the same hash code (modulo the size of {@link
|
|
- * #entries}) as this one.
|
|
- */
|
|
- Entry next;
|
|
-
|
|
- Entry(
|
|
- final int index,
|
|
- final int tag,
|
|
- final String owner,
|
|
- final String name,
|
|
- final String value,
|
|
- final long data,
|
|
- final int hashCode) {
|
|
- super(index, tag, owner, name, value, data);
|
|
- this.hashCode = hashCode;
|
|
- }
|
|
-
|
|
- Entry(final int index, final int tag, final String value, final int hashCode) {
|
|
- super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0);
|
|
- this.hashCode = hashCode;
|
|
- }
|
|
-
|
|
- Entry(final int index, final int tag, final String value, final long data, final int hashCode) {
|
|
- super(index, tag, /* owner = */ null, /* name = */ null, value, data);
|
|
- this.hashCode = hashCode;
|
|
- }
|
|
-
|
|
- Entry(
|
|
- final int index, final int tag, final String name, final String value, final int hashCode) {
|
|
- super(index, tag, /* owner = */ null, name, value, /* data = */ 0);
|
|
- this.hashCode = hashCode;
|
|
- }
|
|
-
|
|
- Entry(final int index, final int tag, final long data, final int hashCode) {
|
|
- super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data);
|
|
- this.hashCode = hashCode;
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/Type.java b/src/main/java/org/mvel2/asm/Type.java
|
|
deleted file mode 100644
|
|
index 79649fd..0000000
|
|
--- a/src/main/java/org/mvel2/asm/Type.java
|
|
+++ /dev/null
|
|
@@ -1,891 +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.lang.reflect.Constructor;
|
|
-import java.lang.reflect.Method;
|
|
-
|
|
-/**
|
|
- * A Java field or method type. This class can be used to make it easier to manipulate type and
|
|
- * method descriptors.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- * @author Chris Nokleberg
|
|
- */
|
|
-public final class Type {
|
|
-
|
|
- /** The sort of the {@code void} type. See {@link #getSort}. */
|
|
- public static final int VOID = 0;
|
|
-
|
|
- /** The sort of the {@code boolean} type. See {@link #getSort}. */
|
|
- public static final int BOOLEAN = 1;
|
|
-
|
|
- /** The sort of the {@code char} type. See {@link #getSort}. */
|
|
- public static final int CHAR = 2;
|
|
-
|
|
- /** The sort of the {@code byte} type. See {@link #getSort}. */
|
|
- public static final int BYTE = 3;
|
|
-
|
|
- /** The sort of the {@code short} type. See {@link #getSort}. */
|
|
- public static final int SHORT = 4;
|
|
-
|
|
- /** The sort of the {@code int} type. See {@link #getSort}. */
|
|
- public static final int INT = 5;
|
|
-
|
|
- /** The sort of the {@code float} type. See {@link #getSort}. */
|
|
- public static final int FLOAT = 6;
|
|
-
|
|
- /** The sort of the {@code long} type. See {@link #getSort}. */
|
|
- public static final int LONG = 7;
|
|
-
|
|
- /** The sort of the {@code double} type. See {@link #getSort}. */
|
|
- public static final int DOUBLE = 8;
|
|
-
|
|
- /** The sort of array reference types. See {@link #getSort}. */
|
|
- public static final int ARRAY = 9;
|
|
-
|
|
- /** The sort of object reference types. See {@link #getSort}. */
|
|
- public static final int OBJECT = 10;
|
|
-
|
|
- /** The sort of method types. See {@link #getSort}. */
|
|
- public static final int METHOD = 11;
|
|
-
|
|
- /** The (private) sort of object reference types represented with an internal name. */
|
|
- private static final int INTERNAL = 12;
|
|
-
|
|
- /** The descriptors of the primitive types. */
|
|
- private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
|
|
-
|
|
- /** The {@code void} type. */
|
|
- public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);
|
|
-
|
|
- /** The {@code boolean} type. */
|
|
- public static final Type BOOLEAN_TYPE =
|
|
- new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1);
|
|
-
|
|
- /** The {@code char} type. */
|
|
- public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1);
|
|
-
|
|
- /** The {@code byte} type. */
|
|
- public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1);
|
|
-
|
|
- /** The {@code short} type. */
|
|
- public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1);
|
|
-
|
|
- /** The {@code int} type. */
|
|
- public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1);
|
|
-
|
|
- /** The {@code float} type. */
|
|
- public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1);
|
|
-
|
|
- /** The {@code long} type. */
|
|
- public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1);
|
|
-
|
|
- /** The {@code double} type. */
|
|
- public static final Type DOUBLE_TYPE =
|
|
- new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1);
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Fields
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE},
|
|
- * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY},
|
|
- * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}.
|
|
- */
|
|
- private final int sort;
|
|
-
|
|
- /**
|
|
- * A buffer containing the value of this field or method type. This value is an internal name for
|
|
- * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other
|
|
- * cases.
|
|
- *
|
|
- * <p>For {@link #OBJECT} types, this field also contains the descriptor: the characters in
|
|
- * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link
|
|
- * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor.
|
|
- */
|
|
- private final String valueBuffer;
|
|
-
|
|
- /**
|
|
- * The beginning index, inclusive, of the value of this Java field or method type in {@link
|
|
- * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types,
|
|
- * and a field or method descriptor in the other cases.
|
|
- */
|
|
- private final int valueBegin;
|
|
-
|
|
- /**
|
|
- * The end index, exclusive, of the value of this Java field or method type in {@link
|
|
- * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types,
|
|
- * and a field or method descriptor in the other cases.
|
|
- */
|
|
- private final int valueEnd;
|
|
-
|
|
- /**
|
|
- * Constructs a reference type.
|
|
- *
|
|
- * @param sort the sort of this type, see {@link #sort}.
|
|
- * @param valueBuffer a buffer containing the value of this field or method type.
|
|
- * @param valueBegin the beginning index, inclusive, of the value of this field or method type in
|
|
- * valueBuffer.
|
|
- * @param valueEnd the end index, exclusive, of the value of this field or method type in
|
|
- * valueBuffer.
|
|
- */
|
|
- private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) {
|
|
- this.sort = sort;
|
|
- this.valueBuffer = valueBuffer;
|
|
- this.valueBegin = valueBegin;
|
|
- this.valueEnd = valueEnd;
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the given type descriptor.
|
|
- *
|
|
- * @param typeDescriptor a field or method type descriptor.
|
|
- * @return the {@link Type} corresponding to the given type descriptor.
|
|
- */
|
|
- public static Type getType(final String typeDescriptor) {
|
|
- return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the given class.
|
|
- *
|
|
- * @param clazz a class.
|
|
- * @return the {@link Type} corresponding to the given class.
|
|
- */
|
|
- public static Type getType(final Class<?> clazz) {
|
|
- if (clazz.isPrimitive()) {
|
|
- if (clazz == Integer.TYPE) {
|
|
- return INT_TYPE;
|
|
- } else if (clazz == Void.TYPE) {
|
|
- return VOID_TYPE;
|
|
- } else if (clazz == Boolean.TYPE) {
|
|
- return BOOLEAN_TYPE;
|
|
- } else if (clazz == Byte.TYPE) {
|
|
- return BYTE_TYPE;
|
|
- } else if (clazz == Character.TYPE) {
|
|
- return CHAR_TYPE;
|
|
- } else if (clazz == Short.TYPE) {
|
|
- return SHORT_TYPE;
|
|
- } else if (clazz == Double.TYPE) {
|
|
- return DOUBLE_TYPE;
|
|
- } else if (clazz == Float.TYPE) {
|
|
- return FLOAT_TYPE;
|
|
- } else if (clazz == Long.TYPE) {
|
|
- return LONG_TYPE;
|
|
- } else {
|
|
- throw new AssertionError();
|
|
- }
|
|
- } else {
|
|
- return getType(getDescriptor(clazz));
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the method {@link Type} corresponding to the given constructor.
|
|
- *
|
|
- * @param constructor a {@link Constructor} object.
|
|
- * @return the method {@link Type} corresponding to the given constructor.
|
|
- */
|
|
- public static Type getType(final Constructor<?> constructor) {
|
|
- return getType(getConstructorDescriptor(constructor));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the method {@link Type} corresponding to the given method.
|
|
- *
|
|
- * @param method a {@link Method} object.
|
|
- * @return the method {@link Type} corresponding to the given method.
|
|
- */
|
|
- public static Type getType(final Method method) {
|
|
- return getType(getMethodDescriptor(method));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the type of the elements of this array type. This method should only be used for an
|
|
- * array type.
|
|
- *
|
|
- * @return Returns the type of the elements of this array type.
|
|
- */
|
|
- public Type getElementType() {
|
|
- final int numDimensions = getDimensions();
|
|
- return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the given internal name.
|
|
- *
|
|
- * @param internalName an internal name.
|
|
- * @return the {@link Type} corresponding to the given internal name.
|
|
- */
|
|
- public static Type getObjectType(final String internalName) {
|
|
- return new Type(
|
|
- internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the given method descriptor. Equivalent to <code>
|
|
- * Type.getType(methodDescriptor)</code>.
|
|
- *
|
|
- * @param methodDescriptor a method descriptor.
|
|
- * @return the {@link Type} corresponding to the given method descriptor.
|
|
- */
|
|
- public static Type getMethodType(final String methodDescriptor) {
|
|
- return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the method {@link Type} corresponding to the given argument and return types.
|
|
- *
|
|
- * @param returnType the return type of the method.
|
|
- * @param argumentTypes the argument types of the method.
|
|
- * @return the method {@link Type} corresponding to the given argument and return types.
|
|
- */
|
|
- public static Type getMethodType(final Type returnType, final Type... argumentTypes) {
|
|
- return getType(getMethodDescriptor(returnType, argumentTypes));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the argument types of methods of this type. This method should only be used for method
|
|
- * types.
|
|
- *
|
|
- * @return the argument types of methods of this type.
|
|
- */
|
|
- public Type[] getArgumentTypes() {
|
|
- return getArgumentTypes(getDescriptor());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} values corresponding to the argument types of the given method
|
|
- * descriptor.
|
|
- *
|
|
- * @param methodDescriptor a method descriptor.
|
|
- * @return the {@link Type} values corresponding to the argument types of the given method
|
|
- * descriptor.
|
|
- */
|
|
- public static Type[] getArgumentTypes(final String methodDescriptor) {
|
|
- // First step: compute the number of argument types in methodDescriptor.
|
|
- int numArgumentTypes = 0;
|
|
- // Skip the first character, which is always a '('.
|
|
- int currentOffset = 1;
|
|
- // Parse the argument types, one at a each loop iteration.
|
|
- while (methodDescriptor.charAt(currentOffset) != ')') {
|
|
- while (methodDescriptor.charAt(currentOffset) == '[') {
|
|
- currentOffset++;
|
|
- }
|
|
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
|
- // Skip the argument descriptor content.
|
|
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
|
|
- }
|
|
- ++numArgumentTypes;
|
|
- }
|
|
-
|
|
- // Second step: create a Type instance for each argument type.
|
|
- Type[] argumentTypes = new Type[numArgumentTypes];
|
|
- // Skip the first character, which is always a '('.
|
|
- currentOffset = 1;
|
|
- // Parse and create the argument types, one at each loop iteration.
|
|
- int currentArgumentTypeIndex = 0;
|
|
- while (methodDescriptor.charAt(currentOffset) != ')') {
|
|
- final int currentArgumentTypeOffset = currentOffset;
|
|
- while (methodDescriptor.charAt(currentOffset) == '[') {
|
|
- currentOffset++;
|
|
- }
|
|
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
|
- // Skip the argument descriptor content.
|
|
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
|
|
- }
|
|
- argumentTypes[currentArgumentTypeIndex++] =
|
|
- getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
|
|
- }
|
|
- return argumentTypes;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} values corresponding to the argument types of the given method.
|
|
- *
|
|
- * @param method a method.
|
|
- * @return the {@link Type} values corresponding to the argument types of the given method.
|
|
- */
|
|
- public static Type[] getArgumentTypes(final Method method) {
|
|
- Class<?>[] classes = method.getParameterTypes();
|
|
- Type[] types = new Type[classes.length];
|
|
- for (int i = classes.length - 1; i >= 0; --i) {
|
|
- types[i] = getType(classes[i]);
|
|
- }
|
|
- return types;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the return type of methods of this type. This method should only be used for method
|
|
- * types.
|
|
- *
|
|
- * @return the return type of methods of this type.
|
|
- */
|
|
- public Type getReturnType() {
|
|
- return getReturnType(getDescriptor());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the return type of the given method descriptor.
|
|
- *
|
|
- * @param methodDescriptor a method descriptor.
|
|
- * @return the {@link Type} corresponding to the return type of the given method descriptor.
|
|
- */
|
|
- public static Type getReturnType(final String methodDescriptor) {
|
|
- // Skip the first character, which is always a '('.
|
|
- int currentOffset = 1;
|
|
- // Skip the argument types, one at a each loop iteration.
|
|
- while (methodDescriptor.charAt(currentOffset) != ')') {
|
|
- while (methodDescriptor.charAt(currentOffset) == '[') {
|
|
- currentOffset++;
|
|
- }
|
|
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
|
- // Skip the argument descriptor content.
|
|
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
|
|
- }
|
|
- }
|
|
- return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the return type of the given method.
|
|
- *
|
|
- * @param method a method.
|
|
- * @return the {@link Type} corresponding to the return type of the given method.
|
|
- */
|
|
- public static Type getReturnType(final Method method) {
|
|
- return getType(method.getReturnType());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@link Type} corresponding to the given field or method descriptor.
|
|
- *
|
|
- * @param descriptorBuffer a buffer containing the field or method descriptor.
|
|
- * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in
|
|
- * descriptorBuffer.
|
|
- * @param descriptorEnd the end index, exclusive, of the field or method descriptor in
|
|
- * descriptorBuffer.
|
|
- * @return the {@link Type} corresponding to the given type descriptor.
|
|
- */
|
|
- private static Type getTypeInternal(
|
|
- final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
|
|
- switch (descriptorBuffer.charAt(descriptorBegin)) {
|
|
- case 'V':
|
|
- return VOID_TYPE;
|
|
- case 'Z':
|
|
- return BOOLEAN_TYPE;
|
|
- case 'C':
|
|
- return CHAR_TYPE;
|
|
- case 'B':
|
|
- return BYTE_TYPE;
|
|
- case 'S':
|
|
- return SHORT_TYPE;
|
|
- case 'I':
|
|
- return INT_TYPE;
|
|
- case 'F':
|
|
- return FLOAT_TYPE;
|
|
- case 'J':
|
|
- return LONG_TYPE;
|
|
- case 'D':
|
|
- return DOUBLE_TYPE;
|
|
- case '[':
|
|
- return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd);
|
|
- case 'L':
|
|
- return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1);
|
|
- case '(':
|
|
- return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd);
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to get class names, internal names or descriptors.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the binary name of the class corresponding to this type. This method must not be used
|
|
- * on method types.
|
|
- *
|
|
- * @return the binary name of the class corresponding to this type.
|
|
- */
|
|
- public String getClassName() {
|
|
- switch (sort) {
|
|
- case VOID:
|
|
- return "void";
|
|
- case BOOLEAN:
|
|
- return "boolean";
|
|
- case CHAR:
|
|
- return "char";
|
|
- case BYTE:
|
|
- return "byte";
|
|
- case SHORT:
|
|
- return "short";
|
|
- case INT:
|
|
- return "int";
|
|
- case FLOAT:
|
|
- return "float";
|
|
- case LONG:
|
|
- return "long";
|
|
- case DOUBLE:
|
|
- return "double";
|
|
- case ARRAY:
|
|
- StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName());
|
|
- for (int i = getDimensions(); i > 0; --i) {
|
|
- stringBuilder.append("[]");
|
|
- }
|
|
- return stringBuilder.toString();
|
|
- case OBJECT:
|
|
- case INTERNAL:
|
|
- return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.');
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the internal name of the class corresponding to this object or array type. The internal
|
|
- * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are
|
|
- * replaced by '/'). This method should only be used for an object or array type.
|
|
- *
|
|
- * @return the internal name of the class corresponding to this object type.
|
|
- */
|
|
- public String getInternalName() {
|
|
- return valueBuffer.substring(valueBegin, valueEnd);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the internal name of the given class. The internal name of a class is its fully
|
|
- * qualified name, as returned by Class.getName(), where '.' are replaced by '/'.
|
|
- *
|
|
- * @param clazz an object or array class.
|
|
- * @return the internal name of the given class.
|
|
- */
|
|
- public static String getInternalName(final Class<?> clazz) {
|
|
- return clazz.getName().replace('.', '/');
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor corresponding to this type.
|
|
- *
|
|
- * @return the descriptor corresponding to this type.
|
|
- */
|
|
- public String getDescriptor() {
|
|
- if (sort == OBJECT) {
|
|
- return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
|
|
- } else if (sort == INTERNAL) {
|
|
- return new StringBuilder()
|
|
- .append('L')
|
|
- .append(valueBuffer, valueBegin, valueEnd)
|
|
- .append(';')
|
|
- .toString();
|
|
- } else {
|
|
- return valueBuffer.substring(valueBegin, valueEnd);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor corresponding to the given class.
|
|
- *
|
|
- * @param clazz an object class, a primitive class or an array class.
|
|
- * @return the descriptor corresponding to the given class.
|
|
- */
|
|
- public static String getDescriptor(final Class<?> clazz) {
|
|
- StringBuilder stringBuilder = new StringBuilder();
|
|
- appendDescriptor(clazz, stringBuilder);
|
|
- return stringBuilder.toString();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor corresponding to the given constructor.
|
|
- *
|
|
- * @param constructor a {@link Constructor} object.
|
|
- * @return the descriptor of the given constructor.
|
|
- */
|
|
- public static String getConstructorDescriptor(final Constructor<?> constructor) {
|
|
- StringBuilder stringBuilder = new StringBuilder();
|
|
- stringBuilder.append('(');
|
|
- Class<?>[] parameters = constructor.getParameterTypes();
|
|
- for (Class<?> parameter : parameters) {
|
|
- appendDescriptor(parameter, stringBuilder);
|
|
- }
|
|
- return stringBuilder.append(")V").toString();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor corresponding to the given argument and return types.
|
|
- *
|
|
- * @param returnType the return type of the method.
|
|
- * @param argumentTypes the argument types of the method.
|
|
- * @return the descriptor corresponding to the given argument and return types.
|
|
- */
|
|
- public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) {
|
|
- StringBuilder stringBuilder = new StringBuilder();
|
|
- stringBuilder.append('(');
|
|
- for (Type argumentType : argumentTypes) {
|
|
- argumentType.appendDescriptor(stringBuilder);
|
|
- }
|
|
- stringBuilder.append(')');
|
|
- returnType.appendDescriptor(stringBuilder);
|
|
- return stringBuilder.toString();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the descriptor corresponding to the given method.
|
|
- *
|
|
- * @param method a {@link Method} object.
|
|
- * @return the descriptor of the given method.
|
|
- */
|
|
- public static String getMethodDescriptor(final Method method) {
|
|
- StringBuilder stringBuilder = new StringBuilder();
|
|
- stringBuilder.append('(');
|
|
- Class<?>[] parameters = method.getParameterTypes();
|
|
- for (Class<?> parameter : parameters) {
|
|
- appendDescriptor(parameter, stringBuilder);
|
|
- }
|
|
- stringBuilder.append(')');
|
|
- appendDescriptor(method.getReturnType(), stringBuilder);
|
|
- return stringBuilder.toString();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Appends the descriptor corresponding to this type to the given string buffer.
|
|
- *
|
|
- * @param stringBuilder the string builder to which the descriptor must be appended.
|
|
- */
|
|
- private void appendDescriptor(final StringBuilder stringBuilder) {
|
|
- if (sort == OBJECT) {
|
|
- stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1);
|
|
- } else if (sort == INTERNAL) {
|
|
- stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';');
|
|
- } else {
|
|
- stringBuilder.append(valueBuffer, valueBegin, valueEnd);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Appends the descriptor of the given class to the given string builder.
|
|
- *
|
|
- * @param clazz the class whose descriptor must be computed.
|
|
- * @param stringBuilder the string builder to which the descriptor must be appended.
|
|
- */
|
|
- private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
|
|
- Class<?> currentClass = clazz;
|
|
- while (currentClass.isArray()) {
|
|
- stringBuilder.append('[');
|
|
- currentClass = currentClass.getComponentType();
|
|
- }
|
|
- if (currentClass.isPrimitive()) {
|
|
- char descriptor;
|
|
- if (currentClass == Integer.TYPE) {
|
|
- descriptor = 'I';
|
|
- } else if (currentClass == Void.TYPE) {
|
|
- descriptor = 'V';
|
|
- } else if (currentClass == Boolean.TYPE) {
|
|
- descriptor = 'Z';
|
|
- } else if (currentClass == Byte.TYPE) {
|
|
- descriptor = 'B';
|
|
- } else if (currentClass == Character.TYPE) {
|
|
- descriptor = 'C';
|
|
- } else if (currentClass == Short.TYPE) {
|
|
- descriptor = 'S';
|
|
- } else if (currentClass == Double.TYPE) {
|
|
- descriptor = 'D';
|
|
- } else if (currentClass == Float.TYPE) {
|
|
- descriptor = 'F';
|
|
- } else if (currentClass == Long.TYPE) {
|
|
- descriptor = 'J';
|
|
- } else {
|
|
- throw new AssertionError();
|
|
- }
|
|
- stringBuilder.append(descriptor);
|
|
- } else {
|
|
- stringBuilder.append('L');
|
|
- String name = currentClass.getName();
|
|
- int nameLength = name.length();
|
|
- for (int i = 0; i < nameLength; ++i) {
|
|
- char car = name.charAt(i);
|
|
- stringBuilder.append(car == '.' ? '/' : car);
|
|
- }
|
|
- stringBuilder.append(';');
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Returns the sort of this type.
|
|
- *
|
|
- * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link
|
|
- * #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or
|
|
- * {@link #METHOD}.
|
|
- */
|
|
- public int getSort() {
|
|
- return sort == INTERNAL ? OBJECT : sort;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of dimensions of this array type. This method should only be used for an
|
|
- * array type.
|
|
- *
|
|
- * @return the number of dimensions of this array type.
|
|
- */
|
|
- public int getDimensions() {
|
|
- int numDimensions = 1;
|
|
- while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
|
|
- numDimensions++;
|
|
- }
|
|
- return numDimensions;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of values of this type. This method must not be used for method types.
|
|
- *
|
|
- * @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for
|
|
- * {@code void} and 1 otherwise.
|
|
- */
|
|
- public int getSize() {
|
|
- switch (sort) {
|
|
- case VOID:
|
|
- return 0;
|
|
- case BOOLEAN:
|
|
- case CHAR:
|
|
- case BYTE:
|
|
- case SHORT:
|
|
- case INT:
|
|
- case FLOAT:
|
|
- case ARRAY:
|
|
- case OBJECT:
|
|
- case INTERNAL:
|
|
- return 1;
|
|
- case LONG:
|
|
- case DOUBLE:
|
|
- return 2;
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the size of the arguments and of the return value of methods of this type. This method
|
|
- * should only be used for method types.
|
|
- *
|
|
- * @return the size of the arguments of the method (plus one for the implicit this argument),
|
|
- * argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
|
- * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
|
- * i >> 2}, and returnSize to {@code i & 0x03}).
|
|
- */
|
|
- public int getArgumentsAndReturnSizes() {
|
|
- return getArgumentsAndReturnSizes(getDescriptor());
|
|
- }
|
|
-
|
|
- /**
|
|
- * Computes the size of the arguments and of the return value of a method.
|
|
- *
|
|
- * @param methodDescriptor a method descriptor.
|
|
- * @return the size of the arguments of the method (plus one for the implicit this argument),
|
|
- * argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
|
- * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
|
- * i >> 2}, and returnSize to {@code i & 0x03}).
|
|
- */
|
|
- public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
|
|
- int argumentsSize = 1;
|
|
- // Skip the first character, which is always a '('.
|
|
- int currentOffset = 1;
|
|
- int currentChar = methodDescriptor.charAt(currentOffset);
|
|
- // Parse the argument types and compute their size, one at a each loop iteration.
|
|
- while (currentChar != ')') {
|
|
- if (currentChar == 'J' || currentChar == 'D') {
|
|
- currentOffset++;
|
|
- argumentsSize += 2;
|
|
- } else {
|
|
- while (methodDescriptor.charAt(currentOffset) == '[') {
|
|
- currentOffset++;
|
|
- }
|
|
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
|
|
- // Skip the argument descriptor content.
|
|
- currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1;
|
|
- }
|
|
- argumentsSize += 1;
|
|
- }
|
|
- currentChar = methodDescriptor.charAt(currentOffset);
|
|
- }
|
|
- currentChar = methodDescriptor.charAt(currentOffset + 1);
|
|
- if (currentChar == 'V') {
|
|
- return argumentsSize << 2;
|
|
- } else {
|
|
- int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
|
|
- return argumentsSize << 2 | returnSize;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for
|
|
- * method types.
|
|
- *
|
|
- * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD,
|
|
- * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and
|
|
- * IRETURN.
|
|
- * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For
|
|
- * example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns
|
|
- * FRETURN.
|
|
- */
|
|
- public int getOpcode(final int opcode) {
|
|
- if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
|
|
- switch (sort) {
|
|
- case BOOLEAN:
|
|
- case BYTE:
|
|
- return opcode + (Opcodes.BALOAD - Opcodes.IALOAD);
|
|
- case CHAR:
|
|
- return opcode + (Opcodes.CALOAD - Opcodes.IALOAD);
|
|
- case SHORT:
|
|
- return opcode + (Opcodes.SALOAD - Opcodes.IALOAD);
|
|
- case INT:
|
|
- return opcode;
|
|
- case FLOAT:
|
|
- return opcode + (Opcodes.FALOAD - Opcodes.IALOAD);
|
|
- case LONG:
|
|
- return opcode + (Opcodes.LALOAD - Opcodes.IALOAD);
|
|
- case DOUBLE:
|
|
- return opcode + (Opcodes.DALOAD - Opcodes.IALOAD);
|
|
- case ARRAY:
|
|
- case OBJECT:
|
|
- case INTERNAL:
|
|
- return opcode + (Opcodes.AALOAD - Opcodes.IALOAD);
|
|
- case METHOD:
|
|
- case VOID:
|
|
- throw new UnsupportedOperationException();
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- } else {
|
|
- switch (sort) {
|
|
- case VOID:
|
|
- if (opcode != Opcodes.IRETURN) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
- return Opcodes.RETURN;
|
|
- case BOOLEAN:
|
|
- case BYTE:
|
|
- case CHAR:
|
|
- case SHORT:
|
|
- case INT:
|
|
- return opcode;
|
|
- case FLOAT:
|
|
- return opcode + (Opcodes.FRETURN - Opcodes.IRETURN);
|
|
- case LONG:
|
|
- return opcode + (Opcodes.LRETURN - Opcodes.IRETURN);
|
|
- case DOUBLE:
|
|
- return opcode + (Opcodes.DRETURN - Opcodes.IRETURN);
|
|
- case ARRAY:
|
|
- case OBJECT:
|
|
- case INTERNAL:
|
|
- if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
- return opcode + (Opcodes.ARETURN - Opcodes.IRETURN);
|
|
- case METHOD:
|
|
- throw new UnsupportedOperationException();
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Equals, hashCode and toString.
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /**
|
|
- * Tests if the given object is equal to this type.
|
|
- *
|
|
- * @param object the object to be compared to this type.
|
|
- * @return {@literal true} if the given object is equal to this type.
|
|
- */
|
|
- @Override
|
|
- public boolean equals(final Object object) {
|
|
- if (this == object) {
|
|
- return true;
|
|
- }
|
|
- if (!(object instanceof Type)) {
|
|
- return false;
|
|
- }
|
|
- Type other = (Type) object;
|
|
- if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) {
|
|
- return false;
|
|
- }
|
|
- int begin = valueBegin;
|
|
- int end = valueEnd;
|
|
- int otherBegin = other.valueBegin;
|
|
- int otherEnd = other.valueEnd;
|
|
- // Compare the values.
|
|
- if (end - begin != otherEnd - otherBegin) {
|
|
- return false;
|
|
- }
|
|
- for (int i = begin, j = otherBegin; i < end; i++, j++) {
|
|
- if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) {
|
|
- return false;
|
|
- }
|
|
- }
|
|
- return true;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a hash code value for this type.
|
|
- *
|
|
- * @return a hash code value for this type.
|
|
- */
|
|
- @Override
|
|
- public int hashCode() {
|
|
- int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort);
|
|
- if (sort >= ARRAY) {
|
|
- for (int i = valueBegin, end = valueEnd; i < end; i++) {
|
|
- hashCode = 17 * (hashCode + valueBuffer.charAt(i));
|
|
- }
|
|
- }
|
|
- return hashCode;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a string representation of this type.
|
|
- *
|
|
- * @return the descriptor of this type.
|
|
- */
|
|
- @Override
|
|
- public String toString() {
|
|
- return getDescriptor();
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/TypePath.java b/src/main/java/org/mvel2/asm/TypePath.java
|
|
deleted file mode 100644
|
|
index a99eca4..0000000
|
|
--- a/src/main/java/org/mvel2/asm/TypePath.java
|
|
+++ /dev/null
|
|
@@ -1,201 +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;
|
|
-
|
|
-/**
|
|
- * The path to a type argument, wildcard bound, array element type, or static inner type within an
|
|
- * enclosing type.
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public final class TypePath {
|
|
-
|
|
- /** A type path step that steps into the element type of an array type. See {@link #getStep}. */
|
|
- public static final int ARRAY_ELEMENT = 0;
|
|
-
|
|
- /** A type path step that steps into the nested type of a class type. See {@link #getStep}. */
|
|
- public static final int INNER_TYPE = 1;
|
|
-
|
|
- /** A type path step that steps into the bound of a wildcard type. See {@link #getStep}. */
|
|
- public static final int WILDCARD_BOUND = 2;
|
|
-
|
|
- /** A type path step that steps into a type argument of a generic type. See {@link #getStep}. */
|
|
- public static final int TYPE_ARGUMENT = 3;
|
|
-
|
|
- /**
|
|
- * The byte array where the 'type_path' structure - as defined in the Java Virtual Machine
|
|
- * Specification (JVMS) - corresponding to this TypePath is stored. The first byte of the
|
|
- * structure in this array is given by {@link #typePathOffset}.
|
|
- *
|
|
- * @see <a
|
|
- * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.2">JVMS
|
|
- * 4.7.20.2</a>
|
|
- */
|
|
- private final byte[] typePathContainer;
|
|
-
|
|
- /** The offset of the first byte of the type_path JVMS structure in {@link #typePathContainer}. */
|
|
- private final int typePathOffset;
|
|
-
|
|
- /**
|
|
- * Constructs a new TypePath.
|
|
- *
|
|
- * @param typePathContainer a byte array containing a type_path JVMS structure.
|
|
- * @param typePathOffset the offset of the first byte of the type_path structure in
|
|
- * typePathContainer.
|
|
- */
|
|
- TypePath(final byte[] typePathContainer, final int typePathOffset) {
|
|
- this.typePathContainer = typePathContainer;
|
|
- this.typePathOffset = typePathOffset;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the length of this path, i.e. its number of steps.
|
|
- *
|
|
- * @return the length of this path.
|
|
- */
|
|
- public int getLength() {
|
|
- // path_length is stored in the first byte of a type_path.
|
|
- return typePathContainer[typePathOffset];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the value of the given step of this path.
|
|
- *
|
|
- * @param index an index between 0 and {@link #getLength()}, exclusive.
|
|
- * @return one of {@link #ARRAY_ELEMENT}, {@link #INNER_TYPE}, {@link #WILDCARD_BOUND}, or {@link
|
|
- * #TYPE_ARGUMENT}.
|
|
- */
|
|
- public int getStep(final int index) {
|
|
- // Returns the type_path_kind of the path element of the given index.
|
|
- return typePathContainer[typePathOffset + 2 * index + 1];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the type argument that the given step is stepping into. This method should
|
|
- * only be used for steps whose value is {@link #TYPE_ARGUMENT}.
|
|
- *
|
|
- * @param index an index between 0 and {@link #getLength()}, exclusive.
|
|
- * @return the index of the type argument that the given step is stepping into.
|
|
- */
|
|
- public int getStepArgument(final int index) {
|
|
- // Returns the type_argument_index of the path element of the given index.
|
|
- return typePathContainer[typePathOffset + 2 * index + 2];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Converts a type path in string form, in the format used by {@link #toString()}, into a TypePath
|
|
- * object.
|
|
- *
|
|
- * @param typePath a type path in string form, in the format used by {@link #toString()}. May be
|
|
- * {@literal null} or empty.
|
|
- * @return the corresponding TypePath object, or {@literal null} if the path is empty.
|
|
- */
|
|
- public static TypePath fromString(final String typePath) {
|
|
- if (typePath == null || typePath.length() == 0) {
|
|
- return null;
|
|
- }
|
|
- int typePathLength = typePath.length();
|
|
- ByteVector output = new ByteVector(typePathLength);
|
|
- output.putByte(0);
|
|
- int typePathIndex = 0;
|
|
- while (typePathIndex < typePathLength) {
|
|
- char c = typePath.charAt(typePathIndex++);
|
|
- if (c == '[') {
|
|
- output.put11(ARRAY_ELEMENT, 0);
|
|
- } else if (c == '.') {
|
|
- output.put11(INNER_TYPE, 0);
|
|
- } else if (c == '*') {
|
|
- output.put11(WILDCARD_BOUND, 0);
|
|
- } else if (c >= '0' && c <= '9') {
|
|
- int typeArg = c - '0';
|
|
- while (typePathIndex < typePathLength) {
|
|
- c = typePath.charAt(typePathIndex++);
|
|
- if (c >= '0' && c <= '9') {
|
|
- typeArg = typeArg * 10 + c - '0';
|
|
- } else if (c == ';') {
|
|
- break;
|
|
- } else {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
- output.put11(TYPE_ARGUMENT, typeArg);
|
|
- } else {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
- output.data[0] = (byte) (output.length / 2);
|
|
- return new TypePath(output.data, 0);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a string representation of this type path. {@link #ARRAY_ELEMENT} steps are represented
|
|
- * with '[', {@link #INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND} steps with '*' and {@link
|
|
- * #TYPE_ARGUMENT} steps with their type argument index in decimal form followed by ';'.
|
|
- */
|
|
- @Override
|
|
- public String toString() {
|
|
- int length = getLength();
|
|
- StringBuilder result = new StringBuilder(length * 2);
|
|
- for (int i = 0; i < length; ++i) {
|
|
- switch (getStep(i)) {
|
|
- case ARRAY_ELEMENT:
|
|
- result.append('[');
|
|
- break;
|
|
- case INNER_TYPE:
|
|
- result.append('.');
|
|
- break;
|
|
- case WILDCARD_BOUND:
|
|
- result.append('*');
|
|
- break;
|
|
- case TYPE_ARGUMENT:
|
|
- result.append(getStepArgument(i)).append(';');
|
|
- break;
|
|
- default:
|
|
- throw new AssertionError();
|
|
- }
|
|
- }
|
|
- return result.toString();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the type_path JVMS structure corresponding to the given TypePath into the given
|
|
- * ByteVector.
|
|
- *
|
|
- * @param typePath a TypePath instance, or {@literal null} for empty paths.
|
|
- * @param output where the type path must be put.
|
|
- */
|
|
- static void put(final TypePath typePath, final ByteVector output) {
|
|
- if (typePath == null) {
|
|
- output.putByte(0);
|
|
- } else {
|
|
- int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1;
|
|
- output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length);
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/TypeReference.java b/src/main/java/org/mvel2/asm/TypeReference.java
|
|
deleted file mode 100644
|
|
index eeae884..0000000
|
|
--- a/src/main/java/org/mvel2/asm/TypeReference.java
|
|
+++ /dev/null
|
|
@@ -1,436 +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 reference to a type appearing in a class, field or method declaration, or on an instruction.
|
|
- * Such a reference designates the part of the class where the referenced type is appearing (e.g. an
|
|
- * 'extends', 'implements' or 'throws' clause, a 'new' instruction, a 'catch' clause, a type cast, a
|
|
- * local variable declaration, etc).
|
|
- *
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public class TypeReference {
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic class. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int CLASS_TYPE_PARAMETER = 0x00;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic method. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int METHOD_TYPE_PARAMETER = 0x01;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the super class of a class or one of the interfaces it
|
|
- * implements. See {@link #getSort}.
|
|
- */
|
|
- public static final int CLASS_EXTENDS = 0x10;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a bound of a type parameter of a generic class. See
|
|
- * {@link #getSort}.
|
|
- */
|
|
- public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a bound of a type parameter of a generic method. See
|
|
- * {@link #getSort}.
|
|
- */
|
|
- public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12;
|
|
-
|
|
- /** The sort of type references that target the type of a field. See {@link #getSort}. */
|
|
- public static final int FIELD = 0x13;
|
|
-
|
|
- /** The sort of type references that target the return type of a method. See {@link #getSort}. */
|
|
- public static final int METHOD_RETURN = 0x14;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the receiver type of a method. See {@link #getSort}.
|
|
- */
|
|
- public static final int METHOD_RECEIVER = 0x15;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of a formal parameter of a method. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int METHOD_FORMAL_PARAMETER = 0x16;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of an exception declared in the throws clause
|
|
- * of a method. See {@link #getSort}.
|
|
- */
|
|
- public static final int THROWS = 0x17;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of a local variable in a method. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int LOCAL_VARIABLE = 0x40;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of a resource variable in a method. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int RESOURCE_VARIABLE = 0x41;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of the exception of a 'catch' clause in a
|
|
- * method. See {@link #getSort}.
|
|
- */
|
|
- public static final int EXCEPTION_PARAMETER = 0x42;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type declared in an 'instanceof' instruction. See
|
|
- * {@link #getSort}.
|
|
- */
|
|
- public static final int INSTANCEOF = 0x43;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type of the object created by a 'new' instruction.
|
|
- * See {@link #getSort}.
|
|
- */
|
|
- public static final int NEW = 0x44;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the receiver type of a constructor reference. See
|
|
- * {@link #getSort}.
|
|
- */
|
|
- public static final int CONSTRUCTOR_REFERENCE = 0x45;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the receiver type of a method reference. See {@link
|
|
- * #getSort}.
|
|
- */
|
|
- public static final int METHOD_REFERENCE = 0x46;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target the type declared in an explicit or implicit cast
|
|
- * instruction. See {@link #getSort}.
|
|
- */
|
|
- public static final int CAST = 0x47;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic constructor in a
|
|
- * constructor call. See {@link #getSort}.
|
|
- */
|
|
- public static final int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic method in a method call.
|
|
- * See {@link #getSort}.
|
|
- */
|
|
- public static final int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic constructor in a
|
|
- * constructor reference. See {@link #getSort}.
|
|
- */
|
|
- public static final int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A;
|
|
-
|
|
- /**
|
|
- * The sort of type references that target a type parameter of a generic method in a method
|
|
- * reference. See {@link #getSort}.
|
|
- */
|
|
- public static final int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B;
|
|
-
|
|
- /**
|
|
- * The target_type and target_info structures - as defined in the Java Virtual Machine
|
|
- * Specification (JVMS) - corresponding to this type reference. target_type uses one byte, and all
|
|
- * the target_info union fields use up to 3 bytes (except localvar_target, handled with the
|
|
- * specific method {@link MethodVisitor#visitLocalVariableAnnotation}). Thus, both structures can
|
|
- * be stored in an int.
|
|
- *
|
|
- * <p>This int field stores target_type (called the TypeReference 'sort' in the public API of this
|
|
- * class) in its most significant byte, followed by the target_info fields. Depending on
|
|
- * target_type, 1, 2 or even 3 least significant bytes of this field are unused. target_info
|
|
- * fields which reference bytecode offsets are set to 0 (these offsets are ignored in ClassReader,
|
|
- * and recomputed in MethodWriter).
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS
|
|
- * 4.7.20</a>
|
|
- * @see <a
|
|
- * href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.1">JVMS
|
|
- * 4.7.20.1</a>
|
|
- */
|
|
- private final int targetTypeAndInfo;
|
|
-
|
|
- /**
|
|
- * Constructs a new TypeReference.
|
|
- *
|
|
- * @param typeRef the int encoded value of the type reference, as received in a visit method
|
|
- * related to type annotations, such as {@link ClassVisitor#visitTypeAnnotation}.
|
|
- */
|
|
- public TypeReference(final int typeRef) {
|
|
- this.targetTypeAndInfo = typeRef;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a type reference of the given sort.
|
|
- *
|
|
- * @param sort one of {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link
|
|
- * #LOCAL_VARIABLE}, {@link #RESOURCE_VARIABLE}, {@link #INSTANCEOF}, {@link #NEW}, {@link
|
|
- * #CONSTRUCTOR_REFERENCE}, or {@link #METHOD_REFERENCE}.
|
|
- * @return a type reference of the given sort.
|
|
- */
|
|
- public static TypeReference newTypeReference(final int sort) {
|
|
- return new TypeReference(sort << 24);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to a type parameter of a generic class or method.
|
|
- *
|
|
- * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}.
|
|
- * @param paramIndex the type parameter index.
|
|
- * @return a reference to the given generic class or method type parameter.
|
|
- */
|
|
- public static TypeReference newTypeParameterReference(final int sort, final int paramIndex) {
|
|
- return new TypeReference((sort << 24) | (paramIndex << 16));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to a type parameter bound of a generic class or method.
|
|
- *
|
|
- * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}.
|
|
- * @param paramIndex the type parameter index.
|
|
- * @param boundIndex the type bound index within the above type parameters.
|
|
- * @return a reference to the given generic class or method type parameter bound.
|
|
- */
|
|
- public static TypeReference newTypeParameterBoundReference(
|
|
- final int sort, final int paramIndex, final int boundIndex) {
|
|
- return new TypeReference((sort << 24) | (paramIndex << 16) | (boundIndex << 8));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to the super class or to an interface of the 'implements' clause of a
|
|
- * class.
|
|
- *
|
|
- * @param itfIndex the index of an interface in the 'implements' clause of a class, or -1 to
|
|
- * reference the super class of the class.
|
|
- * @return a reference to the given super type of a class.
|
|
- */
|
|
- public static TypeReference newSuperTypeReference(final int itfIndex) {
|
|
- return new TypeReference((CLASS_EXTENDS << 24) | ((itfIndex & 0xFFFF) << 8));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to the type of a formal parameter of a method.
|
|
- *
|
|
- * @param paramIndex the formal parameter index.
|
|
- * @return a reference to the type of the given method formal parameter.
|
|
- */
|
|
- public static TypeReference newFormalParameterReference(final int paramIndex) {
|
|
- return new TypeReference((METHOD_FORMAL_PARAMETER << 24) | (paramIndex << 16));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to the type of an exception, in a 'throws' clause of a method.
|
|
- *
|
|
- * @param exceptionIndex the index of an exception in a 'throws' clause of a method.
|
|
- * @return a reference to the type of the given exception.
|
|
- */
|
|
- public static TypeReference newExceptionReference(final int exceptionIndex) {
|
|
- return new TypeReference((THROWS << 24) | (exceptionIndex << 8));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to the type of the exception declared in a 'catch' clause of a method.
|
|
- *
|
|
- * @param tryCatchBlockIndex the index of a try catch block (using the order in which they are
|
|
- * visited with visitTryCatchBlock).
|
|
- * @return a reference to the type of the given exception.
|
|
- */
|
|
- public static TypeReference newTryCatchReference(final int tryCatchBlockIndex) {
|
|
- return new TypeReference((EXCEPTION_PARAMETER << 24) | (tryCatchBlockIndex << 8));
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a reference to the type of a type argument in a constructor or method call or
|
|
- * reference.
|
|
- *
|
|
- * @param sort one of {@link #CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link
|
|
- * #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link
|
|
- * #METHOD_REFERENCE_TYPE_ARGUMENT}.
|
|
- * @param argIndex the type argument index.
|
|
- * @return a reference to the type of the given type argument.
|
|
- */
|
|
- public static TypeReference newTypeArgumentReference(final int sort, final int argIndex) {
|
|
- return new TypeReference((sort << 24) | argIndex);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the sort of this type reference.
|
|
- *
|
|
- * @return one of {@link #CLASS_TYPE_PARAMETER}, {@link #METHOD_TYPE_PARAMETER}, {@link
|
|
- * #CLASS_EXTENDS}, {@link #CLASS_TYPE_PARAMETER_BOUND}, {@link #METHOD_TYPE_PARAMETER_BOUND},
|
|
- * {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link
|
|
- * #METHOD_FORMAL_PARAMETER}, {@link #THROWS}, {@link #LOCAL_VARIABLE}, {@link
|
|
- * #RESOURCE_VARIABLE}, {@link #EXCEPTION_PARAMETER}, {@link #INSTANCEOF}, {@link #NEW},
|
|
- * {@link #CONSTRUCTOR_REFERENCE}, {@link #METHOD_REFERENCE}, {@link #CAST}, {@link
|
|
- * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link
|
|
- * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}.
|
|
- */
|
|
- public int getSort() {
|
|
- return targetTypeAndInfo >>> 24;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the type parameter referenced by this type reference. This method must
|
|
- * only be used for type references whose sort is {@link #CLASS_TYPE_PARAMETER}, {@link
|
|
- * #METHOD_TYPE_PARAMETER}, {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link
|
|
- * #METHOD_TYPE_PARAMETER_BOUND}.
|
|
- *
|
|
- * @return a type parameter index.
|
|
- */
|
|
- public int getTypeParameterIndex() {
|
|
- return (targetTypeAndInfo & 0x00FF0000) >> 16;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the type parameter bound, within the type parameter {@link
|
|
- * #getTypeParameterIndex}, referenced by this type reference. This method must only be used for
|
|
- * type references whose sort is {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link
|
|
- * #METHOD_TYPE_PARAMETER_BOUND}.
|
|
- *
|
|
- * @return a type parameter bound index.
|
|
- */
|
|
- public int getTypeParameterBoundIndex() {
|
|
- return (targetTypeAndInfo & 0x0000FF00) >> 8;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the "super type" of a class that is referenced by this type reference.
|
|
- * This method must only be used for type references whose sort is {@link #CLASS_EXTENDS}.
|
|
- *
|
|
- * @return the index of an interface in the 'implements' clause of a class, or -1 if this type
|
|
- * reference references the type of the super class.
|
|
- */
|
|
- public int getSuperTypeIndex() {
|
|
- return (short) ((targetTypeAndInfo & 0x00FFFF00) >> 8);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the formal parameter whose type is referenced by this type reference. This
|
|
- * method must only be used for type references whose sort is {@link #METHOD_FORMAL_PARAMETER}.
|
|
- *
|
|
- * @return a formal parameter index.
|
|
- */
|
|
- public int getFormalParameterIndex() {
|
|
- return (targetTypeAndInfo & 0x00FF0000) >> 16;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the exception, in a 'throws' clause of a method, whose type is referenced
|
|
- * by this type reference. This method must only be used for type references whose sort is {@link
|
|
- * #THROWS}.
|
|
- *
|
|
- * @return the index of an exception in the 'throws' clause of a method.
|
|
- */
|
|
- public int getExceptionIndex() {
|
|
- return (targetTypeAndInfo & 0x00FFFF00) >> 8;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the try catch block (using the order in which they are visited with
|
|
- * visitTryCatchBlock), whose 'catch' type is referenced by this type reference. This method must
|
|
- * only be used for type references whose sort is {@link #EXCEPTION_PARAMETER} .
|
|
- *
|
|
- * @return the index of an exception in the 'throws' clause of a method.
|
|
- */
|
|
- public int getTryCatchBlockIndex() {
|
|
- return (targetTypeAndInfo & 0x00FFFF00) >> 8;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the index of the type argument referenced by this type reference. This method must only
|
|
- * be used for type references whose sort is {@link #CAST}, {@link
|
|
- * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link
|
|
- * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}.
|
|
- *
|
|
- * @return a type parameter index.
|
|
- */
|
|
- public int getTypeArgumentIndex() {
|
|
- return targetTypeAndInfo & 0xFF;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the int encoded value of this type reference, suitable for use in visit methods related
|
|
- * to type annotations, like visitTypeAnnotation.
|
|
- *
|
|
- * @return the int encoded value of this type reference.
|
|
- */
|
|
- public int getValue() {
|
|
- return targetTypeAndInfo;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Puts the given target_type and target_info JVMS structures into the given ByteVector.
|
|
- *
|
|
- * @param targetTypeAndInfo a target_type and a target_info structures encoded as in {@link
|
|
- * #targetTypeAndInfo}. LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported.
|
|
- * @param output where the type reference must be put.
|
|
- */
|
|
- static void putTarget(final int targetTypeAndInfo, final ByteVector output) {
|
|
- switch (targetTypeAndInfo >>> 24) {
|
|
- case CLASS_TYPE_PARAMETER:
|
|
- case METHOD_TYPE_PARAMETER:
|
|
- case METHOD_FORMAL_PARAMETER:
|
|
- output.putShort(targetTypeAndInfo >>> 16);
|
|
- break;
|
|
- case FIELD:
|
|
- case METHOD_RETURN:
|
|
- case METHOD_RECEIVER:
|
|
- output.putByte(targetTypeAndInfo >>> 24);
|
|
- break;
|
|
- case CAST:
|
|
- case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
|
|
- case METHOD_INVOCATION_TYPE_ARGUMENT:
|
|
- case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
|
|
- case METHOD_REFERENCE_TYPE_ARGUMENT:
|
|
- output.putInt(targetTypeAndInfo);
|
|
- break;
|
|
- case CLASS_EXTENDS:
|
|
- case CLASS_TYPE_PARAMETER_BOUND:
|
|
- case METHOD_TYPE_PARAMETER_BOUND:
|
|
- case THROWS:
|
|
- case EXCEPTION_PARAMETER:
|
|
- case INSTANCEOF:
|
|
- case NEW:
|
|
- case CONSTRUCTOR_REFERENCE:
|
|
- case METHOD_REFERENCE:
|
|
- output.put12(targetTypeAndInfo >>> 24, (targetTypeAndInfo & 0xFFFF00) >> 8);
|
|
- break;
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/package.html b/src/main/java/org/mvel2/asm/package.html
|
|
deleted file mode 100644
|
|
index 6650fc8..0000000
|
|
--- a/src/main/java/org/mvel2/asm/package.html
|
|
+++ /dev/null
|
|
@@ -1,74 +0,0 @@
|
|
-<html>
|
|
-<!--
|
|
- * 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.
|
|
--->
|
|
-<body>
|
|
-Provides a small and fast bytecode manipulation framework.
|
|
-
|
|
-<p>
|
|
-The <a href="http://asm.ow2.org/">ASM</a> framework is organized
|
|
-around the {@link org.mvel2.asm.ClassVisitor ClassVisitor},
|
|
-{@link org.mvel2.asm.FieldVisitor FieldVisitor},
|
|
-{@link org.mvel2.asm.MethodVisitor MethodVisitor} and
|
|
-{@link org.mvel2.asm.AnnotationVisitor AnnotationVisitor} abstract classes,
|
|
-which allow one to visit the fields, methods and annotations of a class,
|
|
-including the bytecode instructions of each method.
|
|
-
|
|
-<p>
|
|
-In addition to these main abstract classes, ASM provides a {@link
|
|
-org.mvel2.asm.ClassReader ClassReader} class, that can parse an
|
|
-existing class and make a given visitor visit it. ASM also provides
|
|
-a {@link org.mvel2.asm.ClassWriter ClassWriter} class, which is
|
|
-a visitor that generates Java class files.
|
|
-
|
|
-<p>
|
|
-In order to generate a class from scratch, only the {@link
|
|
-org.mvel2.asm.ClassWriter ClassWriter} class is necessary. Indeed,
|
|
-in order to generate a class, one must just call its visit<i>Xxx</i>
|
|
-methods with the appropriate arguments to generate the desired fields
|
|
-and methods.
|
|
-
|
|
-<p>
|
|
-In order to modify existing classes, one must use a {@link
|
|
-org.mvel2.asm.ClassReader ClassReader} class to analyze
|
|
-the original class, a class modifier, and a {@link org.mvel2.asm.ClassWriter
|
|
-ClassWriter} to construct the modified class. The class modifier
|
|
-is just a {@link org.mvel2.asm.ClassVisitor ClassVisitor}
|
|
-that delegates most of the work to another {@link org.mvel2.asm.ClassVisitor
|
|
-ClassVisitor}, but that sometimes changes some parameter values,
|
|
-or call additional methods, in order to implement the desired
|
|
-modification process. In order to make it easier to implement such
|
|
-class modifiers, the {@link org.mvel2.asm.ClassVisitor
|
|
-ClassVisitor} and {@link org.mvel2.asm.MethodVisitor MethodVisitor}
|
|
-classes delegate by default all the method calls they receive to an
|
|
-optional visitor.
|
|
-
|
|
-@since ASM 1.3
|
|
-</body>
|
|
-</html>
|
|
diff --git a/src/main/java/org/mvel2/asm/signature/SignatureReader.java b/src/main/java/org/mvel2/asm/signature/SignatureReader.java
|
|
deleted file mode 100644
|
|
index a54d178..0000000
|
|
--- a/src/main/java/org/mvel2/asm/signature/SignatureReader.java
|
|
+++ /dev/null
|
|
@@ -1,252 +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.signature;
|
|
-
|
|
-/**
|
|
- * A parser for signature literals, as defined in the Java Virtual Machine Specification (JVMS), to
|
|
- * visit them with a SignatureVisitor.
|
|
- *
|
|
- * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1">JVMS
|
|
- * 4.7.9.1</a>
|
|
- * @author Thomas Hallgren
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public class SignatureReader {
|
|
-
|
|
- /** The JVMS signature to be read. */
|
|
- private final String signatureValue;
|
|
-
|
|
- /**
|
|
- * Constructs a {@link SignatureReader} for the given signature.
|
|
- *
|
|
- * @param signature A <i>JavaTypeSignature</i>, <i>ClassSignature</i> or <i>MethodSignature</i>.
|
|
- */
|
|
- public SignatureReader(final String signature) {
|
|
- this.signatureValue = signature;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Makes the given visitor visit the signature of this {@link SignatureReader}. This signature is
|
|
- * the one specified in the constructor (see {@link #SignatureReader}). This method is intended to
|
|
- * be called on a {@link SignatureReader} that was created using a <i>ClassSignature</i> (such as
|
|
- * the <code>signature</code> parameter of the {@link org.mvel2.asm.ClassVisitor#visit}
|
|
- * method) or a <i>MethodSignature</i> (such as the <code>signature</code> parameter of the {@link
|
|
- * org.mvel2.asm.ClassVisitor#visitMethod} method).
|
|
- *
|
|
- * @param signatureVistor the visitor that must visit this signature.
|
|
- */
|
|
- public void accept(final SignatureVisitor signatureVistor) {
|
|
- String signature = this.signatureValue;
|
|
- int length = signature.length();
|
|
- int offset; // Current offset in the parsed signature (parsed from left to right).
|
|
- char currentChar; // The signature character at 'offset', or just before.
|
|
-
|
|
- // If the signature starts with '<', it starts with TypeParameters, i.e. a formal type parameter
|
|
- // identifier, followed by one or more pair ':',ReferenceTypeSignature (for its class bound and
|
|
- // interface bounds).
|
|
- if (signature.charAt(0) == '<') {
|
|
- // Invariant: offset points to the second character of a formal type parameter name at the
|
|
- // beginning of each iteration of the loop below.
|
|
- offset = 2;
|
|
- do {
|
|
- // The formal type parameter name is everything between offset - 1 and the first ':'.
|
|
- int classBoundStartOffset = signature.indexOf(':', offset);
|
|
- signatureVistor.visitFormalTypeParameter(
|
|
- signature.substring(offset - 1, classBoundStartOffset));
|
|
-
|
|
- // If the character after the ':' class bound marker is not the start of a
|
|
- // ReferenceTypeSignature, it means the class bound is empty (which is a valid case).
|
|
- offset = classBoundStartOffset + 1;
|
|
- currentChar = signature.charAt(offset);
|
|
- if (currentChar == 'L' || currentChar == '[' || currentChar == 'T') {
|
|
- offset = parseType(signature, offset, signatureVistor.visitClassBound());
|
|
- }
|
|
-
|
|
- // While the character after the class bound or after the last parsed interface bound
|
|
- // is ':', we need to parse another interface bound.
|
|
- while ((currentChar = signature.charAt(offset++)) == ':') {
|
|
- offset = parseType(signature, offset, signatureVistor.visitInterfaceBound());
|
|
- }
|
|
-
|
|
- // At this point a TypeParameter has been fully parsed, and we need to parse the next one
|
|
- // (note that currentChar is now the first character of the next TypeParameter, and that
|
|
- // offset points to the second character), unless the character just after this
|
|
- // TypeParameter signals the end of the TypeParameters.
|
|
- } while (currentChar != '>');
|
|
- } else {
|
|
- offset = 0;
|
|
- }
|
|
-
|
|
- // If the (optional) TypeParameters is followed by '(' this means we are parsing a
|
|
- // MethodSignature, which has JavaTypeSignature type inside parentheses, followed by a Result
|
|
- // type and optional ThrowsSignature types.
|
|
- if (signature.charAt(offset) == '(') {
|
|
- offset++;
|
|
- while (signature.charAt(offset) != ')') {
|
|
- offset = parseType(signature, offset, signatureVistor.visitParameterType());
|
|
- }
|
|
- // Use offset + 1 to skip ')'.
|
|
- offset = parseType(signature, offset + 1, signatureVistor.visitReturnType());
|
|
- while (offset < length) {
|
|
- // Use offset + 1 to skip the first character of a ThrowsSignature, i.e. '^'.
|
|
- offset = parseType(signature, offset + 1, signatureVistor.visitExceptionType());
|
|
- }
|
|
- } else {
|
|
- // Otherwise we are parsing a ClassSignature (by hypothesis on the method input), which has
|
|
- // one or more ClassTypeSignature for the super class and the implemented interfaces.
|
|
- offset = parseType(signature, offset, signatureVistor.visitSuperclass());
|
|
- while (offset < length) {
|
|
- offset = parseType(signature, offset, signatureVistor.visitInterface());
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Makes the given visitor visit the signature of this {@link SignatureReader}. This signature is
|
|
- * the one specified in the constructor (see {@link #SignatureReader}). This method is intended to
|
|
- * be called on a {@link SignatureReader} that was created using a <i>JavaTypeSignature</i>, such
|
|
- * as the <code>signature</code> parameter of the {@link
|
|
- * org.mvel2.asm.ClassVisitor#visitField} or {@link
|
|
- * org.mvel2.asm.MethodVisitor#visitLocalVariable} methods.
|
|
- *
|
|
- * @param signatureVisitor the visitor that must visit this signature.
|
|
- */
|
|
- public void acceptType(final SignatureVisitor signatureVisitor) {
|
|
- parseType(signatureValue, 0, signatureVisitor);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Parses a JavaTypeSignature and makes the given visitor visit it.
|
|
- *
|
|
- * @param signature a string containing the signature that must be parsed.
|
|
- * @param startOffset index of the first character of the signature to parsed.
|
|
- * @param signatureVisitor the visitor that must visit this signature.
|
|
- * @return the index of the first character after the parsed signature.
|
|
- */
|
|
- private static int parseType(
|
|
- final String signature, final int startOffset, final SignatureVisitor signatureVisitor) {
|
|
- int offset = startOffset; // Current offset in the parsed signature.
|
|
- char currentChar = signature.charAt(offset++); // The signature character at 'offset'.
|
|
-
|
|
- // Switch based on the first character of the JavaTypeSignature, which indicates its kind.
|
|
- switch (currentChar) {
|
|
- case 'Z':
|
|
- case 'C':
|
|
- case 'B':
|
|
- case 'S':
|
|
- case 'I':
|
|
- case 'F':
|
|
- case 'J':
|
|
- case 'D':
|
|
- case 'V':
|
|
- // Case of a BaseType or a VoidDescriptor.
|
|
- signatureVisitor.visitBaseType(currentChar);
|
|
- return offset;
|
|
-
|
|
- case '[':
|
|
- // Case of an ArrayTypeSignature, a '[' followed by a JavaTypeSignature.
|
|
- return parseType(signature, offset, signatureVisitor.visitArrayType());
|
|
-
|
|
- case 'T':
|
|
- // Case of TypeVariableSignature, an identifier between 'T' and ';'.
|
|
- int endOffset = signature.indexOf(';', offset);
|
|
- signatureVisitor.visitTypeVariable(signature.substring(offset, endOffset));
|
|
- return endOffset + 1;
|
|
-
|
|
- case 'L':
|
|
- // Case of a ClassTypeSignature, which ends with ';'.
|
|
- // These signatures have a main class type followed by zero or more inner class types
|
|
- // (separated by '.'). Each can have type arguments, inside '<' and '>'.
|
|
- int start = offset; // The start offset of the currently parsed main or inner class name.
|
|
- boolean visited = false; // Whether the currently parsed class name has been visited.
|
|
- boolean inner = false; // Whether we are currently parsing an inner class type.
|
|
- // Parses the signature, one character at a time.
|
|
- while (true) {
|
|
- currentChar = signature.charAt(offset++);
|
|
- if (currentChar == '.' || currentChar == ';') {
|
|
- // If a '.' or ';' is encountered, this means we have fully parsed the main class name
|
|
- // or an inner class name. This name may already have been visited it is was followed by
|
|
- // type arguments between '<' and '>'. If not, we need to visit it here.
|
|
- if (!visited) {
|
|
- String name = signature.substring(start, offset - 1);
|
|
- if (inner) {
|
|
- signatureVisitor.visitInnerClassType(name);
|
|
- } else {
|
|
- signatureVisitor.visitClassType(name);
|
|
- }
|
|
- }
|
|
- // If we reached the end of the ClassTypeSignature return, otherwise start the parsing
|
|
- // of a new class name, which is necessarily an inner class name.
|
|
- if (currentChar == ';') {
|
|
- signatureVisitor.visitEnd();
|
|
- break;
|
|
- }
|
|
- start = offset;
|
|
- visited = false;
|
|
- inner = true;
|
|
- } else if (currentChar == '<') {
|
|
- // If a '<' is encountered, this means we have fully parsed the main class name or an
|
|
- // inner class name, and that we now need to parse TypeArguments. First, we need to
|
|
- // visit the parsed class name.
|
|
- String name = signature.substring(start, offset - 1);
|
|
- if (inner) {
|
|
- signatureVisitor.visitInnerClassType(name);
|
|
- } else {
|
|
- signatureVisitor.visitClassType(name);
|
|
- }
|
|
- visited = true;
|
|
- // Now, parse the TypeArgument(s), one at a time.
|
|
- while ((currentChar = signature.charAt(offset)) != '>') {
|
|
- switch (currentChar) {
|
|
- case '*':
|
|
- // Unbounded TypeArgument.
|
|
- ++offset;
|
|
- signatureVisitor.visitTypeArgument();
|
|
- break;
|
|
- case '+':
|
|
- case '-':
|
|
- // Extends or Super TypeArgument. Use offset + 1 to skip the '+' or '-'.
|
|
- offset =
|
|
- parseType(
|
|
- signature, offset + 1, signatureVisitor.visitTypeArgument(currentChar));
|
|
- break;
|
|
- default:
|
|
- // Instanceof TypeArgument. The '=' is implicit.
|
|
- offset = parseType(signature, offset, signatureVisitor.visitTypeArgument('='));
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
- return offset;
|
|
-
|
|
- default:
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/signature/SignatureVisitor.java b/src/main/java/org/mvel2/asm/signature/SignatureVisitor.java
|
|
deleted file mode 100644
|
|
index 5edcf9b..0000000
|
|
--- a/src/main/java/org/mvel2/asm/signature/SignatureVisitor.java
|
|
+++ /dev/null
|
|
@@ -1,203 +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.signature;
|
|
-
|
|
-import org.mvel2.asm.Opcodes;
|
|
-
|
|
-/**
|
|
- * A visitor to visit a generic signature. The methods of this interface must be called in one of
|
|
- * the three following orders (the last one is the only valid order for a {@link SignatureVisitor}
|
|
- * that is returned by a method of this interface):
|
|
- *
|
|
- * <ul>
|
|
- * <li><i>ClassSignature</i> = ( {@code visitFormalTypeParameter} {@code visitClassBound}? {@code
|
|
- * visitInterfaceBound}* )* ({@code visitSuperclass} {@code visitInterface}* )
|
|
- * <li><i>MethodSignature</i> = ( {@code visitFormalTypeParameter} {@code visitClassBound}? {@code
|
|
- * visitInterfaceBound}* )* ({@code visitParameterType}* {@code visitReturnType} {@code
|
|
- * visitExceptionType}* )
|
|
- * <li><i>TypeSignature</i> = {@code visitBaseType} | {@code visitTypeVariable} | {@code
|
|
- * visitArrayType} | ( {@code visitClassType} {@code visitTypeArgument}* ( {@code
|
|
- * visitInnerClassType} {@code visitTypeArgument}* )* {@code visitEnd} ) )
|
|
- * </ul>
|
|
- *
|
|
- * @author Thomas Hallgren
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public abstract class SignatureVisitor {
|
|
-
|
|
- /** Wildcard for an "extends" type argument. */
|
|
- public static final char EXTENDS = '+';
|
|
-
|
|
- /** Wildcard for a "super" type argument. */
|
|
- public static final char SUPER = '-';
|
|
-
|
|
- /** Wildcard for a normal type argument. */
|
|
- public static final char INSTANCEOF = '=';
|
|
-
|
|
- /**
|
|
- * 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;
|
|
-
|
|
- /**
|
|
- * Constructs a new {@link SignatureVisitor}.
|
|
- *
|
|
- * @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 SignatureVisitor(final int api) {
|
|
- if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- this.api = api;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a formal type parameter.
|
|
- *
|
|
- * @param name the name of the formal parameter.
|
|
- */
|
|
- public void visitFormalTypeParameter(final String name) {}
|
|
-
|
|
- /**
|
|
- * Visits the class bound of the last visited formal type parameter.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the class bound.
|
|
- */
|
|
- public SignatureVisitor visitClassBound() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits an interface bound of the last visited formal type parameter.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the interface bound.
|
|
- */
|
|
- public SignatureVisitor visitInterfaceBound() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the type of the super class.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the super class type.
|
|
- */
|
|
- public SignatureVisitor visitSuperclass() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the type of an interface implemented by the class.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the interface type.
|
|
- */
|
|
- public SignatureVisitor visitInterface() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the type of a method parameter.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the parameter type.
|
|
- */
|
|
- public SignatureVisitor visitParameterType() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the return type of the method.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the return type.
|
|
- */
|
|
- public SignatureVisitor visitReturnType() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits the type of a method exception.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the exception type.
|
|
- */
|
|
- public SignatureVisitor visitExceptionType() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Visits a signature corresponding to a primitive type.
|
|
- *
|
|
- * @param descriptor the descriptor of the primitive type, or 'V' for {@code void} .
|
|
- */
|
|
- public void visitBaseType(final char descriptor) {}
|
|
-
|
|
- /**
|
|
- * Visits a signature corresponding to a type variable.
|
|
- *
|
|
- * @param name the name of the type variable.
|
|
- */
|
|
- public void visitTypeVariable(final String name) {}
|
|
-
|
|
- /**
|
|
- * Visits a signature corresponding to an array type.
|
|
- *
|
|
- * @return a non null visitor to visit the signature of the array element type.
|
|
- */
|
|
- public SignatureVisitor visitArrayType() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Starts the visit of a signature corresponding to a class or interface type.
|
|
- *
|
|
- * @param name the internal name of the class or interface.
|
|
- */
|
|
- public void visitClassType(final String name) {}
|
|
-
|
|
- /**
|
|
- * Visits an inner class.
|
|
- *
|
|
- * @param name the local name of the inner class in its enclosing class.
|
|
- */
|
|
- public void visitInnerClassType(final String name) {}
|
|
-
|
|
- /** Visits an unbounded type argument of the last visited class or inner class type. */
|
|
- public void visitTypeArgument() {}
|
|
-
|
|
- /**
|
|
- * Visits a type argument of the last visited class or inner class type.
|
|
- *
|
|
- * @param wildcard '+', '-' or '='.
|
|
- * @return a non null visitor to visit the signature of the type argument.
|
|
- */
|
|
- public SignatureVisitor visitTypeArgument(final char wildcard) {
|
|
- return this;
|
|
- }
|
|
-
|
|
- /** Ends the visit of a signature corresponding to a class or interface type. */
|
|
- public void visitEnd() {}
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/signature/SignatureWriter.java b/src/main/java/org/mvel2/asm/signature/SignatureWriter.java
|
|
deleted file mode 100644
|
|
index 0a3961b..0000000
|
|
--- a/src/main/java/org/mvel2/asm/signature/SignatureWriter.java
|
|
+++ /dev/null
|
|
@@ -1,240 +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.signature;
|
|
-
|
|
-import org.mvel2.asm.Opcodes;
|
|
-
|
|
-/**
|
|
- * A SignatureVisitor that generates signature literals, 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.9.1">JVMS
|
|
- * 4.7.9.1</a>
|
|
- * @author Thomas Hallgren
|
|
- * @author Eric Bruneton
|
|
- */
|
|
-public class SignatureWriter extends SignatureVisitor {
|
|
-
|
|
- /** The builder used to construct the visited signature. */
|
|
- private final StringBuilder stringBuilder = new StringBuilder();
|
|
-
|
|
- /** Whether the visited signature contains formal type parameters. */
|
|
- private boolean hasFormals;
|
|
-
|
|
- /** Whether the visited signature contains method parameter types. */
|
|
- private boolean hasParameters;
|
|
-
|
|
- /**
|
|
- * The stack used to keep track of class types that have arguments. Each element of this stack is
|
|
- * a boolean encoded in one bit. The top of the stack is the least significant bit. Pushing false
|
|
- * = *2, pushing true = *2+1, popping = /2.
|
|
- *
|
|
- * <p>Class type arguments must be surrounded with '<' and '>' and, because
|
|
- *
|
|
- * <ol>
|
|
- * <li>class types can be nested (because type arguments can themselves be class types),
|
|
- * <li>SignatureWriter always returns 'this' in each visit* method (to avoid allocating new
|
|
- * SignatureWriter instances),
|
|
- * </ol>
|
|
- *
|
|
- * <p>we need a stack to properly balance these 'parentheses'. A new element is pushed on this
|
|
- * stack for each new visited type, and popped when the visit of this type ends (either is
|
|
- * visitEnd, or because visitInnerClassType is called).
|
|
- */
|
|
- private int argumentStack;
|
|
-
|
|
- /** Constructs a new {@link SignatureWriter}. */
|
|
- public SignatureWriter() {
|
|
- super(Opcodes.ASM7);
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Implementation of the SignatureVisitor interface
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- @Override
|
|
- public void visitFormalTypeParameter(final String name) {
|
|
- if (!hasFormals) {
|
|
- hasFormals = true;
|
|
- stringBuilder.append('<');
|
|
- }
|
|
- stringBuilder.append(name);
|
|
- stringBuilder.append(':');
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitClassBound() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitInterfaceBound() {
|
|
- stringBuilder.append(':');
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitSuperclass() {
|
|
- endFormals();
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitInterface() {
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitParameterType() {
|
|
- endFormals();
|
|
- if (!hasParameters) {
|
|
- hasParameters = true;
|
|
- stringBuilder.append('(');
|
|
- }
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitReturnType() {
|
|
- endFormals();
|
|
- if (!hasParameters) {
|
|
- stringBuilder.append('(');
|
|
- }
|
|
- stringBuilder.append(')');
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitExceptionType() {
|
|
- stringBuilder.append('^');
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitBaseType(final char descriptor) {
|
|
- stringBuilder.append(descriptor);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitTypeVariable(final String name) {
|
|
- stringBuilder.append('T');
|
|
- stringBuilder.append(name);
|
|
- stringBuilder.append(';');
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitArrayType() {
|
|
- stringBuilder.append('[');
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitClassType(final String name) {
|
|
- stringBuilder.append('L');
|
|
- stringBuilder.append(name);
|
|
- // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
|
|
- // we can tell at this point).
|
|
- argumentStack *= 2;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitInnerClassType(final String name) {
|
|
- endArguments();
|
|
- stringBuilder.append('.');
|
|
- stringBuilder.append(name);
|
|
- // Pushes 'false' on the stack, meaning that this type does not have type arguments (as far as
|
|
- // we can tell at this point).
|
|
- argumentStack *= 2;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitTypeArgument() {
|
|
- // If the top of the stack is 'false', this means we are visiting the first type argument of the
|
|
- // currently visited type. We therefore need to append a '<', and to replace the top stack
|
|
- // element with 'true' (meaning that the current type does have type arguments).
|
|
- if (argumentStack % 2 == 0) {
|
|
- argumentStack |= 1;
|
|
- stringBuilder.append('<');
|
|
- }
|
|
- stringBuilder.append('*');
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SignatureVisitor visitTypeArgument(final char wildcard) {
|
|
- // If the top of the stack is 'false', this means we are visiting the first type argument of the
|
|
- // currently visited type. We therefore need to append a '<', and to replace the top stack
|
|
- // element with 'true' (meaning that the current type does have type arguments).
|
|
- if (argumentStack % 2 == 0) {
|
|
- argumentStack |= 1;
|
|
- stringBuilder.append('<');
|
|
- }
|
|
- if (wildcard != '=') {
|
|
- stringBuilder.append(wildcard);
|
|
- }
|
|
- return this;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void visitEnd() {
|
|
- endArguments();
|
|
- stringBuilder.append(';');
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the signature that was built by this signature writer.
|
|
- *
|
|
- * @return the signature that was built by this signature writer.
|
|
- */
|
|
- @Override
|
|
- public String toString() {
|
|
- return stringBuilder.toString();
|
|
- }
|
|
-
|
|
- // -----------------------------------------------------------------------------------------------
|
|
- // Utility methods
|
|
- // -----------------------------------------------------------------------------------------------
|
|
-
|
|
- /** Ends the formal type parameters section of the signature. */
|
|
- private void endFormals() {
|
|
- if (hasFormals) {
|
|
- hasFormals = false;
|
|
- stringBuilder.append('>');
|
|
- }
|
|
- }
|
|
-
|
|
- /** Ends the type arguments of a class or inner class type. */
|
|
- private void endArguments() {
|
|
- // If the top of the stack is 'true', this means that some type arguments have been visited for
|
|
- // the type whose visit is now ending. We therefore need to append a '>', and to pop one element
|
|
- // from the stack.
|
|
- if (argumentStack % 2 == 1) {
|
|
- stringBuilder.append('>');
|
|
- }
|
|
- argumentStack /= 2;
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/org/mvel2/asm/signature/package.html b/src/main/java/org/mvel2/asm/signature/package.html
|
|
deleted file mode 100644
|
|
index 0c07d12..0000000
|
|
--- a/src/main/java/org/mvel2/asm/signature/package.html
|
|
+++ /dev/null
|
|
@@ -1,36 +0,0 @@
|
|
-<html>
|
|
-<!--
|
|
- * 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.
|
|
--->
|
|
-<body>
|
|
-Provides support for type signatures.
|
|
-
|
|
-@since ASM 2.0
|
|
-</body>
|
|
-</html>
|
|
diff --git a/src/main/java/org/mvel2/optimizers/OptimizerFactory.java b/src/main/java/org/mvel2/optimizers/OptimizerFactory.java
|
|
index bbe7f1a..8a33218 100644
|
|
--- a/src/main/java/org/mvel2/optimizers/OptimizerFactory.java
|
|
+++ b/src/main/java/org/mvel2/optimizers/OptimizerFactory.java
|
|
@@ -46,9 +46,9 @@ public class OptimizerFactory {
|
|
*/
|
|
try {
|
|
if (OptimizerFactory.class.getClassLoader() != null) {
|
|
- OptimizerFactory.class.getClassLoader().loadClass("org.mvel2.asm.ClassWriter");
|
|
+ OptimizerFactory.class.getClassLoader().loadClass("org.objectweb.asm.ClassWriter");
|
|
} else {
|
|
- ClassLoader.getSystemClassLoader().loadClass("org.mvel2.asm.ClassWriter");
|
|
+ ClassLoader.getSystemClassLoader().loadClass("org.objectweb.asm.ClassWriter");
|
|
}
|
|
accessorCompilers.put("ASM", new ASMAccessorOptimizer());
|
|
}
|
|
diff --git a/src/main/java/org/mvel2/optimizers/impl/asm/ASMAccessorOptimizer.java b/src/main/java/org/mvel2/optimizers/impl/asm/ASMAccessorOptimizer.java
|
|
index 497d7a9..3761964 100644
|
|
--- a/src/main/java/org/mvel2/optimizers/impl/asm/ASMAccessorOptimizer.java
|
|
+++ b/src/main/java/org/mvel2/optimizers/impl/asm/ASMAccessorOptimizer.java
|
|
@@ -37,10 +37,10 @@ import org.mvel2.MVEL;
|
|
import org.mvel2.OptimizationFailure;
|
|
import org.mvel2.ParserContext;
|
|
import org.mvel2.PropertyAccessException;
|
|
-import org.mvel2.asm.ClassWriter;
|
|
-import org.mvel2.asm.Label;
|
|
-import org.mvel2.asm.MethodVisitor;
|
|
-import org.mvel2.asm.Opcodes;
|
|
+import org.objectweb.asm.ClassWriter;
|
|
+import org.objectweb.asm.Label;
|
|
+import org.objectweb.asm.MethodVisitor;
|
|
+import org.objectweb.asm.Opcodes;
|
|
import org.mvel2.ast.FunctionInstance;
|
|
import org.mvel2.ast.TypeDescriptor;
|
|
import org.mvel2.ast.WithNode;
|
|
@@ -76,66 +76,8 @@ import static org.mvel2.DataConversion.canConvert;
|
|
import static org.mvel2.DataConversion.convert;
|
|
import static org.mvel2.MVEL.eval;
|
|
import static org.mvel2.MVEL.isAdvancedDebugging;
|
|
-import static org.mvel2.asm.Opcodes.AALOAD;
|
|
-import static org.mvel2.asm.Opcodes.AASTORE;
|
|
-import static org.mvel2.asm.Opcodes.ACC_PRIVATE;
|
|
-import static org.mvel2.asm.Opcodes.ACC_PUBLIC;
|
|
-import static org.mvel2.asm.Opcodes.ACONST_NULL;
|
|
-import static org.mvel2.asm.Opcodes.ALOAD;
|
|
-import static org.mvel2.asm.Opcodes.ANEWARRAY;
|
|
-import static org.mvel2.asm.Opcodes.ARETURN;
|
|
-import static org.mvel2.asm.Opcodes.ARRAYLENGTH;
|
|
-import static org.mvel2.asm.Opcodes.ASTORE;
|
|
-import static org.mvel2.asm.Opcodes.ATHROW;
|
|
-import static org.mvel2.asm.Opcodes.BALOAD;
|
|
-import static org.mvel2.asm.Opcodes.BASTORE;
|
|
-import static org.mvel2.asm.Opcodes.BIPUSH;
|
|
-import static org.mvel2.asm.Opcodes.CALOAD;
|
|
-import static org.mvel2.asm.Opcodes.CASTORE;
|
|
-import static org.mvel2.asm.Opcodes.CHECKCAST;
|
|
-import static org.mvel2.asm.Opcodes.DALOAD;
|
|
-import static org.mvel2.asm.Opcodes.DASTORE;
|
|
-import static org.mvel2.asm.Opcodes.DUP;
|
|
-import static org.mvel2.asm.Opcodes.DUP_X1;
|
|
-import static org.mvel2.asm.Opcodes.DUP_X2;
|
|
-import static org.mvel2.asm.Opcodes.FALOAD;
|
|
-import static org.mvel2.asm.Opcodes.FASTORE;
|
|
-import static org.mvel2.asm.Opcodes.GETFIELD;
|
|
-import static org.mvel2.asm.Opcodes.GETSTATIC;
|
|
-import static org.mvel2.asm.Opcodes.GOTO;
|
|
-import static org.mvel2.asm.Opcodes.IALOAD;
|
|
-import static org.mvel2.asm.Opcodes.IASTORE;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_0;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_1;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_2;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_3;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_4;
|
|
-import static org.mvel2.asm.Opcodes.ICONST_5;
|
|
-import static org.mvel2.asm.Opcodes.IFEQ;
|
|
-import static org.mvel2.asm.Opcodes.IFNONNULL;
|
|
-import static org.mvel2.asm.Opcodes.IF_ICMPLT;
|
|
-import static org.mvel2.asm.Opcodes.ILOAD;
|
|
-import static org.mvel2.asm.Opcodes.INVOKEINTERFACE;
|
|
-import static org.mvel2.asm.Opcodes.INVOKESPECIAL;
|
|
-import static org.mvel2.asm.Opcodes.INVOKESTATIC;
|
|
-import static org.mvel2.asm.Opcodes.INVOKEVIRTUAL;
|
|
-import static org.mvel2.asm.Opcodes.ISTORE;
|
|
-import static org.mvel2.asm.Opcodes.LALOAD;
|
|
-import static org.mvel2.asm.Opcodes.LASTORE;
|
|
-import static org.mvel2.asm.Opcodes.NEW;
|
|
-import static org.mvel2.asm.Opcodes.NEWARRAY;
|
|
-import static org.mvel2.asm.Opcodes.POP;
|
|
-import static org.mvel2.asm.Opcodes.PUTFIELD;
|
|
-import static org.mvel2.asm.Opcodes.RETURN;
|
|
-import static org.mvel2.asm.Opcodes.SALOAD;
|
|
-import static org.mvel2.asm.Opcodes.SASTORE;
|
|
-import static org.mvel2.asm.Opcodes.SIPUSH;
|
|
-import static org.mvel2.asm.Opcodes.SWAP;
|
|
-import static org.mvel2.asm.Type.getConstructorDescriptor;
|
|
-import static org.mvel2.asm.Type.getDescriptor;
|
|
-import static org.mvel2.asm.Type.getInternalName;
|
|
-import static org.mvel2.asm.Type.getMethodDescriptor;
|
|
-import static org.mvel2.asm.Type.getType;
|
|
+import static org.objectweb.asm.Opcodes.*;
|
|
+import static org.objectweb.asm.Type.*;
|
|
import static org.mvel2.ast.TypeDescriptor.getClassReference;
|
|
import static org.mvel2.integration.GlobalListenerFactory.hasGetListeners;
|
|
import static org.mvel2.integration.GlobalListenerFactory.notifyGetListeners;
|
|
@@ -800,7 +742,7 @@ public class ASMAccessorOptimizer extends AbstractOptimizer implements AccessorO
|
|
if (clazz.isPrimitive()) {
|
|
mv.visitFieldInsn(GETSTATIC, toNonPrimitiveType(clazz).getName().replace(".", "/"), "TYPE", "Ljava/lang/Class;");
|
|
} else {
|
|
- mv.visitLdcInsn(org.mvel2.asm.Type.getType(clazz));
|
|
+ mv.visitLdcInsn(org.objectweb.asm.Type.getType(clazz));
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/org/mvel2/optimizers/impl/asm/ProducesBytecode.java b/src/main/java/org/mvel2/optimizers/impl/asm/ProducesBytecode.java
|
|
index 7b2a17d..89371bb 100644
|
|
--- a/src/main/java/org/mvel2/optimizers/impl/asm/ProducesBytecode.java
|
|
+++ b/src/main/java/org/mvel2/optimizers/impl/asm/ProducesBytecode.java
|
|
@@ -18,7 +18,7 @@
|
|
|
|
package org.mvel2.optimizers.impl.asm;
|
|
|
|
-import org.mvel2.asm.MethodVisitor;
|
|
+import org.objectweb.asm.MethodVisitor;
|
|
import org.mvel2.integration.VariableResolverFactory;
|
|
|
|
/**
|
|
diff --git a/src/test/java/org/mvel2/tests/core/PropertyHandlerTests.java b/src/test/java/org/mvel2/tests/core/PropertyHandlerTests.java
|
|
index e71e511..be37ae0 100644
|
|
--- a/src/test/java/org/mvel2/tests/core/PropertyHandlerTests.java
|
|
+++ b/src/test/java/org/mvel2/tests/core/PropertyHandlerTests.java
|
|
@@ -3,9 +3,9 @@ package org.mvel2.tests.core;
|
|
import junit.framework.TestCase;
|
|
import org.mvel2.MVEL;
|
|
import org.mvel2.PropertyAccessor;
|
|
-import org.mvel2.asm.MethodVisitor;
|
|
+import org.objectweb.asm.MethodVisitor;
|
|
|
|
-import static org.mvel2.asm.Opcodes.*;
|
|
+import static org.objectweb.asm.Opcodes.*;
|
|
|
|
import org.mvel2.integration.*;
|
|
import org.mvel2.optimizers.OptimizerFactory;
|
|
diff --git a/src/test/java/org/mvel2/tests/core/res/SampleBeanAccessor.java b/src/test/java/org/mvel2/tests/core/res/SampleBeanAccessor.java
|
|
index c5912ae..5ca4142 100644
|
|
--- a/src/test/java/org/mvel2/tests/core/res/SampleBeanAccessor.java
|
|
+++ b/src/test/java/org/mvel2/tests/core/res/SampleBeanAccessor.java
|
|
@@ -1,9 +1,9 @@
|
|
package org.mvel2.tests.core.res;
|
|
|
|
-import org.mvel2.asm.MethodVisitor;
|
|
+import org.objectweb.asm.MethodVisitor;
|
|
|
|
-import static org.mvel2.asm.Opcodes.CHECKCAST;
|
|
-import static org.mvel2.asm.Opcodes.INVOKEVIRTUAL;
|
|
+import static org.objectweb.asm.Opcodes.CHECKCAST;
|
|
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
|
|
|
import org.mvel2.integration.PropertyHandler;
|
|
import org.mvel2.integration.VariableResolverFactory;
|