From 45ebf25404dbbb75aa566caa7755fe5279bdd313 Mon Sep 17 00:00:00 2001 From: Alex Macdonald Date: Sep 21 2020 15:15:42 +0000 Subject: update to 2.3.2.Final --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a83d7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/mvel-2.0.19.tar.xz +/mvel2-2.0.19.tar.gz +/mvel2-2.1.6.Final.tar.gz +/mvel2-2.2.2.Final.tar.gz +/mvel2-2.2.6.Final.tar.gz +/mvel2-2.2.7.Final.tar.gz +/mvel2-2.2.8.Final.tar.gz +/mvel2-2.3.2.Final.tar.gz diff --git a/0-use-system-asm.patch b/0-use-system-asm.patch new file mode 100644 index 0000000..3e1cf56 --- /dev/null +++ b/0-use-system-asm.patch @@ -0,0 +1,44715 @@ +diff --git a/pom.xml b/pom.xml +index bd712524..de0e3b37 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -257,6 +257,17 @@ + test + + ++ ++ org.ow2.asm ++ asm ++ 5.0.4 ++ ++ ++ org.ow2.asm ++ asm-util ++ 5.0.4 ++ ++ + + + +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 11ffd0da..00000000 +--- a/src/main/java/org/mvel2/asm/AnnotationVisitor.java ++++ /dev/null +@@ -1,169 +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: ( visit | visitEnum | +- * visitAnnotation | visitArray )* 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- * @param av +- * the annotation visitor to which this visitor must delegate +- * method calls. May be null. +- */ +- public AnnotationVisitor(final int api, final AnnotationVisitor av) { +- if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { +- throw new IllegalArgumentException(); +- } +- this.api = api; +- this.av = av; +- } +- +- /** +- * 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} or OBJECT or 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 visitArray} and visiting each array element +- * in turn, but is more convenient). +- */ +- public void visit(String name, Object value) { +- if (av != null) { +- av.visit(name, value); +- } +- } +- +- /** +- * Visits an enumeration value of the annotation. +- * +- * @param name +- * the value name. +- * @param desc +- * the class descriptor of the enumeration class. +- * @param value +- * the actual enumeration value. +- */ +- public void visitEnum(String name, String desc, String value) { +- if (av != null) { +- av.visitEnum(name, desc, value); +- } +- } +- +- /** +- * Visits a nested annotation value of the annotation. +- * +- * @param name +- * the value name. +- * @param desc +- * the class descriptor of the nested annotation class. +- * @return a visitor to visit the actual nested annotation value, or +- * null if this visitor is not interested in visiting this +- * nested annotation. The nested annotation value must be fully +- * visited before calling other methods on this annotation +- * visitor. +- */ +- public AnnotationVisitor visitAnnotation(String name, String desc) { +- if (av != null) { +- return av.visitAnnotation(name, desc); +- } +- 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 +- * null if this visitor is not interested in visiting these +- * values. The 'name' parameters passed to the methods of this +- * visitor are ignored. All the array values must be visited +- * before calling other methods on this annotation visitor. +- */ +- public AnnotationVisitor visitArray(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 7232e4d7..00000000 +--- a/src/main/java/org/mvel2/asm/AnnotationWriter.java ++++ /dev/null +@@ -1,371 +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 annotations in bytecode form. +- * +- * @author Eric Bruneton +- * @author Eugene Kuleshov +- */ +-final class AnnotationWriter extends AnnotationVisitor { +- +- /** +- * The class writer to which this annotation must be added. +- */ +- private final ClassWriter cw; +- +- /** +- * The number of values in this annotation. +- */ +- private int size; +- +- /** +- * true if values are named, false otherwise. Annotation +- * writers used for annotation default and annotation arrays use unnamed +- * values. +- */ +- private final boolean named; +- +- /** +- * The annotation values in bytecode form. This byte vector only contains +- * the values themselves, i.e. the number of values must be stored as a +- * unsigned short just before these bytes. +- */ +- private final ByteVector bv; +- +- /** +- * The byte vector to be used to store the number of values of this +- * annotation. See {@link #bv}. +- */ +- private final ByteVector parent; +- +- /** +- * Where the number of values of this annotation must be stored in +- * {@link #parent}. +- */ +- private final int offset; +- +- /** +- * Next annotation writer. This field is used to store annotation lists. +- */ +- AnnotationWriter next; +- +- /** +- * Previous annotation writer. This field is used to store annotation lists. +- */ +- AnnotationWriter prev; +- +- // ------------------------------------------------------------------------ +- // Constructor +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new {@link AnnotationWriter}. +- * +- * @param cw +- * the class writer to which this annotation must be added. +- * @param named +- * true if values are named, false otherwise. +- * @param bv +- * where the annotation values must be stored. +- * @param parent +- * where the number of annotation values must be stored. +- * @param offset +- * where in parent the number of annotation values must +- * be stored. +- */ +- AnnotationWriter(final ClassWriter cw, final boolean named, +- final ByteVector bv, final ByteVector parent, final int offset) { +- super(Opcodes.ASM5); +- this.cw = cw; +- this.named = named; +- this.bv = bv; +- this.parent = parent; +- this.offset = offset; +- } +- +- // ------------------------------------------------------------------------ +- // Implementation of the AnnotationVisitor abstract class +- // ------------------------------------------------------------------------ +- +- @Override +- public void visit(final String name, final Object value) { +- ++size; +- if (named) { +- bv.putShort(cw.newUTF8(name)); +- } +- if (value instanceof String) { +- bv.put12('s', cw.newUTF8((String) value)); +- } else if (value instanceof Byte) { +- bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); +- } else if (value instanceof Boolean) { +- int v = ((Boolean) value).booleanValue() ? 1 : 0; +- bv.put12('Z', cw.newInteger(v).index); +- } else if (value instanceof Character) { +- bv.put12('C', cw.newInteger(((Character) value).charValue()).index); +- } else if (value instanceof Short) { +- bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); +- } else if (value instanceof Type) { +- bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); +- } else if (value instanceof byte[]) { +- byte[] v = (byte[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('B', cw.newInteger(v[i]).index); +- } +- } else if (value instanceof boolean[]) { +- boolean[] v = (boolean[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); +- } +- } else if (value instanceof short[]) { +- short[] v = (short[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('S', cw.newInteger(v[i]).index); +- } +- } else if (value instanceof char[]) { +- char[] v = (char[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('C', cw.newInteger(v[i]).index); +- } +- } else if (value instanceof int[]) { +- int[] v = (int[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('I', cw.newInteger(v[i]).index); +- } +- } else if (value instanceof long[]) { +- long[] v = (long[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('J', cw.newLong(v[i]).index); +- } +- } else if (value instanceof float[]) { +- float[] v = (float[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('F', cw.newFloat(v[i]).index); +- } +- } else if (value instanceof double[]) { +- double[] v = (double[]) value; +- bv.put12('[', v.length); +- for (int i = 0; i < v.length; i++) { +- bv.put12('D', cw.newDouble(v[i]).index); +- } +- } else { +- Item i = cw.newConstItem(value); +- bv.put12(".s.IFJDCS".charAt(i.type), i.index); +- } +- } +- +- @Override +- public void visitEnum(final String name, final String desc, +- final String value) { +- ++size; +- if (named) { +- bv.putShort(cw.newUTF8(name)); +- } +- bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); +- } +- +- @Override +- public AnnotationVisitor visitAnnotation(final String name, +- final String desc) { +- ++size; +- if (named) { +- bv.putShort(cw.newUTF8(name)); +- } +- // write tag and type, and reserve space for values count +- bv.put12('@', cw.newUTF8(desc)).putShort(0); +- return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); +- } +- +- @Override +- public AnnotationVisitor visitArray(final String name) { +- ++size; +- if (named) { +- bv.putShort(cw.newUTF8(name)); +- } +- // write tag, and reserve space for array size +- bv.put12('[', 0); +- return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); +- } +- +- @Override +- public void visitEnd() { +- if (parent != null) { +- byte[] data = parent.data; +- data[offset] = (byte) (size >>> 8); +- data[offset + 1] = (byte) size; +- } +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the size of this annotation writer list. +- * +- * @return the size of this annotation writer list. +- */ +- int getSize() { +- int size = 0; +- AnnotationWriter aw = this; +- while (aw != null) { +- size += aw.bv.length; +- aw = aw.next; +- } +- return size; +- } +- +- /** +- * Puts the annotations of this annotation writer list into the given byte +- * vector. +- * +- * @param out +- * where the annotations must be put. +- */ +- void put(final ByteVector out) { +- int n = 0; +- int size = 2; +- AnnotationWriter aw = this; +- AnnotationWriter last = null; +- while (aw != null) { +- ++n; +- size += aw.bv.length; +- aw.visitEnd(); // in case user forgot to call visitEnd +- aw.prev = last; +- last = aw; +- aw = aw.next; +- } +- out.putInt(size); +- out.putShort(n); +- aw = last; +- while (aw != null) { +- out.putByteArray(aw.bv.data, 0, aw.bv.length); +- aw = aw.prev; +- } +- } +- +- /** +- * Puts the given annotation lists into the given byte vector. +- * +- * @param panns +- * an array of annotation writer lists. +- * @param off +- * index of the first annotation to be written. +- * @param out +- * where the annotations must be put. +- */ +- static void put(final AnnotationWriter[] panns, final int off, +- final ByteVector out) { +- int size = 1 + 2 * (panns.length - off); +- for (int i = off; i < panns.length; ++i) { +- size += panns[i] == null ? 0 : panns[i].getSize(); +- } +- out.putInt(size).putByte(panns.length - off); +- for (int i = off; i < panns.length; ++i) { +- AnnotationWriter aw = panns[i]; +- AnnotationWriter last = null; +- int n = 0; +- while (aw != null) { +- ++n; +- aw.visitEnd(); // in case user forgot to call visitEnd +- aw.prev = last; +- last = aw; +- aw = aw.next; +- } +- out.putShort(n); +- aw = last; +- while (aw != null) { +- out.putByteArray(aw.bv.data, 0, aw.bv.length); +- aw = aw.prev; +- } +- } +- } +- +- /** +- * Puts the given type reference and type path into the given bytevector. +- * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. +- * +- * @param typeRef +- * a reference to the annotated type. 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param out +- * where the type reference and type path must be put. +- */ +- static void putTarget(int typeRef, TypePath typePath, ByteVector out) { +- switch (typeRef >>> 24) { +- case 0x00: // CLASS_TYPE_PARAMETER +- case 0x01: // METHOD_TYPE_PARAMETER +- case 0x16: // METHOD_FORMAL_PARAMETER +- out.putShort(typeRef >>> 16); +- break; +- case 0x13: // FIELD +- case 0x14: // METHOD_RETURN +- case 0x15: // METHOD_RECEIVER +- out.putByte(typeRef >>> 24); +- break; +- case 0x47: // CAST +- case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT +- case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT +- out.putInt(typeRef); +- break; +- // case 0x10: // CLASS_EXTENDS +- // case 0x11: // CLASS_TYPE_PARAMETER_BOUND +- // case 0x12: // METHOD_TYPE_PARAMETER_BOUND +- // case 0x17: // THROWS +- // case 0x42: // EXCEPTION_PARAMETER +- // case 0x43: // INSTANCEOF +- // case 0x44: // NEW +- // case 0x45: // CONSTRUCTOR_REFERENCE +- // case 0x46: // METHOD_REFERENCE +- default: +- out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); +- break; +- } +- if (typePath == null) { +- out.putByte(0); +- } else { +- int length = typePath.b[typePath.offset] * 2 + 1; +- out.putByteArray(typePath.b, typePath.offset, length); +- } +- } +-} +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 3b7a2b68..00000000 +--- a/src/main/java/org/mvel2/asm/Attribute.java ++++ /dev/null +@@ -1,255 +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. +- * +- * @author Eric Bruneton +- * @author Eugene Kuleshov +- */ +-public class Attribute { +- +- /** +- * The type of this attribute. +- */ +- public final String type; +- +- /** +- * The raw value of this attribute, used only for unknown attributes. +- */ +- byte[] value; +- +- /** +- * The next attribute in this attribute list. May be null. +- */ +- Attribute next; +- +- /** +- * Constructs a new empty attribute. +- * +- * @param type +- * the type of the attribute. +- */ +- protected Attribute(final String type) { +- this.type = type; +- } +- +- /** +- * Returns true if this type of attribute is unknown. The default +- * implementation of this method always returns true. +- * +- * @return true if this type of attribute is unknown. +- */ +- public boolean isUnknown() { +- return true; +- } +- +- /** +- * Returns true if this type of attribute is a code attribute. +- * +- * @return 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 null if +- * this attribute is not a code attribute that contains labels. +- */ +- protected Label[] getLabels() { +- return null; +- } +- +- /** +- * Reads a {@link #type type} attribute. This method must return a +- * new {@link Attribute} object, of type {@link #type type}, +- * corresponding to the len bytes starting at the given offset, in +- * the given class reader. +- * +- * @param cr +- * the class that contains the attribute to be read. +- * @param off +- * index of the first byte of the attribute's content in +- * {@link ClassReader#b cr.b}. The 6 attribute header bytes, +- * containing the type and the length of the attribute, are not +- * taken into account here. +- * @param len +- * the length of the attribute's content. +- * @param buf +- * buffer to be used to call {@link ClassReader#readUTF8 +- * readUTF8}, {@link ClassReader#readClass(int,char[]) readClass} +- * or {@link ClassReader#readConst readConst}. +- * @param codeOff +- * index of the first byte of code's attribute content in +- * {@link ClassReader#b cr.b}, or -1 if the attribute to be read +- * is not a code attribute. The 6 attribute header bytes, +- * containing the type and the length of the attribute, are not +- * taken into account here. +- * @param labels +- * the labels of the method's code, or null if the +- * attribute to be read is not a code attribute. +- * @return a new {@link Attribute} object corresponding to the given +- * bytes. +- */ +- protected Attribute read(final ClassReader cr, final int off, +- final int len, final char[] buf, final int codeOff, +- final Label[] labels) { +- Attribute attr = new Attribute(type); +- attr.value = new byte[len]; +- System.arraycopy(cr.b, off, attr.value, 0, len); +- return attr; +- } +- +- /** +- * Returns the byte array form of this attribute. +- * +- * @param cw +- * the class to which this attribute must be added. This +- * parameter can be used to add to the constant pool of this +- * class the items that corresponds to this attribute. +- * @param code +- * the bytecode of the method corresponding to this code +- * attribute, or null if this attribute is not a code +- * attributes. +- * @param len +- * the length of the bytecode of the method corresponding to this +- * code attribute, or null if this attribute is not a +- * 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 cw, final byte[] code, +- final int len, final int maxStack, final int maxLocals) { +- ByteVector v = new ByteVector(); +- v.data = value; +- v.length = value.length; +- return v; +- } +- +- /** +- * Returns the length of the attribute list that begins with this attribute. +- * +- * @return the length of the attribute list that begins with this attribute. +- */ +- final int getCount() { +- int count = 0; +- Attribute attr = this; +- while (attr != null) { +- count += 1; +- attr = attr.next; +- } +- return count; +- } +- +- /** +- * Returns the size of all the attributes in this attribute list. +- * +- * @param cw +- * the class writer to be used to convert the attributes into +- * byte arrays, with the {@link #write write} method. +- * @param code +- * the bytecode of the method corresponding to these code +- * attributes, or null if these attributes are not code +- * attributes. +- * @param len +- * the length of the bytecode of the method corresponding to +- * these code attributes, or null if these attributes +- * are not code attributes. +- * @param maxStack +- * the maximum stack size of the method corresponding to these +- * code attributes, or -1 if these attributes are not code +- * attributes. +- * @param maxLocals +- * the maximum number of local variables of the method +- * corresponding to these code attributes, or -1 if these +- * attributes are not code attributes. +- * @return the size of all the attributes in this attribute list. This size +- * includes the size of the attribute headers. +- */ +- final int getSize(final ClassWriter cw, final byte[] code, final int len, +- final int maxStack, final int maxLocals) { +- Attribute attr = this; +- int size = 0; +- while (attr != null) { +- cw.newUTF8(attr.type); +- size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; +- attr = attr.next; +- } +- return size; +- } +- +- /** +- * Writes all the attributes of this attribute list in the given byte +- * vector. +- * +- * @param cw +- * the class writer to be used to convert the attributes into +- * byte arrays, with the {@link #write write} method. +- * @param code +- * the bytecode of the method corresponding to these code +- * attributes, or null if these attributes are not code +- * attributes. +- * @param len +- * the length of the bytecode of the method corresponding to +- * these code attributes, or null if these attributes +- * are not code attributes. +- * @param maxStack +- * the maximum stack size of the method corresponding to these +- * code attributes, or -1 if these attributes are not code +- * attributes. +- * @param maxLocals +- * the maximum number of local variables of the method +- * corresponding to these code attributes, or -1 if these +- * attributes are not code attributes. +- * @param out +- * where the attributes must be written. +- */ +- final void put(final ClassWriter cw, final byte[] code, final int len, +- final int maxStack, final int maxLocals, final ByteVector out) { +- Attribute attr = this; +- while (attr != null) { +- ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); +- out.putShort(cw.newUTF8(attr.type)).putInt(b.length); +- out.putByteArray(b.data, 0, b.length); +- attr = attr.next; +- } +- } +-} +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 0f66722c..00000000 +--- a/src/main/java/org/mvel2/asm/ByteVector.java ++++ /dev/null +@@ -1,339 +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. +- */ +- byte[] data; +- +- /** +- * Actual number of bytes in this vector. +- */ +- int length; +- +- /** +- * Constructs a new {@link ByteVector ByteVector} with a default initial +- * size. +- */ +- public ByteVector() { +- data = new byte[64]; +- } +- +- /** +- * Constructs a new {@link ByteVector ByteVector} with the given initial +- * size. +- * +- * @param initialSize +- * the initial size of the byte vector to be constructed. +- */ +- public ByteVector(final int initialSize) { +- data = new byte[initialSize]; +- } +- +- /** +- * Puts a byte into this byte vector. The byte vector is automatically +- * enlarged if necessary. +- * +- * @param b +- * a byte. +- * @return this byte vector. +- */ +- public ByteVector putByte(final int b) { +- int length = this.length; +- if (length + 1 > data.length) { +- enlarge(1); +- } +- data[length++] = (byte) b; +- this.length = length; +- return this; +- } +- +- /** +- * Puts two bytes into this byte vector. The byte vector is automatically +- * enlarged if necessary. +- * +- * @param b1 +- * a byte. +- * @param b2 +- * another byte. +- * @return this byte vector. +- */ +- ByteVector put11(final int b1, final int b2) { +- int length = this.length; +- if (length + 2 > data.length) { +- enlarge(2); +- } +- byte[] data = this.data; +- data[length++] = (byte) b1; +- data[length++] = (byte) b2; +- this.length = length; +- return this; +- } +- +- /** +- * Puts a short into this byte vector. The byte vector is automatically +- * enlarged if necessary. +- * +- * @param s +- * a short. +- * @return this byte vector. +- */ +- public ByteVector putShort(final int s) { +- int length = this.length; +- if (length + 2 > data.length) { +- enlarge(2); +- } +- byte[] data = this.data; +- data[length++] = (byte) (s >>> 8); +- data[length++] = (byte) s; +- this.length = length; +- return this; +- } +- +- /** +- * Puts a byte and a short into this byte vector. The byte vector is +- * automatically enlarged if necessary. +- * +- * @param b +- * a byte. +- * @param s +- * a short. +- * @return this byte vector. +- */ +- ByteVector put12(final int b, final int s) { +- int length = this.length; +- if (length + 3 > data.length) { +- enlarge(3); +- } +- byte[] data = this.data; +- data[length++] = (byte) b; +- data[length++] = (byte) (s >>> 8); +- data[length++] = (byte) s; +- this.length = length; +- return this; +- } +- +- /** +- * Puts an int into this byte vector. The byte vector is automatically +- * enlarged if necessary. +- * +- * @param i +- * an int. +- * @return this byte vector. +- */ +- public ByteVector putInt(final int i) { +- int length = this.length; +- if (length + 4 > data.length) { +- enlarge(4); +- } +- byte[] data = this.data; +- data[length++] = (byte) (i >>> 24); +- data[length++] = (byte) (i >>> 16); +- data[length++] = (byte) (i >>> 8); +- data[length++] = (byte) i; +- this.length = length; +- return this; +- } +- +- /** +- * Puts a long into this byte vector. The byte vector is automatically +- * enlarged if necessary. +- * +- * @param l +- * a long. +- * @return this byte vector. +- */ +- public ByteVector putLong(final long l) { +- int length = this.length; +- if (length + 8 > data.length) { +- enlarge(8); +- } +- byte[] data = this.data; +- int i = (int) (l >>> 32); +- data[length++] = (byte) (i >>> 24); +- data[length++] = (byte) (i >>> 16); +- data[length++] = (byte) (i >>> 8); +- data[length++] = (byte) i; +- i = (int) l; +- data[length++] = (byte) (i >>> 24); +- data[length++] = (byte) (i >>> 16); +- data[length++] = (byte) (i >>> 8); +- data[length++] = (byte) i; +- this.length = length; +- return this; +- } +- +- /** +- * Puts an UTF8 string into this byte vector. The byte vector is +- * automatically enlarged if necessary. +- * +- * @param s +- * a String whose UTF8 encoded length must be less than 65536. +- * @return this byte vector. +- */ +- public ByteVector putUTF8(final String s) { +- int charLength = s.length(); +- if (charLength > 65535) { +- throw new IllegalArgumentException(); +- } +- int len = length; +- if (len + 2 + charLength > data.length) { +- enlarge(2 + charLength); +- } +- byte[] data = this.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. +- data[len++] = (byte) (charLength >>> 8); +- data[len++] = (byte) charLength; +- for (int i = 0; i < charLength; ++i) { +- char c = s.charAt(i); +- if (c >= '\001' && c <= '\177') { +- data[len++] = (byte) c; +- } else { +- length = len; +- return encodeUTF8(s, i, 65535); +- } +- } +- length = len; +- 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 - i - 2 >= 0). +- * +- * @param s +- * the String to encode. +- * @param i +- * 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. +- */ +- ByteVector encodeUTF8(final String s, int i, int maxByteLength) { +- int charLength = s.length(); +- int byteLength = i; +- char c; +- for (int j = i; j < charLength; ++j) { +- c = s.charAt(j); +- if (c >= '\001' && c <= '\177') { +- byteLength++; +- } else if (c > '\u07FF') { +- byteLength += 3; +- } else { +- byteLength += 2; +- } +- } +- if (byteLength > maxByteLength) { +- throw new IllegalArgumentException(); +- } +- int start = length - i - 2; +- if (start >= 0) { +- data[start] = (byte) (byteLength >>> 8); +- data[start + 1] = (byte) byteLength; +- } +- if (length + byteLength - i > data.length) { +- enlarge(byteLength - i); +- } +- int len = length; +- for (int j = i; j < charLength; ++j) { +- c = s.charAt(j); +- if (c >= '\001' && c <= '\177') { +- data[len++] = (byte) c; +- } else if (c > '\u07FF') { +- data[len++] = (byte) (0xE0 | c >> 12 & 0xF); +- data[len++] = (byte) (0x80 | c >> 6 & 0x3F); +- data[len++] = (byte) (0x80 | c & 0x3F); +- } else { +- data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); +- data[len++] = (byte) (0x80 | c & 0x3F); +- } +- } +- length = len; +- return this; +- } +- +- /** +- * Puts an array of bytes into this byte vector. The byte vector is +- * automatically enlarged if necessary. +- * +- * @param b +- * an array of bytes. May be null to put len +- * null bytes into this byte vector. +- * @param off +- * index of the fist byte of b that must be copied. +- * @param len +- * number of bytes of b that must be copied. +- * @return this byte vector. +- */ +- public ByteVector putByteArray(final byte[] b, final int off, final int len) { +- if (length + len > data.length) { +- enlarge(len); +- } +- if (b != null) { +- System.arraycopy(b, off, data, length, len); +- } +- length += len; +- return this; +- } +- +- /** +- * Enlarge this byte vector so that it can receive n more bytes. +- * +- * @param size +- * number of additional bytes that this byte vector should be +- * able to receive. +- */ +- private void enlarge(final int size) { +- int length1 = 2 * data.length; +- int length2 = length + size; +- byte[] newData = new byte[length1 > length2 ? length1 : length2]; +- 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 605acbf2..00000000 +--- a/src/main/java/org/mvel2/asm/ClassReader.java ++++ /dev/null +@@ -1,2496 +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.IOException; +-import java.io.InputStream; +- +-/** +- * A Java class parser to make a {@link ClassVisitor} visit an existing class. +- * This class parses a byte array conforming to the Java class file format and +- * calls the appropriate visit methods of a given class visitor for each field, +- * method and bytecode instruction encountered. +- * +- * @author Eric Bruneton +- * @author Eugene Kuleshov +- */ +-public class ClassReader { +- +- /** +- * True to enable signatures support. +- */ +- static final boolean SIGNATURES = true; +- +- /** +- * True to enable annotations support. +- */ +- static final boolean ANNOTATIONS = true; +- +- /** +- * True to enable stack map frames support. +- */ +- static final boolean FRAMES = true; +- +- /** +- * True to enable bytecode writing support. +- */ +- static final boolean WRITER = true; +- +- /** +- * True to enable JSR_W and GOTO_W support. +- */ +- static final boolean RESIZE = true; +- +- /** +- * Flag to skip method code. If this class is set CODE +- * attribute won't be visited. This can be used, for example, to retrieve +- * annotations for methods and method parameters. +- */ +- public static final int SKIP_CODE = 1; +- +- /** +- * Flag to skip the debug information in the class. If this flag is set the +- * debug information of the class is not visited, i.e. the +- * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and +- * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be +- * called. +- */ +- public static final int SKIP_DEBUG = 2; +- +- /** +- * Flag to skip the stack map frames in the class. If this flag is set the +- * stack map frames of the class is not visited, i.e. the +- * {@link MethodVisitor#visitFrame visitFrame} method will not be 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 in the class writer. +- */ +- public static final int SKIP_FRAMES = 4; +- +- /** +- * 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/recompression step in ClassReader and +- * ClassWriter which degrades performances quite a lot). +- */ +- public static final int EXPAND_FRAMES = 8; +- +- /** +- * The class to be parsed. 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 generators or adapters. +- */ +- public final byte[] b; +- +- /** +- * The start index of each constant pool item in {@link #b b}, plus one. The +- * one byte offset skips the constant pool item tag that indicates its type. +- */ +- private final int[] items; +- +- /** +- * The String objects corresponding to the CONSTANT_Utf8 items. This cache +- * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, +- * which GREATLY improves performances (by a factor 2 to 3). This caching +- * strategy could be extended to all constant pool items, but its benefit +- * would not be so great for these items (because they are much less +- * expensive to parse than CONSTANT_Utf8 items). +- */ +- private final String[] strings; +- +- /** +- * Maximum length of the strings contained in the constant pool of the +- * class. +- */ +- private final int maxStringLength; +- +- /** +- * Start index of the class header information (access, name...) in +- * {@link #b b}. +- */ +- public final int header; +- +- // ------------------------------------------------------------------------ +- // Constructors +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new {@link ClassReader} object. +- * +- * @param b +- * the bytecode of the class to be read. +- */ +- public ClassReader(final byte[] b) { +- this(b, 0, b.length); +- } +- +- /** +- * Constructs a new {@link ClassReader} object. +- * +- * @param b +- * the bytecode of the class to be read. +- * @param off +- * the start offset of the class data. +- * @param len +- * the length of the class data. +- */ +- public ClassReader(final byte[] b, final int off, final int len) { +- this.b = b; +- // checks the class version +- if (readShort(off + 6) > Opcodes.V1_9) { +- throw new IllegalArgumentException(); +- } +- // parses the constant pool +- items = new int[readUnsignedShort(off + 8)]; +- int n = items.length; +- strings = new String[n]; +- int max = 0; +- int index = off + 10; +- for (int i = 1; i < n; ++i) { +- items[i] = index + 1; +- int size; +- switch (b[index]) { +- case ClassWriter.FIELD: +- case ClassWriter.METH: +- case ClassWriter.IMETH: +- case ClassWriter.INT: +- case ClassWriter.FLOAT: +- case ClassWriter.NAME_TYPE: +- case ClassWriter.INDY: +- size = 5; +- break; +- case ClassWriter.LONG: +- case ClassWriter.DOUBLE: +- size = 9; +- ++i; +- break; +- case ClassWriter.UTF8: +- size = 3 + readUnsignedShort(index + 1); +- if (size > max) { +- max = size; +- } +- break; +- case ClassWriter.HANDLE: +- size = 4; +- break; +- // case ClassWriter.CLASS: +- // case ClassWriter.STR: +- // case ClassWriter.MTYPE +- default: +- size = 3; +- break; +- } +- index += size; +- } +- maxStringLength = max; +- // the class header information starts just after the constant pool +- header = index; +- } +- +- /** +- * 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() getInternalName}). +- * +- * @return the internal class name +- * +- * @see ClassVisitor#visit(int, int, String, String, String, String[]) +- */ +- public String getClassName() { +- return readClass(header + 2, new char[maxStringLength]); +- } +- +- /** +- * Returns the internal of name of the super class (see +- * {@link Type#getInternalName() getInternalName}). For interfaces, the +- * super class is {@link Object}. +- * +- * @return the internal name of super class, or null for +- * {@link Object} class. +- * +- * @see ClassVisitor#visit(int, int, String, String, String, String[]) +- */ +- public String getSuperName() { +- return readClass(header + 4, new char[maxStringLength]); +- } +- +- /** +- * Returns the internal names of the class's interfaces (see +- * {@link Type#getInternalName() getInternalName}). +- * +- * @return the array of internal names for all implemented interfaces or +- * null. +- * +- * @see ClassVisitor#visit(int, int, String, String, String, String[]) +- */ +- public String[] getInterfaces() { +- int index = header + 6; +- int n = readUnsignedShort(index); +- String[] interfaces = new String[n]; +- if (n > 0) { +- char[] buf = new char[maxStringLength]; +- for (int i = 0; i < n; ++i) { +- index += 2; +- interfaces[i] = readClass(index, buf); +- } +- } +- return interfaces; +- } +- +- /** +- * Copies the constant pool data into the given {@link ClassWriter}. Should +- * be called before the {@link #accept(ClassVisitor,int)} method. +- * +- * @param classWriter +- * the {@link ClassWriter} to copy constant pool into. +- */ +- void copyPool(final ClassWriter classWriter) { +- char[] buf = new char[maxStringLength]; +- int ll = items.length; +- Item[] items2 = new Item[ll]; +- for (int i = 1; i < ll; i++) { +- int index = items[i]; +- int tag = b[index - 1]; +- Item item = new Item(i); +- int nameType; +- switch (tag) { +- case ClassWriter.FIELD: +- case ClassWriter.METH: +- case ClassWriter.IMETH: +- nameType = items[readUnsignedShort(index + 2)]; +- item.set(tag, readClass(index, buf), readUTF8(nameType, buf), +- readUTF8(nameType + 2, buf)); +- break; +- case ClassWriter.INT: +- item.set(readInt(index)); +- break; +- case ClassWriter.FLOAT: +- item.set(Float.intBitsToFloat(readInt(index))); +- break; +- case ClassWriter.NAME_TYPE: +- item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf), +- null); +- break; +- case ClassWriter.LONG: +- item.set(readLong(index)); +- ++i; +- break; +- case ClassWriter.DOUBLE: +- item.set(Double.longBitsToDouble(readLong(index))); +- ++i; +- break; +- case ClassWriter.UTF8: { +- String s = strings[i]; +- if (s == null) { +- index = items[i]; +- s = strings[i] = readUTF(index + 2, +- readUnsignedShort(index), buf); +- } +- item.set(tag, s, null, null); +- break; +- } +- case ClassWriter.HANDLE: { +- int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; +- nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; +- item.set(ClassWriter.HANDLE_BASE + readByte(index), +- readClass(fieldOrMethodRef, buf), +- readUTF8(nameType, buf), readUTF8(nameType + 2, buf)); +- break; +- } +- case ClassWriter.INDY: +- if (classWriter.bootstrapMethods == null) { +- copyBootstrapMethods(classWriter, items2, buf); +- } +- nameType = items[readUnsignedShort(index + 2)]; +- item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf), +- readUnsignedShort(index)); +- break; +- // case ClassWriter.STR: +- // case ClassWriter.CLASS: +- // case ClassWriter.MTYPE +- default: +- item.set(tag, readUTF8(index, buf), null, null); +- break; +- } +- +- int index2 = item.hashCode % items2.length; +- item.next = items2[index2]; +- items2[index2] = item; +- } +- +- int off = items[1] - 1; +- classWriter.pool.putByteArray(b, off, header - off); +- classWriter.items = items2; +- classWriter.threshold = (int) (0.75d * ll); +- classWriter.index = ll; +- } +- +- /** +- * Copies the bootstrap method data into the given {@link ClassWriter}. +- * Should be called before the {@link #accept(ClassVisitor,int)} method. +- * +- * @param classWriter +- * the {@link ClassWriter} to copy bootstrap methods into. +- */ +- private void copyBootstrapMethods(final ClassWriter classWriter, +- final Item[] items, final char[] c) { +- // finds the "BootstrapMethods" attribute +- int u = getAttributes(); +- boolean found = false; +- for (int i = readUnsignedShort(u); i > 0; --i) { +- String attrName = readUTF8(u + 2, c); +- if ("BootstrapMethods".equals(attrName)) { +- found = true; +- break; +- } +- u += 6 + readInt(u + 4); +- } +- if (!found) { +- return; +- } +- // copies the bootstrap methods in the class writer +- int boostrapMethodCount = readUnsignedShort(u + 8); +- for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { +- int position = v - u - 10; +- int hashCode = readConst(readUnsignedShort(v), c).hashCode(); +- for (int k = readUnsignedShort(v + 2); k > 0; --k) { +- hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); +- v += 2; +- } +- v += 4; +- Item item = new Item(j); +- item.set(position, hashCode & 0x7FFFFFFF); +- int index = item.hashCode % items.length; +- item.next = items[index]; +- items[index] = item; +- } +- int attrSize = readInt(u + 4); +- ByteVector bootstrapMethods = new ByteVector(attrSize + 62); +- bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); +- classWriter.bootstrapMethodsCount = boostrapMethodCount; +- classWriter.bootstrapMethods = bootstrapMethods; +- } +- +- /** +- * Constructs a new {@link ClassReader} object. +- * +- * @param is +- * an input stream from which to read the class. +- * @throws IOException +- * if a problem occurs during reading. +- */ +- public ClassReader(final InputStream is) throws IOException { +- this(readClass(is, false)); +- } +- +- /** +- * Constructs a new {@link ClassReader} object. +- * +- * @param name +- * the binary qualified name of the class to be read. +- * @throws IOException +- * if an exception occurs during reading. +- */ +- public ClassReader(final String name) throws IOException { +- this(readClass( +- ClassLoader.getSystemResourceAsStream(name.replace('.', '/') +- + ".class"), true)); +- } +- +- /** +- * Reads the bytecode of a class. +- * +- * @param is +- * an input stream from which to read the class. +- * @param close +- * true to close the input stream after reading. +- * @return the bytecode read from the given input stream. +- * @throws IOException +- * if a problem occurs during reading. +- */ +- private static byte[] readClass(final InputStream is, boolean close) +- throws IOException { +- if (is == null) { +- throw new IOException("Class not found"); +- } +- try { +- byte[] b = new byte[is.available()]; +- int len = 0; +- while (true) { +- int n = is.read(b, len, b.length - len); +- if (n == -1) { +- if (len < b.length) { +- byte[] c = new byte[len]; +- System.arraycopy(b, 0, c, 0, len); +- b = c; +- } +- return b; +- } +- len += n; +- if (len == b.length) { +- int last = is.read(); +- if (last < 0) { +- return b; +- } +- byte[] c = new byte[b.length + 1000]; +- System.arraycopy(b, 0, c, 0, len); +- c[len++] = (byte) last; +- b = c; +- } +- } +- } finally { +- if (close) { +- is.close(); +- } +- } +- } +- +- // ------------------------------------------------------------------------ +- // Public methods +- // ------------------------------------------------------------------------ +- +- /** +- * Makes the given visitor visit the Java class of this {@link ClassReader} +- * . This class is the one specified in the constructor (see +- * {@link #ClassReader(byte[]) ClassReader}). +- * +- * @param classVisitor +- * the visitor that must visit this class. +- * @param flags +- * option flags that can be used to modify the default behavior +- * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} +- * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. +- */ +- public void accept(final ClassVisitor classVisitor, final int flags) { +- accept(classVisitor, new Attribute[0], flags); +- } +- +- /** +- * Makes the given visitor visit the Java class of this {@link ClassReader}. +- * This class is the one specified in the constructor (see +- * {@link #ClassReader(byte[]) ClassReader}). +- * +- * @param classVisitor +- * the visitor that must visit this class. +- * @param attrs +- * 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. +- * 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. +- * @param flags +- * option flags that can be used to modify the default behavior +- * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} +- * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. +- */ +- public void accept(final ClassVisitor classVisitor, +- final Attribute[] attrs, final int flags) { +- int u = header; // current offset in the class file +- char[] c = new char[maxStringLength]; // buffer used to read strings +- +- Context context = new Context(); +- context.attrs = attrs; +- context.flags = flags; +- context.buffer = c; +- +- // reads the class declaration +- int access = readUnsignedShort(u); +- String name = readClass(u + 2, c); +- String superClass = readClass(u + 4, c); +- String[] interfaces = new String[readUnsignedShort(u + 6)]; +- u += 8; +- for (int i = 0; i < interfaces.length; ++i) { +- interfaces[i] = readClass(u, c); +- u += 2; +- } +- +- // reads the class attributes +- String signature = null; +- String sourceFile = null; +- String sourceDebug = null; +- String enclosingOwner = null; +- String enclosingName = null; +- String enclosingDesc = null; +- int anns = 0; +- int ianns = 0; +- int tanns = 0; +- int itanns = 0; +- int innerClasses = 0; +- Attribute attributes = null; +- +- u = getAttributes(); +- for (int i = readUnsignedShort(u); i > 0; --i) { +- String attrName = readUTF8(u + 2, c); +- // tests are sorted in decreasing frequency order +- // (based on frequencies observed on typical classes) +- if ("SourceFile".equals(attrName)) { +- sourceFile = readUTF8(u + 8, c); +- } else if ("InnerClasses".equals(attrName)) { +- innerClasses = u + 8; +- } else if ("EnclosingMethod".equals(attrName)) { +- enclosingOwner = readClass(u + 8, c); +- int item = readUnsignedShort(u + 10); +- if (item != 0) { +- enclosingName = readUTF8(items[item], c); +- enclosingDesc = readUTF8(items[item] + 2, c); +- } +- } else if (SIGNATURES && "Signature".equals(attrName)) { +- signature = readUTF8(u + 8, c); +- } else if (ANNOTATIONS +- && "RuntimeVisibleAnnotations".equals(attrName)) { +- anns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeVisibleTypeAnnotations".equals(attrName)) { +- tanns = u + 8; +- } else if ("Deprecated".equals(attrName)) { +- access |= Opcodes.ACC_DEPRECATED; +- } else if ("Synthetic".equals(attrName)) { +- access |= Opcodes.ACC_SYNTHETIC +- | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; +- } else if ("SourceDebugExtension".equals(attrName)) { +- int len = readInt(u + 4); +- sourceDebug = readUTF(u + 8, len, new char[len]); +- } else if (ANNOTATIONS +- && "RuntimeInvisibleAnnotations".equals(attrName)) { +- ianns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { +- itanns = u + 8; +- } else if ("BootstrapMethods".equals(attrName)) { +- int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; +- for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { +- bootstrapMethods[j] = v; +- v += 2 + readUnsignedShort(v + 2) << 1; +- } +- context.bootstrapMethods = bootstrapMethods; +- } else { +- Attribute attr = readAttribute(attrs, attrName, u + 8, +- readInt(u + 4), c, -1, null); +- if (attr != null) { +- attr.next = attributes; +- attributes = attr; +- } +- } +- u += 6 + readInt(u + 4); +- } +- +- // visits the class declaration +- classVisitor.visit(readInt(items[1] - 7), access, name, signature, +- superClass, interfaces); +- +- // visits the source and debug info +- if ((flags & SKIP_DEBUG) == 0 +- && (sourceFile != null || sourceDebug != null)) { +- classVisitor.visitSource(sourceFile, sourceDebug); +- } +- +- // visits the outer class +- if (enclosingOwner != null) { +- classVisitor.visitOuterClass(enclosingOwner, enclosingName, +- enclosingDesc); +- } +- +- // visits the class annotations and type annotations +- if (ANNOTATIONS && anns != 0) { +- for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- classVisitor.visitAnnotation(readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && ianns != 0) { +- for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- classVisitor.visitAnnotation(readUTF8(v, c), false)); +- } +- } +- if (ANNOTATIONS && tanns != 0) { +- for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- classVisitor.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && itanns != 0) { +- for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- classVisitor.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), false)); +- } +- } +- +- // visits the attributes +- while (attributes != null) { +- Attribute attr = attributes.next; +- attributes.next = null; +- classVisitor.visitAttribute(attributes); +- attributes = attr; +- } +- +- // visits the inner classes +- if (innerClasses != 0) { +- int v = innerClasses + 2; +- for (int i = readUnsignedShort(innerClasses); i > 0; --i) { +- classVisitor.visitInnerClass(readClass(v, c), +- readClass(v + 2, c), readUTF8(v + 4, c), +- readUnsignedShort(v + 6)); +- v += 8; +- } +- } +- +- // visits the fields and methods +- u = header + 10 + 2 * interfaces.length; +- for (int i = readUnsignedShort(u - 2); i > 0; --i) { +- u = readField(classVisitor, context, u); +- } +- u += 2; +- for (int i = readUnsignedShort(u - 2); i > 0; --i) { +- u = readMethod(classVisitor, context, u); +- } +- +- // visits the end of the class +- classVisitor.visitEnd(); +- } +- +- /** +- * Reads a field 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 u +- * the start offset of the field in the class file. +- * @return the offset of the first byte following the field in the class. +- */ +- private int readField(final ClassVisitor classVisitor, +- final Context context, int u) { +- // reads the field declaration +- char[] c = context.buffer; +- int access = readUnsignedShort(u); +- String name = readUTF8(u + 2, c); +- String desc = readUTF8(u + 4, c); +- u += 6; +- +- // reads the field attributes +- String signature = null; +- int anns = 0; +- int ianns = 0; +- int tanns = 0; +- int itanns = 0; +- Object value = null; +- Attribute attributes = null; +- +- for (int i = readUnsignedShort(u); i > 0; --i) { +- String attrName = readUTF8(u + 2, c); +- // tests are sorted in decreasing frequency order +- // (based on frequencies observed on typical classes) +- if ("ConstantValue".equals(attrName)) { +- int item = readUnsignedShort(u + 8); +- value = item == 0 ? null : readConst(item, c); +- } else if (SIGNATURES && "Signature".equals(attrName)) { +- signature = readUTF8(u + 8, c); +- } else if ("Deprecated".equals(attrName)) { +- access |= Opcodes.ACC_DEPRECATED; +- } else if ("Synthetic".equals(attrName)) { +- access |= Opcodes.ACC_SYNTHETIC +- | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; +- } else if (ANNOTATIONS +- && "RuntimeVisibleAnnotations".equals(attrName)) { +- anns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeVisibleTypeAnnotations".equals(attrName)) { +- tanns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleAnnotations".equals(attrName)) { +- ianns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { +- itanns = u + 8; +- } else { +- Attribute attr = readAttribute(context.attrs, attrName, u + 8, +- readInt(u + 4), c, -1, null); +- if (attr != null) { +- attr.next = attributes; +- attributes = attr; +- } +- } +- u += 6 + readInt(u + 4); +- } +- u += 2; +- +- // visits the field declaration +- FieldVisitor fv = classVisitor.visitField(access, name, desc, +- signature, value); +- if (fv == null) { +- return u; +- } +- +- // visits the field annotations and type annotations +- if (ANNOTATIONS && anns != 0) { +- for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- fv.visitAnnotation(readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && ianns != 0) { +- for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- fv.visitAnnotation(readUTF8(v, c), false)); +- } +- } +- if (ANNOTATIONS && tanns != 0) { +- for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- fv.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && itanns != 0) { +- for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- fv.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), false)); +- } +- } +- +- // visits the field attributes +- while (attributes != null) { +- Attribute attr = attributes.next; +- attributes.next = null; +- fv.visitAttribute(attributes); +- attributes = attr; +- } +- +- // visits the end of the field +- fv.visitEnd(); +- +- return u; +- } +- +- /** +- * Reads a method 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 u +- * the start offset of the method in the class file. +- * @return the offset of the first byte following the method in the class. +- */ +- private int readMethod(final ClassVisitor classVisitor, +- final Context context, int u) { +- // reads the method declaration +- char[] c = context.buffer; +- context.access = readUnsignedShort(u); +- context.name = readUTF8(u + 2, c); +- context.desc = readUTF8(u + 4, c); +- u += 6; +- +- // reads the method attributes +- int code = 0; +- int exception = 0; +- String[] exceptions = null; +- String signature = null; +- int methodParameters = 0; +- int anns = 0; +- int ianns = 0; +- int tanns = 0; +- int itanns = 0; +- int dann = 0; +- int mpanns = 0; +- int impanns = 0; +- int firstAttribute = u; +- Attribute attributes = null; +- +- for (int i = readUnsignedShort(u); i > 0; --i) { +- String attrName = readUTF8(u + 2, c); +- // tests are sorted in decreasing frequency order +- // (based on frequencies observed on typical classes) +- if ("Code".equals(attrName)) { +- if ((context.flags & SKIP_CODE) == 0) { +- code = u + 8; +- } +- } else if ("Exceptions".equals(attrName)) { +- exceptions = new String[readUnsignedShort(u + 8)]; +- exception = u + 10; +- for (int j = 0; j < exceptions.length; ++j) { +- exceptions[j] = readClass(exception, c); +- exception += 2; +- } +- } else if (SIGNATURES && "Signature".equals(attrName)) { +- signature = readUTF8(u + 8, c); +- } else if ("Deprecated".equals(attrName)) { +- context.access |= Opcodes.ACC_DEPRECATED; +- } else if (ANNOTATIONS +- && "RuntimeVisibleAnnotations".equals(attrName)) { +- anns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeVisibleTypeAnnotations".equals(attrName)) { +- tanns = u + 8; +- } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { +- dann = u + 8; +- } else if ("Synthetic".equals(attrName)) { +- context.access |= Opcodes.ACC_SYNTHETIC +- | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleAnnotations".equals(attrName)) { +- ianns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { +- itanns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeVisibleParameterAnnotations".equals(attrName)) { +- mpanns = u + 8; +- } else if (ANNOTATIONS +- && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { +- impanns = u + 8; +- } else if ("MethodParameters".equals(attrName)) { +- methodParameters = u + 8; +- } else { +- Attribute attr = readAttribute(context.attrs, attrName, u + 8, +- readInt(u + 4), c, -1, null); +- if (attr != null) { +- attr.next = attributes; +- attributes = attr; +- } +- } +- u += 6 + readInt(u + 4); +- } +- u += 2; +- +- // visits the method declaration +- MethodVisitor mv = classVisitor.visitMethod(context.access, +- context.name, context.desc, signature, exceptions); +- if (mv == null) { +- return u; +- } +- +- /* +- * if the returned MethodVisitor is in fact a MethodWriter, it means +- * there is no method adapter between the reader and the writer. If, in +- * addition, the writer's constant pool was copied from this reader +- * (mw.cw.cr == this), and the signature and exceptions of the method +- * have not been changed, then it is possible to skip all visit events +- * and just copy the original code of the method to the writer (the +- * access, name and descriptor can have been changed, this is not +- * important since they are not copied as is from the reader). +- */ +- if (WRITER && mv instanceof MethodWriter) { +- MethodWriter mw = (MethodWriter) mv; +- if (mw.cw.cr == this && signature == mw.signature) { +- boolean sameExceptions = false; +- if (exceptions == null) { +- sameExceptions = mw.exceptionCount == 0; +- } else if (exceptions.length == mw.exceptionCount) { +- sameExceptions = true; +- for (int j = exceptions.length - 1; j >= 0; --j) { +- exception -= 2; +- if (mw.exceptions[j] != readUnsignedShort(exception)) { +- sameExceptions = false; +- break; +- } +- } +- } +- if (sameExceptions) { +- /* +- * we do not copy directly the code into MethodWriter to +- * save a byte array copy operation. The real copy will be +- * done in ClassWriter.toByteArray(). +- */ +- mw.classReaderOffset = firstAttribute; +- mw.classReaderLength = u - firstAttribute; +- return u; +- } +- } +- } +- +- // visit the method parameters +- if (methodParameters != 0) { +- for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { +- mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); +- } +- } +- +- // visits the method annotations +- if (ANNOTATIONS && dann != 0) { +- AnnotationVisitor dv = mv.visitAnnotationDefault(); +- readAnnotationValue(dann, c, null, dv); +- if (dv != null) { +- dv.visitEnd(); +- } +- } +- if (ANNOTATIONS && anns != 0) { +- for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- mv.visitAnnotation(readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && ianns != 0) { +- for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { +- v = readAnnotationValues(v + 2, c, true, +- mv.visitAnnotation(readUTF8(v, c), false)); +- } +- } +- if (ANNOTATIONS && tanns != 0) { +- for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- mv.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), true)); +- } +- } +- if (ANNOTATIONS && itanns != 0) { +- for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { +- v = readAnnotationTarget(context, v); +- v = readAnnotationValues(v + 2, c, true, +- mv.visitTypeAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), false)); +- } +- } +- if (ANNOTATIONS && mpanns != 0) { +- readParameterAnnotations(mv, context, mpanns, true); +- } +- if (ANNOTATIONS && impanns != 0) { +- readParameterAnnotations(mv, context, impanns, false); +- } +- +- // visits the method attributes +- while (attributes != null) { +- Attribute attr = attributes.next; +- attributes.next = null; +- mv.visitAttribute(attributes); +- attributes = attr; +- } +- +- // visits the method code +- if (code != 0) { +- mv.visitCode(); +- readCode(mv, context, code); +- } +- +- // visits the end of the method +- mv.visitEnd(); +- +- return u; +- } +- +- /** +- * Reads the bytecode of a method and makes the given visitor visit it. +- * +- * @param mv +- * the visitor that must visit the method's code. +- * @param context +- * information about the class being parsed. +- * @param u +- * the start offset of the code attribute in the class file. +- */ +- private void readCode(final MethodVisitor mv, final Context context, int u) { +- // reads the header +- byte[] b = this.b; +- char[] c = context.buffer; +- int maxStack = readUnsignedShort(u); +- int maxLocals = readUnsignedShort(u + 2); +- int codeLength = readInt(u + 4); +- u += 8; +- +- // reads the bytecode to find the labels +- int codeStart = u; +- int codeEnd = u + codeLength; +- Label[] labels = context.labels = new Label[codeLength + 2]; +- readLabel(codeLength + 1, labels); +- while (u < codeEnd) { +- int offset = u - codeStart; +- int opcode = b[u] & 0xFF; +- switch (ClassWriter.TYPE[opcode]) { +- case ClassWriter.NOARG_INSN: +- case ClassWriter.IMPLVAR_INSN: +- u += 1; +- break; +- case ClassWriter.LABEL_INSN: +- readLabel(offset + readShort(u + 1), labels); +- u += 3; +- break; +- case ClassWriter.LABELW_INSN: +- readLabel(offset + readInt(u + 1), labels); +- u += 5; +- break; +- case ClassWriter.WIDE_INSN: +- opcode = b[u + 1] & 0xFF; +- if (opcode == Opcodes.IINC) { +- u += 6; +- } else { +- u += 4; +- } +- break; +- case ClassWriter.TABL_INSN: +- // skips 0 to 3 padding bytes +- u = u + 4 - (offset & 3); +- // reads instruction +- readLabel(offset + readInt(u), labels); +- for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { +- readLabel(offset + readInt(u + 12), labels); +- u += 4; +- } +- u += 12; +- break; +- case ClassWriter.LOOK_INSN: +- // skips 0 to 3 padding bytes +- u = u + 4 - (offset & 3); +- // reads instruction +- readLabel(offset + readInt(u), labels); +- for (int i = readInt(u + 4); i > 0; --i) { +- readLabel(offset + readInt(u + 12), labels); +- u += 8; +- } +- u += 8; +- break; +- case ClassWriter.VAR_INSN: +- case ClassWriter.SBYTE_INSN: +- case ClassWriter.LDC_INSN: +- u += 2; +- break; +- case ClassWriter.SHORT_INSN: +- case ClassWriter.LDCW_INSN: +- case ClassWriter.FIELDORMETH_INSN: +- case ClassWriter.TYPE_INSN: +- case ClassWriter.IINC_INSN: +- u += 3; +- break; +- case ClassWriter.ITFMETH_INSN: +- case ClassWriter.INDYMETH_INSN: +- u += 5; +- break; +- // case MANA_INSN: +- default: +- u += 4; +- break; +- } +- } +- +- // reads the try catch entries to find the labels, and also visits them +- for (int i = readUnsignedShort(u); i > 0; --i) { +- Label start = readLabel(readUnsignedShort(u + 2), labels); +- Label end = readLabel(readUnsignedShort(u + 4), labels); +- Label handler = readLabel(readUnsignedShort(u + 6), labels); +- String type = readUTF8(items[readUnsignedShort(u + 8)], c); +- mv.visitTryCatchBlock(start, end, handler, type); +- u += 8; +- } +- u += 2; +- +- // reads the code attributes +- int[] tanns = null; // start index of each visible type annotation +- int[] itanns = null; // start index of each invisible type annotation +- int tann = 0; // current index in tanns array +- int itann = 0; // current index in itanns array +- int ntoff = -1; // next visible type annotation code offset +- int nitoff = -1; // next invisible type annotation code offset +- int varTable = 0; +- int varTypeTable = 0; +- boolean zip = true; +- boolean unzip = (context.flags & EXPAND_FRAMES) != 0; +- int stackMap = 0; +- int stackMapSize = 0; +- int frameCount = 0; +- Context frame = null; +- Attribute attributes = null; +- +- for (int i = readUnsignedShort(u); i > 0; --i) { +- String attrName = readUTF8(u + 2, c); +- if ("LocalVariableTable".equals(attrName)) { +- if ((context.flags & SKIP_DEBUG) == 0) { +- varTable = u + 8; +- for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { +- int label = readUnsignedShort(v + 10); +- if (labels[label] == null) { +- readLabel(label, labels).status |= Label.DEBUG; +- } +- label += readUnsignedShort(v + 12); +- if (labels[label] == null) { +- readLabel(label, labels).status |= Label.DEBUG; +- } +- v += 10; +- } +- } +- } else if ("LocalVariableTypeTable".equals(attrName)) { +- varTypeTable = u + 8; +- } else if ("LineNumberTable".equals(attrName)) { +- if ((context.flags & SKIP_DEBUG) == 0) { +- for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { +- int label = readUnsignedShort(v + 10); +- if (labels[label] == null) { +- readLabel(label, labels).status |= Label.DEBUG; +- } +- labels[label].line = readUnsignedShort(v + 12); +- v += 4; +- } +- } +- } else if (ANNOTATIONS +- && "RuntimeVisibleTypeAnnotations".equals(attrName)) { +- tanns = readTypeAnnotations(mv, context, u + 8, true); +- ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 +- : readUnsignedShort(tanns[0] + 1); +- } else if (ANNOTATIONS +- && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { +- itanns = readTypeAnnotations(mv, context, u + 8, false); +- nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 +- : readUnsignedShort(itanns[0] + 1); +- } else if (FRAMES && "StackMapTable".equals(attrName)) { +- if ((context.flags & SKIP_FRAMES) == 0) { +- stackMap = u + 10; +- stackMapSize = readInt(u + 4); +- frameCount = readUnsignedShort(u + 8); +- } +- /* +- * 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 in the second +- * phase (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 insn and for which no +- * Label exist. +- */ +- /* +- * This is not true for UNINITIALIZED type offsets. We solve +- * this by parsing the stack map table without a full decoding +- * (see below). +- */ +- } else if (FRAMES && "StackMap".equals(attrName)) { +- if ((context.flags & SKIP_FRAMES) == 0) { +- zip = false; +- stackMap = u + 10; +- stackMapSize = readInt(u + 4); +- frameCount = readUnsignedShort(u + 8); +- } +- /* +- * IMPORTANT! here we assume that the frames are ordered, as in +- * the StackMapTable attribute, although this is not guaranteed +- * by the attribute format. +- */ +- } else { +- for (int j = 0; j < context.attrs.length; ++j) { +- if (context.attrs[j].type.equals(attrName)) { +- Attribute attr = context.attrs[j].read(this, u + 8, +- readInt(u + 4), c, codeStart - 8, labels); +- if (attr != null) { +- attr.next = attributes; +- attributes = attr; +- } +- } +- } +- } +- u += 6 + readInt(u + 4); +- } +- u += 2; +- +- // generates the first (implicit) stack map frame +- if (FRAMES && stackMap != 0) { +- /* +- * for the first explicit frame the offset is not offset_delta + 1 +- * but only offset_delta; setting the implicit frame offset to -1 +- * allow the use of the "offset_delta + 1" rule in all cases +- */ +- frame = context; +- frame.offset = -1; +- frame.mode = 0; +- frame.localCount = 0; +- frame.localDiff = 0; +- frame.stackCount = 0; +- frame.local = new Object[maxLocals]; +- frame.stack = new Object[maxStack]; +- if (unzip) { +- getImplicitFrame(context); +- } +- /* +- * Finds 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 8, offset +- * within code 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 i = stackMap; i < stackMap + stackMapSize - 2; ++i) { +- if (b[i] == 8) { // UNINITIALIZED FRAME TYPE +- int v = readUnsignedShort(i + 1); +- if (v >= 0 && v < codeLength) { +- if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { +- readLabel(v, labels); +- } +- } +- } +- } +- } +- +- // visits the instructions +- u = codeStart; +- while (u < codeEnd) { +- int offset = u - codeStart; +- +- // visits the label and line number for this offset, if any +- Label l = labels[offset]; +- if (l != null) { +- mv.visitLabel(l); +- if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { +- mv.visitLineNumber(l.line, l); +- } +- } +- +- // visits the frame for this offset, if any +- while (FRAMES && frame != null +- && (frame.offset == offset || frame.offset == -1)) { +- // if there is a frame for this offset, makes the visitor visit +- // it, and reads the next frame if there is one. +- if (frame.offset != -1) { +- if (!zip || unzip) { +- mv.visitFrame(Opcodes.F_NEW, frame.localCount, +- frame.local, frame.stackCount, frame.stack); +- } else { +- mv.visitFrame(frame.mode, frame.localDiff, frame.local, +- frame.stackCount, frame.stack); +- } +- } +- if (frameCount > 0) { +- stackMap = readFrame(stackMap, zip, unzip, frame); +- --frameCount; +- } else { +- frame = null; +- } +- } +- +- // visits the instruction at this offset +- int opcode = b[u] & 0xFF; +- switch (ClassWriter.TYPE[opcode]) { +- case ClassWriter.NOARG_INSN: +- mv.visitInsn(opcode); +- u += 1; +- break; +- case ClassWriter.IMPLVAR_INSN: +- if (opcode > Opcodes.ISTORE) { +- opcode -= 59; // ISTORE_0 +- mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), +- opcode & 0x3); +- } else { +- opcode -= 26; // ILOAD_0 +- mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); +- } +- u += 1; +- break; +- case ClassWriter.LABEL_INSN: +- mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); +- u += 3; +- break; +- case ClassWriter.LABELW_INSN: +- mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); +- u += 5; +- break; +- case ClassWriter.WIDE_INSN: +- opcode = b[u + 1] & 0xFF; +- if (opcode == Opcodes.IINC) { +- mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); +- u += 6; +- } else { +- mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); +- u += 4; +- } +- break; +- case ClassWriter.TABL_INSN: { +- // skips 0 to 3 padding bytes +- u = u + 4 - (offset & 3); +- // reads instruction +- int label = offset + readInt(u); +- int min = readInt(u + 4); +- int max = readInt(u + 8); +- Label[] table = new Label[max - min + 1]; +- u += 12; +- for (int i = 0; i < table.length; ++i) { +- table[i] = labels[offset + readInt(u)]; +- u += 4; +- } +- mv.visitTableSwitchInsn(min, max, labels[label], table); +- break; +- } +- case ClassWriter.LOOK_INSN: { +- // skips 0 to 3 padding bytes +- u = u + 4 - (offset & 3); +- // reads instruction +- int label = offset + readInt(u); +- int len = readInt(u + 4); +- int[] keys = new int[len]; +- Label[] values = new Label[len]; +- u += 8; +- for (int i = 0; i < len; ++i) { +- keys[i] = readInt(u); +- values[i] = labels[offset + readInt(u + 4)]; +- u += 8; +- } +- mv.visitLookupSwitchInsn(labels[label], keys, values); +- break; +- } +- case ClassWriter.VAR_INSN: +- mv.visitVarInsn(opcode, b[u + 1] & 0xFF); +- u += 2; +- break; +- case ClassWriter.SBYTE_INSN: +- mv.visitIntInsn(opcode, b[u + 1]); +- u += 2; +- break; +- case ClassWriter.SHORT_INSN: +- mv.visitIntInsn(opcode, readShort(u + 1)); +- u += 3; +- break; +- case ClassWriter.LDC_INSN: +- mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); +- u += 2; +- break; +- case ClassWriter.LDCW_INSN: +- mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); +- u += 3; +- break; +- case ClassWriter.FIELDORMETH_INSN: +- case ClassWriter.ITFMETH_INSN: { +- int cpIndex = items[readUnsignedShort(u + 1)]; +- boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; +- String iowner = readClass(cpIndex, c); +- cpIndex = items[readUnsignedShort(cpIndex + 2)]; +- String iname = readUTF8(cpIndex, c); +- String idesc = readUTF8(cpIndex + 2, c); +- if (opcode < Opcodes.INVOKEVIRTUAL) { +- mv.visitFieldInsn(opcode, iowner, iname, idesc); +- } else { +- mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); +- } +- if (opcode == Opcodes.INVOKEINTERFACE) { +- u += 5; +- } else { +- u += 3; +- } +- break; +- } +- case ClassWriter.INDYMETH_INSN: { +- int cpIndex = items[readUnsignedShort(u + 1)]; +- int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; +- Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); +- int bsmArgCount = readUnsignedShort(bsmIndex + 2); +- Object[] bsmArgs = new Object[bsmArgCount]; +- bsmIndex += 4; +- for (int i = 0; i < bsmArgCount; i++) { +- bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); +- bsmIndex += 2; +- } +- cpIndex = items[readUnsignedShort(cpIndex + 2)]; +- String iname = readUTF8(cpIndex, c); +- String idesc = readUTF8(cpIndex + 2, c); +- mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); +- u += 5; +- break; +- } +- case ClassWriter.TYPE_INSN: +- mv.visitTypeInsn(opcode, readClass(u + 1, c)); +- u += 3; +- break; +- case ClassWriter.IINC_INSN: +- mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); +- u += 3; +- break; +- // case MANA_INSN: +- default: +- mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); +- u += 4; +- break; +- } +- +- // visit the instruction annotations, if any +- while (tanns != null && tann < tanns.length && ntoff <= offset) { +- if (ntoff == offset) { +- int v = readAnnotationTarget(context, tanns[tann]); +- readAnnotationValues(v + 2, c, true, +- mv.visitInsnAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), true)); +- } +- ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 +- : readUnsignedShort(tanns[tann] + 1); +- } +- while (itanns != null && itann < itanns.length && nitoff <= offset) { +- if (nitoff == offset) { +- int v = readAnnotationTarget(context, itanns[itann]); +- readAnnotationValues(v + 2, c, true, +- mv.visitInsnAnnotation(context.typeRef, +- context.typePath, readUTF8(v, c), false)); +- } +- nitoff = ++itann >= itanns.length +- || readByte(itanns[itann]) < 0x43 ? -1 +- : readUnsignedShort(itanns[itann] + 1); +- } +- } +- if (labels[codeLength] != null) { +- mv.visitLabel(labels[codeLength]); +- } +- +- // visits the local variable tables +- if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { +- int[] typeTable = null; +- if (varTypeTable != 0) { +- u = varTypeTable + 2; +- typeTable = new int[readUnsignedShort(varTypeTable) * 3]; +- for (int i = typeTable.length; i > 0;) { +- typeTable[--i] = u + 6; // signature +- typeTable[--i] = readUnsignedShort(u + 8); // index +- typeTable[--i] = readUnsignedShort(u); // start +- u += 10; +- } +- } +- u = varTable + 2; +- for (int i = readUnsignedShort(varTable); i > 0; --i) { +- int start = readUnsignedShort(u); +- int length = readUnsignedShort(u + 2); +- int index = readUnsignedShort(u + 8); +- String vsignature = null; +- if (typeTable != null) { +- for (int j = 0; j < typeTable.length; j += 3) { +- if (typeTable[j] == start && typeTable[j + 1] == index) { +- vsignature = readUTF8(typeTable[j + 2], c); +- break; +- } +- } +- } +- mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), +- vsignature, labels[start], labels[start + length], +- index); +- u += 10; +- } +- } +- +- // visits the local variables type annotations +- if (tanns != null) { +- for (int i = 0; i < tanns.length; ++i) { +- if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { +- int v = readAnnotationTarget(context, tanns[i]); +- v = readAnnotationValues(v + 2, c, true, +- mv.visitLocalVariableAnnotation(context.typeRef, +- context.typePath, context.start, +- context.end, context.index, readUTF8(v, c), +- true)); +- } +- } +- } +- if (itanns != null) { +- for (int i = 0; i < itanns.length; ++i) { +- if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { +- int v = readAnnotationTarget(context, itanns[i]); +- v = readAnnotationValues(v + 2, c, true, +- mv.visitLocalVariableAnnotation(context.typeRef, +- context.typePath, context.start, +- context.end, context.index, readUTF8(v, c), +- false)); +- } +- } +- } +- +- // visits the code attributes +- while (attributes != null) { +- Attribute attr = attributes.next; +- attributes.next = null; +- mv.visitAttribute(attributes); +- attributes = attr; +- } +- +- // visits the max stack and max locals values +- mv.visitMaxs(maxStack, maxLocals); +- } +- +- /** +- * Parses a type annotation table to find the labels, and to visit the try +- * catch block annotations. +- * +- * @param u +- * the start offset of a type annotation table. +- * @param mv +- * the method visitor to be used to visit the try catch block +- * annotations. +- * @param context +- * information about the class being parsed. +- * @param visible +- * if the type annotation table to parse contains runtime visible +- * annotations. +- * @return the start offset of each type annotation in the parsed table. +- */ +- private int[] readTypeAnnotations(final MethodVisitor mv, +- final Context context, int u, boolean visible) { +- char[] c = context.buffer; +- int[] offsets = new int[readUnsignedShort(u)]; +- u += 2; +- for (int i = 0; i < offsets.length; ++i) { +- offsets[i] = u; +- int target = readInt(u); +- switch (target >>> 24) { +- case 0x00: // CLASS_TYPE_PARAMETER +- case 0x01: // METHOD_TYPE_PARAMETER +- case 0x16: // METHOD_FORMAL_PARAMETER +- u += 2; +- break; +- case 0x13: // FIELD +- case 0x14: // METHOD_RETURN +- case 0x15: // METHOD_RECEIVER +- u += 1; +- break; +- case 0x40: // LOCAL_VARIABLE +- case 0x41: // RESOURCE_VARIABLE +- for (int j = readUnsignedShort(u + 1); j > 0; --j) { +- int start = readUnsignedShort(u + 3); +- int length = readUnsignedShort(u + 5); +- readLabel(start, context.labels); +- readLabel(start + length, context.labels); +- u += 6; +- } +- u += 3; +- break; +- case 0x47: // CAST +- case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT +- case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT +- u += 4; +- break; +- // case 0x10: // CLASS_EXTENDS +- // case 0x11: // CLASS_TYPE_PARAMETER_BOUND +- // case 0x12: // METHOD_TYPE_PARAMETER_BOUND +- // case 0x17: // THROWS +- // case 0x42: // EXCEPTION_PARAMETER +- // case 0x43: // INSTANCEOF +- // case 0x44: // NEW +- // case 0x45: // CONSTRUCTOR_REFERENCE +- // case 0x46: // METHOD_REFERENCE +- default: +- u += 3; +- break; +- } +- int pathLength = readByte(u); +- if ((target >>> 24) == 0x42) { +- TypePath path = pathLength == 0 ? null : new TypePath(b, u); +- u += 1 + 2 * pathLength; +- u = readAnnotationValues(u + 2, c, true, +- mv.visitTryCatchAnnotation(target, path, +- readUTF8(u, c), visible)); +- } else { +- u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null); +- } +- } +- return offsets; +- } +- +- /** +- * Parses the header of a type annotation to extract its target_type and +- * target_path (the result is stored in the given context), and returns the +- * start offset of the rest of the type_annotation structure (i.e. the +- * offset to the type_index field, which is followed by +- * num_element_value_pairs and then the name,value pairs). +- * +- * @param context +- * information about the class being parsed. This is where the +- * extracted target_type and target_path must be stored. +- * @param u +- * the start offset of a type_annotation structure. +- * @return the start offset of the rest of the type_annotation structure. +- */ +- private int readAnnotationTarget(final Context context, int u) { +- int target = readInt(u); +- switch (target >>> 24) { +- case 0x00: // CLASS_TYPE_PARAMETER +- case 0x01: // METHOD_TYPE_PARAMETER +- case 0x16: // METHOD_FORMAL_PARAMETER +- target &= 0xFFFF0000; +- u += 2; +- break; +- case 0x13: // FIELD +- case 0x14: // METHOD_RETURN +- case 0x15: // METHOD_RECEIVER +- target &= 0xFF000000; +- u += 1; +- break; +- case 0x40: // LOCAL_VARIABLE +- case 0x41: { // RESOURCE_VARIABLE +- target &= 0xFF000000; +- int n = readUnsignedShort(u + 1); +- context.start = new Label[n]; +- context.end = new Label[n]; +- context.index = new int[n]; +- u += 3; +- for (int i = 0; i < n; ++i) { +- int start = readUnsignedShort(u); +- int length = readUnsignedShort(u + 2); +- context.start[i] = readLabel(start, context.labels); +- context.end[i] = readLabel(start + length, context.labels); +- context.index[i] = readUnsignedShort(u + 4); +- u += 6; +- } +- break; +- } +- case 0x47: // CAST +- case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT +- case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT +- target &= 0xFF0000FF; +- u += 4; +- break; +- // case 0x10: // CLASS_EXTENDS +- // case 0x11: // CLASS_TYPE_PARAMETER_BOUND +- // case 0x12: // METHOD_TYPE_PARAMETER_BOUND +- // case 0x17: // THROWS +- // case 0x42: // EXCEPTION_PARAMETER +- // case 0x43: // INSTANCEOF +- // case 0x44: // NEW +- // case 0x45: // CONSTRUCTOR_REFERENCE +- // case 0x46: // METHOD_REFERENCE +- default: +- target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000; +- u += 3; +- break; +- } +- int pathLength = readByte(u); +- context.typeRef = target; +- context.typePath = pathLength == 0 ? null : new TypePath(b, u); +- return u + 1 + 2 * pathLength; +- } +- +- /** +- * Reads parameter annotations and makes the given visitor visit them. +- * +- * @param mv +- * the visitor that must visit the annotations. +- * @param context +- * information about the class being parsed. +- * @param v +- * start offset in {@link #b b} of the annotations to be read. +- * @param visible +- * true if the annotations to be read are visible at +- * runtime. +- */ +- private void readParameterAnnotations(final MethodVisitor mv, +- final Context context, int v, final boolean visible) { +- int i; +- int n = b[v++] & 0xFF; +- // workaround for a bug in javac (javac compiler generates a parameter +- // annotation array whose size is equal to the number of parameters in +- // the Java source file, while it should generate an array whose size is +- // equal to the number of parameters in the method descriptor - which +- // includes the synthetic parameters added by the compiler). This work- +- // around supposes that the synthetic parameters are the first ones. +- int synthetics = Type.getArgumentTypes(context.desc).length - n; +- AnnotationVisitor av; +- for (i = 0; i < synthetics; ++i) { +- // virtual annotation to detect synthetic parameters in MethodWriter +- av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false); +- if (av != null) { +- av.visitEnd(); +- } +- } +- char[] c = context.buffer; +- for (; i < n + synthetics; ++i) { +- int j = readUnsignedShort(v); +- v += 2; +- for (; j > 0; --j) { +- av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible); +- v = readAnnotationValues(v + 2, c, true, av); +- } +- } +- } +- +- /** +- * Reads the values of an annotation and makes the given visitor visit them. +- * +- * @param v +- * the start offset in {@link #b b} of the values to be read +- * (including the unsigned short that gives the number of +- * values). +- * @param buf +- * buffer to be used to call {@link #readUTF8 readUTF8}, +- * {@link #readClass(int,char[]) readClass} or {@link #readConst +- * readConst}. +- * @param named +- * if the annotation values are named or not. +- * @param av +- * the visitor that must visit the values. +- * @return the end offset of the annotation values. +- */ +- private int readAnnotationValues(int v, final char[] buf, +- final boolean named, final AnnotationVisitor av) { +- int i = readUnsignedShort(v); +- v += 2; +- if (named) { +- for (; i > 0; --i) { +- v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); +- } +- } else { +- for (; i > 0; --i) { +- v = readAnnotationValue(v, buf, null, av); +- } +- } +- if (av != null) { +- av.visitEnd(); +- } +- return v; +- } +- +- /** +- * Reads a value of an annotation and makes the given visitor visit it. +- * +- * @param v +- * the start offset in {@link #b b} of the value to be read +- * (not including the value name constant pool index). +- * @param buf +- * buffer to be used to call {@link #readUTF8 readUTF8}, +- * {@link #readClass(int,char[]) readClass} or {@link #readConst +- * readConst}. +- * @param name +- * the name of the value to be read. +- * @param av +- * the visitor that must visit the value. +- * @return the end offset of the annotation value. +- */ +- private int readAnnotationValue(int v, final char[] buf, final String name, +- final AnnotationVisitor av) { +- int i; +- if (av == null) { +- switch (b[v] & 0xFF) { +- case 'e': // enum_const_value +- return v + 5; +- case '@': // annotation_value +- return readAnnotationValues(v + 3, buf, true, null); +- case '[': // array_value +- return readAnnotationValues(v + 1, buf, false, null); +- default: +- return v + 3; +- } +- } +- switch (b[v++] & 0xFF) { +- case 'I': // pointer to CONSTANT_Integer +- case 'J': // pointer to CONSTANT_Long +- case 'F': // pointer to CONSTANT_Float +- case 'D': // pointer to CONSTANT_Double +- av.visit(name, readConst(readUnsignedShort(v), buf)); +- v += 2; +- break; +- case 'B': // pointer to CONSTANT_Byte +- av.visit(name, +- new Byte((byte) readInt(items[readUnsignedShort(v)]))); +- v += 2; +- break; +- case 'Z': // pointer to CONSTANT_Boolean +- av.visit(name, +- readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE +- : Boolean.TRUE); +- v += 2; +- break; +- case 'S': // pointer to CONSTANT_Short +- av.visit(name, new Short( +- (short) readInt(items[readUnsignedShort(v)]))); +- v += 2; +- break; +- case 'C': // pointer to CONSTANT_Char +- av.visit(name, new Character( +- (char) readInt(items[readUnsignedShort(v)]))); +- v += 2; +- break; +- case 's': // pointer to CONSTANT_Utf8 +- av.visit(name, readUTF8(v, buf)); +- v += 2; +- break; +- case 'e': // enum_const_value +- av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); +- v += 4; +- break; +- case 'c': // class_info +- av.visit(name, Type.getType(readUTF8(v, buf))); +- v += 2; +- break; +- case '@': // annotation_value +- v = readAnnotationValues(v + 2, buf, true, +- av.visitAnnotation(name, readUTF8(v, buf))); +- break; +- case '[': // array_value +- int size = readUnsignedShort(v); +- v += 2; +- if (size == 0) { +- return readAnnotationValues(v - 2, buf, false, +- av.visitArray(name)); +- } +- switch (this.b[v++] & 0xFF) { +- case 'B': +- byte[] bv = new byte[size]; +- for (i = 0; i < size; i++) { +- bv[i] = (byte) readInt(items[readUnsignedShort(v)]); +- v += 3; +- } +- av.visit(name, bv); +- --v; +- break; +- case 'Z': +- boolean[] zv = new boolean[size]; +- for (i = 0; i < size; i++) { +- zv[i] = readInt(items[readUnsignedShort(v)]) != 0; +- v += 3; +- } +- av.visit(name, zv); +- --v; +- break; +- case 'S': +- short[] sv = new short[size]; +- for (i = 0; i < size; i++) { +- sv[i] = (short) readInt(items[readUnsignedShort(v)]); +- v += 3; +- } +- av.visit(name, sv); +- --v; +- break; +- case 'C': +- char[] cv = new char[size]; +- for (i = 0; i < size; i++) { +- cv[i] = (char) readInt(items[readUnsignedShort(v)]); +- v += 3; +- } +- av.visit(name, cv); +- --v; +- break; +- case 'I': +- int[] iv = new int[size]; +- for (i = 0; i < size; i++) { +- iv[i] = readInt(items[readUnsignedShort(v)]); +- v += 3; +- } +- av.visit(name, iv); +- --v; +- break; +- case 'J': +- long[] lv = new long[size]; +- for (i = 0; i < size; i++) { +- lv[i] = readLong(items[readUnsignedShort(v)]); +- v += 3; +- } +- av.visit(name, lv); +- --v; +- break; +- case 'F': +- float[] fv = new float[size]; +- for (i = 0; i < size; i++) { +- fv[i] = Float +- .intBitsToFloat(readInt(items[readUnsignedShort(v)])); +- v += 3; +- } +- av.visit(name, fv); +- --v; +- break; +- case 'D': +- double[] dv = new double[size]; +- for (i = 0; i < size; i++) { +- dv[i] = Double +- .longBitsToDouble(readLong(items[readUnsignedShort(v)])); +- v += 3; +- } +- av.visit(name, dv); +- --v; +- break; +- default: +- v = readAnnotationValues(v - 3, buf, false, av.visitArray(name)); +- } +- } +- return v; +- } +- +- /** +- * 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 frame +- * information about the class being parsed. +- */ +- private void getImplicitFrame(final Context frame) { +- String desc = frame.desc; +- Object[] locals = frame.local; +- int local = 0; +- if ((frame.access & Opcodes.ACC_STATIC) == 0) { +- if ("".equals(frame.name)) { +- locals[local++] = Opcodes.UNINITIALIZED_THIS; +- } else { +- locals[local++] = readClass(header + 2, frame.buffer); +- } +- } +- int i = 1; +- loop: while (true) { +- int j = i; +- switch (desc.charAt(i++)) { +- case 'Z': +- case 'C': +- case 'B': +- case 'S': +- case 'I': +- locals[local++] = Opcodes.INTEGER; +- break; +- case 'F': +- locals[local++] = Opcodes.FLOAT; +- break; +- case 'J': +- locals[local++] = Opcodes.LONG; +- break; +- case 'D': +- locals[local++] = Opcodes.DOUBLE; +- break; +- case '[': +- while (desc.charAt(i) == '[') { +- ++i; +- } +- if (desc.charAt(i) == 'L') { +- ++i; +- while (desc.charAt(i) != ';') { +- ++i; +- } +- } +- locals[local++] = desc.substring(j, ++i); +- break; +- case 'L': +- while (desc.charAt(i) != ';') { +- ++i; +- } +- locals[local++] = desc.substring(j + 1, i++); +- break; +- default: +- break loop; +- } +- } +- frame.localCount = local; +- } +- +- /** +- * Reads a stack map frame and stores the result in the given +- * {@link Context} object. +- * +- * @param stackMap +- * the start offset of a stack map frame in the class file. +- * @param zip +- * if the stack map frame at stackMap is compressed or not. +- * @param unzip +- * if the stack map frame must be uncompressed. +- * @param frame +- * where the parsed stack map frame must be stored. +- * @return the offset of the first byte following the parsed frame. +- */ +- private int readFrame(int stackMap, boolean zip, boolean unzip, +- Context frame) { +- char[] c = frame.buffer; +- Label[] labels = frame.labels; +- int tag; +- int delta; +- if (zip) { +- tag = b[stackMap++] & 0xFF; +- } else { +- tag = MethodWriter.FULL_FRAME; +- frame.offset = -1; +- } +- frame.localDiff = 0; +- if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { +- delta = tag; +- frame.mode = Opcodes.F_SAME; +- frame.stackCount = 0; +- } else if (tag < MethodWriter.RESERVED) { +- delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; +- stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); +- frame.mode = Opcodes.F_SAME1; +- frame.stackCount = 1; +- } else { +- delta = readUnsignedShort(stackMap); +- stackMap += 2; +- if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { +- stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); +- frame.mode = Opcodes.F_SAME1; +- frame.stackCount = 1; +- } else if (tag >= MethodWriter.CHOP_FRAME +- && tag < MethodWriter.SAME_FRAME_EXTENDED) { +- frame.mode = Opcodes.F_CHOP; +- frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; +- frame.localCount -= frame.localDiff; +- frame.stackCount = 0; +- } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) { +- frame.mode = Opcodes.F_SAME; +- frame.stackCount = 0; +- } else if (tag < MethodWriter.FULL_FRAME) { +- int local = unzip ? frame.localCount : 0; +- for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) { +- stackMap = readFrameType(frame.local, local++, stackMap, c, +- labels); +- } +- frame.mode = Opcodes.F_APPEND; +- frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; +- frame.localCount += frame.localDiff; +- frame.stackCount = 0; +- } else { // if (tag == FULL_FRAME) { +- frame.mode = Opcodes.F_FULL; +- int n = readUnsignedShort(stackMap); +- stackMap += 2; +- frame.localDiff = n; +- frame.localCount = n; +- for (int local = 0; n > 0; n--) { +- stackMap = readFrameType(frame.local, local++, stackMap, c, +- labels); +- } +- n = readUnsignedShort(stackMap); +- stackMap += 2; +- frame.stackCount = n; +- for (int stack = 0; n > 0; n--) { +- stackMap = readFrameType(frame.stack, stack++, stackMap, c, +- labels); +- } +- } +- } +- frame.offset += delta + 1; +- readLabel(frame.offset, labels); +- return stackMap; +- } +- +- /** +- * Reads a stack map frame type and stores it at the given index in the +- * given array. +- * +- * @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 v +- * the start offset of the stack map frame type to read. +- * @param buf +- * a buffer to read strings. +- * @param labels +- * the labels of the method currently being parsed, indexed by +- * their offset. If the parsed type is an Uninitialized type, a +- * new label for the corresponding NEW instruction is stored in +- * this array if it does not already exist. +- * @return the offset of the first byte after the parsed type. +- */ +- private int readFrameType(final Object[] frame, final int index, int v, +- final char[] buf, final Label[] labels) { +- int type = b[v++] & 0xFF; +- switch (type) { +- case 0: +- frame[index] = Opcodes.TOP; +- break; +- case 1: +- frame[index] = Opcodes.INTEGER; +- break; +- case 2: +- frame[index] = Opcodes.FLOAT; +- break; +- case 3: +- frame[index] = Opcodes.DOUBLE; +- break; +- case 4: +- frame[index] = Opcodes.LONG; +- break; +- case 5: +- frame[index] = Opcodes.NULL; +- break; +- case 6: +- frame[index] = Opcodes.UNINITIALIZED_THIS; +- break; +- case 7: // Object +- frame[index] = readClass(v, buf); +- v += 2; +- break; +- default: // Uninitialized +- frame[index] = readLabel(readUnsignedShort(v), labels); +- v += 2; +- } +- return v; +- } +- +- /** +- * Returns the label corresponding to the given offset. The default +- * implementation of this method creates a label for the given offset if it +- * has not been already created. +- * +- * @param offset +- * a bytecode offset in a method. +- * @param labels +- * the already created labels, indexed by their offset. If a +- * label already exists for offset 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[offset]. +- */ +- protected Label readLabel(int offset, Label[] labels) { +- if (labels[offset] == null) { +- labels[offset] = new Label(); +- } +- return labels[offset]; +- } +- +- /** +- * Returns the start index of the attribute_info structure of this class. +- * +- * @return the start index of the attribute_info structure of this class. +- */ +- private int getAttributes() { +- // skips the header +- int u = header + 8 + readUnsignedShort(header + 6) * 2; +- // skips fields and methods +- for (int i = readUnsignedShort(u); i > 0; --i) { +- for (int j = readUnsignedShort(u + 8); j > 0; --j) { +- u += 6 + readInt(u + 12); +- } +- u += 8; +- } +- u += 2; +- for (int i = readUnsignedShort(u); i > 0; --i) { +- for (int j = readUnsignedShort(u + 8); j > 0; --j) { +- u += 6 + readInt(u + 12); +- } +- u += 8; +- } +- // the attribute_info structure starts just after the methods +- return u + 2; +- } +- +- /** +- * Reads an attribute in {@link #b b}. +- * +- * @param attrs +- * 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 is ignored (i.e. an empty +- * {@link Attribute} instance is returned). +- * @param type +- * the type of the attribute. +- * @param off +- * index of the first byte of the attribute's content in +- * {@link #b b}. The 6 attribute header bytes, containing the +- * type and the length of the attribute, are not taken into +- * account here (they have already been read). +- * @param len +- * the length of the attribute's content. +- * @param buf +- * buffer to be used to call {@link #readUTF8 readUTF8}, +- * {@link #readClass(int,char[]) readClass} or {@link #readConst +- * readConst}. +- * @param codeOff +- * index of the first byte of code's attribute content in +- * {@link #b b}, or -1 if the attribute to be read is not a code +- * attribute. The 6 attribute header bytes, containing the type +- * and the length of the attribute, are not taken into account +- * here. +- * @param labels +- * the labels of the method's code, or null if the +- * attribute to be read is not a code attribute. +- * @return the attribute that has been read, or null to skip this +- * attribute. +- */ +- private Attribute readAttribute(final Attribute[] attrs, final String type, +- final int off, final int len, final char[] buf, final int codeOff, +- final Label[] labels) { +- for (int i = 0; i < attrs.length; ++i) { +- if (attrs[i].type.equals(type)) { +- return attrs[i].read(this, off, len, buf, codeOff, labels); +- } +- } +- return new Attribute(type).read(this, off, len, null, -1, null); +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: low level parsing +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the number of constant pool items in {@link #b b}. +- * +- * @return the number of constant pool items in {@link #b b}. +- */ +- public int getItemCount() { +- return items.length; +- } +- +- /** +- * Returns the start index of the constant pool item in {@link #b b}, plus +- * one. This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param item +- * the index a constant pool item. +- * @return the start index of the constant pool item in {@link #b b}, plus +- * one. +- */ +- public int getItem(final int item) { +- return items[item]; +- } +- +- /** +- * Returns the maximum length of the strings contained in the constant pool +- * of the class. +- * +- * @return the maximum length of the strings contained in the constant pool +- * of the class. +- */ +- public int getMaxStringLength() { +- return maxStringLength; +- } +- +- /** +- * Reads a byte value in {@link #b b}. This method is intended for +- * {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @param index +- * the start index of the value to be read in {@link #b b}. +- * @return the read value. +- */ +- public int readByte(final int index) { +- return b[index] & 0xFF; +- } +- +- /** +- * Reads an unsigned short value in {@link #b b}. This method is intended +- * for {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @param index +- * the start index of the value to be read in {@link #b b}. +- * @return the read value. +- */ +- public int readUnsignedShort(final int index) { +- byte[] b = this.b; +- return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); +- } +- +- /** +- * Reads a signed short value in {@link #b b}. This method is intended +- * for {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @param index +- * the start index of the value to be read in {@link #b b}. +- * @return the read value. +- */ +- public short readShort(final int index) { +- byte[] b = this.b; +- return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); +- } +- +- /** +- * Reads a signed int value in {@link #b b}. This method is intended for +- * {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @param index +- * the start index of the value to be read in {@link #b b}. +- * @return the read value. +- */ +- public int readInt(final int index) { +- byte[] b = this.b; +- return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) +- | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); +- } +- +- /** +- * Reads a signed long value in {@link #b b}. This method is intended for +- * {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @param index +- * the start index of the value to be read in {@link #b b}. +- * @return the read value. +- */ +- public long readLong(final int index) { +- long l1 = readInt(index); +- long l0 = readInt(index + 4) & 0xFFFFFFFFL; +- return (l1 << 32) | l0; +- } +- +- /** +- * Reads an UTF8 string constant pool item in {@link #b b}. This method +- * is intended for {@link Attribute} sub classes, and is normally not needed +- * by class generators or adapters. +- * +- * @param index +- * the start index of an unsigned short value in {@link #b b}, +- * whose value is the index of an UTF8 constant pool item. +- * @param buf +- * 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 UTF8 item. +- */ +- public String readUTF8(int index, final char[] buf) { +- int item = readUnsignedShort(index); +- if (index == 0 || item == 0) { +- return null; +- } +- String s = strings[item]; +- if (s != null) { +- return s; +- } +- index = items[item]; +- return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); +- } +- +- /** +- * Reads UTF8 string in {@link #b b}. +- * +- * @param index +- * start offset of the UTF8 string to be read. +- * @param utfLen +- * length of the UTF8 string to be read. +- * @param buf +- * 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(int index, final int utfLen, final char[] buf) { +- int endIndex = index + utfLen; +- byte[] b = this.b; +- int strLen = 0; +- int c; +- int st = 0; +- char cc = 0; +- while (index < endIndex) { +- c = b[index++]; +- switch (st) { +- case 0: +- c = c & 0xFF; +- if (c < 0x80) { // 0xxxxxxx +- buf[strLen++] = (char) c; +- } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx +- cc = (char) (c & 0x1F); +- st = 1; +- } else { // 1110 xxxx 10xx xxxx 10xx xxxx +- cc = (char) (c & 0x0F); +- st = 2; +- } +- break; +- +- case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char +- buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); +- st = 0; +- break; +- +- case 2: // byte 2 of 3-byte char +- cc = (char) ((cc << 6) | (c & 0x3F)); +- st = 1; +- break; +- } +- } +- return new String(buf, 0, strLen); +- } +- +- /** +- * Reads a class constant pool item in {@link #b b}. This method is +- * intended for {@link Attribute} sub classes, and is normally not needed by +- * class generators or adapters. +- * +- * @param index +- * the start index of an unsigned short value in {@link #b b}, +- * whose value is the index of a class constant pool item. +- * @param buf +- * 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 class item. +- */ +- public String readClass(final int index, final char[] buf) { +- // computes the start index of the CONSTANT_Class item in b +- // and reads the CONSTANT_Utf8 item designated by +- // the first two bytes of this CONSTANT_Class item +- return readUTF8(items[readUnsignedShort(index)], buf); +- } +- +- /** +- * Reads a numeric or string constant pool item in {@link #b b}. This +- * method is intended for {@link Attribute} sub classes, and is normally not +- * needed by class generators or adapters. +- * +- * @param item +- * the index of a constant pool item. +- * @param buf +- * buffer to be used to read the item. 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} or {@link Handle} corresponding to +- * the given constant pool item. +- */ +- public Object readConst(final int item, final char[] buf) { +- int index = items[item]; +- switch (b[index - 1]) { +- case ClassWriter.INT: +- return new Integer(readInt(index)); +- case ClassWriter.FLOAT: +- return new Float(Float.intBitsToFloat(readInt(index))); +- case ClassWriter.LONG: +- return new Long(readLong(index)); +- case ClassWriter.DOUBLE: +- return new Double(Double.longBitsToDouble(readLong(index))); +- case ClassWriter.CLASS: +- return Type.getObjectType(readUTF8(index, buf)); +- case ClassWriter.STR: +- return readUTF8(index, buf); +- case ClassWriter.MTYPE: +- return Type.getMethodType(readUTF8(index, buf)); +- default: // case ClassWriter.HANDLE_BASE + [1..9]: +- int tag = readByte(index); +- int[] items = this.items; +- int cpIndex = items[readUnsignedShort(index + 1)]; +- String owner = readClass(cpIndex, buf); +- cpIndex = items[readUnsignedShort(cpIndex + 2)]; +- String name = readUTF8(cpIndex, buf); +- String desc = readUTF8(cpIndex + 2, buf); +- return new Handle(tag, owner, name, desc); +- } +- } +-} +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 5e1c46fd..00000000 +--- a/src/main/java/org/mvel2/asm/ClassVisitor.java ++++ /dev/null +@@ -1,320 +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: visit [ visitSource ] [ +- * visitOuterClass ] ( visitAnnotation | +- * visitTypeAnnotation | visitAttribute )* ( +- * visitInnerClass | visitField | visitMethod )* +- * 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- * @param cv +- * the class visitor to which this visitor must delegate method +- * calls. May be null. +- */ +- public ClassVisitor(final int api, final ClassVisitor cv) { +- if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { +- throw new IllegalArgumentException(); +- } +- this.api = api; +- this.cv = cv; +- } +- +- /** +- * Visits the header of the class. +- * +- * @param version +- * the class version. +- * @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() getInternalName}). +- * @param signature +- * the signature of this class. May be 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() getInternalName}). For +- * interfaces, the super class is {@link Object}. May be +- * null, but only for the {@link Object} class. +- * @param interfaces +- * the internal names of the class's interfaces (see +- * {@link Type#getInternalName() getInternalName}). May be +- * null. +- */ +- public void visit(int version, int access, String name, String signature, +- String superName, 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 null. +- * @param debug +- * additional debug information to compute the correspondance +- * between source and compiled elements of the class. May be +- * null. +- */ +- public void visitSource(String source, String debug) { +- if (cv != null) { +- cv.visitSource(source, debug); +- } +- } +- +- /** +- * 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 +- * null if the class is not enclosed in a method of its +- * enclosing class. +- * @param desc +- * the descriptor of the method that contains the class, or +- * null if the class is not enclosed in a method of its +- * enclosing class. +- */ +- public void visitOuterClass(String owner, String name, String desc) { +- if (cv != null) { +- cv.visitOuterClass(owner, name, desc); +- } +- } +- +- /** +- * Visits an annotation of the class. +- * +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitAnnotation(String desc, boolean visible) { +- if (cv != null) { +- return cv.visitAnnotation(desc, 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 +- * CLASS_TYPE_PARAMETER}, +- * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND +- * CLASS_TYPE_PARAMETER_BOUND} or +- * {@link TypeReference#CLASS_EXTENDS 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitTypeAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (cv != null) { +- return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); +- } +- return null; +- } +- +- /** +- * Visits a non standard attribute of the class. +- * +- * @param attr +- * an attribute. +- */ +- public void visitAttribute(Attribute attr) { +- if (cv != null) { +- cv.visitAttribute(attr); +- } +- } +- +- /** +- * 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() getInternalName}). +- * @param outerName +- * the internal name of the class to which the inner class +- * belongs (see {@link Type#getInternalName() getInternalName}). +- * May be null for not member classes. +- * @param innerName +- * the (simple) name of the inner class inside its enclosing +- * class. May be null for anonymous inner classes. +- * @param access +- * the access flags of the inner class as originally declared in +- * the enclosing class. +- */ +- public void visitInnerClass(String name, String outerName, +- String innerName, 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 desc +- * the field's descriptor (see {@link Type Type}). +- * @param signature +- * the field's signature. May be null if the field's +- * type does not use generic types. +- * @param value +- * the field's initial value. This parameter, which may be +- * 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 int, +- * float, long or String fields +- * respectively). This parameter is only used for static +- * fields. 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 +- * null if this class visitor is not interested in visiting +- * these annotations and attributes. +- */ +- public FieldVisitor visitField(int access, String name, String desc, +- String signature, Object value) { +- if (cv != null) { +- return cv.visitField(access, name, desc, signature, value); +- } +- return null; +- } +- +- /** +- * Visits a method of the class. This method must return a new +- * {@link MethodVisitor} instance (or 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 desc +- * the method's descriptor (see {@link Type Type}). +- * @param signature +- * the method's signature. May be 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() getInternalName}). May be +- * null. +- * @return an object to visit the byte code of the method, or null +- * if this class visitor is not interested in visiting the code of +- * this method. +- */ +- public MethodVisitor visitMethod(int access, String name, String desc, +- String signature, String[] exceptions) { +- if (cv != null) { +- return cv.visitMethod(access, name, desc, 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 3222dabb..00000000 +--- a/src/main/java/org/mvel2/asm/ClassWriter.java ++++ /dev/null +@@ -1,1776 +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 classes in bytecode form. More +- * precisely this visitor generates a byte array conforming to the Java class +- * file format. It can be used alone, to generate a Java class "from scratch", +- * or with one or more {@link ClassReader ClassReader} and adapter class visitor +- * to generate a modified class from one or more existing Java classes. +- * +- * @author Eric Bruneton +- */ +-public class ClassWriter extends ClassVisitor { +- +- /** +- * 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 visitMaxs} method of the +- * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} +- * method will be ignored, and computed automatically from the signature and +- * the bytecode of each method. +- * +- * @see #ClassWriter(int) +- */ +- public static final int COMPUTE_MAXS = 1; +- +- /** +- * 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 visitMaxs} method are also ignored and +- * recomputed from the bytecode. In other words, computeFrames implies +- * computeMaxs. +- * +- * @see #ClassWriter(int) +- */ +- public static final int COMPUTE_FRAMES = 2; +- +- /** +- * Pseudo access flag to distinguish between the synthetic attribute and the +- * synthetic access flag. +- */ +- static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000; +- +- /** +- * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC. +- */ +- static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE +- / Opcodes.ACC_SYNTHETIC; +- +- /** +- * The type of instructions without any argument. +- */ +- static final int NOARG_INSN = 0; +- +- /** +- * The type of instructions with an signed byte argument. +- */ +- static final int SBYTE_INSN = 1; +- +- /** +- * The type of instructions with an signed short argument. +- */ +- static final int SHORT_INSN = 2; +- +- /** +- * The type of instructions with a local variable index argument. +- */ +- static final int VAR_INSN = 3; +- +- /** +- * The type of instructions with an implicit local variable index argument. +- */ +- static final int IMPLVAR_INSN = 4; +- +- /** +- * The type of instructions with a type descriptor argument. +- */ +- static final int TYPE_INSN = 5; +- +- /** +- * The type of field and method invocations instructions. +- */ +- static final int FIELDORMETH_INSN = 6; +- +- /** +- * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction. +- */ +- static final int ITFMETH_INSN = 7; +- +- /** +- * The type of the INVOKEDYNAMIC instruction. +- */ +- static final int INDYMETH_INSN = 8; +- +- /** +- * The type of instructions with a 2 bytes bytecode offset label. +- */ +- static final int LABEL_INSN = 9; +- +- /** +- * The type of instructions with a 4 bytes bytecode offset label. +- */ +- static final int LABELW_INSN = 10; +- +- /** +- * The type of the LDC instruction. +- */ +- static final int LDC_INSN = 11; +- +- /** +- * The type of the LDC_W and LDC2_W instructions. +- */ +- static final int LDCW_INSN = 12; +- +- /** +- * The type of the IINC instruction. +- */ +- static final int IINC_INSN = 13; +- +- /** +- * The type of the TABLESWITCH instruction. +- */ +- static final int TABL_INSN = 14; +- +- /** +- * The type of the LOOKUPSWITCH instruction. +- */ +- static final int LOOK_INSN = 15; +- +- /** +- * The type of the MULTIANEWARRAY instruction. +- */ +- static final int MANA_INSN = 16; +- +- /** +- * The type of the WIDE instruction. +- */ +- static final int WIDE_INSN = 17; +- +- /** +- * The instruction types of all JVM opcodes. +- */ +- static final byte[] TYPE; +- +- /** +- * The type of CONSTANT_Class constant pool items. +- */ +- static final int CLASS = 7; +- +- /** +- * The type of CONSTANT_Fieldref constant pool items. +- */ +- static final int FIELD = 9; +- +- /** +- * The type of CONSTANT_Methodref constant pool items. +- */ +- static final int METH = 10; +- +- /** +- * The type of CONSTANT_InterfaceMethodref constant pool items. +- */ +- static final int IMETH = 11; +- +- /** +- * The type of CONSTANT_String constant pool items. +- */ +- static final int STR = 8; +- +- /** +- * The type of CONSTANT_Integer constant pool items. +- */ +- static final int INT = 3; +- +- /** +- * The type of CONSTANT_Float constant pool items. +- */ +- static final int FLOAT = 4; +- +- /** +- * The type of CONSTANT_Long constant pool items. +- */ +- static final int LONG = 5; +- +- /** +- * The type of CONSTANT_Double constant pool items. +- */ +- static final int DOUBLE = 6; +- +- /** +- * The type of CONSTANT_NameAndType constant pool items. +- */ +- static final int NAME_TYPE = 12; +- +- /** +- * The type of CONSTANT_Utf8 constant pool items. +- */ +- static final int UTF8 = 1; +- +- /** +- * The type of CONSTANT_MethodType constant pool items. +- */ +- static final int MTYPE = 16; +- +- /** +- * The type of CONSTANT_MethodHandle constant pool items. +- */ +- static final int HANDLE = 15; +- +- /** +- * The type of CONSTANT_InvokeDynamic constant pool items. +- */ +- static final int INDY = 18; +- +- /** +- * The base value for all CONSTANT_MethodHandle constant pool items. +- * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 +- * different items. +- */ +- static final int HANDLE_BASE = 20; +- +- /** +- * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, +- * instead of the constant pool, in order to avoid clashes with normal +- * constant pool items in the ClassWriter constant pool's hash table. +- */ +- static final int TYPE_NORMAL = 30; +- +- /** +- * Uninitialized type Item stored in the ClassWriter +- * {@link ClassWriter#typeTable}, instead of the constant pool, in order to +- * avoid clashes with normal constant pool items in the ClassWriter constant +- * pool's hash table. +- */ +- static final int TYPE_UNINIT = 31; +- +- /** +- * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, +- * instead of the constant pool, in order to avoid clashes with normal +- * constant pool items in the ClassWriter constant pool's hash table. +- */ +- static final int TYPE_MERGED = 32; +- +- /** +- * The type of BootstrapMethods items. These items are stored in a special +- * class attribute named BootstrapMethods and not in the constant pool. +- */ +- static final int BSM = 33; +- +- /** +- * The class reader from which this class writer was constructed, if any. +- */ +- ClassReader cr; +- +- /** +- * Minor and major version numbers of the class to be generated. +- */ +- int version; +- +- /** +- * Index of the next item to be added in the constant pool. +- */ +- int index; +- +- /** +- * The constant pool of this class. +- */ +- final ByteVector pool; +- +- /** +- * The constant pool's hash table data. +- */ +- Item[] items; +- +- /** +- * The threshold of the constant pool's hash table. +- */ +- int threshold; +- +- /** +- * A reusable key used to look for items in the {@link #items} hash table. +- */ +- final Item key; +- +- /** +- * A reusable key used to look for items in the {@link #items} hash table. +- */ +- final Item key2; +- +- /** +- * A reusable key used to look for items in the {@link #items} hash table. +- */ +- final Item key3; +- +- /** +- * A reusable key used to look for items in the {@link #items} hash table. +- */ +- final Item key4; +- +- /** +- * A 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 associates to each index i +- * the Item whose index is i. All Item objects stored in this array +- * are also stored in the {@link #items} hash table. These two arrays allow +- * to retrieve an Item from its index or, conversely, to get the index of an +- * Item from its value. Each Item stores an internal name in its +- * {@link Item#strVal1} field. +- */ +- Item[] typeTable; +- +- /** +- * Number of elements in the {@link #typeTable} array. +- */ +- private short typeCount; +- +- /** +- * The access flags of this class. +- */ +- private int access; +- +- /** +- * The constant pool item that contains the internal name of this class. +- */ +- private int name; +- +- /** +- * The internal name of this class. +- */ +- String thisName; +- +- /** +- * The constant pool item that contains the signature of this class. +- */ +- private int signature; +- +- /** +- * The constant pool item that contains the internal name of the super class +- * of this class. +- */ +- private int superName; +- +- /** +- * Number of interfaces implemented or extended by this class or interface. +- */ +- private int interfaceCount; +- +- /** +- * The interfaces implemented or extended by this class or interface. More +- * precisely, this array contains the indexes of the constant pool items +- * that contain the internal names of these interfaces. +- */ +- private int[] interfaces; +- +- /** +- * The index of the constant pool item that contains the name of the source +- * file from which this class was compiled. +- */ +- private int sourceFile; +- +- /** +- * The SourceDebug attribute of this class. +- */ +- private ByteVector sourceDebug; +- +- /** +- * The constant pool item that contains the name of the enclosing class of +- * this class. +- */ +- private int enclosingMethodOwner; +- +- /** +- * The constant pool item that contains the name and descriptor of the +- * enclosing method of this class. +- */ +- private int enclosingMethod; +- +- /** +- * The runtime visible annotations of this class. +- */ +- private AnnotationWriter anns; +- +- /** +- * The runtime invisible annotations of this class. +- */ +- private AnnotationWriter ianns; +- +- /** +- * The runtime visible type annotations of this class. +- */ +- private AnnotationWriter tanns; +- +- /** +- * The runtime invisible type annotations of this class. +- */ +- private AnnotationWriter itanns; +- +- /** +- * The non standard attributes of this class. +- */ +- private Attribute attrs; +- +- /** +- * The number of entries in the InnerClasses attribute. +- */ +- private int innerClassesCount; +- +- /** +- * The InnerClasses attribute. +- */ +- private ByteVector innerClasses; +- +- /** +- * The number of entries in the BootstrapMethods attribute. +- */ +- int bootstrapMethodsCount; +- +- /** +- * The BootstrapMethods attribute. +- */ +- ByteVector bootstrapMethods; +- +- /** +- * The fields of this class. These fields are stored in a linked list of +- * {@link FieldWriter} objects, linked to each other by their +- * {@link FieldWriter#fv} field. This field stores the first element of this +- * list. +- */ +- FieldWriter firstField; +- +- /** +- * The fields of this class. These fields are stored in a linked list of +- * {@link FieldWriter} objects, linked to each other by their +- * {@link FieldWriter#fv} field. This field stores the last element of this +- * list. +- */ +- FieldWriter lastField; +- +- /** +- * The methods of this class. These methods are stored in a linked list of +- * {@link MethodWriter} objects, linked to each other by their +- * {@link MethodWriter#mv} field. This field stores the first element of +- * this list. +- */ +- MethodWriter firstMethod; +- +- /** +- * The methods of this class. These methods are stored in a linked list of +- * {@link MethodWriter} objects, linked to each other by their +- * {@link MethodWriter#mv} field. This field stores the last element of this +- * list. +- */ +- MethodWriter lastMethod; +- +- /** +- * true if the maximum stack size and number of local variables +- * must be automatically computed. +- */ +- private boolean computeMaxs; +- +- /** +- * true if the stack map frames must be recomputed from scratch. +- */ +- private boolean computeFrames; +- +- /** +- * true if the stack map tables of this class are invalid. The +- * {@link MethodWriter#resizeInstructions} method cannot transform existing +- * stack map tables, and so produces potentially invalid classes when it is +- * executed. In this case the class is reread and rewritten with the +- * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize +- * stack map tables when this option is used). +- */ +- boolean invalidFrames; +- +- // ------------------------------------------------------------------------ +- // Static initializer +- // ------------------------------------------------------------------------ +- +- /** +- * Computes the instruction types of JVM opcodes. +- */ +- static { +- int i; +- byte[] b = new byte[220]; +- String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" +- + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +- + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" +- + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ"; +- for (i = 0; i < b.length; ++i) { +- b[i] = (byte) (s.charAt(i) - 'A'); +- } +- TYPE = b; +- +- // code to generate the above string +- // +- // // SBYTE_INSN instructions +- // b[Constants.NEWARRAY] = SBYTE_INSN; +- // b[Constants.BIPUSH] = SBYTE_INSN; +- // +- // // SHORT_INSN instructions +- // b[Constants.SIPUSH] = SHORT_INSN; +- // +- // // (IMPL)VAR_INSN instructions +- // b[Constants.RET] = VAR_INSN; +- // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { +- // b[i] = VAR_INSN; +- // } +- // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { +- // b[i] = VAR_INSN; +- // } +- // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 +- // b[i] = IMPLVAR_INSN; +- // } +- // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 +- // b[i] = IMPLVAR_INSN; +- // } +- // +- // // TYPE_INSN instructions +- // b[Constants.NEW] = TYPE_INSN; +- // b[Constants.ANEWARRAY] = TYPE_INSN; +- // b[Constants.CHECKCAST] = TYPE_INSN; +- // b[Constants.INSTANCEOF] = TYPE_INSN; +- // +- // // (Set)FIELDORMETH_INSN instructions +- // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { +- // b[i] = FIELDORMETH_INSN; +- // } +- // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; +- // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN; +- // +- // // LABEL(W)_INSN instructions +- // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { +- // b[i] = LABEL_INSN; +- // } +- // b[Constants.IFNULL] = LABEL_INSN; +- // b[Constants.IFNONNULL] = LABEL_INSN; +- // b[200] = LABELW_INSN; // GOTO_W +- // b[201] = LABELW_INSN; // JSR_W +- // // temporary opcodes used internally by ASM - see Label and +- // MethodWriter +- // for (i = 202; i < 220; ++i) { +- // b[i] = LABEL_INSN; +- // } +- // +- // // LDC(_W) instructions +- // b[Constants.LDC] = LDC_INSN; +- // b[19] = LDCW_INSN; // LDC_W +- // b[20] = LDCW_INSN; // LDC2_W +- // +- // // special instructions +- // b[Constants.IINC] = IINC_INSN; +- // b[Constants.TABLESWITCH] = TABL_INSN; +- // b[Constants.LOOKUPSWITCH] = LOOK_INSN; +- // b[Constants.MULTIANEWARRAY] = MANA_INSN; +- // b[196] = WIDE_INSN; // WIDE +- // +- // for (i = 0; i < b.length; ++i) { +- // System.err.print((char)('A' + b[i])); +- // } +- // System.err.println(); +- } +- +- // ------------------------------------------------------------------------ +- // Constructor +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new {@link ClassWriter} object. +- * +- * @param flags +- * option flags that can be used to modify the default behavior +- * of this class. See {@link #COMPUTE_MAXS}, +- * {@link #COMPUTE_FRAMES}. +- */ +- public ClassWriter(final int flags) { +- super(Opcodes.ASM5); +- index = 1; +- pool = new ByteVector(); +- items = new Item[256]; +- threshold = (int) (0.75d * items.length); +- key = new Item(); +- key2 = new Item(); +- key3 = new Item(); +- key4 = new Item(); +- this.computeMaxs = (flags & COMPUTE_MAXS) != 0; +- this.computeFrames = (flags & COMPUTE_FRAMES) != 0; +- } +- +- /** +- * Constructs a new {@link ClassWriter} object and enables optimizations for +- * "mostly add" bytecode transformations. These optimizations are the +- * following: +- * +- *
    +- *
  • The constant pool from the original class is copied as is in the new +- * class, which saves time. New constant pool entries will be added at the +- * end if necessary, but unused constant pool entries won't be +- * removed.
  • +- *
  • 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 lot 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).
  • +- *
+- * +- * @param classReader +- * the {@link ClassReader} used to read the original class. It +- * will be used to copy the entire constant pool 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. These option flags do not affect methods +- * that are copied as is in the new class. This means that the +- * maximum stack size nor the stack frames will be computed for +- * these methods. See {@link #COMPUTE_MAXS}, +- * {@link #COMPUTE_FRAMES}. +- */ +- public ClassWriter(final ClassReader classReader, final int flags) { +- this(flags); +- classReader.copyPool(this); +- this.cr = classReader; +- } +- +- // ------------------------------------------------------------------------ +- // 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.access = access; +- this.name = newClass(name); +- thisName = name; +- if (ClassReader.SIGNATURES && signature != null) { +- this.signature = newUTF8(signature); +- } +- this.superName = superName == null ? 0 : newClass(superName); +- if (interfaces != null && interfaces.length > 0) { +- interfaceCount = interfaces.length; +- this.interfaces = new int[interfaceCount]; +- for (int i = 0; i < interfaceCount; ++i) { +- this.interfaces[i] = newClass(interfaces[i]); +- } +- } +- } +- +- @Override +- public final void visitSource(final String file, final String debug) { +- if (file != null) { +- sourceFile = newUTF8(file); +- } +- if (debug != null) { +- sourceDebug = new ByteVector().encodeUTF8(debug, 0, +- Integer.MAX_VALUE); +- } +- } +- +- @Override +- public final void visitOuterClass(final String owner, final String name, +- final String desc) { +- enclosingMethodOwner = newClass(owner); +- if (name != null && desc != null) { +- enclosingMethod = newNameType(name, desc); +- } +- } +- +- @Override +- public final AnnotationVisitor visitAnnotation(final String desc, +- final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write type, and reserve space for values count +- bv.putShort(newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); +- if (visible) { +- aw.next = anns; +- anns = aw; +- } else { +- aw.next = ianns; +- ianns = aw; +- } +- return aw; +- } +- +- @Override +- public final AnnotationVisitor visitTypeAnnotation(int typeRef, +- TypePath typePath, final String desc, final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- AnnotationWriter.putTarget(typeRef, typePath, bv); +- // write type, and reserve space for values count +- bv.putShort(newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = tanns; +- tanns = aw; +- } else { +- aw.next = itanns; +- itanns = aw; +- } +- return aw; +- } +- +- @Override +- public final void visitAttribute(final Attribute attr) { +- attr.next = attrs; +- attrs = attr; +- } +- +- @Override +- public final void visitInnerClass(final String name, +- final String outerName, final String innerName, final int access) { +- if (innerClasses == null) { +- innerClasses = new ByteVector(); +- } +- // �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 intVal field +- // of the Item of each CONSTANT_Class_info entry C whether an inner +- // class entry has already been added for C (this field is unused for +- // class entries, and changing its value does not change the hashcode +- // and equality tests). If so we store the index of this inner class +- // entry (plus one) in intVal. This hack allows duplicate detection in +- // O(1) time. +- Item nameItem = newClassItem(name); +- if (nameItem.intVal == 0) { +- ++innerClassesCount; +- innerClasses.putShort(nameItem.index); +- innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); +- innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); +- innerClasses.putShort(access); +- nameItem.intVal = innerClassesCount; +- } else { +- // Compare the inner classes entry nameItem.intVal - 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 desc, final String signature, final Object value) { +- return new FieldWriter(this, access, name, desc, signature, value); +- } +- +- @Override +- public final MethodVisitor visitMethod(final int access, final String name, +- final String desc, final String signature, final String[] exceptions) { +- return new MethodWriter(this, access, name, desc, signature, +- exceptions, computeMaxs, computeFrames); +- } +- +- @Override +- public final void visitEnd() { +- } +- +- // ------------------------------------------------------------------------ +- // Other public methods +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the bytecode of the class that was build with this class writer. +- * +- * @return the bytecode of the class that was build with this class writer. +- */ +- public byte[] toByteArray() { +- if (index > 0xFFFF) { +- throw new RuntimeException("Class file too large!"); +- } +- // computes the real size of the bytecode of this class +- int size = 24 + 2 * interfaceCount; +- int nbFields = 0; +- FieldWriter fb = firstField; +- while (fb != null) { +- ++nbFields; +- size += fb.getSize(); +- fb = (FieldWriter) fb.fv; +- } +- int nbMethods = 0; +- MethodWriter mb = firstMethod; +- while (mb != null) { +- ++nbMethods; +- size += mb.getSize(); +- mb = (MethodWriter) mb.mv; +- } +- int attributeCount = 0; +- if (bootstrapMethods != null) { +- // we put it as first attribute in order to improve a bit +- // ClassReader.copyBootstrapMethods +- ++attributeCount; +- size += 8 + bootstrapMethods.length; +- newUTF8("BootstrapMethods"); +- } +- if (ClassReader.SIGNATURES && signature != 0) { +- ++attributeCount; +- size += 8; +- newUTF8("Signature"); +- } +- if (sourceFile != 0) { +- ++attributeCount; +- size += 8; +- newUTF8("SourceFile"); +- } +- if (sourceDebug != null) { +- ++attributeCount; +- size += sourceDebug.length + 6; +- newUTF8("SourceDebugExtension"); +- } +- if (enclosingMethodOwner != 0) { +- ++attributeCount; +- size += 10; +- newUTF8("EnclosingMethod"); +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- ++attributeCount; +- size += 6; +- newUTF8("Deprecated"); +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((version & 0xFFFF) < Opcodes.V1_5 +- || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- ++attributeCount; +- size += 6; +- newUTF8("Synthetic"); +- } +- } +- if (innerClasses != null) { +- ++attributeCount; +- size += 8 + innerClasses.length; +- newUTF8("InnerClasses"); +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- ++attributeCount; +- size += 8 + anns.getSize(); +- newUTF8("RuntimeVisibleAnnotations"); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- ++attributeCount; +- size += 8 + ianns.getSize(); +- newUTF8("RuntimeInvisibleAnnotations"); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- ++attributeCount; +- size += 8 + tanns.getSize(); +- newUTF8("RuntimeVisibleTypeAnnotations"); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- ++attributeCount; +- size += 8 + itanns.getSize(); +- newUTF8("RuntimeInvisibleTypeAnnotations"); +- } +- if (attrs != null) { +- attributeCount += attrs.getCount(); +- size += attrs.getSize(this, null, 0, -1, -1); +- } +- size += pool.length; +- // allocates a byte vector of this size, in order to avoid unnecessary +- // arraycopy operations in the ByteVector.enlarge() method +- ByteVector out = new ByteVector(size); +- out.putInt(0xCAFEBABE).putInt(version); +- out.putShort(index).putByteArray(pool.data, 0, pool.length); +- int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE +- | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); +- out.putShort(access & ~mask).putShort(name).putShort(superName); +- out.putShort(interfaceCount); +- for (int i = 0; i < interfaceCount; ++i) { +- out.putShort(interfaces[i]); +- } +- out.putShort(nbFields); +- fb = firstField; +- while (fb != null) { +- fb.put(out); +- fb = (FieldWriter) fb.fv; +- } +- out.putShort(nbMethods); +- mb = firstMethod; +- while (mb != null) { +- mb.put(out); +- mb = (MethodWriter) mb.mv; +- } +- out.putShort(attributeCount); +- if (bootstrapMethods != null) { +- out.putShort(newUTF8("BootstrapMethods")); +- out.putInt(bootstrapMethods.length + 2).putShort( +- bootstrapMethodsCount); +- out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); +- } +- if (ClassReader.SIGNATURES && signature != 0) { +- out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); +- } +- if (sourceFile != 0) { +- out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); +- } +- if (sourceDebug != null) { +- int len = sourceDebug.length; +- out.putShort(newUTF8("SourceDebugExtension")).putInt(len); +- out.putByteArray(sourceDebug.data, 0, len); +- } +- if (enclosingMethodOwner != 0) { +- out.putShort(newUTF8("EnclosingMethod")).putInt(4); +- out.putShort(enclosingMethodOwner).putShort(enclosingMethod); +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- out.putShort(newUTF8("Deprecated")).putInt(0); +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((version & 0xFFFF) < Opcodes.V1_5 +- || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- out.putShort(newUTF8("Synthetic")).putInt(0); +- } +- } +- if (innerClasses != null) { +- out.putShort(newUTF8("InnerClasses")); +- out.putInt(innerClasses.length + 2).putShort(innerClassesCount); +- out.putByteArray(innerClasses.data, 0, innerClasses.length); +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- out.putShort(newUTF8("RuntimeVisibleAnnotations")); +- anns.put(out); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- out.putShort(newUTF8("RuntimeInvisibleAnnotations")); +- ianns.put(out); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); +- tanns.put(out); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); +- itanns.put(out); +- } +- if (attrs != null) { +- attrs.put(this, null, 0, -1, -1, out); +- } +- if (invalidFrames) { +- anns = null; +- ianns = null; +- attrs = null; +- innerClassesCount = 0; +- innerClasses = null; +- bootstrapMethodsCount = 0; +- bootstrapMethods = null; +- firstField = null; +- lastField = null; +- firstMethod = null; +- lastMethod = null; +- computeMaxs = false; +- computeFrames = true; +- invalidFrames = false; +- new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); +- return toByteArray(); +- } +- return out.data; +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: constant pool management +- // ------------------------------------------------------------------------ +- +- /** +- * 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. +- * +- * @param cst +- * 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}, a {@link String} or a +- * {@link Type}. +- * @return a new or already existing constant item with the given value. +- */ +- Item newConstItem(final Object cst) { +- if (cst instanceof Integer) { +- int val = ((Integer) cst).intValue(); +- return newInteger(val); +- } else if (cst instanceof Byte) { +- int val = ((Byte) cst).intValue(); +- return newInteger(val); +- } else if (cst instanceof Character) { +- int val = ((Character) cst).charValue(); +- return newInteger(val); +- } else if (cst instanceof Short) { +- int val = ((Short) cst).intValue(); +- return newInteger(val); +- } else if (cst instanceof Boolean) { +- int val = ((Boolean) cst).booleanValue() ? 1 : 0; +- return newInteger(val); +- } else if (cst instanceof Float) { +- float val = ((Float) cst).floatValue(); +- return newFloat(val); +- } else if (cst instanceof Long) { +- long val = ((Long) cst).longValue(); +- return newLong(val); +- } else if (cst instanceof Double) { +- double val = ((Double) cst).doubleValue(); +- return newDouble(val); +- } else if (cst instanceof String) { +- return newString((String) cst); +- } else if (cst instanceof Type) { +- Type t = (Type) cst; +- int s = t.getSort(); +- if (s == Type.OBJECT) { +- return newClassItem(t.getInternalName()); +- } else if (s == Type.METHOD) { +- return newMethodTypeItem(t.getDescriptor()); +- } else { // s == primitive type or array +- return newClassItem(t.getDescriptor()); +- } +- } else if (cst instanceof Handle) { +- Handle h = (Handle) cst; +- return newHandleItem(h.tag, h.owner, h.name, h.desc); +- } else { +- throw new IllegalArgumentException("value " + cst); +- } +- } +- +- /** +- * 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. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param cst +- * 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 cst) { +- return newConstItem(cst).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. This +- * method is intended for {@link Attribute} sub classes, and is normally not +- * needed by class generators or adapters. +- * +- * @param value +- * the String value. +- * @return the index of a new or already existing UTF8 item. +- */ +- public int newUTF8(final String value) { +- key.set(UTF8, value, null, null); +- Item result = get(key); +- if (result == null) { +- pool.putByte(UTF8).putUTF8(value); +- result = new Item(index++, key); +- put(result); +- } +- return result.index; +- } +- +- /** +- * Adds a class reference to the constant pool of the class being build. +- * Does nothing if the constant pool already contains a similar item. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param value +- * the internal name of the class. +- * @return a new or already existing class reference item. +- */ +- Item newClassItem(final String value) { +- key2.set(CLASS, value, null, null); +- Item result = get(key2); +- if (result == null) { +- pool.put12(CLASS, newUTF8(value)); +- result = new Item(index++, key2); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a class reference to the constant pool of the class being build. +- * Does nothing if the constant pool already contains a similar item. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @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 newClassItem(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. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param methodDesc +- * method descriptor of the method type. +- * @return a new or already existing method type reference item. +- */ +- Item newMethodTypeItem(final String methodDesc) { +- key2.set(MTYPE, methodDesc, null, null); +- Item result = get(key2); +- if (result == null) { +- pool.put12(MTYPE, newUTF8(methodDesc)); +- result = new Item(index++, key2); +- put(result); +- } +- return result; +- } +- +- /** +- * 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. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param methodDesc +- * method descriptor of the method type. +- * @return the index of a new or already existing method type reference +- * item. +- */ +- public int newMethodType(final String methodDesc) { +- return newMethodTypeItem(methodDesc).index; +- } +- +- /** +- * Adds a handle to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. This method is +- * intended for {@link Attribute} sub classes, and is normally not needed by +- * class generators or adapters. +- * +- * @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 desc +- * the descriptor of the field or method. +- * @return a new or an already existing method type reference item. +- */ +- Item newHandleItem(final int tag, final String owner, final String name, +- final String desc) { +- key4.set(HANDLE_BASE + tag, owner, name, desc); +- Item result = get(key4); +- if (result == null) { +- if (tag <= Opcodes.H_PUTSTATIC) { +- put112(HANDLE, tag, newField(owner, name, desc)); +- } else { +- put112(HANDLE, +- tag, +- newMethod(owner, name, desc, +- tag == Opcodes.H_INVOKEINTERFACE)); +- } +- result = new Item(index++, key4); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a handle to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. This method is +- * intended for {@link Attribute} sub classes, and is normally not needed by +- * class generators or adapters. +- * +- * @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 desc +- * the descriptor of the field or method. +- * @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 desc) { +- return newHandleItem(tag, owner, name, desc).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. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param name +- * name of the invoked method. +- * @param desc +- * descriptor of the invoke method. +- * @param bsm +- * the bootstrap method. +- * @param bsmArgs +- * the bootstrap method constant arguments. +- * +- * @return a new or an already existing invokedynamic type reference item. +- */ +- Item newInvokeDynamicItem(final String name, final String desc, +- final Handle bsm, final Object... bsmArgs) { +- // cache for performance +- ByteVector bootstrapMethods = this.bootstrapMethods; +- if (bootstrapMethods == null) { +- bootstrapMethods = this.bootstrapMethods = new ByteVector(); +- } +- +- int position = bootstrapMethods.length; // record current position +- +- int hashCode = bsm.hashCode(); +- bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, +- bsm.desc)); +- +- int argsLength = bsmArgs.length; +- bootstrapMethods.putShort(argsLength); +- +- for (int i = 0; i < argsLength; i++) { +- Object bsmArg = bsmArgs[i]; +- hashCode ^= bsmArg.hashCode(); +- bootstrapMethods.putShort(newConst(bsmArg)); +- } +- +- byte[] data = bootstrapMethods.data; +- int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) +- hashCode &= 0x7FFFFFFF; +- Item result = items[hashCode % items.length]; +- loop: while (result != null) { +- if (result.type != BSM || result.hashCode != hashCode) { +- result = result.next; +- continue; +- } +- +- // because the data encode the size of the argument +- // we don't need to test if these size are equals +- int resultPosition = result.intVal; +- for (int p = 0; p < length; p++) { +- if (data[position + p] != data[resultPosition + p]) { +- result = result.next; +- continue loop; +- } +- } +- break; +- } +- +- int bootstrapMethodIndex; +- if (result != null) { +- bootstrapMethodIndex = result.index; +- bootstrapMethods.length = position; // revert to old position +- } else { +- bootstrapMethodIndex = bootstrapMethodsCount++; +- result = new Item(bootstrapMethodIndex); +- result.set(position, hashCode); +- put(result); +- } +- +- // now, create the InvokeDynamic constant +- key3.set(name, desc, bootstrapMethodIndex); +- result = get(key3); +- if (result == null) { +- put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); +- result = new Item(index++, key3); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds an invokedynamic reference to the constant pool of the class being +- * build. Does nothing if the constant pool already contains a similar item. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param name +- * name of the invoked method. +- * @param desc +- * descriptor of the invoke method. +- * @param bsm +- * the bootstrap method. +- * @param bsmArgs +- * 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 desc, +- final Handle bsm, final Object... bsmArgs) { +- return newInvokeDynamicItem(name, desc, bsm, bsmArgs).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. +- * +- * @param owner +- * the internal name of the field's owner class. +- * @param name +- * the field's name. +- * @param desc +- * the field's descriptor. +- * @return a new or already existing field reference item. +- */ +- Item newFieldItem(final String owner, final String name, final String desc) { +- key3.set(FIELD, owner, name, desc); +- Item result = get(key3); +- if (result == null) { +- put122(FIELD, newClass(owner), newNameType(name, desc)); +- result = new Item(index++, key3); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a field reference to the constant pool of the class being build. +- * Does nothing if the constant pool already contains a similar item. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param owner +- * the internal name of the field's owner class. +- * @param name +- * the field's name. +- * @param desc +- * 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 desc) { +- return newFieldItem(owner, name, desc).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. +- * +- * @param owner +- * the internal name of the method's owner class. +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor. +- * @param itf +- * true if owner is an interface. +- * @return a new or already existing method reference item. +- */ +- Item newMethodItem(final String owner, final String name, +- final String desc, final boolean itf) { +- int type = itf ? IMETH : METH; +- key3.set(type, owner, name, desc); +- Item result = get(key3); +- if (result == null) { +- put122(type, newClass(owner), newNameType(name, desc)); +- result = new Item(index++, key3); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a method reference to the constant pool of the class being build. +- * Does nothing if the constant pool already contains a similar item. +- * This method is intended for {@link Attribute} sub classes, and is +- * normally not needed by class generators or adapters. +- * +- * @param owner +- * the internal name of the method's owner class. +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor. +- * @param itf +- * true if 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 desc, final boolean itf) { +- return newMethodItem(owner, name, desc, itf).index; +- } +- +- /** +- * Adds an integer to the constant pool of the class being build. Does +- * nothing if the constant pool already contains a similar item. +- * +- * @param value +- * the int value. +- * @return a new or already existing int item. +- */ +- Item newInteger(final int value) { +- key.set(value); +- Item result = get(key); +- if (result == null) { +- pool.putByte(INT).putInt(value); +- result = new Item(index++, key); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a float to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. +- * +- * @param value +- * the float value. +- * @return a new or already existing float item. +- */ +- Item newFloat(final float value) { +- key.set(value); +- Item result = get(key); +- if (result == null) { +- pool.putByte(FLOAT).putInt(key.intVal); +- result = new Item(index++, key); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a long to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. +- * +- * @param value +- * the long value. +- * @return a new or already existing long item. +- */ +- Item newLong(final long value) { +- key.set(value); +- Item result = get(key); +- if (result == null) { +- pool.putByte(LONG).putLong(value); +- result = new Item(index, key); +- index += 2; +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a double to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. +- * +- * @param value +- * the double value. +- * @return a new or already existing double item. +- */ +- Item newDouble(final double value) { +- key.set(value); +- Item result = get(key); +- if (result == null) { +- pool.putByte(DOUBLE).putLong(key.longVal); +- result = new Item(index, key); +- index += 2; +- put(result); +- } +- return result; +- } +- +- /** +- * Adds a string to the constant pool of the class being build. Does nothing +- * if the constant pool already contains a similar item. +- * +- * @param value +- * the String value. +- * @return a new or already existing string item. +- */ +- private Item newString(final String value) { +- key2.set(STR, value, null, null); +- Item result = get(key2); +- if (result == null) { +- pool.put12(STR, newUTF8(value)); +- result = new Item(index++, key2); +- put(result); +- } +- return result; +- } +- +- /** +- * 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. This +- * method is intended for {@link Attribute} sub classes, and is normally not +- * needed by class generators or adapters. +- * +- * @param name +- * a name. +- * @param desc +- * a type descriptor. +- * @return the index of a new or already existing name and type item. +- */ +- public int newNameType(final String name, final String desc) { +- return newNameTypeItem(name, desc).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. +- * +- * @param name +- * a name. +- * @param desc +- * a type descriptor. +- * @return a new or already existing name and type item. +- */ +- Item newNameTypeItem(final String name, final String desc) { +- key2.set(NAME_TYPE, name, desc, null); +- Item result = get(key2); +- if (result == null) { +- put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); +- result = new Item(index++, key2); +- put(result); +- } +- return result; +- } +- +- /** +- * Adds the given internal name to {@link #typeTable} and returns its index. +- * Does nothing if the type table already contains this internal name. +- * +- * @param type +- * the internal name to be added to the type table. +- * @return the index of this internal name in the type table. +- */ +- int addType(final String type) { +- key.set(TYPE_NORMAL, type, null, null); +- Item result = get(key); +- if (result == null) { +- result = addType(key); +- } +- return result.index; +- } +- +- /** +- * Adds the given "uninitialized" type to {@link #typeTable} and returns its +- * index. This method is used for UNINITIALIZED types, made of an internal +- * name and a bytecode offset. +- * +- * @param type +- * the internal name to be added to the type table. +- * @param offset +- * the bytecode offset of the NEW instruction that created this +- * UNINITIALIZED type value. +- * @return the index of this internal name in the type table. +- */ +- int addUninitializedType(final String type, final int offset) { +- key.type = TYPE_UNINIT; +- key.intVal = offset; +- key.strVal1 = type; +- key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); +- Item result = get(key); +- if (result == null) { +- result = addType(key); +- } +- return result.index; +- } +- +- /** +- * Adds the given Item to {@link #typeTable}. +- * +- * @param item +- * the value to be added to the type table. +- * @return the added Item, which a new Item instance with the same value as +- * the given Item. +- */ +- private Item addType(final Item item) { +- ++typeCount; +- Item result = new Item(typeCount, key); +- put(result); +- if (typeTable == null) { +- typeTable = new Item[16]; +- } +- if (typeCount == typeTable.length) { +- Item[] newTable = new Item[2 * typeTable.length]; +- System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); +- typeTable = newTable; +- } +- typeTable[typeCount] = result; +- return result; +- } +- +- /** +- * Returns the index of the common super type of the two given types. This +- * method calls {@link #getCommonSuperClass} and caches the result in the +- * {@link #items} hash table to speedup future calls with the same +- * parameters. +- * +- * @param type1 +- * index of an internal name in {@link #typeTable}. +- * @param type2 +- * index of an internal name in {@link #typeTable}. +- * @return the index of the common super type of the two given types. +- */ +- int getMergedType(final int type1, final int type2) { +- key2.type = TYPE_MERGED; +- key2.longVal = type1 | (((long) type2) << 32); +- key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); +- Item result = get(key2); +- if (result == null) { +- String t = typeTable[type1].strVal1; +- String u = typeTable[type2].strVal1; +- key2.intVal = addType(getCommonSuperClass(t, u)); +- result = new Item((short) 0, key2); +- put(result); +- } +- return result.intVal; +- } +- +- /** +- * Returns the common super type of the two given types. The default +- * implementation of this method loads 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) { +- Class c, d; +- ClassLoader classLoader = getClass().getClassLoader(); +- try { +- c = Class.forName(type1.replace('/', '.'), false, classLoader); +- d = Class.forName(type2.replace('/', '.'), false, classLoader); +- } catch (Exception e) { +- throw new RuntimeException(e.toString()); +- } +- if (c.isAssignableFrom(d)) { +- return type1; +- } +- if (d.isAssignableFrom(c)) { +- return type2; +- } +- if (c.isInterface() || d.isInterface()) { +- return "java/lang/Object"; +- } else { +- do { +- c = c.getSuperclass(); +- } while (!c.isAssignableFrom(d)); +- return c.getName().replace('.', '/'); +- } +- } +- +- /** +- * Returns the constant pool's hash table item which is equal to the given +- * item. +- * +- * @param key +- * a constant pool item. +- * @return the constant pool's hash table item which is equal to the given +- * item, or null if there is no such item. +- */ +- private Item get(final Item key) { +- Item i = items[key.hashCode % items.length]; +- while (i != null && (i.type != key.type || !key.isEqualTo(i))) { +- i = i.next; +- } +- return i; +- } +- +- /** +- * Puts the given item in the constant pool's hash table. The hash table +- * must not already contains this item. +- * +- * @param i +- * the item to be added to the constant pool's hash table. +- */ +- private void put(final Item i) { +- if (index + typeCount > threshold) { +- int ll = items.length; +- int nl = ll * 2 + 1; +- Item[] newItems = new Item[nl]; +- for (int l = ll - 1; l >= 0; --l) { +- Item j = items[l]; +- while (j != null) { +- int index = j.hashCode % newItems.length; +- Item k = j.next; +- j.next = newItems[index]; +- newItems[index] = j; +- j = k; +- } +- } +- items = newItems; +- threshold = (int) (nl * 0.75); +- } +- int index = i.hashCode % items.length; +- i.next = items[index]; +- items[index] = i; +- } +- +- /** +- * Puts one byte and two shorts into the constant pool. +- * +- * @param b +- * a byte. +- * @param s1 +- * a short. +- * @param s2 +- * another short. +- */ +- private void put122(final int b, final int s1, final int s2) { +- pool.put12(b, s1).putShort(s2); +- } +- +- /** +- * Puts two bytes and one short into the constant pool. +- * +- * @param b1 +- * a byte. +- * @param b2 +- * another byte. +- * @param s +- * a short. +- */ +- private void put112(final int b1, final int b2, final int s) { +- pool.put11(b1, b2).putShort(s); +- } +-} +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 c09e5b5b..00000000 +--- a/src/main/java/org/mvel2/asm/Context.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; +- +-/** +- * Information about a class being parsed in a {@link ClassReader}. +- * +- * @author Eric Bruneton +- */ +-class Context { +- +- /** +- * Prototypes of the attributes that must be parsed for this class. +- */ +- Attribute[] attrs; +- +- /** +- * The {@link ClassReader} option flags for the parsing of this class. +- */ +- int flags; +- +- /** +- * The buffer used to read strings. +- */ +- char[] buffer; +- +- /** +- * The start index of each bootstrap method. +- */ +- int[] bootstrapMethods; +- +- /** +- * The access flags of the method currently being parsed. +- */ +- int access; +- +- /** +- * The name of the method currently being parsed. +- */ +- String name; +- +- /** +- * The descriptor of the method currently being parsed. +- */ +- String desc; +- +- /** +- * The label objects, indexed by bytecode offset, of the method currently +- * being parsed (only bytecode offsets for which a label is needed have a +- * non null associated Label object). +- */ +- Label[] labels; +- +- /** +- * The target of the type annotation currently being parsed. +- */ +- int typeRef; +- +- /** +- * The path of the type annotation currently being parsed. +- */ +- TypePath typePath; +- +- /** +- * The offset of the latest stack map frame that has been parsed. +- */ +- int offset; +- +- /** +- * The labels corresponding to the start of the local variable ranges in the +- * local variable type annotation currently being parsed. +- */ +- Label[] start; +- +- /** +- * The labels corresponding to the end of the local variable ranges in the +- * local variable type annotation currently being parsed. +- */ +- Label[] end; +- +- /** +- * The local variable indices for each local variable range in the local +- * variable type annotation currently being parsed. +- */ +- int[] index; +- +- /** +- * The encoding of the latest stack map frame that has been parsed. +- */ +- int mode; +- +- /** +- * The number of locals in the latest stack map frame that has been parsed. +- */ +- int localCount; +- +- /** +- * The number locals in the latest stack map frame that has been parsed, +- * minus the number of locals in the previous frame. +- */ +- int localDiff; +- +- /** +- * The local values of the latest stack map frame that has been parsed. +- */ +- Object[] local; +- +- /** +- * The stack size of the latest stack map frame that has been parsed. +- */ +- int stackCount; +- +- /** +- * The stack values of the latest stack map frame that has been parsed. +- */ +- Object[] stack; +-} +\ No newline at end of file +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 8c1934c8..00000000 +--- a/src/main/java/org/mvel2/asm/Edge.java ++++ /dev/null +@@ -1,75 +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 body. See {@link Label Label}. +- * +- * @author Eric Bruneton +- */ +-class Edge { +- +- /** +- * Denotes a normal control flow graph edge. +- */ +- static final int NORMAL = 0; +- +- /** +- * Denotes a control flow graph edge corresponding to an exception handler. +- * More precisely any {@link Edge} whose {@link #info} is strictly positive +- * corresponds to an exception handler. The actual value of {@link #info} is +- * the index, in the {@link ClassWriter} type table, of the exception that +- * is catched. +- */ +- static final int EXCEPTION = 0x7FFFFFFF; +- +- /** +- * Information about this control flow graph edge. If +- * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) +- * stack size in the basic block from which this edge originates. This size +- * is equal to the stack size at the "jump" instruction to which this edge +- * corresponds, relatively to the stack size at the beginning of the +- * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, +- * this field is the kind of this control flow graph edge (i.e. NORMAL or +- * EXCEPTION). +- */ +- int info; +- +- /** +- * The successor block of the basic block from which this edge originates. +- */ +- Label successor; +- +- /** +- * The next edge in the list of successors of the originating basic block. +- * See {@link Label#successors successors}. +- */ +- Edge next; +-} +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 8ea416bf..00000000 +--- a/src/main/java/org/mvel2/asm/FieldVisitor.java ++++ /dev/null +@@ -1,150 +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: ( visitAnnotation | +- * visitTypeAnnotation | visitAttribute )* 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- * @param fv +- * the field visitor to which this visitor must delegate method +- * calls. May be null. +- */ +- public FieldVisitor(final int api, final FieldVisitor fv) { +- if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { +- throw new IllegalArgumentException(); +- } +- this.api = api; +- this.fv = fv; +- } +- +- /** +- * Visits an annotation of the field. +- * +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitAnnotation(String desc, boolean visible) { +- if (fv != null) { +- return fv.visitAnnotation(desc, 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 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitTypeAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (fv != null) { +- return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); +- } +- return null; +- } +- +- /** +- * Visits a non standard attribute of the field. +- * +- * @param attr +- * an attribute. +- */ +- public void visitAttribute(Attribute attr) { +- if (fv != null) { +- fv.visitAttribute(attr); +- } +- } +- +- /** +- * 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 f09b4942..00000000 +--- a/src/main/java/org/mvel2/asm/FieldWriter.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; +- +-/** +- * An {@link FieldVisitor} that generates Java fields in bytecode form. +- * +- * @author Eric Bruneton +- */ +-final class FieldWriter extends FieldVisitor { +- +- /** +- * The class writer to which this field must be added. +- */ +- private final ClassWriter cw; +- +- /** +- * Access flags of this field. +- */ +- private final int access; +- +- /** +- * The index of the constant pool item that contains the name of this +- * method. +- */ +- private final int name; +- +- /** +- * The index of the constant pool item that contains the descriptor of this +- * field. +- */ +- private final int desc; +- +- /** +- * The index of the constant pool item that contains the signature of this +- * field. +- */ +- private int signature; +- +- /** +- * The index of the constant pool item that contains the constant value of +- * this field. +- */ +- private int value; +- +- /** +- * The runtime visible annotations of this field. May be null. +- */ +- private AnnotationWriter anns; +- +- /** +- * The runtime invisible annotations of this field. May be null. +- */ +- private AnnotationWriter ianns; +- +- /** +- * The runtime visible type annotations of this field. May be null. +- */ +- private AnnotationWriter tanns; +- +- /** +- * The runtime invisible type annotations of this field. May be +- * null. +- */ +- private AnnotationWriter itanns; +- +- /** +- * The non standard attributes of this field. May be null. +- */ +- private Attribute attrs; +- +- // ------------------------------------------------------------------------ +- // Constructor +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new {@link FieldWriter}. +- * +- * @param cw +- * the class writer to which this field must be added. +- * @param access +- * the field's access flags (see {@link Opcodes}). +- * @param name +- * the field's name. +- * @param desc +- * the field's descriptor (see {@link Type}). +- * @param signature +- * the field's signature. May be null. +- * @param value +- * the field's constant value. May be null. +- */ +- FieldWriter(final ClassWriter cw, final int access, final String name, +- final String desc, final String signature, final Object value) { +- super(Opcodes.ASM5); +- if (cw.firstField == null) { +- cw.firstField = this; +- } else { +- cw.lastField.fv = this; +- } +- cw.lastField = this; +- this.cw = cw; +- this.access = access; +- this.name = cw.newUTF8(name); +- this.desc = cw.newUTF8(desc); +- if (ClassReader.SIGNATURES && signature != null) { +- this.signature = cw.newUTF8(signature); +- } +- if (value != null) { +- this.value = cw.newConstItem(value).index; +- } +- } +- +- // ------------------------------------------------------------------------ +- // Implementation of the FieldVisitor abstract class +- // ------------------------------------------------------------------------ +- +- @Override +- public AnnotationVisitor visitAnnotation(final String desc, +- final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); +- if (visible) { +- aw.next = anns; +- anns = aw; +- } else { +- aw.next = ianns; +- ianns = aw; +- } +- return aw; +- } +- +- @Override +- public AnnotationVisitor visitTypeAnnotation(final int typeRef, +- final TypePath typePath, final String desc, final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- AnnotationWriter.putTarget(typeRef, typePath, bv); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = tanns; +- tanns = aw; +- } else { +- aw.next = itanns; +- itanns = aw; +- } +- return aw; +- } +- +- @Override +- public void visitAttribute(final Attribute attr) { +- attr.next = attrs; +- attrs = attr; +- } +- +- @Override +- public void visitEnd() { +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the size of this field. +- * +- * @return the size of this field. +- */ +- int getSize() { +- int size = 8; +- if (value != 0) { +- cw.newUTF8("ConstantValue"); +- size += 8; +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- cw.newUTF8("Synthetic"); +- size += 6; +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- cw.newUTF8("Deprecated"); +- size += 6; +- } +- if (ClassReader.SIGNATURES && signature != 0) { +- cw.newUTF8("Signature"); +- size += 8; +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- cw.newUTF8("RuntimeVisibleAnnotations"); +- size += 8 + anns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- cw.newUTF8("RuntimeInvisibleAnnotations"); +- size += 8 + ianns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- cw.newUTF8("RuntimeVisibleTypeAnnotations"); +- size += 8 + tanns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- cw.newUTF8("RuntimeInvisibleTypeAnnotations"); +- size += 8 + itanns.getSize(); +- } +- if (attrs != null) { +- size += attrs.getSize(cw, null, 0, -1, -1); +- } +- return size; +- } +- +- /** +- * Puts the content of this field into the given byte vector. +- * +- * @param out +- * where the content of this field must be put. +- */ +- void put(final ByteVector out) { +- final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; +- int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE +- | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); +- out.putShort(access & ~mask).putShort(name).putShort(desc); +- int attributeCount = 0; +- if (value != 0) { +- ++attributeCount; +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- ++attributeCount; +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- ++attributeCount; +- } +- if (ClassReader.SIGNATURES && signature != 0) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- ++attributeCount; +- } +- if (attrs != null) { +- attributeCount += attrs.getCount(); +- } +- out.putShort(attributeCount); +- if (value != 0) { +- out.putShort(cw.newUTF8("ConstantValue")); +- out.putInt(2).putShort(value); +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- out.putShort(cw.newUTF8("Synthetic")).putInt(0); +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- out.putShort(cw.newUTF8("Deprecated")).putInt(0); +- } +- if (ClassReader.SIGNATURES && signature != 0) { +- out.putShort(cw.newUTF8("Signature")); +- out.putInt(2).putShort(signature); +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); +- anns.put(out); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); +- ianns.put(out); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); +- tanns.put(out); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); +- itanns.put(out); +- } +- if (attrs != null) { +- attrs.put(cw, null, 0, -1, -1, out); +- } +- } +-} +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 62879dbd..00000000 +--- a/src/main/java/org/mvel2/asm/Frame.java ++++ /dev/null +@@ -1,1458 +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 and output stack map frames of a basic block. +- * +- * @author Eric Bruneton +- */ +-final class Frame { +- +- /* +- * Frames are computed in a two steps process: during the visit of each +- * instruction, the state of the frame at the end of current basic block is +- * updated by simulating the action of the instruction on the previous state +- * of this so called "output frame". In visitMaxs, a fix point algorithm is +- * used to compute the "input frame" of each basic block, i.e. the stack map +- * frame at the beginning of the basic block, starting from the input frame +- * of the first basic block (which is computed from the method descriptor), +- * and by using the previously computed output frames to compute the input +- * state of the other blocks. +- * +- * All output and input frames are stored as arrays of integers. Reference +- * and array types are represented by an index into a type table (which is +- * not the same as the constant pool of the class, in order to avoid adding +- * unnecessary constants in the pool - not all computed frames will end up +- * being stored in the stack map table). This allows very fast type +- * comparisons. +- * +- * 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 output frames. +- * +- * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a +- * signed number of array dimensions (from -8 to 7). KIND is either BASE, +- * LOCAL or STACK. BASE is used for types that are not relative to the input +- * frame. LOCAL is used for types that are relative to the input local +- * variable types. STACK is used for types that are relative to the input +- * stack types. VALUE depends on KIND. For LOCAL types, it is an index in +- * the input local variable types. For STACK types, it is a position +- * relatively to the top of input frame stack. For BASE types, it is either +- * one of the constants defined below, or for OBJECT and UNINITIALIZED +- * types, a tag and an index in the type table. +- * +- * Output frames can contain types of any kind and with a positive or +- * negative dimension (and even unassigned types, represented by 0 - which +- * does not correspond to any valid type value). Input frames can only +- * contain BASE types of positive or null dimension. In all cases the type +- * table contains only internal type names (array type descriptors are +- * forbidden - dimensions must be represented through the DIM field). +- * +- * The LONG and DOUBLE types are always represented by using two slots (LONG +- * + TOP or DOUBLE + TOP), for local variable types 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 actual type values +- * if types were always represented by a single slot in the stack (and this +- * is not possible, since actual type values are not always known - cf LOCAL +- * and STACK type kinds). +- */ +- +- /** +- * Mask to get the dimension of a frame type. This dimension is a signed +- * integer between -8 and 7. +- */ +- static final int DIM = 0xF0000000; +- +- /** +- * Constant to be added to a type to get a type with one more dimension. +- */ +- static final int ARRAY_OF = 0x10000000; +- +- /** +- * Constant to be added to a type to get a type with one less dimension. +- */ +- static final int ELEMENT_OF = 0xF0000000; +- +- /** +- * Mask to get the kind of a frame type. +- * +- * @see #BASE +- * @see #LOCAL +- * @see #STACK +- */ +- static final int KIND = 0xF000000; +- +- /** +- * Flag used for LOCAL and STACK types. Indicates that if this type happens +- * to be a long or double type (during the computations of input frames), +- * then it must be set to TOP because the second word of this value has been +- * reused to store other data in the basic block. Hence the first word no +- * longer stores a valid long or double value. +- */ +- static final int TOP_IF_LONG_OR_DOUBLE = 0x800000; +- +- /** +- * Mask to get the value of a frame type. +- */ +- static final int VALUE = 0x7FFFFF; +- +- /** +- * Mask to get the kind of base types. +- */ +- static final int BASE_KIND = 0xFF00000; +- +- /** +- * Mask to get the value of base types. +- */ +- static final int BASE_VALUE = 0xFFFFF; +- +- /** +- * Kind of the types that are not relative to an input stack map frame. +- */ +- static final int BASE = 0x1000000; +- +- /** +- * Base kind of the base reference types. The BASE_VALUE of such types is an +- * index into the type table. +- */ +- static final int OBJECT = BASE | 0x700000; +- +- /** +- * Base kind of the uninitialized base types. The BASE_VALUE of such types +- * in an index into the type table (the Item at that index contains both an +- * instruction offset and an internal class name). +- */ +- static final int UNINITIALIZED = BASE | 0x800000; +- +- /** +- * Kind of the types that are relative to the local variable types of an +- * input stack map frame. The value of such types is a local variable index. +- */ +- private static final int LOCAL = 0x2000000; +- +- /** +- * Kind of the the types that are relative to the stack of an input stack +- * map frame. The value of such types is a position relatively to the top of +- * this stack. +- */ +- private static final int STACK = 0x3000000; +- +- /** +- * The TOP type. This is a BASE type. +- */ +- static final int TOP = BASE | 0; +- +- /** +- * The BOOLEAN type. This is a BASE type mainly used for array types. +- */ +- static final int BOOLEAN = BASE | 9; +- +- /** +- * The BYTE type. This is a BASE type mainly used for array types. +- */ +- static final int BYTE = BASE | 10; +- +- /** +- * The CHAR type. This is a BASE type mainly used for array types. +- */ +- static final int CHAR = BASE | 11; +- +- /** +- * The SHORT type. This is a BASE type mainly used for array types. +- */ +- static final int SHORT = BASE | 12; +- +- /** +- * The INTEGER type. This is a BASE type. +- */ +- static final int INTEGER = BASE | 1; +- +- /** +- * The FLOAT type. This is a BASE type. +- */ +- static final int FLOAT = BASE | 2; +- +- /** +- * The DOUBLE type. This is a BASE type. +- */ +- static final int DOUBLE = BASE | 3; +- +- /** +- * The LONG type. This is a BASE type. +- */ +- static final int LONG = BASE | 4; +- +- /** +- * The NULL type. This is a BASE type. +- */ +- static final int NULL = BASE | 5; +- +- /** +- * The UNINITIALIZED_THIS type. This is a BASE type. +- */ +- static final int UNINITIALIZED_THIS = BASE | 6; +- +- /** +- * The stack size variation corresponding to each JVM instruction. This +- * stack variation is equal to the size of the values produced by an +- * instruction, minus the size of the values consumed by this instruction. +- */ +- static final int[] SIZE; +- +- /** +- * Computes the stack size variation corresponding to each JVM instruction. +- */ +- static { +- int i; +- int[] b = new int[202]; +- String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" +- + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" +- + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" +- + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; +- for (i = 0; i < b.length; ++i) { +- b[i] = s.charAt(i) - 'E'; +- } +- SIZE = b; +- +- // code to generate the above string +- // +- // int NA = 0; // not applicable (unused opcode or variable size opcode) +- // +- // b = new int[] { +- // 0, //NOP, // visitInsn +- // 1, //ACONST_NULL, // - +- // 1, //ICONST_M1, // - +- // 1, //ICONST_0, // - +- // 1, //ICONST_1, // - +- // 1, //ICONST_2, // - +- // 1, //ICONST_3, // - +- // 1, //ICONST_4, // - +- // 1, //ICONST_5, // - +- // 2, //LCONST_0, // - +- // 2, //LCONST_1, // - +- // 1, //FCONST_0, // - +- // 1, //FCONST_1, // - +- // 1, //FCONST_2, // - +- // 2, //DCONST_0, // - +- // 2, //DCONST_1, // - +- // 1, //BIPUSH, // visitIntInsn +- // 1, //SIPUSH, // - +- // 1, //LDC, // visitLdcInsn +- // NA, //LDC_W, // - +- // NA, //LDC2_W, // - +- // 1, //ILOAD, // visitVarInsn +- // 2, //LLOAD, // - +- // 1, //FLOAD, // - +- // 2, //DLOAD, // - +- // 1, //ALOAD, // - +- // NA, //ILOAD_0, // - +- // NA, //ILOAD_1, // - +- // NA, //ILOAD_2, // - +- // NA, //ILOAD_3, // - +- // NA, //LLOAD_0, // - +- // NA, //LLOAD_1, // - +- // NA, //LLOAD_2, // - +- // NA, //LLOAD_3, // - +- // NA, //FLOAD_0, // - +- // NA, //FLOAD_1, // - +- // NA, //FLOAD_2, // - +- // NA, //FLOAD_3, // - +- // NA, //DLOAD_0, // - +- // NA, //DLOAD_1, // - +- // NA, //DLOAD_2, // - +- // NA, //DLOAD_3, // - +- // NA, //ALOAD_0, // - +- // NA, //ALOAD_1, // - +- // NA, //ALOAD_2, // - +- // NA, //ALOAD_3, // - +- // -1, //IALOAD, // visitInsn +- // 0, //LALOAD, // - +- // -1, //FALOAD, // - +- // 0, //DALOAD, // - +- // -1, //AALOAD, // - +- // -1, //BALOAD, // - +- // -1, //CALOAD, // - +- // -1, //SALOAD, // - +- // -1, //ISTORE, // visitVarInsn +- // -2, //LSTORE, // - +- // -1, //FSTORE, // - +- // -2, //DSTORE, // - +- // -1, //ASTORE, // - +- // NA, //ISTORE_0, // - +- // NA, //ISTORE_1, // - +- // NA, //ISTORE_2, // - +- // NA, //ISTORE_3, // - +- // NA, //LSTORE_0, // - +- // NA, //LSTORE_1, // - +- // NA, //LSTORE_2, // - +- // NA, //LSTORE_3, // - +- // NA, //FSTORE_0, // - +- // NA, //FSTORE_1, // - +- // NA, //FSTORE_2, // - +- // NA, //FSTORE_3, // - +- // NA, //DSTORE_0, // - +- // NA, //DSTORE_1, // - +- // NA, //DSTORE_2, // - +- // NA, //DSTORE_3, // - +- // NA, //ASTORE_0, // - +- // NA, //ASTORE_1, // - +- // NA, //ASTORE_2, // - +- // NA, //ASTORE_3, // - +- // -3, //IASTORE, // visitInsn +- // -4, //LASTORE, // - +- // -3, //FASTORE, // - +- // -4, //DASTORE, // - +- // -3, //AASTORE, // - +- // -3, //BASTORE, // - +- // -3, //CASTORE, // - +- // -3, //SASTORE, // - +- // -1, //POP, // - +- // -2, //POP2, // - +- // 1, //DUP, // - +- // 1, //DUP_X1, // - +- // 1, //DUP_X2, // - +- // 2, //DUP2, // - +- // 2, //DUP2_X1, // - +- // 2, //DUP2_X2, // - +- // 0, //SWAP, // - +- // -1, //IADD, // - +- // -2, //LADD, // - +- // -1, //FADD, // - +- // -2, //DADD, // - +- // -1, //ISUB, // - +- // -2, //LSUB, // - +- // -1, //FSUB, // - +- // -2, //DSUB, // - +- // -1, //IMUL, // - +- // -2, //LMUL, // - +- // -1, //FMUL, // - +- // -2, //DMUL, // - +- // -1, //IDIV, // - +- // -2, //LDIV, // - +- // -1, //FDIV, // - +- // -2, //DDIV, // - +- // -1, //IREM, // - +- // -2, //LREM, // - +- // -1, //FREM, // - +- // -2, //DREM, // - +- // 0, //INEG, // - +- // 0, //LNEG, // - +- // 0, //FNEG, // - +- // 0, //DNEG, // - +- // -1, //ISHL, // - +- // -1, //LSHL, // - +- // -1, //ISHR, // - +- // -1, //LSHR, // - +- // -1, //IUSHR, // - +- // -1, //LUSHR, // - +- // -1, //IAND, // - +- // -2, //LAND, // - +- // -1, //IOR, // - +- // -2, //LOR, // - +- // -1, //IXOR, // - +- // -2, //LXOR, // - +- // 0, //IINC, // visitIincInsn +- // 1, //I2L, // visitInsn +- // 0, //I2F, // - +- // 1, //I2D, // - +- // -1, //L2I, // - +- // -1, //L2F, // - +- // 0, //L2D, // - +- // 0, //F2I, // - +- // 1, //F2L, // - +- // 1, //F2D, // - +- // -1, //D2I, // - +- // 0, //D2L, // - +- // -1, //D2F, // - +- // 0, //I2B, // - +- // 0, //I2C, // - +- // 0, //I2S, // - +- // -3, //LCMP, // - +- // -1, //FCMPL, // - +- // -1, //FCMPG, // - +- // -3, //DCMPL, // - +- // -3, //DCMPG, // - +- // -1, //IFEQ, // visitJumpInsn +- // -1, //IFNE, // - +- // -1, //IFLT, // - +- // -1, //IFGE, // - +- // -1, //IFGT, // - +- // -1, //IFLE, // - +- // -2, //IF_ICMPEQ, // - +- // -2, //IF_ICMPNE, // - +- // -2, //IF_ICMPLT, // - +- // -2, //IF_ICMPGE, // - +- // -2, //IF_ICMPGT, // - +- // -2, //IF_ICMPLE, // - +- // -2, //IF_ACMPEQ, // - +- // -2, //IF_ACMPNE, // - +- // 0, //GOTO, // - +- // 1, //JSR, // - +- // 0, //RET, // visitVarInsn +- // -1, //TABLESWITCH, // visiTableSwitchInsn +- // -1, //LOOKUPSWITCH, // visitLookupSwitch +- // -1, //IRETURN, // visitInsn +- // -2, //LRETURN, // - +- // -1, //FRETURN, // - +- // -2, //DRETURN, // - +- // -1, //ARETURN, // - +- // 0, //RETURN, // - +- // NA, //GETSTATIC, // visitFieldInsn +- // NA, //PUTSTATIC, // - +- // NA, //GETFIELD, // - +- // NA, //PUTFIELD, // - +- // NA, //INVOKEVIRTUAL, // visitMethodInsn +- // NA, //INVOKESPECIAL, // - +- // NA, //INVOKESTATIC, // - +- // NA, //INVOKEINTERFACE, // - +- // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn +- // 1, //NEW, // visitTypeInsn +- // 0, //NEWARRAY, // visitIntInsn +- // 0, //ANEWARRAY, // visitTypeInsn +- // 0, //ARRAYLENGTH, // visitInsn +- // NA, //ATHROW, // - +- // 0, //CHECKCAST, // visitTypeInsn +- // 0, //INSTANCEOF, // - +- // -1, //MONITORENTER, // visitInsn +- // -1, //MONITOREXIT, // - +- // NA, //WIDE, // NOT VISITED +- // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn +- // -1, //IFNULL, // visitJumpInsn +- // -1, //IFNONNULL, // - +- // NA, //GOTO_W, // - +- // NA, //JSR_W, // - +- // }; +- // for (i = 0; i < b.length; ++i) { +- // System.err.print((char)('E' + b[i])); +- // } +- // System.err.println(); +- } +- +- /** +- * The label (i.e. basic block) to which these input and output stack map +- * frames correspond. +- */ +- Label owner; +- +- /** +- * The input stack map frame locals. +- */ +- int[] inputLocals; +- +- /** +- * The input stack map frame stack. +- */ +- int[] inputStack; +- +- /** +- * The output stack map frame locals. +- */ +- private int[] outputLocals; +- +- /** +- * The output stack map frame stack. +- */ +- private int[] outputStack; +- +- /** +- * Relative size of the output stack. The exact semantics of this field +- * depends on the algorithm that is used. +- * +- * When only the maximum stack size is computed, this field is the size of +- * the output stack relatively to the top of the input stack. +- * +- * When the stack map frames are completely computed, this field is the +- * actual number of types in {@link #outputStack}. +- */ +- private int outputStackTop; +- +- /** +- * Number of types that are initialized in the basic block. +- * +- * @see #initializations +- */ +- private int initializationCount; +- +- /** +- * The types that are initialized in the basic block. A constructor +- * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace +- * every occurence of this type in the local variables and in the +- * operand stack. This cannot be done during the first phase of the +- * algorithm since, during this phase, the local variables and the operand +- * stack are not completely computed. It is therefore necessary to store the +- * types on which constructors are invoked in the basic block, in order to +- * do this replacement during the second phase of the algorithm, where the +- * frames are fully computed. Note that this array can contain types that +- * are relative to input locals or to the input stack (see below for the +- * description of the algorithm). +- */ +- private int[] initializations; +- +- /** +- * Returns the output frame local variable type at the given index. +- * +- * @param local +- * the index of the local that must be returned. +- * @return the output frame local variable type at the given index. +- */ +- private int get(final int local) { +- if (outputLocals == null || local >= outputLocals.length) { +- // this local has never been assigned in this basic block, +- // so it is still equal to its value in the input frame +- return LOCAL | local; +- } else { +- int type = outputLocals[local]; +- if (type == 0) { +- // this local has never been assigned in this basic block, +- // so it is still equal to its value in the input frame +- type = outputLocals[local] = LOCAL | local; +- } +- return type; +- } +- } +- +- /** +- * Sets the output frame local variable type at the given index. +- * +- * @param local +- * the index of the local that must be set. +- * @param type +- * the value of the local that must be set. +- */ +- private void set(final int local, final int type) { +- // creates and/or resizes the output local variables array if necessary +- if (outputLocals == null) { +- outputLocals = new int[10]; +- } +- int n = outputLocals.length; +- if (local >= n) { +- int[] t = new int[Math.max(local + 1, 2 * n)]; +- System.arraycopy(outputLocals, 0, t, 0, n); +- outputLocals = t; +- } +- // sets the local variable +- outputLocals[local] = type; +- } +- +- /** +- * Pushes a new type onto the output frame stack. +- * +- * @param type +- * the type that must be pushed. +- */ +- private void push(final int type) { +- // creates and/or resizes the output stack array if necessary +- if (outputStack == null) { +- outputStack = new int[10]; +- } +- int n = outputStack.length; +- if (outputStackTop >= n) { +- int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; +- System.arraycopy(outputStack, 0, t, 0, n); +- outputStack = t; +- } +- // pushes the type on the output stack +- outputStack[outputStackTop++] = type; +- // updates the maximun height reached by the output stack, if needed +- int top = owner.inputStackTop + outputStackTop; +- if (top > owner.outputStackMax) { +- owner.outputStackMax = top; +- } +- } +- +- /** +- * Pushes a new type onto the output frame stack. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param desc +- * the descriptor of the type to be pushed. Can also be a method +- * descriptor (in this case this method pushes its return type +- * onto the output frame stack). +- */ +- private void push(final ClassWriter cw, final String desc) { +- int type = type(cw, desc); +- if (type != 0) { +- push(type); +- if (type == LONG || type == DOUBLE) { +- push(TOP); +- } +- } +- } +- +- /** +- * Returns the int encoding of the given type. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param desc +- * a type descriptor. +- * @return the int encoding of the given type. +- */ +- private static int type(final ClassWriter cw, final String desc) { +- String t; +- int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; +- switch (desc.charAt(index)) { +- 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': +- // stores the internal name, not the descriptor! +- t = desc.substring(index + 1, desc.length() - 1); +- return OBJECT | cw.addType(t); +- // case '[': +- default: +- // extracts the dimensions and the element type +- int data; +- int dims = index + 1; +- while (desc.charAt(dims) == '[') { +- ++dims; +- } +- switch (desc.charAt(dims)) { +- case 'Z': +- data = BOOLEAN; +- break; +- case 'C': +- data = CHAR; +- break; +- case 'B': +- data = BYTE; +- break; +- case 'S': +- data = SHORT; +- break; +- case 'I': +- data = INTEGER; +- break; +- case 'F': +- data = FLOAT; +- break; +- case 'J': +- data = LONG; +- break; +- case 'D': +- data = DOUBLE; +- break; +- // case 'L': +- default: +- // stores the internal name, not the descriptor +- t = desc.substring(dims + 1, desc.length() - 1); +- data = OBJECT | cw.addType(t); +- } +- return (dims - index) << 28 | data; +- } +- } +- +- /** +- * Pops a type from the output frame stack and returns its value. +- * +- * @return the 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, pops from the input stack +- return STACK | -(--owner.inputStackTop); +- } +- } +- +- /** +- * Pops the given number of types from the output frame stack. +- * +- * @param elements +- * the number of 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 pops the remaining +- // elements from the input stack. +- owner.inputStackTop -= elements - outputStackTop; +- outputStackTop = 0; +- } +- } +- +- /** +- * Pops a type from the output frame stack. +- * +- * @param desc +- * the descriptor of the type to be popped. Can also be a method +- * descriptor (in this case this method pops the types +- * corresponding to the method arguments). +- */ +- private void pop(final String desc) { +- char c = desc.charAt(0); +- if (c == '(') { +- pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1); +- } else if (c == 'J' || c == 'D') { +- pop(2); +- } else { +- pop(1); +- } +- } +- +- /** +- * Adds a new type to the list of types on which a constructor is invoked in +- * the basic block. +- * +- * @param var +- * a type on a which a constructor is invoked. +- */ +- private void init(final int var) { +- // creates and/or resizes the initializations array if necessary +- if (initializations == null) { +- initializations = new int[2]; +- } +- int n = initializations.length; +- if (initializationCount >= n) { +- int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; +- System.arraycopy(initializations, 0, t, 0, n); +- initializations = t; +- } +- // stores the type to be initialized +- initializations[initializationCount++] = var; +- } +- +- /** +- * Replaces the given type with the appropriate type if it is one of the +- * types on which a constructor is invoked in the basic block. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param t +- * a type +- * @return t or, if t is one of the types on which a constructor is invoked +- * in the basic block, the type corresponding to this constructor. +- */ +- private int init(final ClassWriter cw, final int t) { +- int s; +- if (t == UNINITIALIZED_THIS) { +- s = OBJECT | cw.addType(cw.thisName); +- } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) { +- String type = cw.typeTable[t & BASE_VALUE].strVal1; +- s = OBJECT | cw.addType(type); +- } else { +- return t; +- } +- for (int j = 0; j < initializationCount; ++j) { +- int u = initializations[j]; +- int dim = u & DIM; +- int kind = u & KIND; +- if (kind == LOCAL) { +- u = dim + inputLocals[u & VALUE]; +- } else if (kind == STACK) { +- u = dim + inputStack[inputStack.length - (u & VALUE)]; +- } +- if (t == u) { +- return s; +- } +- } +- return t; +- } +- +- /** +- * Initializes the input frame of the first basic block from the method +- * descriptor. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param access +- * the access flags of the method to which this label belongs. +- * @param args +- * the formal parameter types of this method. +- * @param maxLocals +- * the maximum number of local variables of this method. +- */ +- void initInputFrame(final ClassWriter cw, final int access, +- final Type[] args, final int maxLocals) { +- inputLocals = new int[maxLocals]; +- inputStack = new int[0]; +- int i = 0; +- if ((access & Opcodes.ACC_STATIC) == 0) { +- if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) { +- inputLocals[i++] = OBJECT | cw.addType(cw.thisName); +- } else { +- inputLocals[i++] = UNINITIALIZED_THIS; +- } +- } +- for (int j = 0; j < args.length; ++j) { +- int t = type(cw, args[j].getDescriptor()); +- inputLocals[i++] = t; +- if (t == LONG || t == DOUBLE) { +- inputLocals[i++] = TOP; +- } +- } +- while (i < maxLocals) { +- inputLocals[i++] = TOP; +- } +- } +- +- /** +- * Simulates the action of the given instruction on the output stack frame. +- * +- * @param opcode +- * the opcode of the instruction. +- * @param arg +- * the operand of the instruction, if any. +- * @param cw +- * the class writer to which this label belongs. +- * @param item +- * the operand of the instructions, if any. +- */ +- void execute(final int opcode, final int arg, final ClassWriter cw, +- final Item item) { +- int t1, t2, t3, t4; +- 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 (item.type) { +- case ClassWriter.INT: +- push(INTEGER); +- break; +- case ClassWriter.LONG: +- push(LONG); +- push(TOP); +- break; +- case ClassWriter.FLOAT: +- push(FLOAT); +- break; +- case ClassWriter.DOUBLE: +- push(DOUBLE); +- push(TOP); +- break; +- case ClassWriter.CLASS: +- push(OBJECT | cw.addType("java/lang/Class")); +- break; +- case ClassWriter.STR: +- push(OBJECT | cw.addType("java/lang/String")); +- break; +- case ClassWriter.MTYPE: +- push(OBJECT | cw.addType("java/lang/invoke/MethodType")); +- break; +- // case ClassWriter.HANDLE_BASE + [1..9]: +- default: +- push(OBJECT | cw.addType("java/lang/invoke/MethodHandle")); +- } +- break; +- case Opcodes.ALOAD: +- push(get(arg)); +- break; +- case Opcodes.IALOAD: +- case Opcodes.BALOAD: +- case Opcodes.CALOAD: +- case Opcodes.SALOAD: +- pop(2); +- push(INTEGER); +- break; +- case Opcodes.LALOAD: +- case Opcodes.D2L: +- pop(2); +- push(LONG); +- push(TOP); +- break; +- case Opcodes.FALOAD: +- pop(2); +- push(FLOAT); +- break; +- case Opcodes.DALOAD: +- case Opcodes.L2D: +- pop(2); +- push(DOUBLE); +- push(TOP); +- break; +- case Opcodes.AALOAD: +- pop(1); +- t1 = pop(); +- push(ELEMENT_OF + t1); +- break; +- case Opcodes.ISTORE: +- case Opcodes.FSTORE: +- case Opcodes.ASTORE: +- t1 = pop(); +- set(arg, t1); +- if (arg > 0) { +- t2 = get(arg - 1); +- // if t2 is of kind STACK or LOCAL we cannot know its size! +- if (t2 == LONG || t2 == DOUBLE) { +- set(arg - 1, TOP); +- } else if ((t2 & KIND) != BASE) { +- set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); +- } +- } +- break; +- case Opcodes.LSTORE: +- case Opcodes.DSTORE: +- pop(1); +- t1 = pop(); +- set(arg, t1); +- set(arg + 1, TOP); +- if (arg > 0) { +- t2 = get(arg - 1); +- // if t2 is of kind STACK or LOCAL we cannot know its size! +- if (t2 == LONG || t2 == DOUBLE) { +- set(arg - 1, TOP); +- } else if ((t2 & KIND) != BASE) { +- set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); +- } +- } +- 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: +- t1 = pop(); +- push(t1); +- push(t1); +- break; +- case Opcodes.DUP_X1: +- t1 = pop(); +- t2 = pop(); +- push(t1); +- push(t2); +- push(t1); +- break; +- case Opcodes.DUP_X2: +- t1 = pop(); +- t2 = pop(); +- t3 = pop(); +- push(t1); +- push(t3); +- push(t2); +- push(t1); +- break; +- case Opcodes.DUP2: +- t1 = pop(); +- t2 = pop(); +- push(t2); +- push(t1); +- push(t2); +- push(t1); +- break; +- case Opcodes.DUP2_X1: +- t1 = pop(); +- t2 = pop(); +- t3 = pop(); +- push(t2); +- push(t1); +- push(t3); +- push(t2); +- push(t1); +- break; +- case Opcodes.DUP2_X2: +- t1 = pop(); +- t2 = pop(); +- t3 = pop(); +- t4 = pop(); +- push(t2); +- push(t1); +- push(t4); +- push(t3); +- push(t2); +- push(t1); +- break; +- case Opcodes.SWAP: +- t1 = pop(); +- t2 = pop(); +- push(t1); +- push(t2); +- break; +- 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.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: +- set(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 RuntimeException( +- "JSR/RET are not supported with computeFrames option"); +- case Opcodes.GETSTATIC: +- push(cw, item.strVal3); +- break; +- case Opcodes.PUTSTATIC: +- pop(item.strVal3); +- break; +- case Opcodes.GETFIELD: +- pop(1); +- push(cw, item.strVal3); +- break; +- case Opcodes.PUTFIELD: +- pop(item.strVal3); +- pop(); +- break; +- case Opcodes.INVOKEVIRTUAL: +- case Opcodes.INVOKESPECIAL: +- case Opcodes.INVOKESTATIC: +- case Opcodes.INVOKEINTERFACE: +- pop(item.strVal3); +- if (opcode != Opcodes.INVOKESTATIC) { +- t1 = pop(); +- if (opcode == Opcodes.INVOKESPECIAL +- && item.strVal2.charAt(0) == '<') { +- init(t1); +- } +- } +- push(cw, item.strVal3); +- break; +- case Opcodes.INVOKEDYNAMIC: +- pop(item.strVal2); +- push(cw, item.strVal2); +- break; +- case Opcodes.NEW: +- push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, 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: +- default: +- push(ARRAY_OF | LONG); +- break; +- } +- break; +- case Opcodes.ANEWARRAY: +- String s = item.strVal1; +- pop(); +- if (s.charAt(0) == '[') { +- push(cw, '[' + s); +- } else { +- push(ARRAY_OF | OBJECT | cw.addType(s)); +- } +- break; +- case Opcodes.CHECKCAST: +- s = item.strVal1; +- pop(); +- if (s.charAt(0) == '[') { +- push(cw, s); +- } else { +- push(OBJECT | cw.addType(s)); +- } +- break; +- // case Opcodes.MULTIANEWARRAY: +- default: +- pop(arg); +- push(cw, item.strVal1); +- break; +- } +- } +- +- /** +- * Merges the input frame of the given basic block with the input and output +- * frames of this basic block. Returns true if the input frame of +- * the given label has been changed by this operation. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param frame +- * the basic block whose input frame must be updated. +- * @param edge +- * the kind of the {@link Edge} between this label and 'label'. +- * See {@link Edge#info}. +- * @return true if the input frame of the given label has been +- * changed by this operation. +- */ +- boolean merge(final ClassWriter cw, final Frame frame, final int edge) { +- boolean changed = false; +- int i, s, dim, kind, t; +- +- int nLocal = inputLocals.length; +- int nStack = inputStack.length; +- if (frame.inputLocals == null) { +- frame.inputLocals = new int[nLocal]; +- changed = true; +- } +- +- for (i = 0; i < nLocal; ++i) { +- if (outputLocals != null && i < outputLocals.length) { +- s = outputLocals[i]; +- if (s == 0) { +- t = inputLocals[i]; +- } else { +- dim = s & DIM; +- kind = s & KIND; +- if (kind == BASE) { +- t = s; +- } else { +- if (kind == LOCAL) { +- t = dim + inputLocals[s & VALUE]; +- } else { +- t = dim + inputStack[nStack - (s & VALUE)]; +- } +- if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 +- && (t == LONG || t == DOUBLE)) { +- t = TOP; +- } +- } +- } +- } else { +- t = inputLocals[i]; +- } +- if (initializations != null) { +- t = init(cw, t); +- } +- changed |= merge(cw, t, frame.inputLocals, i); +- } +- +- if (edge > 0) { +- for (i = 0; i < nLocal; ++i) { +- t = inputLocals[i]; +- changed |= merge(cw, t, frame.inputLocals, i); +- } +- if (frame.inputStack == null) { +- frame.inputStack = new int[1]; +- changed = true; +- } +- changed |= merge(cw, edge, frame.inputStack, 0); +- return changed; +- } +- +- int nInputStack = inputStack.length + owner.inputStackTop; +- if (frame.inputStack == null) { +- frame.inputStack = new int[nInputStack + outputStackTop]; +- changed = true; +- } +- +- for (i = 0; i < nInputStack; ++i) { +- t = inputStack[i]; +- if (initializations != null) { +- t = init(cw, t); +- } +- changed |= merge(cw, t, frame.inputStack, i); +- } +- for (i = 0; i < outputStackTop; ++i) { +- s = outputStack[i]; +- dim = s & DIM; +- kind = s & KIND; +- if (kind == BASE) { +- t = s; +- } else { +- if (kind == LOCAL) { +- t = dim + inputLocals[s & VALUE]; +- } else { +- t = dim + inputStack[nStack - (s & VALUE)]; +- } +- if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 +- && (t == LONG || t == DOUBLE)) { +- t = TOP; +- } +- } +- if (initializations != null) { +- t = init(cw, t); +- } +- changed |= merge(cw, t, frame.inputStack, nInputStack + i); +- } +- return changed; +- } +- +- /** +- * Merges the type at the given index in the given type array with the given +- * type. Returns true if the type array has been modified by this +- * operation. +- * +- * @param cw +- * the ClassWriter to which this label belongs. +- * @param t +- * the type with which the type array element must be merged. +- * @param types +- * an array of types. +- * @param index +- * the index of the type that must be merged in 'types'. +- * @return true if the type array has been modified by this +- * operation. +- */ +- private static boolean merge(final ClassWriter cw, int t, +- final int[] types, final int index) { +- int u = types[index]; +- if (u == t) { +- // if the types are equal, merge(u,t)=u, so there is no change +- return false; +- } +- if ((t & ~DIM) == NULL) { +- if (u == NULL) { +- return false; +- } +- t = NULL; +- } +- if (u == 0) { +- // if types[index] has never been assigned, merge(u,t)=t +- types[index] = t; +- return true; +- } +- int v; +- if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) { +- // if u is a reference type of any dimension +- if (t == NULL) { +- // if t is the NULL type, merge(u,t)=u, so there is no change +- return false; +- } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { +- // if t and u have the same dimension and same base kind +- if ((u & BASE_KIND) == OBJECT) { +- // if t is also a reference type, and if u and t have the +- // same dimension merge(u,t) = dim(t) | common parent of the +- // element types of u and t +- v = (t & DIM) | OBJECT +- | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); +- } else { +- // if u and t are array types, but not with the same element +- // type, merge(u,t)=java/lang/Object +- v = OBJECT | cw.addType("java/lang/Object"); +- } +- } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { +- // if t is any other reference or array type, the merged type +- // is Object, or min(dim(u), dim(t)) | java/lang/Object is u +- // and t have different array dimensions +- int tdim = t & DIM; +- int udim = u & DIM; +- v = (udim != tdim ? Math.min(tdim, udim) : 0) | OBJECT +- | cw.addType("java/lang/Object"); +- } else { +- // if t is any other type, merge(u,t)=TOP +- v = TOP; +- } +- } else if (u == NULL) { +- // if u is the NULL type, merge(u,t)=t, +- // or TOP if t is not a reference type +- v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; +- } else { +- // if u is any other type, merge(u,t)=TOP whatever t +- v = TOP; +- } +- if (u != v) { +- types[index] = v; +- return true; +- } +- return false; +- } +-} +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 7683f525..00000000 +--- a/src/main/java/org/mvel2/asm/Handle.java ++++ /dev/null +@@ -1,170 +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}. +- */ +- final int tag; +- +- /** +- * The internal name of the class that owns the field or method designated +- * by this handle. +- */ +- final String owner; +- +- /** +- * The name of the field or method designated by this handle. +- */ +- final String name; +- +- /** +- * The descriptor of the field or method designated by this handle. +- */ +- final String desc; +- +- /** +- * 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 desc +- * the descriptor of the field or method designated by this +- * handle. +- */ +- public Handle(int tag, String owner, String name, String desc) { +- this.tag = tag; +- this.owner = owner; +- this.name = name; +- this.desc = desc; +- } +- +- /** +- * 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 desc; +- } +- +- @Override +- public boolean equals(Object obj) { +- if (obj == this) { +- return true; +- } +- if (!(obj instanceof Handle)) { +- return false; +- } +- Handle h = (Handle) obj; +- return tag == h.tag && owner.equals(h.owner) && name.equals(h.name) +- && desc.equals(h.desc); +- } +- +- @Override +- public int hashCode() { +- return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); +- } +- +- /** +- * Returns the textual representation of this handle. The textual +- * representation is: +- * +- *
+-     * owner '.' name desc ' ' '(' tag ')'
+-     * 
+- * +- * . As this format is unambiguous, it can be parsed if necessary. +- */ +- @Override +- public String toString() { +- return owner + '.' + name + desc + " (" + tag + ')'; +- } +-} +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 88859841..00000000 +--- a/src/main/java/org/mvel2/asm/Handler.java ++++ /dev/null +@@ -1,121 +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 block. +- * +- * @author Eric Bruneton +- */ +-class Handler { +- +- /** +- * Beginning of the exception handler's scope (inclusive). +- */ +- Label start; +- +- /** +- * End of the exception handler's scope (exclusive). +- */ +- Label end; +- +- /** +- * Beginning of the exception handler's code. +- */ +- Label handler; +- +- /** +- * Internal name of the type of exceptions handled by this handler, or +- * null to catch any exceptions. +- */ +- String desc; +- +- /** +- * Constant pool index of the internal name of the type of exceptions +- * handled by this handler, or 0 to catch any exceptions. +- */ +- int type; +- +- /** +- * Next exception handler block info. +- */ +- Handler next; +- +- /** +- * Removes the range between start and end from the given exception +- * handlers. +- * +- * @param h +- * an exception handler list. +- * @param start +- * the start of the range to be removed. +- * @param end +- * the end of the range to be removed. Maybe null. +- * @return the exception handler list with the start-end range removed. +- */ +- static Handler remove(Handler h, Label start, Label end) { +- if (h == null) { +- return null; +- } else { +- h.next = remove(h.next, start, end); +- } +- int hstart = h.start.position; +- int hend = h.end.position; +- int s = start.position; +- int e = end == null ? Integer.MAX_VALUE : end.position; +- // if [hstart,hend[ and [s,e[ intervals intersect... +- if (s < hend && e > hstart) { +- if (s <= hstart) { +- if (e >= hend) { +- // [hstart,hend[ fully included in [s,e[, h removed +- h = h.next; +- } else { +- // [hstart,hend[ minus [s,e[ = [e,hend[ +- h.start = end; +- } +- } else if (e >= hend) { +- // [hstart,hend[ minus [s,e[ = [hstart,s[ +- h.end = start; +- } else { +- // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[ +- Handler g = new Handler(); +- g.start = end; +- g.end = h.end; +- g.handler = h.handler; +- g.desc = h.desc; +- g.type = h.type; +- g.next = h.next; +- h.end = start; +- h.next = g; +- } +- } +- return h; +- } +-} +diff --git a/src/main/java/org/mvel2/asm/Item.java b/src/main/java/org/mvel2/asm/Item.java +deleted file mode 100644 +index 1831c283..00000000 +--- a/src/main/java/org/mvel2/asm/Item.java ++++ /dev/null +@@ -1,311 +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 constant pool item. Constant pool items can be created with the 'newXXX' +- * methods in the {@link ClassWriter} class. +- * +- * @author Eric Bruneton +- */ +-final class Item { +- +- /** +- * Index of this item in the constant pool. +- */ +- int index; +- +- /** +- * Type of this constant pool item. A single class is used to represent all +- * constant pool item types, in order to minimize the bytecode size of this +- * package. The value of this field is one of {@link ClassWriter#INT}, +- * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, +- * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, +- * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, +- * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, +- * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, +- * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. +- * +- * MethodHandle constant 9 variations are stored using a range of 9 values +- * from {@link ClassWriter#HANDLE_BASE} + 1 to +- * {@link ClassWriter#HANDLE_BASE} + 9. +- * +- * Special Item types are used for Items that are stored in the ClassWriter +- * {@link ClassWriter#typeTable}, instead of the constant pool, in order to +- * avoid clashes with normal constant pool items in the ClassWriter constant +- * pool's hash table. These special item types are +- * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and +- * {@link ClassWriter#TYPE_MERGED}. +- */ +- int type; +- +- /** +- * Value of this item, for an integer item. +- */ +- int intVal; +- +- /** +- * Value of this item, for a long item. +- */ +- long longVal; +- +- /** +- * First part of the value of this item, for items that do not hold a +- * primitive value. +- */ +- String strVal1; +- +- /** +- * Second part of the value of this item, for items that do not hold a +- * primitive value. +- */ +- String strVal2; +- +- /** +- * Third part of the value of this item, for items that do not hold a +- * primitive value. +- */ +- String strVal3; +- +- /** +- * The hash code value of this constant pool item. +- */ +- int hashCode; +- +- /** +- * Link to another constant pool item, used for collision lists in the +- * constant pool's hash table. +- */ +- Item next; +- +- /** +- * Constructs an uninitialized {@link Item}. +- */ +- Item() { +- } +- +- /** +- * Constructs an uninitialized {@link Item} for constant pool element at +- * given position. +- * +- * @param index +- * index of the item to be constructed. +- */ +- Item(final int index) { +- this.index = index; +- } +- +- /** +- * Constructs a copy of the given item. +- * +- * @param index +- * index of the item to be constructed. +- * @param i +- * the item that must be copied into the item to be constructed. +- */ +- Item(final int index, final Item i) { +- this.index = index; +- type = i.type; +- intVal = i.intVal; +- longVal = i.longVal; +- strVal1 = i.strVal1; +- strVal2 = i.strVal2; +- strVal3 = i.strVal3; +- hashCode = i.hashCode; +- } +- +- /** +- * Sets this item to an integer item. +- * +- * @param intVal +- * the value of this item. +- */ +- void set(final int intVal) { +- this.type = ClassWriter.INT; +- this.intVal = intVal; +- this.hashCode = 0x7FFFFFFF & (type + intVal); +- } +- +- /** +- * Sets this item to a long item. +- * +- * @param longVal +- * the value of this item. +- */ +- void set(final long longVal) { +- this.type = ClassWriter.LONG; +- this.longVal = longVal; +- this.hashCode = 0x7FFFFFFF & (type + (int) longVal); +- } +- +- /** +- * Sets this item to a float item. +- * +- * @param floatVal +- * the value of this item. +- */ +- void set(final float floatVal) { +- this.type = ClassWriter.FLOAT; +- this.intVal = Float.floatToRawIntBits(floatVal); +- this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); +- } +- +- /** +- * Sets this item to a double item. +- * +- * @param doubleVal +- * the value of this item. +- */ +- void set(final double doubleVal) { +- this.type = ClassWriter.DOUBLE; +- this.longVal = Double.doubleToRawLongBits(doubleVal); +- this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); +- } +- +- /** +- * Sets this item to an item that do not hold a primitive value. +- * +- * @param type +- * the type of this item. +- * @param strVal1 +- * first part of the value of this item. +- * @param strVal2 +- * second part of the value of this item. +- * @param strVal3 +- * third part of the value of this item. +- */ +- void set(final int type, final String strVal1, final String strVal2, +- final String strVal3) { +- this.type = type; +- this.strVal1 = strVal1; +- this.strVal2 = strVal2; +- this.strVal3 = strVal3; +- switch (type) { +- case ClassWriter.UTF8: +- case ClassWriter.STR: +- case ClassWriter.CLASS: +- case ClassWriter.MTYPE: +- case ClassWriter.TYPE_NORMAL: +- hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); +- return; +- case ClassWriter.NAME_TYPE: { +- hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() +- * strVal2.hashCode()); +- return; +- } +- // ClassWriter.FIELD: +- // ClassWriter.METH: +- // ClassWriter.IMETH: +- // ClassWriter.HANDLE_BASE + 1..9 +- default: +- hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() +- * strVal2.hashCode() * strVal3.hashCode()); +- } +- } +- +- /** +- * Sets the item to an InvokeDynamic item. +- * +- * @param name +- * invokedynamic's name. +- * @param desc +- * invokedynamic's desc. +- * @param bsmIndex +- * zero based index into the class attribute BootrapMethods. +- */ +- void set(String name, String desc, int bsmIndex) { +- this.type = ClassWriter.INDY; +- this.longVal = bsmIndex; +- this.strVal1 = name; +- this.strVal2 = desc; +- this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex +- * strVal1.hashCode() * strVal2.hashCode()); +- } +- +- /** +- * Sets the item to a BootstrapMethod item. +- * +- * @param position +- * position in byte in the class attribute BootrapMethods. +- * @param hashCode +- * hashcode of the item. This hashcode is processed from the +- * hashcode of the bootstrap method and the hashcode of all +- * bootstrap arguments. +- */ +- void set(int position, int hashCode) { +- this.type = ClassWriter.BSM; +- this.intVal = position; +- this.hashCode = hashCode; +- } +- +- /** +- * Indicates if the given item is equal to this one. This method assumes +- * that the two items have the same {@link #type}. +- * +- * @param i +- * the item to be compared to this one. Both items must have the +- * same {@link #type}. +- * @return true if the given item if equal to this one, +- * false otherwise. +- */ +- boolean isEqualTo(final Item i) { +- switch (type) { +- case ClassWriter.UTF8: +- case ClassWriter.STR: +- case ClassWriter.CLASS: +- case ClassWriter.MTYPE: +- case ClassWriter.TYPE_NORMAL: +- return i.strVal1.equals(strVal1); +- case ClassWriter.TYPE_MERGED: +- case ClassWriter.LONG: +- case ClassWriter.DOUBLE: +- return i.longVal == longVal; +- case ClassWriter.INT: +- case ClassWriter.FLOAT: +- return i.intVal == intVal; +- case ClassWriter.TYPE_UNINIT: +- return i.intVal == intVal && i.strVal1.equals(strVal1); +- case ClassWriter.NAME_TYPE: +- return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2); +- case ClassWriter.INDY: { +- return i.longVal == longVal && i.strVal1.equals(strVal1) +- && i.strVal2.equals(strVal2); +- } +- // case ClassWriter.FIELD: +- // case ClassWriter.METH: +- // case ClassWriter.IMETH: +- // case ClassWriter.HANDLE_BASE + 1..9 +- default: +- return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) +- && i.strVal3.equals(strVal3); +- } +- } +- +-} +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 b3d9b665..00000000 +--- a/src/main/java/org/mvel2/asm/Label.java ++++ /dev/null +@@ -1,560 +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 label represents 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 instruction 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 { +- +- /** +- * Indicates if this 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 DEBUG = 1; +- +- /** +- * Indicates if the position of this label is known. +- */ +- static final int RESOLVED = 2; +- +- /** +- * Indicates if this label has been updated, after instruction resizing. +- */ +- static final int RESIZED = 4; +- +- /** +- * Indicates if this basic block has been pushed in the basic block stack. +- * See {@link MethodWriter#visitMaxs visitMaxs}. +- */ +- static final int PUSHED = 8; +- +- /** +- * Indicates if this label is the target of a jump instruction, or the start +- * of an exception handler. +- */ +- static final int TARGET = 16; +- +- /** +- * Indicates if a stack map frame must be stored for this label. +- */ +- static final int STORE = 32; +- +- /** +- * Indicates if this label corresponds to a reachable basic block. +- */ +- static final int REACHABLE = 64; +- +- /** +- * Indicates if this basic block ends with a JSR instruction. +- */ +- static final int JSR = 128; +- +- /** +- * Indicates if this basic block ends with a RET instruction. +- */ +- static final int RET = 256; +- +- /** +- * Indicates if this basic block is the start of a subroutine. +- */ +- static final int SUBROUTINE = 512; +- +- /** +- * Indicates if this subroutine basic block has been visited by a +- * visitSubroutine(null, ...) call. +- */ +- static final int VISITED = 1024; +- +- /** +- * Indicates if this subroutine basic block has been visited by a +- * visitSubroutine(!null, ...) call. +- */ +- static final int VISITED2 = 2048; +- +- /** +- * Field used to associate user information to a 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 +- * {@link org.mvel2.asm.tree.MethodNode#getLabelNode} method. +- */ +- public Object info; +- +- /** +- * Flags that indicate the status of this label. +- * +- * @see #DEBUG +- * @see #RESOLVED +- * @see #RESIZED +- * @see #PUSHED +- * @see #TARGET +- * @see #STORE +- * @see #REACHABLE +- * @see #JSR +- * @see #RET +- */ +- int status; +- +- /** +- * The line number corresponding to this label, if known. +- */ +- int line; +- +- /** +- * The position of this label in the code, if known. +- */ +- int position; +- +- /** +- * Number of forward references to this label, times two. +- */ +- private int referenceCount; +- +- /** +- * Informations about forward references. Each forward reference is +- * described by two consecutive integers in this array: the first one is the +- * position of the first byte of the bytecode instruction that contains the +- * forward reference, while the second is the position of the first byte of +- * the forward reference itself. In fact the sign of the first integer +- * indicates if this reference uses 2 or 4 bytes, and its absolute value +- * gives the position of the bytecode instruction. This array is also used +- * as a bitset to store the subroutines to which a basic block belongs. This +- * information is needed in {@linked MethodWriter#visitMaxs}, after all +- * forward references have been resolved. Hence the same array can be used +- * for both purposes without problems. +- */ +- private int[] srcAndRefPositions; +- +- // ------------------------------------------------------------------------ +- +- /* +- * 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 by 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", relatively to the frame +- * state at the beginning of the basic block, which is called the "input +- * frame", and which is unknown during this step. The second step, in +- * {@link MethodWriter#visitMaxs}, 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. +- */ +- +- /** +- * Start of the output stack relatively to the input stack. The exact +- * semantics of this field depends on the algorithm that is used. +- * +- * When only the maximum stack size is computed, this field is the number of +- * elements in the input stack. +- * +- * When the stack map frames are completely computed, this field is the +- * offset of the first output stack element relatively to the top of 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. +- */ +- int inputStackTop; +- +- /** +- * Maximum height reached by the output stack, relatively to the top of the +- * input stack. This maximum is always positive or null. +- */ +- int outputStackMax; +- +- /** +- * Information about the input and output stack map frames of this basic +- * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} +- * option is used. +- */ +- Frame frame; +- +- /** +- * The successor of this label, in the order they are visited. This linked +- * list does not include labels used for debug info only. If +- * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it +- * does not contain successive labels that denote the same bytecode position +- * (in this case only the first label appears in this list). +- */ +- Label successor; +- +- /** +- * The successors of this node in the control flow graph. These successors +- * are stored in a linked list of {@link Edge Edge} objects, linked to each +- * other by their {@link Edge#next} field. +- */ +- Edge successors; +- +- /** +- * The next basic block in the basic block stack. This stack is used in the +- * main loop of the fix point algorithm used in the second step of the +- * control flow analysis algorithms. It is also used in +- * {@link #visitSubroutine} to avoid using a recursive method. +- * +- * @see MethodWriter#visitMaxs +- */ +- Label next; +- +- // ------------------------------------------------------------------------ +- // Constructor +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new label. +- */ +- public Label() { +- } +- +- // ------------------------------------------------------------------------ +- // Methods to compute offsets and to manage forward references +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the offset corresponding to this label. This offset is computed +- * from the start of the method's bytecode. This method is intended for +- * {@link Attribute} sub classes, and is normally not needed by class +- * generators or adapters. +- * +- * @return the offset corresponding to this label. +- * @throws IllegalStateException +- * if this label is not resolved yet. +- */ +- public int getOffset() { +- if ((status & RESOLVED) == 0) { +- throw new IllegalStateException( +- "Label offset position has not been resolved yet"); +- } +- return position; +- } +- +- /** +- * Puts a reference to this label in the bytecode of a method. If the +- * position of the label is known, the offset is computed and written +- * directly. Otherwise, a null offset is written and a new forward reference +- * is declared for this label. +- * +- * @param owner +- * the code writer that calls this method. +- * @param out +- * the bytecode of the method. +- * @param source +- * the position of first byte of the bytecode instruction that +- * contains this label. +- * @param wideOffset +- * true if the reference must be stored in 4 bytes, or +- * false if it must be stored with 2 bytes. +- * @throws IllegalArgumentException +- * if this label has not been created by the given code writer. +- */ +- void put(final MethodWriter owner, final ByteVector out, final int source, +- final boolean wideOffset) { +- if ((status & RESOLVED) == 0) { +- if (wideOffset) { +- addReference(-1 - source, out.length); +- out.putInt(-1); +- } else { +- addReference(source, out.length); +- out.putShort(-1); +- } +- } else { +- if (wideOffset) { +- out.putInt(position - source); +- } else { +- out.putShort(position - source); +- } +- } +- } +- +- /** +- * 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 offset of the reference can be, and +- * must be, computed and stored directly. +- * +- * @param sourcePosition +- * the position of the referencing instruction. This position +- * will be used to compute the offset of this forward reference. +- * @param referencePosition +- * the position where the offset for this forward reference must +- * be stored. +- */ +- private void addReference(final int sourcePosition, +- final int referencePosition) { +- if (srcAndRefPositions == null) { +- srcAndRefPositions = new int[6]; +- } +- if (referenceCount >= srcAndRefPositions.length) { +- int[] a = new int[srcAndRefPositions.length + 6]; +- System.arraycopy(srcAndRefPositions, 0, a, 0, +- srcAndRefPositions.length); +- srcAndRefPositions = a; +- } +- srcAndRefPositions[referenceCount++] = sourcePosition; +- srcAndRefPositions[referenceCount++] = referencePosition; +- } +- +- /** +- * Resolves all forward references to this label. This method must be called +- * when this label is added to the bytecode of the method, i.e. when its +- * position becomes known. This method fills in the blanks that where left +- * in the bytecode by each forward reference previously added to this label. +- * +- * @param owner +- * the code writer that calls this method. +- * @param position +- * the position of this label in the bytecode. +- * @param data +- * the bytecode of the method. +- * @return true if a blank that was left for this label was to +- * small to store the offset. In such a case the corresponding jump +- * instruction is replaced with a pseudo instruction (using unused +- * opcodes) using an unsigned two bytes offset. These pseudo +- * instructions will need to be replaced with true instructions with +- * wider offsets (4 bytes instead of 2). This is done in +- * {@link MethodWriter#resizeInstructions}. +- * @throws IllegalArgumentException +- * if this label has already been resolved, or if it has not +- * been created by the given code writer. +- */ +- boolean resolve(final MethodWriter owner, final int position, +- final byte[] data) { +- boolean needUpdate = false; +- this.status |= RESOLVED; +- this.position = position; +- int i = 0; +- while (i < referenceCount) { +- int source = srcAndRefPositions[i++]; +- int reference = srcAndRefPositions[i++]; +- int offset; +- if (source >= 0) { +- offset = position - source; +- if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { +- /* +- * changes the opcode of the jump instruction, in order to +- * be able to find it later (see resizeInstructions in +- * MethodWriter). These temporary 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 = data[reference - 1] & 0xFF; +- if (opcode <= Opcodes.JSR) { +- // changes IFEQ ... JSR to opcodes 202 to 217 +- data[reference - 1] = (byte) (opcode + 49); +- } else { +- // changes IFNULL and IFNONNULL to opcodes 218 and 219 +- data[reference - 1] = (byte) (opcode + 20); +- } +- needUpdate = true; +- } +- data[reference++] = (byte) (offset >>> 8); +- data[reference] = (byte) offset; +- } else { +- offset = position + source + 1; +- data[reference++] = (byte) (offset >>> 24); +- data[reference++] = (byte) (offset >>> 16); +- data[reference++] = (byte) (offset >>> 8); +- data[reference] = (byte) offset; +- } +- } +- return needUpdate; +- } +- +- /** +- * Returns the first label of the series to which this label belongs. For an +- * isolated label or for the first label in a series of successive labels, +- * this method returns the label itself. For other labels it returns the +- * first label of the series. +- * +- * @return the first label of the series to which this label belongs. +- */ +- Label getFirst() { +- return !ClassReader.FRAMES || frame == null ? this : frame.owner; +- } +- +- // ------------------------------------------------------------------------ +- // Methods related to subroutines +- // ------------------------------------------------------------------------ +- +- /** +- * Returns true is this basic block belongs to the given subroutine. +- * +- * @param id +- * a subroutine id. +- * @return true is this basic block belongs to the given subroutine. +- */ +- boolean inSubroutine(final long id) { +- if ((status & Label.VISITED) != 0) { +- return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0; +- } +- return false; +- } +- +- /** +- * Returns true if this basic block and the given one belong to a common +- * subroutine. +- * +- * @param block +- * another basic block. +- * @return true if this basic block and the given one belong to a common +- * subroutine. +- */ +- boolean inSameSubroutine(final Label block) { +- if ((status & VISITED) == 0 || (block.status & VISITED) == 0) { +- return false; +- } +- for (int i = 0; i < srcAndRefPositions.length; ++i) { +- if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) { +- return true; +- } +- } +- return false; +- } +- +- /** +- * Marks this basic block as belonging to the given subroutine. +- * +- * @param id +- * a subroutine id. +- * @param nbSubroutines +- * the total number of subroutines in the method. +- */ +- void addToSubroutine(final long id, final int nbSubroutines) { +- if ((status & VISITED) == 0) { +- status |= VISITED; +- srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1]; +- } +- srcAndRefPositions[(int) (id >>> 32)] |= (int) id; +- } +- +- /** +- * Finds the basic blocks that belong to a given subroutine, 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 +- * block WITHOUT following any JSR target. +- * +- * @param JSR +- * a JSR block that jumps to this subroutine. If this JSR is not +- * null it is added to the successor of the RET blocks found in +- * the subroutine. +- * @param id +- * the id of this subroutine. +- * @param nbSubroutines +- * the total number of subroutines in the method. +- */ +- void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) { +- // user managed stack of labels, to avoid using a recursive method +- // (recursivity can lead to stack overflow with very large methods) +- Label stack = this; +- while (stack != null) { +- // removes a label l from the stack +- Label l = stack; +- stack = l.next; +- l.next = null; +- +- if (JSR != null) { +- if ((l.status & VISITED2) != 0) { +- continue; +- } +- l.status |= VISITED2; +- // adds JSR to the successors of l, if it is a RET block +- if ((l.status & RET) != 0) { +- if (!l.inSameSubroutine(JSR)) { +- Edge e = new Edge(); +- e.info = l.inputStackTop; +- e.successor = JSR.successors.successor; +- e.next = l.successors; +- l.successors = e; +- } +- } +- } else { +- // if the l block already belongs to subroutine 'id', continue +- if (l.inSubroutine(id)) { +- continue; +- } +- // marks the l block as belonging to subroutine 'id' +- l.addToSubroutine(id, nbSubroutines); +- } +- // pushes each successor of l on the stack, except JSR targets +- Edge e = l.successors; +- while (e != null) { +- // if the l block is a JSR block, then 'l.successors.next' leads +- // to the JSR target (see {@link #visitJumpInsn}) and must +- // therefore not be followed +- if ((l.status & Label.JSR) == 0 || e != l.successors.next) { +- // pushes e.successor on the stack if it not already added +- if (e.successor.next == null) { +- e.successor.next = stack; +- stack = e.successor; +- } +- } +- e = e.next; +- } +- } +- } +- +- // ------------------------------------------------------------------------ +- // Overriden 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/MethodVisitor.java b/src/main/java/org/mvel2/asm/MethodVisitor.java +deleted file mode 100644 +index 9ab001a6..00000000 +--- a/src/main/java/org/mvel2/asm/MethodVisitor.java ++++ /dev/null +@@ -1,880 +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: ( visitParameter )* [ +- * visitAnnotationDefault ] ( visitAnnotation | +- * visitTypeAnnotation | visitAttribute )* [ +- * visitCode ( visitFrame | visitXInsn | +- * visitLabel | visitInsnAnnotation | +- * visitTryCatchBlock | visitTryCatchBlockAnnotation | +- * visitLocalVariable | visitLocalVariableAnnotation | +- * visitLineNumber )* visitMaxs ] visitEnd. In +- * addition, the visitXInsn and visitLabel methods must +- * be called in the sequential order of the bytecode instructions of the visited +- * code, visitInsnAnnotation must be called after the annotated +- * instruction, visitTryCatchBlock must be called before the +- * labels passed as arguments have been visited, +- * visitTryCatchBlockAnnotation must be called after the +- * corresponding try catch block has been visited, and the +- * visitLocalVariable, visitLocalVariableAnnotation and +- * visitLineNumber methods must be called after the labels +- * passed as arguments have been visited. +- * +- * @author Eric Bruneton +- */ +-public abstract class MethodVisitor { +- +- /** +- * The ASM API version implemented by this visitor. The value of this field +- * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- */ +- 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} or {@link Opcodes#ASM5}. +- * @param mv +- * the method visitor to which this visitor must delegate method +- * calls. May be null. +- */ +- public MethodVisitor(final int api, final MethodVisitor mv) { +- if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { +- throw new IllegalArgumentException(); +- } +- this.api = api; +- this.mv = mv; +- } +- +- // ------------------------------------------------------------------------- +- // 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 ACC_FINAL, +- * ACC_SYNTHETIC or/and ACC_MANDATED are +- * allowed (see {@link Opcodes}). +- */ +- public void visitParameter(String name, int access) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- 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 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 desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitAnnotation(String desc, boolean visible) { +- if (mv != null) { +- return mv.visitAnnotation(desc, 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 +- * METHOD_TYPE_PARAMETER}, +- * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND +- * METHOD_TYPE_PARAMETER_BOUND}, +- * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, +- * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, +- * {@link TypeReference#METHOD_FORMAL_PARAMETER +- * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS +- * 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitTypeAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (mv != null) { +- return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); +- } +- return null; +- } +- +- /** +- * Visits an annotation of a parameter this method. +- * +- * @param parameter +- * the parameter index. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitParameterAnnotation(int parameter, +- String desc, boolean visible) { +- if (mv != null) { +- return mv.visitParameterAnnotation(parameter, desc, visible); +- } +- return null; +- } +- +- /** +- * Visits a non standard attribute of this method. +- * +- * @param attr +- * an attribute. +- */ +- public void visitAttribute(Attribute attr) { +- if (mv != null) { +- mv.visitAttribute(attr); +- } +- } +- +- /** +- * 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 just before any +- * instruction i 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 just +- * before i is executed.
+- *
+- * (*) this is mandatory only for classes whose version is greater than or +- * equal to {@link Opcodes#V1_6 V1_6}.
+- *
+- * 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): +- *
    +- *
  • In expanded form, all frames must have the F_NEW type.
  • +- *
  • In compressed form, frames are basically "deltas" from the state of +- * the previous frame: +- *
      +- *
    • {@link Opcodes#F_SAME} representing frame with exactly the same +- * locals as the previous frame and with the empty stack.
    • +- *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same +- * locals as the previous frame and with single value on the stack ( +- * nStack is 1 and stack[0] contains value for the +- * type of the stack item).
    • +- *
    • {@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 (nLocal is 1, 2 or 3 and +- * local elements contains values representing added types).
    • +- *
    • {@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 (nLocals is 1, 2 or 3).
    • +- *
    • {@link Opcodes#F_FULL} representing complete frame data.
    • +- *
    +- *
  • +- *
+- *
+- * 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 nLocal +- * 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 nStack +- * 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(int type, int nLocal, Object[] local, int nStack, +- Object[] stack) { +- if (mv != null) { +- mv.visitFrame(type, nLocal, local, nStack, 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(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.
+- * When opcode is BIPUSH, operand value should be between +- * Byte.MIN_VALUE and Byte.MAX_VALUE.
+- * When opcode is SIPUSH, operand value should be between +- * Short.MIN_VALUE and Short.MAX_VALUE.
+- * 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(int opcode, 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(int opcode, 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() getInternalName}). +- */ +- public void visitTypeInsn(int opcode, 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() getInternalName}). +- * @param name +- * the field's name. +- * @param desc +- * the field's descriptor (see {@link Type Type}). +- */ +- public void visitFieldInsn(int opcode, String owner, String name, +- String desc) { +- if (mv != null) { +- mv.visitFieldInsn(opcode, owner, name, desc); +- } +- } +- +- /** +- * 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() getInternalName}). +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor (see {@link Type Type}). +- */ +- @Deprecated +- public void visitMethodInsn(int opcode, String owner, String name, +- String desc) { +- if (api >= Opcodes.ASM5) { +- boolean itf = opcode == Opcodes.INVOKEINTERFACE; +- visitMethodInsn(opcode, owner, name, desc, itf); +- return; +- } +- if (mv != null) { +- mv.visitMethodInsn(opcode, owner, name, desc); +- } +- } +- +- /** +- * 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() getInternalName}). +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor (see {@link Type Type}). +- * @param itf +- * if the method's owner class is an interface. +- */ +- public void visitMethodInsn(int opcode, String owner, String name, +- String desc, boolean itf) { +- if (api < Opcodes.ASM5) { +- if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { +- throw new IllegalArgumentException( +- "INVOKESPECIAL/STATIC on interfaces require ASM 5"); +- } +- visitMethodInsn(opcode, owner, name, desc); +- return; +- } +- if (mv != null) { +- mv.visitMethodInsn(opcode, owner, name, desc, itf); +- } +- } +- +- /** +- * Visits an invokedynamic instruction. +- * +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor (see {@link Type Type}). +- * @param bsm +- * the bootstrap method. +- * @param bsmArgs +- * the bootstrap method constant arguments. Each argument must be +- * an {@link Integer}, {@link Float}, {@link Long}, +- * {@link Double}, {@link String}, {@link Type} or {@link Handle} +- * 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(String name, String desc, Handle bsm, +- Object... bsmArgs) { +- if (mv != null) { +- mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); +- } +- } +- +- /** +- * 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(int opcode, 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 Label} object. +- */ +- public void visitLabel(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: +- * +- *
+-     * 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 {
+-     *     // throw an exception
+-     * }
+-     * 
+- * +- * @param cst +- * 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 .class constants, for classes whose +- * version is 49.0, a {@link Type} of METHOD sort or a +- * {@link Handle} for MethodType and MethodHandle constants, for +- * classes whose version is 51.0. +- */ +- public void visitLdcInsn(Object cst) { +- if (mv != null) { +- mv.visitLdcInsn(cst); +- } +- } +- +- /** +- * 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(int var, 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. labels[i] is the +- * beginning of the handler block for the min + i key. +- */ +- public void visitTableSwitchInsn(int min, int max, Label dflt, +- 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. labels[i] is the +- * beginning of the handler block for the keys[i] key. +- */ +- public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { +- if (mv != null) { +- mv.visitLookupSwitchInsn(dflt, keys, labels); +- } +- } +- +- /** +- * Visits a MULTIANEWARRAY instruction. +- * +- * @param desc +- * an array type descriptor (see {@link Type Type}). +- * @param dims +- * number of dimensions of the array to allocate. +- */ +- public void visitMultiANewArrayInsn(String desc, int dims) { +- if (mv != null) { +- mv.visitMultiANewArrayInsn(desc, dims); +- } +- } +- +- /** +- * Visits an annotation on an instruction. This method must be called just +- * after 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 INSTANCEOF}, +- * {@link TypeReference#NEW NEW}, +- * {@link TypeReference#CONSTRUCTOR_REFERENCE +- * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE +- * METHOD_REFERENCE}, {@link TypeReference#CAST CAST}, +- * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, +- * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT +- * METHOD_INVOCATION_TYPE_ARGUMENT}, +- * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or +- * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT +- * 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitInsnAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (mv != null) { +- return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); +- } +- return null; +- } +- +- // ------------------------------------------------------------------------- +- // Exceptions table entries, debug information, max stack and max locals +- // ------------------------------------------------------------------------- +- +- /** +- * Visits a try catch block. +- * +- * @param start +- * beginning of the exception handler's scope (inclusive). +- * @param end +- * end of the exception handler's scope (exclusive). +- * @param handler +- * beginning of the exception handler's code. +- * @param type +- * internal name of the type of exceptions handled by the +- * handler, or 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 visitLabel} method). +- */ +- public void visitTryCatchBlock(Label start, Label end, Label handler, +- String type) { +- if (mv != null) { +- mv.visitTryCatchBlock(start, end, handler, type); +- } +- } +- +- /** +- * Visits an annotation on an exception handler type. This method must be +- * called after 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 +- * 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 +- * null if the annotation targets 'typeRef' as a whole. +- * @param desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitTryCatchAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (mv != null) { +- return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); +- } +- return null; +- } +- +- /** +- * Visits a local variable declaration. +- * +- * @param name +- * the name of a local variable. +- * @param desc +- * the type descriptor of this local variable. +- * @param signature +- * the type signature of this local variable. May be +- * 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 visitLabel} method). +- */ +- public void visitLocalVariable(String name, String desc, String signature, +- Label start, Label end, int index) { +- if (mv != null) { +- mv.visitLocalVariable(name, desc, 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 +- * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE +- * 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 +- * 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 desc +- * the class descriptor of the annotation class. +- * @param visible +- * true if the annotation is visible at runtime. +- * @return a visitor to visit the annotation values, or null if +- * this visitor is not interested in visiting this annotation. +- */ +- public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, +- TypePath typePath, Label[] start, Label[] end, int[] index, +- String desc, boolean visible) { +- if (api < Opcodes.ASM5) { +- throw new RuntimeException(); +- } +- if (mv != null) { +- return mv.visitLocalVariableAnnotation(typeRef, typePath, start, +- end, index, desc, 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 start has not already been visited by this +- * visitor (by the {@link #visitLabel visitLabel} method). +- */ +- public void visitLineNumber(int line, 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(int maxStack, 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 9107e349..00000000 +--- a/src/main/java/org/mvel2/asm/MethodWriter.java ++++ /dev/null +@@ -1,2913 +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 methods in bytecode form. Each visit +- * method of this class appends the bytecode corresponding to the visited +- * instruction to a byte vector, in the order these methods are called. +- * +- * @author Eric Bruneton +- * @author Eugene Kuleshov +- */ +-class MethodWriter extends MethodVisitor { +- +- /** +- * Pseudo access flag used to denote constructors. +- */ +- static final int ACC_CONSTRUCTOR = 0x80000; +- +- /** +- * Frame has exactly the same locals as the previous stack map frame and +- * number of stack items is zero. +- */ +- static final int SAME_FRAME = 0; // to 63 (0-3f) +- +- /** +- * Frame has exactly the same locals as the previous stack map frame and +- * number of stack items is 1 +- */ +- static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) +- +- /** +- * Reserved for future use +- */ +- static final int RESERVED = 128; +- +- /** +- * Frame has exactly the same locals as the previous stack map frame and +- * number of stack items is 1. Offset is bigger then 63; +- */ +- static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 +- +- /** +- * Frame where current locals are the same as the locals in the previous +- * frame, except that the k last locals are absent. The value of k is given +- * by the formula 251-frame_type. +- */ +- static final int CHOP_FRAME = 248; // to 250 (f8-fA) +- +- /** +- * Frame has exactly the same locals as the previous stack map frame and +- * number of stack items is zero. Offset is bigger then 63; +- */ +- static final int SAME_FRAME_EXTENDED = 251; // fb +- +- /** +- * Frame where current locals are the same as the locals in the previous +- * frame, except that k additional locals are defined. The value of k is +- * given by the formula frame_type-251. +- */ +- static final int APPEND_FRAME = 252; // to 254 // fc-fe +- +- /** +- * Full frame +- */ +- static final int FULL_FRAME = 255; // ff +- +- /** +- * Indicates that the stack map frames must be recomputed from scratch. In +- * this case the maximum stack size and number of local variables is also +- * recomputed from scratch. +- * +- * @see #compute +- */ +- private static final int FRAMES = 0; +- +- /** +- * Indicates that the maximum stack size and number of local variables must +- * be automatically computed. +- * +- * @see #compute +- */ +- private static final int MAXS = 1; +- +- /** +- * Indicates that nothing must be automatically computed. +- * +- * @see #compute +- */ +- private static final int NOTHING = 2; +- +- /** +- * The class writer to which this method must be added. +- */ +- final ClassWriter cw; +- +- /** +- * Access flags of this method. +- */ +- private int access; +- +- /** +- * The index of the constant pool item that contains the name of this +- * method. +- */ +- private final int name; +- +- /** +- * The index of the constant pool item that contains the descriptor of this +- * method. +- */ +- private final int desc; +- +- /** +- * The descriptor of this method. +- */ +- private final String descriptor; +- +- /** +- * The signature of this method. +- */ +- String signature; +- +- /** +- * If not zero, indicates that the code of this method must be copied from +- * the ClassReader associated to this writer in cw.cr. More +- * precisely, this field gives the index of the first byte to copied from +- * cw.cr.b. +- */ +- int classReaderOffset; +- +- /** +- * If not zero, indicates that the code of this method must be copied from +- * the ClassReader associated to this writer in cw.cr. More +- * precisely, this field gives the number of bytes to copied from +- * cw.cr.b. +- */ +- int classReaderLength; +- +- /** +- * Number of exceptions that can be thrown by this method. +- */ +- int exceptionCount; +- +- /** +- * The exceptions that can be thrown by this method. More precisely, this +- * array contains the indexes of the constant pool items that contain the +- * internal names of these exception classes. +- */ +- int[] exceptions; +- +- /** +- * The annotation default attribute of this method. May be null. +- */ +- private ByteVector annd; +- +- /** +- * The runtime visible annotations of this method. May be null. +- */ +- private AnnotationWriter anns; +- +- /** +- * The runtime invisible annotations of this method. May be null. +- */ +- private AnnotationWriter ianns; +- +- /** +- * The runtime visible type annotations of this method. May be null +- * . +- */ +- private AnnotationWriter tanns; +- +- /** +- * The runtime invisible type annotations of this method. May be +- * null. +- */ +- private AnnotationWriter itanns; +- +- /** +- * The runtime visible parameter annotations of this method. May be +- * null. +- */ +- private AnnotationWriter[] panns; +- +- /** +- * The runtime invisible parameter annotations of this method. May be +- * null. +- */ +- private AnnotationWriter[] ipanns; +- +- /** +- * The number of synthetic parameters of this method. +- */ +- private int synthetics; +- +- /** +- * The non standard attributes of the method. +- */ +- private Attribute attrs; +- +- /** +- * The bytecode of this method. +- */ +- private ByteVector code = new ByteVector(); +- +- /** +- * Maximum stack size of this method. +- */ +- private int maxStack; +- +- /** +- * Maximum number of local variables for this method. +- */ +- private int maxLocals; +- +- /** +- * Number of local variables in the current stack map frame. +- */ +- private int currentLocals; +- +- /** +- * Number of stack map frames in the StackMapTable attribute. +- */ +- private int frameCount; +- +- /** +- * The StackMapTable attribute. +- */ +- private ByteVector stackMap; +- +- /** +- * The offset of the last frame that was written in the StackMapTable +- * attribute. +- */ +- private int previousFrameOffset; +- +- /** +- * The last frame that was written in the StackMapTable attribute. +- * +- * @see #frame +- */ +- private int[] previousFrame; +- +- /** +- * The current stack map frame. The first element contains the 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 +- * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = +- * nStack, frame[3] = nLocal. All types are encoded as integers, with the +- * same format as the one used in {@link Label}, but limited to BASE types. +- */ +- private int[] frame; +- +- /** +- * Number of elements in the exception handler list. +- */ +- private int handlerCount; +- +- /** +- * The first element in the exception handler list. +- */ +- private Handler firstHandler; +- +- /** +- * The last element in the exception handler list. +- */ +- private Handler lastHandler; +- +- /** +- * Number of entries in the MethodParameters attribute. +- */ +- private int methodParametersCount; +- +- /** +- * The MethodParameters attribute. +- */ +- private ByteVector methodParameters; +- +- /** +- * Number of entries in the LocalVariableTable attribute. +- */ +- private int localVarCount; +- +- /** +- * The LocalVariableTable attribute. +- */ +- private ByteVector localVar; +- +- /** +- * Number of entries in the LocalVariableTypeTable attribute. +- */ +- private int localVarTypeCount; +- +- /** +- * The LocalVariableTypeTable attribute. +- */ +- private ByteVector localVarType; +- +- /** +- * Number of entries in the LineNumberTable attribute. +- */ +- private int lineNumberCount; +- +- /** +- * The LineNumberTable attribute. +- */ +- private ByteVector lineNumber; +- +- /** +- * The start offset of the last visited instruction. +- */ +- private int lastCodeOffset; +- +- /** +- * The runtime visible type annotations of the code. May be null. +- */ +- private AnnotationWriter ctanns; +- +- /** +- * The runtime invisible type annotations of the code. May be null. +- */ +- private AnnotationWriter ictanns; +- +- /** +- * The non standard attributes of the method's code. +- */ +- private Attribute cattrs; +- +- /** +- * Indicates if some jump instructions are too small and need to be resized. +- */ +- private boolean resize; +- +- /** +- * The number of subroutines in this method. +- */ +- private int subroutines; +- +- // ------------------------------------------------------------------------ +- +- /* +- * Fields for the control flow graph analysis algorithm (used to compute the +- * maximum stack size). 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 by 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. +- */ +- +- /** +- * Indicates what must be automatically computed. +- * +- * @see #FRAMES +- * @see #MAXS +- * @see #NOTHING +- */ +- private final int compute; +- +- /** +- * A list of labels. This list is the list of basic blocks in the method, +- * i.e. a list of Label objects linked to each other by their +- * {@link Label#successor} field, in the order they are visited by +- * {@link MethodVisitor#visitLabel}, and starting with the first basic +- * block. +- */ +- private Label labels; +- +- /** +- * The previous basic block. +- */ +- private Label previousBlock; +- +- /** +- * The current basic block. +- */ +- private Label currentBlock; +- +- /** +- * The (relative) stack size after the last visited instruction. This size +- * is relative to the beginning of the current basic block, i.e., the true +- * stack size after the last visited instruction is equal to the +- * {@link Label#inputStackTop beginStackSize} of the current basic block +- * plus stackSize. +- */ +- private int stackSize; +- +- /** +- * The (relative) maximum stack size after the last visited instruction. +- * This size is relative to the beginning of the current basic block, i.e., +- * the true maximum stack size after the last visited instruction is equal +- * to the {@link Label#inputStackTop beginStackSize} of the current basic +- * block plus stackSize. +- */ +- private int maxStackSize; +- +- // ------------------------------------------------------------------------ +- // Constructor +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a new {@link MethodWriter}. +- * +- * @param cw +- * the class writer in which the method must be added. +- * @param access +- * the method's access flags (see {@link Opcodes}). +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor (see {@link Type}). +- * @param signature +- * the method's signature. May be null. +- * @param exceptions +- * the internal names of the method's exceptions. May be +- * null. +- * @param computeMaxs +- * true if the maximum stack size and number of local +- * variables must be automatically computed. +- * @param computeFrames +- * true if the stack map tables must be recomputed from +- * scratch. +- */ +- MethodWriter(final ClassWriter cw, final int access, final String name, +- final String desc, final String signature, +- final String[] exceptions, final boolean computeMaxs, +- final boolean computeFrames) { +- super(Opcodes.ASM5); +- if (cw.firstMethod == null) { +- cw.firstMethod = this; +- } else { +- cw.lastMethod.mv = this; +- } +- cw.lastMethod = this; +- this.cw = cw; +- this.access = access; +- if ("".equals(name)) { +- this.access |= ACC_CONSTRUCTOR; +- } +- this.name = cw.newUTF8(name); +- this.desc = cw.newUTF8(desc); +- this.descriptor = desc; +- if (ClassReader.SIGNATURES) { +- this.signature = signature; +- } +- if (exceptions != null && exceptions.length > 0) { +- exceptionCount = exceptions.length; +- this.exceptions = new int[exceptionCount]; +- for (int i = 0; i < exceptionCount; ++i) { +- this.exceptions[i] = cw.newClass(exceptions[i]); +- } +- } +- this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); +- if (computeMaxs || computeFrames) { +- // updates maxLocals +- int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; +- if ((access & Opcodes.ACC_STATIC) != 0) { +- --size; +- } +- maxLocals = size; +- currentLocals = size; +- // creates and visits the label for the first basic block +- labels = new Label(); +- labels.status |= Label.PUSHED; +- visitLabel(labels); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Implementation of the MethodVisitor abstract class +- // ------------------------------------------------------------------------ +- +- @Override +- public void visitParameter(String name, int access) { +- if (methodParameters == null) { +- methodParameters = new ByteVector(); +- } +- ++methodParametersCount; +- methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) +- .putShort(access); +- } +- +- @Override +- public AnnotationVisitor visitAnnotationDefault() { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- annd = new ByteVector(); +- return new AnnotationWriter(cw, false, annd, null, 0); +- } +- +- @Override +- public AnnotationVisitor visitAnnotation(final String desc, +- final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); +- if (visible) { +- aw.next = anns; +- anns = aw; +- } else { +- aw.next = ianns; +- ianns = aw; +- } +- return aw; +- } +- +- @Override +- public AnnotationVisitor visitTypeAnnotation(final int typeRef, +- final TypePath typePath, final String desc, final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- AnnotationWriter.putTarget(typeRef, typePath, bv); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = tanns; +- tanns = aw; +- } else { +- aw.next = itanns; +- itanns = aw; +- } +- return aw; +- } +- +- @Override +- public AnnotationVisitor visitParameterAnnotation(final int parameter, +- final String desc, final boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- if ("Ljava/lang/Synthetic;".equals(desc)) { +- // workaround for a bug in javac with synthetic parameters +- // see ClassReader.readParameterAnnotations +- synthetics = Math.max(synthetics, parameter + 1); +- return new AnnotationWriter(cw, false, bv, null, 0); +- } +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); +- if (visible) { +- if (panns == null) { +- panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; +- } +- aw.next = panns[parameter]; +- panns[parameter] = aw; +- } else { +- if (ipanns == null) { +- ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; +- } +- aw.next = ipanns[parameter]; +- ipanns[parameter] = aw; +- } +- return aw; +- } +- +- @Override +- public void visitAttribute(final Attribute attr) { +- if (attr.isCodeAttribute()) { +- attr.next = cattrs; +- cattrs = attr; +- } else { +- attr.next = attrs; +- attrs = attr; +- } +- } +- +- @Override +- public void visitCode() { +- } +- +- @Override +- public void visitFrame(final int type, final int nLocal, +- final Object[] local, final int nStack, final Object[] stack) { +- if (!ClassReader.FRAMES || compute == FRAMES) { +- return; +- } +- +- if (type == Opcodes.F_NEW) { +- if (previousFrame == null) { +- visitImplicitFirstFrame(); +- } +- currentLocals = nLocal; +- int frameIndex = startFrame(code.length, nLocal, nStack); +- for (int i = 0; i < nLocal; ++i) { +- if (local[i] instanceof String) { +- frame[frameIndex++] = Frame.OBJECT +- | cw.addType((String) local[i]); +- } else if (local[i] instanceof Integer) { +- frame[frameIndex++] = ((Integer) local[i]).intValue(); +- } else { +- frame[frameIndex++] = Frame.UNINITIALIZED +- | cw.addUninitializedType("", +- ((Label) local[i]).position); +- } +- } +- for (int i = 0; i < nStack; ++i) { +- if (stack[i] instanceof String) { +- frame[frameIndex++] = Frame.OBJECT +- | cw.addType((String) stack[i]); +- } else if (stack[i] instanceof Integer) { +- frame[frameIndex++] = ((Integer) stack[i]).intValue(); +- } else { +- frame[frameIndex++] = Frame.UNINITIALIZED +- | cw.addUninitializedType("", +- ((Label) stack[i]).position); +- } +- } +- endFrame(); +- } else { +- int delta; +- if (stackMap == null) { +- stackMap = new ByteVector(); +- delta = code.length; +- } else { +- delta = code.length - previousFrameOffset - 1; +- if (delta < 0) { +- if (type == Opcodes.F_SAME) { +- return; +- } else { +- throw new IllegalStateException(); +- } +- } +- } +- +- switch (type) { +- case Opcodes.F_FULL: +- currentLocals = nLocal; +- stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); +- for (int i = 0; i < nLocal; ++i) { +- writeFrameType(local[i]); +- } +- stackMap.putShort(nStack); +- for (int i = 0; i < nStack; ++i) { +- writeFrameType(stack[i]); +- } +- break; +- case Opcodes.F_APPEND: +- currentLocals += nLocal; +- stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); +- for (int i = 0; i < nLocal; ++i) { +- writeFrameType(local[i]); +- } +- break; +- case Opcodes.F_CHOP: +- currentLocals -= nLocal; +- stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); +- break; +- case Opcodes.F_SAME: +- if (delta < 64) { +- stackMap.putByte(delta); +- } else { +- stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); +- } +- break; +- case Opcodes.F_SAME1: +- if (delta < 64) { +- stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); +- } else { +- stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) +- .putShort(delta); +- } +- writeFrameType(stack[0]); +- break; +- } +- +- previousFrameOffset = code.length; +- ++frameCount; +- } +- +- maxStack = Math.max(maxStack, nStack); +- maxLocals = Math.max(maxLocals, currentLocals); +- } +- +- @Override +- public void visitInsn(final int opcode) { +- lastCodeOffset = code.length; +- // adds the instruction to the bytecode of the method +- code.putByte(opcode); +- // update currentBlock +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, 0, null, null); +- } else { +- // updates current and max stack sizes +- int size = stackSize + Frame.SIZE[opcode]; +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- // if opcode == ATHROW or xRETURN, ends current block (no successor) +- if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) +- || opcode == Opcodes.ATHROW) { +- noSuccessor(); +- } +- } +- } +- +- @Override +- public void visitIntInsn(final int opcode, final int operand) { +- lastCodeOffset = code.length; +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, operand, null, null); +- } else if (opcode != Opcodes.NEWARRAY) { +- // updates current and max stack sizes only for NEWARRAY +- // (stack size variation = 0 for BIPUSH or SIPUSH) +- int size = stackSize + 1; +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- if (opcode == Opcodes.SIPUSH) { +- code.put12(opcode, operand); +- } else { // BIPUSH or NEWARRAY +- code.put11(opcode, operand); +- } +- } +- +- @Override +- public void visitVarInsn(final int opcode, final int var) { +- lastCodeOffset = code.length; +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, var, null, null); +- } else { +- // updates current and max stack sizes +- if (opcode == Opcodes.RET) { +- // no stack change, but end of current block (no successor) +- currentBlock.status |= Label.RET; +- // save 'stackSize' here for future use +- // (see {@link #findSubroutineSuccessors}) +- currentBlock.inputStackTop = stackSize; +- noSuccessor(); +- } else { // xLOAD or xSTORE +- int size = stackSize + Frame.SIZE[opcode]; +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- } +- if (compute != NOTHING) { +- // updates max locals +- int n; +- if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD +- || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { +- n = var + 2; +- } else { +- n = var + 1; +- } +- if (n > maxLocals) { +- maxLocals = n; +- } +- } +- // adds the instruction to the bytecode of the method +- if (var < 4 && opcode != Opcodes.RET) { +- int opt; +- if (opcode < Opcodes.ISTORE) { +- /* ILOAD_0 */ +- opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; +- } else { +- /* ISTORE_0 */ +- opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; +- } +- code.putByte(opt); +- } else if (var >= 256) { +- code.putByte(196 /* WIDE */).put12(opcode, var); +- } else { +- code.put11(opcode, var); +- } +- if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { +- visitLabel(new Label()); +- } +- } +- +- @Override +- public void visitTypeInsn(final int opcode, final String type) { +- lastCodeOffset = code.length; +- Item i = cw.newClassItem(type); +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, code.length, cw, i); +- } else if (opcode == Opcodes.NEW) { +- // updates current and max stack sizes only if opcode == NEW +- // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) +- int size = stackSize + 1; +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- code.put12(opcode, i.index); +- } +- +- @Override +- public void visitFieldInsn(final int opcode, final String owner, +- final String name, final String desc) { +- lastCodeOffset = code.length; +- Item i = cw.newFieldItem(owner, name, desc); +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, 0, cw, i); +- } else { +- int size; +- // computes the stack size variation +- char c = desc.charAt(0); +- switch (opcode) { +- case Opcodes.GETSTATIC: +- size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); +- break; +- case Opcodes.PUTSTATIC: +- size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); +- break; +- case Opcodes.GETFIELD: +- size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); +- break; +- // case Constants.PUTFIELD: +- default: +- size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); +- break; +- } +- // updates current and max stack sizes +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- code.put12(opcode, i.index); +- } +- +- @Override +- public void visitMethodInsn(final int opcode, final String owner, +- final String name, final String desc, final boolean itf) { +- lastCodeOffset = code.length; +- Item i = cw.newMethodItem(owner, name, desc, itf); +- int argSize = i.intVal; +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, 0, cw, i); +- } else { +- /* +- * computes the stack size variation. In order not to recompute +- * several times this variation for the same Item, we use the +- * intVal field of this item to store this variation, once it +- * has been computed. More precisely this intVal field stores +- * the sizes of the arguments and of the return value +- * corresponding to desc. +- */ +- if (argSize == 0) { +- // the above sizes have not been computed yet, +- // so we compute them... +- argSize = Type.getArgumentsAndReturnSizes(desc); +- // ... and we save them in order +- // not to recompute them in the future +- i.intVal = argSize; +- } +- int size; +- if (opcode == Opcodes.INVOKESTATIC) { +- size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; +- } else { +- size = stackSize - (argSize >> 2) + (argSize & 0x03); +- } +- // updates current and max stack sizes +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- if (opcode == Opcodes.INVOKEINTERFACE) { +- if (argSize == 0) { +- argSize = Type.getArgumentsAndReturnSizes(desc); +- i.intVal = argSize; +- } +- code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); +- } else { +- code.put12(opcode, i.index); +- } +- } +- +- @Override +- public void visitInvokeDynamicInsn(final String name, final String desc, +- final Handle bsm, final Object... bsmArgs) { +- lastCodeOffset = code.length; +- Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); +- int argSize = i.intVal; +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); +- } else { +- /* +- * computes the stack size variation. In order not to recompute +- * several times this variation for the same Item, we use the +- * intVal field of this item to store this variation, once it +- * has been computed. More precisely this intVal field stores +- * the sizes of the arguments and of the return value +- * corresponding to desc. +- */ +- if (argSize == 0) { +- // the above sizes have not been computed yet, +- // so we compute them... +- argSize = Type.getArgumentsAndReturnSizes(desc); +- // ... and we save them in order +- // not to recompute them in the future +- i.intVal = argSize; +- } +- int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; +- +- // updates current and max stack sizes +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- code.put12(Opcodes.INVOKEDYNAMIC, i.index); +- code.putShort(0); +- } +- +- @Override +- public void visitJumpInsn(final int opcode, final Label label) { +- lastCodeOffset = code.length; +- Label nextInsn = null; +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(opcode, 0, null, null); +- // 'label' is the target of a jump instruction +- label.getFirst().status |= Label.TARGET; +- // adds 'label' as a successor of this basic block +- addSuccessor(Edge.NORMAL, label); +- if (opcode != Opcodes.GOTO) { +- // creates a Label for the next basic block +- nextInsn = new Label(); +- } +- } else { +- if (opcode == Opcodes.JSR) { +- if ((label.status & Label.SUBROUTINE) == 0) { +- label.status |= Label.SUBROUTINE; +- ++subroutines; +- } +- currentBlock.status |= Label.JSR; +- addSuccessor(stackSize + 1, label); +- // creates a Label for the next basic block +- nextInsn = new Label(); +- /* +- * note that, by construction in this method, a JSR block +- * has at least two successors in the control flow graph: +- * the first one leads the next instruction after the JSR, +- * while the second one leads to the JSR target. +- */ +- } else { +- // updates current stack size (max stack size unchanged +- // because stack size variation always negative in this +- // case) +- stackSize += Frame.SIZE[opcode]; +- addSuccessor(stackSize, label); +- } +- } +- } +- // adds the instruction to the bytecode of the method +- if ((label.status & Label.RESOLVED) != 0 +- && label.position - 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 +- * with IFNOTxxx GOTO_W , where IFNOTxxx is the +- * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where +- * designates the instruction just after the GOTO_W. +- */ +- if (opcode == Opcodes.GOTO) { +- code.putByte(200); // GOTO_W +- } else if (opcode == Opcodes.JSR) { +- code.putByte(201); // JSR_W +- } else { +- // if the IF instruction is transformed into IFNOT GOTO_W the +- // next instruction becomes the target of the IFNOT instruction +- if (nextInsn != null) { +- nextInsn.status |= Label.TARGET; +- } +- code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 +- : opcode ^ 1); +- code.putShort(8); // jump offset +- code.putByte(200); // GOTO_W +- } +- label.put(this, code, code.length - 1, true); +- } else { +- /* +- * case of a backward jump with an offset >= -32768, or of a forward +- * jump with, of course, an unknown offset. In these cases we store +- * the offset in 2 bytes (which will be increased in +- * resizeInstructions, if needed). +- */ +- code.putByte(opcode); +- label.put(this, code, code.length - 1, false); +- } +- if (currentBlock != null) { +- if (nextInsn != null) { +- // if the jump instruction is not a GOTO, the next instruction +- // is also a successor of this instruction. Calling visitLabel +- // adds the label of this next instruction as a successor of the +- // current block, and starts a new basic block +- visitLabel(nextInsn); +- } +- if (opcode == Opcodes.GOTO) { +- noSuccessor(); +- } +- } +- } +- +- @Override +- public void visitLabel(final Label label) { +- // resolves previous forward references to label, if any +- resize |= label.resolve(this, code.length, code.data); +- // updates currentBlock +- if ((label.status & Label.DEBUG) != 0) { +- return; +- } +- if (compute == FRAMES) { +- if (currentBlock != null) { +- if (label.position == currentBlock.position) { +- // successive labels, do not start a new basic block +- currentBlock.status |= (label.status & Label.TARGET); +- label.frame = currentBlock.frame; +- return; +- } +- // ends current block (with one new successor) +- addSuccessor(Edge.NORMAL, label); +- } +- // begins a new current block +- currentBlock = label; +- if (label.frame == null) { +- label.frame = new Frame(); +- label.frame.owner = label; +- } +- // updates the basic block list +- if (previousBlock != null) { +- if (label.position == previousBlock.position) { +- previousBlock.status |= (label.status & Label.TARGET); +- label.frame = previousBlock.frame; +- currentBlock = previousBlock; +- return; +- } +- previousBlock.successor = label; +- } +- previousBlock = label; +- } else if (compute == MAXS) { +- if (currentBlock != null) { +- // ends current block (with one new successor) +- currentBlock.outputStackMax = maxStackSize; +- addSuccessor(stackSize, label); +- } +- // begins a new current block +- currentBlock = label; +- // resets the relative current and max stack sizes +- stackSize = 0; +- maxStackSize = 0; +- // updates the basic block list +- if (previousBlock != null) { +- previousBlock.successor = label; +- } +- previousBlock = label; +- } +- } +- +- @Override +- public void visitLdcInsn(final Object cst) { +- lastCodeOffset = code.length; +- Item i = cw.newConstItem(cst); +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); +- } else { +- int size; +- // computes the stack size variation +- if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { +- size = stackSize + 2; +- } else { +- size = stackSize + 1; +- } +- // updates current and max stack sizes +- if (size > maxStackSize) { +- maxStackSize = size; +- } +- stackSize = size; +- } +- } +- // adds the instruction to the bytecode of the method +- int index = i.index; +- if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { +- code.put12(20 /* LDC2_W */, index); +- } else if (index >= 256) { +- code.put12(19 /* LDC_W */, index); +- } else { +- code.put11(Opcodes.LDC, index); +- } +- } +- +- @Override +- public void visitIincInsn(final int var, final int increment) { +- lastCodeOffset = code.length; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(Opcodes.IINC, var, null, null); +- } +- } +- if (compute != NOTHING) { +- // updates max locals +- int n = var + 1; +- if (n > maxLocals) { +- maxLocals = n; +- } +- } +- // adds the instruction to the bytecode of the method +- if ((var > 255) || (increment > 127) || (increment < -128)) { +- code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var) +- .putShort(increment); +- } else { +- code.putByte(Opcodes.IINC).put11(var, increment); +- } +- } +- +- @Override +- public void visitTableSwitchInsn(final int min, final int max, +- final Label dflt, final Label... labels) { +- lastCodeOffset = code.length; +- // adds the instruction to the bytecode of the method +- int source = code.length; +- code.putByte(Opcodes.TABLESWITCH); +- code.putByteArray(null, 0, (4 - code.length % 4) % 4); +- dflt.put(this, code, source, true); +- code.putInt(min).putInt(max); +- for (int i = 0; i < labels.length; ++i) { +- labels[i].put(this, code, source, true); +- } +- // updates currentBlock +- visitSwitchInsn(dflt, labels); +- } +- +- @Override +- public void visitLookupSwitchInsn(final Label dflt, final int[] keys, +- final Label[] labels) { +- lastCodeOffset = code.length; +- // adds the instruction to the bytecode of the method +- int source = code.length; +- code.putByte(Opcodes.LOOKUPSWITCH); +- code.putByteArray(null, 0, (4 - code.length % 4) % 4); +- dflt.put(this, code, source, true); +- code.putInt(labels.length); +- for (int i = 0; i < labels.length; ++i) { +- code.putInt(keys[i]); +- labels[i].put(this, code, source, true); +- } +- // updates currentBlock +- visitSwitchInsn(dflt, labels); +- } +- +- private void visitSwitchInsn(final Label dflt, final Label[] labels) { +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); +- // adds current block successors +- addSuccessor(Edge.NORMAL, dflt); +- dflt.getFirst().status |= Label.TARGET; +- for (int i = 0; i < labels.length; ++i) { +- addSuccessor(Edge.NORMAL, labels[i]); +- labels[i].getFirst().status |= Label.TARGET; +- } +- } else { +- // updates current stack size (max stack size unchanged) +- --stackSize; +- // adds current block successors +- addSuccessor(stackSize, dflt); +- for (int i = 0; i < labels.length; ++i) { +- addSuccessor(stackSize, labels[i]); +- } +- } +- // ends current block +- noSuccessor(); +- } +- } +- +- @Override +- public void visitMultiANewArrayInsn(final String desc, final int dims) { +- lastCodeOffset = code.length; +- Item i = cw.newClassItem(desc); +- // Label currentBlock = this.currentBlock; +- if (currentBlock != null) { +- if (compute == FRAMES) { +- currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); +- } else { +- // updates current stack size (max stack size unchanged because +- // stack size variation always negative or null) +- stackSize += 1 - dims; +- } +- } +- // adds the instruction to the bytecode of the method +- code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); +- } +- +- @Override +- public AnnotationVisitor visitInsnAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); +- AnnotationWriter.putTarget(typeRef, typePath, bv); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = ctanns; +- ctanns = aw; +- } else { +- aw.next = ictanns; +- ictanns = aw; +- } +- return aw; +- } +- +- @Override +- public void visitTryCatchBlock(final Label start, final Label end, +- final Label handler, final String type) { +- ++handlerCount; +- Handler h = new Handler(); +- h.start = start; +- h.end = end; +- h.handler = handler; +- h.desc = type; +- h.type = type != null ? cw.newClass(type) : 0; +- if (lastHandler == null) { +- firstHandler = h; +- } else { +- lastHandler.next = h; +- } +- lastHandler = h; +- } +- +- @Override +- public AnnotationVisitor visitTryCatchAnnotation(int typeRef, +- TypePath typePath, String desc, boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- AnnotationWriter.putTarget(typeRef, typePath, bv); +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = ctanns; +- ctanns = aw; +- } else { +- aw.next = ictanns; +- ictanns = aw; +- } +- return aw; +- } +- +- @Override +- public void visitLocalVariable(final String name, final String desc, +- final String signature, final Label start, final Label end, +- final int index) { +- if (signature != null) { +- if (localVarType == null) { +- localVarType = new ByteVector(); +- } +- ++localVarTypeCount; +- localVarType.putShort(start.position) +- .putShort(end.position - start.position) +- .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) +- .putShort(index); +- } +- if (localVar == null) { +- localVar = new ByteVector(); +- } +- ++localVarCount; +- localVar.putShort(start.position) +- .putShort(end.position - start.position) +- .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) +- .putShort(index); +- if (compute != NOTHING) { +- // updates max locals +- char c = desc.charAt(0); +- int n = index + (c == 'J' || c == 'D' ? 2 : 1); +- if (n > maxLocals) { +- maxLocals = n; +- } +- } +- } +- +- @Override +- public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, +- TypePath typePath, Label[] start, Label[] end, int[] index, +- String desc, boolean visible) { +- if (!ClassReader.ANNOTATIONS) { +- return null; +- } +- ByteVector bv = new ByteVector(); +- // write target_type and target_info +- bv.putByte(typeRef >>> 24).putShort(start.length); +- for (int i = 0; i < start.length; ++i) { +- bv.putShort(start[i].position) +- .putShort(end[i].position - start[i].position) +- .putShort(index[i]); +- } +- if (typePath == null) { +- bv.putByte(0); +- } else { +- int length = typePath.b[typePath.offset] * 2 + 1; +- bv.putByteArray(typePath.b, typePath.offset, length); +- } +- // write type, and reserve space for values count +- bv.putShort(cw.newUTF8(desc)).putShort(0); +- AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, +- bv.length - 2); +- if (visible) { +- aw.next = ctanns; +- ctanns = aw; +- } else { +- aw.next = ictanns; +- ictanns = aw; +- } +- return aw; +- } +- +- @Override +- public void visitLineNumber(final int line, final Label start) { +- if (lineNumber == null) { +- lineNumber = new ByteVector(); +- } +- ++lineNumberCount; +- lineNumber.putShort(start.position); +- lineNumber.putShort(line); +- } +- +- @Override +- public void visitMaxs(final int maxStack, final int maxLocals) { +- if (resize) { +- // replaces the temporary jump opcodes introduced by Label.resolve. +- if (ClassReader.RESIZE) { +- resizeInstructions(); +- } else { +- throw new RuntimeException("Method code too large!"); +- } +- } +- if (ClassReader.FRAMES && compute == FRAMES) { +- // completes the control flow graph with exception handler blocks +- Handler handler = firstHandler; +- while (handler != null) { +- Label l = handler.start.getFirst(); +- Label h = handler.handler.getFirst(); +- Label e = handler.end.getFirst(); +- // computes the kind of the edges to 'h' +- String t = handler.desc == null ? "java/lang/Throwable" +- : handler.desc; +- int kind = Frame.OBJECT | cw.addType(t); +- // h is an exception handler +- h.status |= Label.TARGET; +- // adds 'h' as a successor of labels between 'start' and 'end' +- while (l != e) { +- // creates an edge to 'h' +- Edge b = new Edge(); +- b.info = kind; +- b.successor = h; +- // adds it to the successors of 'l' +- b.next = l.successors; +- l.successors = b; +- // goes to the next label +- l = l.successor; +- } +- handler = handler.next; +- } +- +- // creates and visits the first (implicit) frame +- Frame f = labels.frame; +- Type[] args = Type.getArgumentTypes(descriptor); +- f.initInputFrame(cw, access, args, this.maxLocals); +- visitFrame(f); +- +- /* +- * fix point algorithm: mark the first basic block as 'changed' +- * (i.e. put it in the 'changed' list) and, while there are changed +- * basic blocks, choose one, mark it as unchanged, and update its +- * successors (which can be changed in the process). +- */ +- int max = 0; +- Label changed = labels; +- while (changed != null) { +- // removes a basic block from the list of changed basic blocks +- Label l = changed; +- changed = changed.next; +- l.next = null; +- f = l.frame; +- // a reachable jump target must be stored in the stack map +- if ((l.status & Label.TARGET) != 0) { +- l.status |= Label.STORE; +- } +- // all visited labels are reachable, by definition +- l.status |= Label.REACHABLE; +- // updates the (absolute) maximum stack size +- int blockMax = f.inputStack.length + l.outputStackMax; +- if (blockMax > max) { +- max = blockMax; +- } +- // updates the successors of the current basic block +- Edge e = l.successors; +- while (e != null) { +- Label n = e.successor.getFirst(); +- boolean change = f.merge(cw, n.frame, e.info); +- if (change && n.next == null) { +- // if n has changed and is not already in the 'changed' +- // list, adds it to this list +- n.next = changed; +- changed = n; +- } +- e = e.next; +- } +- } +- +- // visits all the frames that must be stored in the stack map +- Label l = labels; +- while (l != null) { +- f = l.frame; +- if ((l.status & Label.STORE) != 0) { +- visitFrame(f); +- } +- if ((l.status & Label.REACHABLE) == 0) { +- // finds start and end of dead basic block +- Label k = l.successor; +- int start = l.position; +- int end = (k == null ? code.length : k.position) - 1; +- // if non empty basic block +- if (end >= start) { +- max = Math.max(max, 1); +- // replaces instructions with NOP ... NOP ATHROW +- for (int i = start; i < end; ++i) { +- code.data[i] = Opcodes.NOP; +- } +- code.data[end] = (byte) Opcodes.ATHROW; +- // emits a frame for this unreachable block +- int frameIndex = startFrame(start, 0, 1); +- frame[frameIndex] = Frame.OBJECT +- | cw.addType("java/lang/Throwable"); +- endFrame(); +- // removes the start-end range from the exception +- // handlers +- firstHandler = Handler.remove(firstHandler, l, k); +- } +- } +- l = l.successor; +- } +- +- handler = firstHandler; +- handlerCount = 0; +- while (handler != null) { +- handlerCount += 1; +- handler = handler.next; +- } +- +- this.maxStack = max; +- } else if (compute == MAXS) { +- // completes the control flow graph with exception handler blocks +- Handler handler = firstHandler; +- while (handler != null) { +- Label l = handler.start; +- Label h = handler.handler; +- Label e = handler.end; +- // adds 'h' as a successor of labels between 'start' and 'end' +- while (l != e) { +- // creates an edge to 'h' +- Edge b = new Edge(); +- b.info = Edge.EXCEPTION; +- b.successor = h; +- // adds it to the successors of 'l' +- if ((l.status & Label.JSR) == 0) { +- b.next = l.successors; +- l.successors = b; +- } else { +- // if l is a JSR block, adds b after the first two edges +- // to preserve the hypothesis about JSR block successors +- // order (see {@link #visitJumpInsn}) +- b.next = l.successors.next.next; +- l.successors.next.next = b; +- } +- // goes to the next label +- l = l.successor; +- } +- handler = handler.next; +- } +- +- if (subroutines > 0) { +- // completes the control flow graph with the RET successors +- /* +- * first step: finds the subroutines. This step determines, for +- * each basic block, to which subroutine(s) it belongs. +- */ +- // finds the basic blocks that belong to the "main" subroutine +- int id = 0; +- labels.visitSubroutine(null, 1, subroutines); +- // finds the basic blocks that belong to the real subroutines +- Label l = labels; +- while (l != null) { +- if ((l.status & Label.JSR) != 0) { +- // the subroutine is defined by l's TARGET, not by l +- Label subroutine = l.successors.next.successor; +- // if this subroutine has not been visited yet... +- if ((subroutine.status & Label.VISITED) == 0) { +- // ...assigns it a new id and finds its basic blocks +- id += 1; +- subroutine.visitSubroutine(null, (id / 32L) << 32 +- | (1L << (id % 32)), subroutines); +- } +- } +- l = l.successor; +- } +- // second step: finds the successors of RET blocks +- l = labels; +- while (l != null) { +- if ((l.status & Label.JSR) != 0) { +- Label L = labels; +- while (L != null) { +- L.status &= ~Label.VISITED2; +- L = L.successor; +- } +- // the subroutine is defined by l's TARGET, not by l +- Label subroutine = l.successors.next.successor; +- subroutine.visitSubroutine(l, 0, subroutines); +- } +- l = l.successor; +- } +- } +- +- /* +- * control flow analysis algorithm: while the block stack is not +- * empty, pop a block from this stack, update the max stack size, +- * compute the true (non relative) begin stack size of the +- * successors of this block, and push these successors onto the +- * stack (unless they have already been pushed onto the stack). +- * Note: by hypothesis, the {@link Label#inputStackTop} of the +- * blocks in the block stack are the true (non relative) beginning +- * stack sizes of these blocks. +- */ +- int max = 0; +- Label stack = labels; +- while (stack != null) { +- // pops a block from the stack +- Label l = stack; +- stack = stack.next; +- // computes the true (non relative) max stack size of this block +- int start = l.inputStackTop; +- int blockMax = start + l.outputStackMax; +- // updates the global max stack size +- if (blockMax > max) { +- max = blockMax; +- } +- // analyzes the successors of the block +- Edge b = l.successors; +- if ((l.status & Label.JSR) != 0) { +- // ignores the first edge of JSR blocks (virtual successor) +- b = b.next; +- } +- while (b != null) { +- l = b.successor; +- // if this successor has not already been pushed... +- if ((l.status & Label.PUSHED) == 0) { +- // computes its true beginning stack size... +- l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start +- + b.info; +- // ...and pushes it onto the stack +- l.status |= Label.PUSHED; +- l.next = stack; +- stack = l; +- } +- b = b.next; +- } +- } +- this.maxStack = Math.max(maxStack, max); +- } else { +- this.maxStack = maxStack; +- this.maxLocals = maxLocals; +- } +- } +- +- @Override +- public void visitEnd() { +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: control flow analysis algorithm +- // ------------------------------------------------------------------------ +- +- /** +- * Adds a successor to the {@link #currentBlock currentBlock} block. +- * +- * @param info +- * information about the control flow edge to be added. +- * @param successor +- * the successor block to be added to the current block. +- */ +- private void addSuccessor(final int info, final Label successor) { +- // creates and initializes an Edge object... +- Edge b = new Edge(); +- b.info = info; +- b.successor = successor; +- // ...and adds it to the successor list of the currentBlock block +- b.next = currentBlock.successors; +- currentBlock.successors = b; +- } +- +- /** +- * Ends the current basic block. This method must be used in the case where +- * the current basic block does not have any successor. +- */ +- private void noSuccessor() { +- if (compute == FRAMES) { +- Label l = new Label(); +- l.frame = new Frame(); +- l.frame.owner = l; +- l.resolve(this, code.length, code.data); +- previousBlock.successor = l; +- previousBlock = l; +- } else { +- currentBlock.outputStackMax = maxStackSize; +- } +- currentBlock = null; +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: stack map frames +- // ------------------------------------------------------------------------ +- +- /** +- * Visits a frame that has been computed from scratch. +- * +- * @param f +- * the frame that must be visited. +- */ +- private void visitFrame(final Frame f) { +- int i, t; +- int nTop = 0; +- int nLocal = 0; +- int nStack = 0; +- int[] locals = f.inputLocals; +- int[] stacks = f.inputStack; +- // computes the number of locals (ignores TOP types that are just after +- // a LONG or a DOUBLE, and all trailing TOP types) +- for (i = 0; i < locals.length; ++i) { +- t = locals[i]; +- if (t == Frame.TOP) { +- ++nTop; +- } else { +- nLocal += nTop + 1; +- nTop = 0; +- } +- if (t == Frame.LONG || t == Frame.DOUBLE) { +- ++i; +- } +- } +- // computes the stack size (ignores TOP types that are just after +- // a LONG or a DOUBLE) +- for (i = 0; i < stacks.length; ++i) { +- t = stacks[i]; +- ++nStack; +- if (t == Frame.LONG || t == Frame.DOUBLE) { +- ++i; +- } +- } +- // visits the frame and its content +- int frameIndex = startFrame(f.owner.position, nLocal, nStack); +- for (i = 0; nLocal > 0; ++i, --nLocal) { +- t = locals[i]; +- frame[frameIndex++] = t; +- if (t == Frame.LONG || t == Frame.DOUBLE) { +- ++i; +- } +- } +- for (i = 0; i < stacks.length; ++i) { +- t = stacks[i]; +- frame[frameIndex++] = t; +- if (t == Frame.LONG || t == Frame.DOUBLE) { +- ++i; +- } +- } +- endFrame(); +- } +- +- /** +- * Visit the implicit first frame of this method. +- */ +- private void visitImplicitFirstFrame() { +- // There can be at most descriptor.length() + 1 locals +- int frameIndex = startFrame(0, descriptor.length() + 1, 0); +- if ((access & Opcodes.ACC_STATIC) == 0) { +- if ((access & ACC_CONSTRUCTOR) == 0) { +- frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); +- } else { +- frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS; +- } +- } +- int i = 1; +- loop: while (true) { +- int j = i; +- switch (descriptor.charAt(i++)) { +- case 'Z': +- case 'C': +- case 'B': +- case 'S': +- case 'I': +- frame[frameIndex++] = 1; // Opcodes.INTEGER; +- break; +- case 'F': +- frame[frameIndex++] = 2; // Opcodes.FLOAT; +- break; +- case 'J': +- frame[frameIndex++] = 4; // Opcodes.LONG; +- break; +- case 'D': +- frame[frameIndex++] = 3; // Opcodes.DOUBLE; +- break; +- case '[': +- while (descriptor.charAt(i) == '[') { +- ++i; +- } +- if (descriptor.charAt(i) == 'L') { +- ++i; +- while (descriptor.charAt(i) != ';') { +- ++i; +- } +- } +- frame[frameIndex++] = Frame.OBJECT +- | cw.addType(descriptor.substring(j, ++i)); +- break; +- case 'L': +- while (descriptor.charAt(i) != ';') { +- ++i; +- } +- frame[frameIndex++] = Frame.OBJECT +- | cw.addType(descriptor.substring(j + 1, i++)); +- break; +- default: +- break loop; +- } +- } +- frame[1] = frameIndex - 3; +- endFrame(); +- } +- +- /** +- * Starts the visit of a stack map frame. +- * +- * @param offset +- * the offset of the instruction to which the frame corresponds. +- * @param nLocal +- * the number of local variables in the frame. +- * @param nStack +- * the number of stack elements in the frame. +- * @return the index of the next element to be written in this frame. +- */ +- private int startFrame(final int offset, final int nLocal, final int nStack) { +- int n = 3 + nLocal + nStack; +- if (frame == null || frame.length < n) { +- frame = new int[n]; +- } +- frame[0] = offset; +- frame[1] = nLocal; +- frame[2] = nStack; +- return 3; +- } +- +- /** +- * Checks if the visit of the current frame {@link #frame} is finished, and +- * if yes, write it in the StackMapTable attribute. +- */ +- private void endFrame() { +- if (previousFrame != null) { // do not write the first frame +- if (stackMap == null) { +- stackMap = new ByteVector(); +- } +- writeFrame(); +- ++frameCount; +- } +- previousFrame = frame; +- frame = null; +- } +- +- /** +- * Compress and writes the current frame {@link #frame} in the StackMapTable +- * attribute. +- */ +- private void writeFrame() { +- int clocalsSize = frame[1]; +- int cstackSize = frame[2]; +- if ((cw.version & 0xFFFF) < Opcodes.V1_6) { +- stackMap.putShort(frame[0]).putShort(clocalsSize); +- writeFrameTypes(3, 3 + clocalsSize); +- stackMap.putShort(cstackSize); +- writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); +- return; +- } +- int localsSize = previousFrame[1]; +- int type = FULL_FRAME; +- int k = 0; +- int delta; +- if (frameCount == 0) { +- delta = frame[0]; +- } else { +- delta = frame[0] - previousFrame[0] - 1; +- } +- if (cstackSize == 0) { +- k = clocalsSize - localsSize; +- switch (k) { +- case -3: +- case -2: +- case -1: +- type = CHOP_FRAME; +- localsSize = clocalsSize; +- break; +- case 0: +- type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; +- break; +- case 1: +- case 2: +- case 3: +- type = APPEND_FRAME; +- break; +- } +- } else if (clocalsSize == localsSize && cstackSize == 1) { +- type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME +- : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; +- } +- if (type != FULL_FRAME) { +- // verify if locals are the same +- int l = 3; +- for (int j = 0; j < localsSize; j++) { +- if (frame[l] != previousFrame[l]) { +- type = FULL_FRAME; +- break; +- } +- l++; +- } +- } +- switch (type) { +- case SAME_FRAME: +- stackMap.putByte(delta); +- break; +- case SAME_LOCALS_1_STACK_ITEM_FRAME: +- stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); +- writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); +- break; +- case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: +- stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( +- delta); +- writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); +- break; +- case SAME_FRAME_EXTENDED: +- stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); +- break; +- case CHOP_FRAME: +- stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); +- break; +- case APPEND_FRAME: +- stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); +- writeFrameTypes(3 + localsSize, 3 + clocalsSize); +- break; +- // case FULL_FRAME: +- default: +- stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); +- writeFrameTypes(3, 3 + clocalsSize); +- stackMap.putShort(cstackSize); +- writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); +- } +- } +- +- /** +- * Writes some types of the current frame {@link #frame} into the +- * StackMapTableAttribute. This method converts types from the format used +- * in {@link Label} to the format used in StackMapTable attributes. In +- * particular, it converts type table indexes to constant pool indexes. +- * +- * @param start +- * index of the first type in {@link #frame} to write. +- * @param end +- * index of last type in {@link #frame} to write (exclusive). +- */ +- private void writeFrameTypes(final int start, final int end) { +- for (int i = start; i < end; ++i) { +- int t = frame[i]; +- int d = t & Frame.DIM; +- if (d == 0) { +- int v = t & Frame.BASE_VALUE; +- switch (t & Frame.BASE_KIND) { +- case Frame.OBJECT: +- stackMap.putByte(7).putShort( +- cw.newClass(cw.typeTable[v].strVal1)); +- break; +- case Frame.UNINITIALIZED: +- stackMap.putByte(8).putShort(cw.typeTable[v].intVal); +- break; +- default: +- stackMap.putByte(v); +- } +- } else { +- StringBuffer buf = new StringBuffer(); +- d >>= 28; +- while (d-- > 0) { +- buf.append('['); +- } +- if ((t & Frame.BASE_KIND) == Frame.OBJECT) { +- buf.append('L'); +- buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); +- buf.append(';'); +- } else { +- switch (t & 0xF) { +- case 1: +- buf.append('I'); +- break; +- case 2: +- buf.append('F'); +- break; +- case 3: +- buf.append('D'); +- break; +- case 9: +- buf.append('Z'); +- break; +- case 10: +- buf.append('B'); +- break; +- case 11: +- buf.append('C'); +- break; +- case 12: +- buf.append('S'); +- break; +- default: +- buf.append('J'); +- } +- } +- stackMap.putByte(7).putShort(cw.newClass(buf.toString())); +- } +- } +- } +- +- private void writeFrameType(final Object type) { +- if (type instanceof String) { +- stackMap.putByte(7).putShort(cw.newClass((String) type)); +- } else if (type instanceof Integer) { +- stackMap.putByte(((Integer) type).intValue()); +- } else { +- stackMap.putByte(8).putShort(((Label) type).position); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: dump bytecode array +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the size of the bytecode of this method. +- * +- * @return the size of the bytecode of this method. +- */ +- final int getSize() { +- if (classReaderOffset != 0) { +- return 6 + classReaderLength; +- } +- int size = 8; +- if (code.length > 0) { +- if (code.length > 65536) { +- throw new RuntimeException("Method code too large!"); +- } +- cw.newUTF8("Code"); +- size += 18 + code.length + 8 * handlerCount; +- if (localVar != null) { +- cw.newUTF8("LocalVariableTable"); +- size += 8 + localVar.length; +- } +- if (localVarType != null) { +- cw.newUTF8("LocalVariableTypeTable"); +- size += 8 + localVarType.length; +- } +- if (lineNumber != null) { +- cw.newUTF8("LineNumberTable"); +- size += 8 + lineNumber.length; +- } +- if (stackMap != null) { +- boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; +- cw.newUTF8(zip ? "StackMapTable" : "StackMap"); +- size += 8 + stackMap.length; +- } +- if (ClassReader.ANNOTATIONS && ctanns != null) { +- cw.newUTF8("RuntimeVisibleTypeAnnotations"); +- size += 8 + ctanns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && ictanns != null) { +- cw.newUTF8("RuntimeInvisibleTypeAnnotations"); +- size += 8 + ictanns.getSize(); +- } +- if (cattrs != null) { +- size += cattrs.getSize(cw, code.data, code.length, maxStack, +- maxLocals); +- } +- } +- if (exceptionCount > 0) { +- cw.newUTF8("Exceptions"); +- size += 8 + 2 * exceptionCount; +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- cw.newUTF8("Synthetic"); +- size += 6; +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- cw.newUTF8("Deprecated"); +- size += 6; +- } +- if (ClassReader.SIGNATURES && signature != null) { +- cw.newUTF8("Signature"); +- cw.newUTF8(signature); +- size += 8; +- } +- if (methodParameters != null) { +- cw.newUTF8("MethodParameters"); +- size += 7 + methodParameters.length; +- } +- if (ClassReader.ANNOTATIONS && annd != null) { +- cw.newUTF8("AnnotationDefault"); +- size += 6 + annd.length; +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- cw.newUTF8("RuntimeVisibleAnnotations"); +- size += 8 + anns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- cw.newUTF8("RuntimeInvisibleAnnotations"); +- size += 8 + ianns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- cw.newUTF8("RuntimeVisibleTypeAnnotations"); +- size += 8 + tanns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- cw.newUTF8("RuntimeInvisibleTypeAnnotations"); +- size += 8 + itanns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && panns != null) { +- cw.newUTF8("RuntimeVisibleParameterAnnotations"); +- size += 7 + 2 * (panns.length - synthetics); +- for (int i = panns.length - 1; i >= synthetics; --i) { +- size += panns[i] == null ? 0 : panns[i].getSize(); +- } +- } +- if (ClassReader.ANNOTATIONS && ipanns != null) { +- cw.newUTF8("RuntimeInvisibleParameterAnnotations"); +- size += 7 + 2 * (ipanns.length - synthetics); +- for (int i = ipanns.length - 1; i >= synthetics; --i) { +- size += ipanns[i] == null ? 0 : ipanns[i].getSize(); +- } +- } +- if (attrs != null) { +- size += attrs.getSize(cw, null, 0, -1, -1); +- } +- return size; +- } +- +- /** +- * Puts the bytecode of this method in the given byte vector. +- * +- * @param out +- * the byte vector into which the bytecode of this method must be +- * copied. +- */ +- final void put(final ByteVector out) { +- final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; +- int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED +- | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE +- | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); +- out.putShort(access & ~mask).putShort(name).putShort(desc); +- if (classReaderOffset != 0) { +- out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); +- return; +- } +- int attributeCount = 0; +- if (code.length > 0) { +- ++attributeCount; +- } +- if (exceptionCount > 0) { +- ++attributeCount; +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- ++attributeCount; +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- ++attributeCount; +- } +- if (ClassReader.SIGNATURES && signature != null) { +- ++attributeCount; +- } +- if (methodParameters != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && annd != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && panns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && ipanns != null) { +- ++attributeCount; +- } +- if (attrs != null) { +- attributeCount += attrs.getCount(); +- } +- out.putShort(attributeCount); +- if (code.length > 0) { +- int size = 12 + code.length + 8 * handlerCount; +- if (localVar != null) { +- size += 8 + localVar.length; +- } +- if (localVarType != null) { +- size += 8 + localVarType.length; +- } +- if (lineNumber != null) { +- size += 8 + lineNumber.length; +- } +- if (stackMap != null) { +- size += 8 + stackMap.length; +- } +- if (ClassReader.ANNOTATIONS && ctanns != null) { +- size += 8 + ctanns.getSize(); +- } +- if (ClassReader.ANNOTATIONS && ictanns != null) { +- size += 8 + ictanns.getSize(); +- } +- if (cattrs != null) { +- size += cattrs.getSize(cw, code.data, code.length, maxStack, +- maxLocals); +- } +- out.putShort(cw.newUTF8("Code")).putInt(size); +- out.putShort(maxStack).putShort(maxLocals); +- out.putInt(code.length).putByteArray(code.data, 0, code.length); +- out.putShort(handlerCount); +- if (handlerCount > 0) { +- Handler h = firstHandler; +- while (h != null) { +- out.putShort(h.start.position).putShort(h.end.position) +- .putShort(h.handler.position).putShort(h.type); +- h = h.next; +- } +- } +- attributeCount = 0; +- if (localVar != null) { +- ++attributeCount; +- } +- if (localVarType != null) { +- ++attributeCount; +- } +- if (lineNumber != null) { +- ++attributeCount; +- } +- if (stackMap != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && ctanns != null) { +- ++attributeCount; +- } +- if (ClassReader.ANNOTATIONS && ictanns != null) { +- ++attributeCount; +- } +- if (cattrs != null) { +- attributeCount += cattrs.getCount(); +- } +- out.putShort(attributeCount); +- if (localVar != null) { +- out.putShort(cw.newUTF8("LocalVariableTable")); +- out.putInt(localVar.length + 2).putShort(localVarCount); +- out.putByteArray(localVar.data, 0, localVar.length); +- } +- if (localVarType != null) { +- out.putShort(cw.newUTF8("LocalVariableTypeTable")); +- out.putInt(localVarType.length + 2).putShort(localVarTypeCount); +- out.putByteArray(localVarType.data, 0, localVarType.length); +- } +- if (lineNumber != null) { +- out.putShort(cw.newUTF8("LineNumberTable")); +- out.putInt(lineNumber.length + 2).putShort(lineNumberCount); +- out.putByteArray(lineNumber.data, 0, lineNumber.length); +- } +- if (stackMap != null) { +- boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; +- out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); +- out.putInt(stackMap.length + 2).putShort(frameCount); +- out.putByteArray(stackMap.data, 0, stackMap.length); +- } +- if (ClassReader.ANNOTATIONS && ctanns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); +- ctanns.put(out); +- } +- if (ClassReader.ANNOTATIONS && ictanns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); +- ictanns.put(out); +- } +- if (cattrs != null) { +- cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); +- } +- } +- if (exceptionCount > 0) { +- out.putShort(cw.newUTF8("Exceptions")).putInt( +- 2 * exceptionCount + 2); +- out.putShort(exceptionCount); +- for (int i = 0; i < exceptionCount; ++i) { +- out.putShort(exceptions[i]); +- } +- } +- if ((access & Opcodes.ACC_SYNTHETIC) != 0) { +- if ((cw.version & 0xFFFF) < Opcodes.V1_5 +- || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { +- out.putShort(cw.newUTF8("Synthetic")).putInt(0); +- } +- } +- if ((access & Opcodes.ACC_DEPRECATED) != 0) { +- out.putShort(cw.newUTF8("Deprecated")).putInt(0); +- } +- if (ClassReader.SIGNATURES && signature != null) { +- out.putShort(cw.newUTF8("Signature")).putInt(2) +- .putShort(cw.newUTF8(signature)); +- } +- if (methodParameters != null) { +- out.putShort(cw.newUTF8("MethodParameters")); +- out.putInt(methodParameters.length + 1).putByte( +- methodParametersCount); +- out.putByteArray(methodParameters.data, 0, methodParameters.length); +- } +- if (ClassReader.ANNOTATIONS && annd != null) { +- out.putShort(cw.newUTF8("AnnotationDefault")); +- out.putInt(annd.length); +- out.putByteArray(annd.data, 0, annd.length); +- } +- if (ClassReader.ANNOTATIONS && anns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); +- anns.put(out); +- } +- if (ClassReader.ANNOTATIONS && ianns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); +- ianns.put(out); +- } +- if (ClassReader.ANNOTATIONS && tanns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); +- tanns.put(out); +- } +- if (ClassReader.ANNOTATIONS && itanns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); +- itanns.put(out); +- } +- if (ClassReader.ANNOTATIONS && panns != null) { +- out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); +- AnnotationWriter.put(panns, synthetics, out); +- } +- if (ClassReader.ANNOTATIONS && ipanns != null) { +- out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); +- AnnotationWriter.put(ipanns, synthetics, out); +- } +- if (attrs != null) { +- attrs.put(cw, null, 0, -1, -1, out); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) +- // ------------------------------------------------------------------------ +- +- /** +- * Resizes and replaces the temporary instructions inserted by +- * {@link Label#resolve} for wide forward jumps, while keeping jump offsets +- * and instruction addresses consistent. This may require to resize other +- * existing instructions, or even to introduce new instructions: for +- * example, increasing the size of an instruction by 2 at the middle of a +- * method can increases the offset of an IFEQ instruction from 32766 to +- * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W +- * 32765. This, in turn, may require to increase the size of another jump +- * instruction, and so on... All these operations are handled automatically +- * by this method. +- *

+- * This method must be called after all the method that is being built +- * has been visited. In particular, the {@link Label Label} objects used +- * to construct the method are no longer valid after this method has been +- * called. +- */ +- private void resizeInstructions() { +- byte[] b = code.data; // bytecode of the method +- int u, v, label; // indexes in b +- int i, j; // loop indexes +- /* +- * 1st step: As explained above, resizing an instruction may require to +- * resize another one, which may require to resize yet another one, and +- * so on. The first step of the algorithm consists in finding all the +- * instructions that need to be resized, without modifying the code. +- * This is done by the following "fix point" algorithm: +- * +- * Parse the code to find the jump instructions whose offset will need +- * more than 2 bytes to be stored (the future offset is computed from +- * the current offset and from the number of bytes that will be inserted +- * or removed between the source and target instructions). For each such +- * instruction, adds an entry in (a copy of) the indexes and sizes +- * arrays (if this has not already been done in a previous iteration!). +- * +- * If at least one entry has been added during the previous step, go +- * back to the beginning, otherwise stop. +- * +- * In fact the real algorithm is complicated by the fact that the size +- * of TABLESWITCH and LOOKUPSWITCH instructions depends on their +- * position in the bytecode (because of padding). In order to ensure the +- * convergence of the algorithm, the number of bytes to be added or +- * removed from these instructions is over estimated during the previous +- * loop, and computed exactly only after the loop is finished (this +- * requires another pass to parse the bytecode of the method). +- */ +- int[] allIndexes = new int[0]; // copy of indexes +- int[] allSizes = new int[0]; // copy of sizes +- boolean[] resize; // instructions to be resized +- int newOffset; // future offset of a jump instruction +- +- resize = new boolean[code.length]; +- +- // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done +- int state = 3; +- do { +- if (state == 3) { +- state = 2; +- } +- u = 0; +- while (u < b.length) { +- int opcode = b[u] & 0xFF; // opcode of current instruction +- int insert = 0; // bytes to be added after this instruction +- +- switch (ClassWriter.TYPE[opcode]) { +- case ClassWriter.NOARG_INSN: +- case ClassWriter.IMPLVAR_INSN: +- u += 1; +- break; +- case ClassWriter.LABEL_INSN: +- if (opcode > 201) { +- // converts temporary opcodes 202 to 217, 218 and +- // 219 to IFEQ ... JSR (inclusive), IFNULL and +- // IFNONNULL +- opcode = opcode < 218 ? opcode - 49 : opcode - 20; +- label = u + readUnsignedShort(b, u + 1); +- } else { +- label = u + readShort(b, u + 1); +- } +- newOffset = getNewOffset(allIndexes, allSizes, u, label); +- if (newOffset < Short.MIN_VALUE +- || newOffset > Short.MAX_VALUE) { +- if (!resize[u]) { +- if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { +- // two additional bytes will be required to +- // replace this GOTO or JSR instruction with +- // a GOTO_W or a JSR_W +- insert = 2; +- } else { +- // five additional bytes will be required to +- // replace this IFxxx instruction with +- // IFNOTxxx GOTO_W , where IFNOTxxx +- // is the "opposite" opcode of IFxxx (i.e., +- // IFNE for IFEQ) and where designates +- // the instruction just after the GOTO_W. +- insert = 5; +- } +- resize[u] = true; +- } +- } +- u += 3; +- break; +- case ClassWriter.LABELW_INSN: +- u += 5; +- break; +- case ClassWriter.TABL_INSN: +- if (state == 1) { +- // true number of bytes to be added (or removed) +- // from this instruction = (future number of padding +- // bytes - current number of padding byte) - +- // previously over estimated variation = +- // = ((3 - newOffset%4) - (3 - u%4)) - u%4 +- // = (-newOffset%4 + u%4) - u%4 +- // = -(newOffset & 3) +- newOffset = getNewOffset(allIndexes, allSizes, 0, u); +- insert = -(newOffset & 3); +- } else if (!resize[u]) { +- // over estimation of the number of bytes to be +- // added to this instruction = 3 - current number +- // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 +- insert = u & 3; +- resize[u] = true; +- } +- // skips instruction +- u = u + 4 - (u & 3); +- u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; +- break; +- case ClassWriter.LOOK_INSN: +- if (state == 1) { +- // like TABL_INSN +- newOffset = getNewOffset(allIndexes, allSizes, 0, u); +- insert = -(newOffset & 3); +- } else if (!resize[u]) { +- // like TABL_INSN +- insert = u & 3; +- resize[u] = true; +- } +- // skips instruction +- u = u + 4 - (u & 3); +- u += 8 * readInt(b, u + 4) + 8; +- break; +- case ClassWriter.WIDE_INSN: +- opcode = b[u + 1] & 0xFF; +- if (opcode == Opcodes.IINC) { +- u += 6; +- } else { +- u += 4; +- } +- break; +- case ClassWriter.VAR_INSN: +- case ClassWriter.SBYTE_INSN: +- case ClassWriter.LDC_INSN: +- u += 2; +- break; +- case ClassWriter.SHORT_INSN: +- case ClassWriter.LDCW_INSN: +- case ClassWriter.FIELDORMETH_INSN: +- case ClassWriter.TYPE_INSN: +- case ClassWriter.IINC_INSN: +- u += 3; +- break; +- case ClassWriter.ITFMETH_INSN: +- case ClassWriter.INDYMETH_INSN: +- u += 5; +- break; +- // case ClassWriter.MANA_INSN: +- default: +- u += 4; +- break; +- } +- if (insert != 0) { +- // adds a new (u, insert) entry in the allIndexes and +- // allSizes arrays +- int[] newIndexes = new int[allIndexes.length + 1]; +- int[] newSizes = new int[allSizes.length + 1]; +- System.arraycopy(allIndexes, 0, newIndexes, 0, +- allIndexes.length); +- System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); +- newIndexes[allIndexes.length] = u; +- newSizes[allSizes.length] = insert; +- allIndexes = newIndexes; +- allSizes = newSizes; +- if (insert > 0) { +- state = 3; +- } +- } +- } +- if (state < 3) { +- --state; +- } +- } while (state != 0); +- +- // 2nd step: +- // copies the bytecode of the method into a new bytevector, updates the +- // offsets, and inserts (or removes) bytes as requested. +- +- ByteVector newCode = new ByteVector(code.length); +- +- u = 0; +- while (u < code.length) { +- int opcode = b[u] & 0xFF; +- switch (ClassWriter.TYPE[opcode]) { +- case ClassWriter.NOARG_INSN: +- case ClassWriter.IMPLVAR_INSN: +- newCode.putByte(opcode); +- u += 1; +- break; +- case ClassWriter.LABEL_INSN: +- if (opcode > 201) { +- // changes temporary opcodes 202 to 217 (inclusive), 218 +- // and 219 to IFEQ ... JSR (inclusive), IFNULL and +- // IFNONNULL +- opcode = opcode < 218 ? opcode - 49 : opcode - 20; +- label = u + readUnsignedShort(b, u + 1); +- } else { +- label = u + readShort(b, u + 1); +- } +- newOffset = getNewOffset(allIndexes, allSizes, u, label); +- if (resize[u]) { +- // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx +- // with IFNOTxxx GOTO_W , where IFNOTxxx is +- // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) +- // and where designates the instruction just after +- // the GOTO_W. +- if (opcode == Opcodes.GOTO) { +- newCode.putByte(200); // GOTO_W +- } else if (opcode == Opcodes.JSR) { +- newCode.putByte(201); // JSR_W +- } else { +- newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 +- : opcode ^ 1); +- newCode.putShort(8); // jump offset +- newCode.putByte(200); // GOTO_W +- // newOffset now computed from start of GOTO_W +- newOffset -= 3; +- } +- newCode.putInt(newOffset); +- } else { +- newCode.putByte(opcode); +- newCode.putShort(newOffset); +- } +- u += 3; +- break; +- case ClassWriter.LABELW_INSN: +- label = u + readInt(b, u + 1); +- newOffset = getNewOffset(allIndexes, allSizes, u, label); +- newCode.putByte(opcode); +- newCode.putInt(newOffset); +- u += 5; +- break; +- case ClassWriter.TABL_INSN: +- // skips 0 to 3 padding bytes +- v = u; +- u = u + 4 - (v & 3); +- // reads and copies instruction +- newCode.putByte(Opcodes.TABLESWITCH); +- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); +- label = v + readInt(b, u); +- u += 4; +- newOffset = getNewOffset(allIndexes, allSizes, v, label); +- newCode.putInt(newOffset); +- j = readInt(b, u); +- u += 4; +- newCode.putInt(j); +- j = readInt(b, u) - j + 1; +- u += 4; +- newCode.putInt(readInt(b, u - 4)); +- for (; j > 0; --j) { +- label = v + readInt(b, u); +- u += 4; +- newOffset = getNewOffset(allIndexes, allSizes, v, label); +- newCode.putInt(newOffset); +- } +- break; +- case ClassWriter.LOOK_INSN: +- // skips 0 to 3 padding bytes +- v = u; +- u = u + 4 - (v & 3); +- // reads and copies instruction +- newCode.putByte(Opcodes.LOOKUPSWITCH); +- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); +- label = v + readInt(b, u); +- u += 4; +- newOffset = getNewOffset(allIndexes, allSizes, v, label); +- newCode.putInt(newOffset); +- j = readInt(b, u); +- u += 4; +- newCode.putInt(j); +- for (; j > 0; --j) { +- newCode.putInt(readInt(b, u)); +- u += 4; +- label = v + readInt(b, u); +- u += 4; +- newOffset = getNewOffset(allIndexes, allSizes, v, label); +- newCode.putInt(newOffset); +- } +- break; +- case ClassWriter.WIDE_INSN: +- opcode = b[u + 1] & 0xFF; +- if (opcode == Opcodes.IINC) { +- newCode.putByteArray(b, u, 6); +- u += 6; +- } else { +- newCode.putByteArray(b, u, 4); +- u += 4; +- } +- break; +- case ClassWriter.VAR_INSN: +- case ClassWriter.SBYTE_INSN: +- case ClassWriter.LDC_INSN: +- newCode.putByteArray(b, u, 2); +- u += 2; +- break; +- case ClassWriter.SHORT_INSN: +- case ClassWriter.LDCW_INSN: +- case ClassWriter.FIELDORMETH_INSN: +- case ClassWriter.TYPE_INSN: +- case ClassWriter.IINC_INSN: +- newCode.putByteArray(b, u, 3); +- u += 3; +- break; +- case ClassWriter.ITFMETH_INSN: +- case ClassWriter.INDYMETH_INSN: +- newCode.putByteArray(b, u, 5); +- u += 5; +- break; +- // case MANA_INSN: +- default: +- newCode.putByteArray(b, u, 4); +- u += 4; +- break; +- } +- } +- +- // updates the stack map frame labels +- if (compute == FRAMES) { +- Label l = labels; +- while (l != null) { +- /* +- * Detects the labels that are just after an IF instruction that +- * has been resized with the IFNOT GOTO_W pattern. These labels +- * are now the target of a jump instruction (the IFNOT +- * instruction). Note that we need the original label position +- * here. getNewOffset must therefore never have been called for +- * this label. +- */ +- u = l.position - 3; +- if (u >= 0 && resize[u]) { +- l.status |= Label.TARGET; +- } +- getNewOffset(allIndexes, allSizes, l); +- l = l.successor; +- } +- // Update the offsets in the uninitialized types +- for (i = 0; i < cw.typeTable.length; ++i) { +- Item item = cw.typeTable[i]; +- if (item != null && item.type == ClassWriter.TYPE_UNINIT) { +- item.intVal = getNewOffset(allIndexes, allSizes, 0, +- item.intVal); +- } +- } +- // The stack map frames are not serialized yet, so we don't need +- // to update them. They will be serialized in visitMaxs. +- } else if (frameCount > 0) { +- /* +- * Resizing an existing stack map frame table is really hard. Not +- * only the table must be parsed to update the offets, but new +- * frames may be needed for jump instructions that were inserted by +- * this method. And updating the offsets or inserting frames can +- * change the format of the following frames, in case of packed +- * frames. In practice the whole table must be recomputed. For this +- * the frames are marked as potentially invalid. This will cause the +- * whole class to be reread and rewritten with the COMPUTE_FRAMES +- * option (see the ClassWriter.toByteArray method). This is not very +- * efficient but is much easier and requires much less code than any +- * other method I can think of. +- */ +- cw.invalidFrames = true; +- } +- // updates the exception handler block labels +- Handler h = firstHandler; +- while (h != null) { +- getNewOffset(allIndexes, allSizes, h.start); +- getNewOffset(allIndexes, allSizes, h.end); +- getNewOffset(allIndexes, allSizes, h.handler); +- h = h.next; +- } +- // updates the instructions addresses in the +- // local var and line number tables +- for (i = 0; i < 2; ++i) { +- ByteVector bv = i == 0 ? localVar : localVarType; +- if (bv != null) { +- b = bv.data; +- u = 0; +- while (u < bv.length) { +- label = readUnsignedShort(b, u); +- newOffset = getNewOffset(allIndexes, allSizes, 0, label); +- writeShort(b, u, newOffset); +- label += readUnsignedShort(b, u + 2); +- newOffset = getNewOffset(allIndexes, allSizes, 0, label) +- - newOffset; +- writeShort(b, u + 2, newOffset); +- u += 10; +- } +- } +- } +- if (lineNumber != null) { +- b = lineNumber.data; +- u = 0; +- while (u < lineNumber.length) { +- writeShort( +- b, +- u, +- getNewOffset(allIndexes, allSizes, 0, +- readUnsignedShort(b, u))); +- u += 4; +- } +- } +- // updates the labels of the other attributes +- Attribute attr = cattrs; +- while (attr != null) { +- Label[] labels = attr.getLabels(); +- if (labels != null) { +- for (i = labels.length - 1; i >= 0; --i) { +- getNewOffset(allIndexes, allSizes, labels[i]); +- } +- } +- attr = attr.next; +- } +- +- // replaces old bytecodes with new ones +- code = newCode; +- } +- +- /** +- * Reads an unsigned short value in the given byte array. +- * +- * @param b +- * a byte array. +- * @param index +- * the start index of the value to be read. +- * @return the read value. +- */ +- static int readUnsignedShort(final byte[] b, final int index) { +- return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); +- } +- +- /** +- * Reads a signed short value in the given byte array. +- * +- * @param b +- * a byte array. +- * @param index +- * the start index of the value to be read. +- * @return the read value. +- */ +- static short readShort(final byte[] b, final int index) { +- return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); +- } +- +- /** +- * Reads a signed int value in the given byte array. +- * +- * @param b +- * a byte array. +- * @param index +- * the start index of the value to be read. +- * @return the read value. +- */ +- static int readInt(final byte[] b, final int index) { +- return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) +- | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); +- } +- +- /** +- * Writes a short value in the given byte array. +- * +- * @param b +- * a byte array. +- * @param index +- * where the first byte of the short value must be written. +- * @param s +- * the value to be written in the given byte array. +- */ +- static void writeShort(final byte[] b, final int index, final int s) { +- b[index] = (byte) (s >>> 8); +- b[index + 1] = (byte) s; +- } +- +- /** +- * Computes the future value of a bytecode offset. +- *

+- * Note: it is possible to have several entries for the same instruction in +- * the indexes and sizes: two entries (index=a,size=b) and +- * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). +- * +- * @param indexes +- * current positions of the instructions to be resized. Each +- * instruction must be designated by the index of its last +- * byte, plus one (or, in other words, by the index of the +- * first byte of the next instruction). +- * @param sizes +- * the number of bytes to be added to the above +- * instructions. More precisely, for each i < len, +- * sizes[i] bytes will be added at the end of the +- * instruction designated by indexes[i] or, if +- * sizes[i] is negative, the last | +- * sizes[i]| bytes of the instruction will be removed +- * (the instruction size must not become negative or +- * null). +- * @param begin +- * index of the first byte of the source instruction. +- * @param end +- * index of the first byte of the target instruction. +- * @return the future value of the given bytecode offset. +- */ +- static int getNewOffset(final int[] indexes, final int[] sizes, +- final int begin, final int end) { +- int offset = end - begin; +- for (int i = 0; i < indexes.length; ++i) { +- if (begin < indexes[i] && indexes[i] <= end) { +- // forward jump +- offset += sizes[i]; +- } else if (end < indexes[i] && indexes[i] <= begin) { +- // backward jump +- offset -= sizes[i]; +- } +- } +- return offset; +- } +- +- /** +- * Updates the offset of the given label. +- * +- * @param indexes +- * current positions of the instructions to be resized. Each +- * instruction must be designated by the index of its last +- * byte, plus one (or, in other words, by the index of the +- * first byte of the next instruction). +- * @param sizes +- * the number of bytes to be added to the above +- * instructions. More precisely, for each i < len, +- * sizes[i] bytes will be added at the end of the +- * instruction designated by indexes[i] or, if +- * sizes[i] is negative, the last | +- * sizes[i]| bytes of the instruction will be removed +- * (the instruction size must not become negative or +- * null). +- * @param label +- * the label whose offset must be updated. +- */ +- static void getNewOffset(final int[] indexes, final int[] sizes, +- final Label label) { +- if ((label.status & Label.RESIZED) == 0) { +- label.position = getNewOffset(indexes, sizes, 0, label.position); +- label.status |= Label.RESIZED; +- } +- } +-} +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 d40e1192..00000000 +--- a/src/main/java/org/mvel2/asm/Opcodes.java ++++ /dev/null +@@ -1,362 +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 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. +- * +- * @author Eric Bruneton +- * @author Eugene Kuleshov +- */ +-public interface Opcodes { +- +- // ASM API versions +- +- int ASM4 = 4 << 16 | 0 << 8 | 0; +- int ASM5 = 5 << 16 | 0 << 8 | 0; +- +- // versions +- +- 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 V1_9 = 0 << 16 | 53; +- +- // access flags +- +- 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_VOLATILE = 0x0040; // field +- int ACC_BRIDGE = 0x0040; // method +- 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 +- int ACC_ANNOTATION = 0x2000; // class +- int ACC_ENUM = 0x4000; // class(?) field inner +- int ACC_MANDATED = 0x8000; // parameter +- +- // ASM specific pseudo access flags +- +- int ACC_DEPRECATED = 0x20000; // class, field, method +- +- // types for 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; +- +- // tags for Handle +- +- 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; +- +- // stack map frame types +- +- /** +- * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. +- */ +- int F_NEW = -1; +- +- /** +- * Represents a compressed frame with complete frame data. +- */ +- int F_FULL = 0; +- +- /** +- * Represents 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; +- +- /** +- * Represents 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; +- +- /** +- * Represents a compressed frame with exactly the same locals as the +- * previous frame and with an empty stack. +- */ +- int F_SAME = 3; +- +- /** +- * Represents a compressed frame with exactly the same locals as the +- * previous frame and with a single value on the stack. +- */ +- int F_SAME1 = 4; +- +- Integer TOP = new Integer(0); +- Integer INTEGER = new Integer(1); +- Integer FLOAT = new Integer(2); +- Integer DOUBLE = new Integer(3); +- Integer LONG = new Integer(4); +- Integer NULL = new Integer(5); +- Integer UNINITIALIZED_THIS = new Integer(6); +- +- // opcodes // visit method (- = idem) +- +- 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 LDC_W = 19; // - +- // int LDC2_W = 20; // - +- int ILOAD = 21; // visitVarInsn +- int LLOAD = 22; // - +- int FLOAD = 23; // - +- int DLOAD = 24; // - +- int ALOAD = 25; // - +- // int ILOAD_0 = 26; // - +- // int ILOAD_1 = 27; // - +- // int ILOAD_2 = 28; // - +- // int ILOAD_3 = 29; // - +- // int LLOAD_0 = 30; // - +- // int LLOAD_1 = 31; // - +- // int LLOAD_2 = 32; // - +- // int LLOAD_3 = 33; // - +- // int FLOAD_0 = 34; // - +- // int FLOAD_1 = 35; // - +- // int FLOAD_2 = 36; // - +- // int FLOAD_3 = 37; // - +- // int DLOAD_0 = 38; // - +- // int DLOAD_1 = 39; // - +- // int DLOAD_2 = 40; // - +- // int DLOAD_3 = 41; // - +- // int ALOAD_0 = 42; // - +- // int ALOAD_1 = 43; // - +- // int ALOAD_2 = 44; // - +- // int ALOAD_3 = 45; // - +- 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 ISTORE_0 = 59; // - +- // int ISTORE_1 = 60; // - +- // int ISTORE_2 = 61; // - +- // int ISTORE_3 = 62; // - +- // int LSTORE_0 = 63; // - +- // int LSTORE_1 = 64; // - +- // int LSTORE_2 = 65; // - +- // int LSTORE_3 = 66; // - +- // int FSTORE_0 = 67; // - +- // int FSTORE_1 = 68; // - +- // int FSTORE_2 = 69; // - +- // int FSTORE_3 = 70; // - +- // int DSTORE_0 = 71; // - +- // int DSTORE_1 = 72; // - +- // int DSTORE_2 = 73; // - +- // int DSTORE_3 = 74; // - +- // int ASTORE_0 = 75; // - +- // int ASTORE_1 = 76; // - +- // int ASTORE_2 = 77; // - +- // int ASTORE_3 = 78; // - +- 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 WIDE = 196; // NOT VISITED +- int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn +- int IFNULL = 198; // visitJumpInsn +- int IFNONNULL = 199; // - +- // int GOTO_W = 200; // - +- // int JSR_W = 201; // - +-} +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 cb03565c..00000000 +--- a/src/main/java/org/mvel2/asm/Type.java ++++ /dev/null +@@ -1,896 +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 class Type { +- +- /** +- * The sort of the void type. See {@link #getSort getSort}. +- */ +- public static final int VOID = 0; +- +- /** +- * The sort of the boolean type. See {@link #getSort getSort}. +- */ +- public static final int BOOLEAN = 1; +- +- /** +- * The sort of the char type. See {@link #getSort getSort}. +- */ +- public static final int CHAR = 2; +- +- /** +- * The sort of the byte type. See {@link #getSort getSort}. +- */ +- public static final int BYTE = 3; +- +- /** +- * The sort of the short type. See {@link #getSort getSort}. +- */ +- public static final int SHORT = 4; +- +- /** +- * The sort of the int type. See {@link #getSort getSort}. +- */ +- public static final int INT = 5; +- +- /** +- * The sort of the float type. See {@link #getSort getSort}. +- */ +- public static final int FLOAT = 6; +- +- /** +- * The sort of the long type. See {@link #getSort getSort}. +- */ +- public static final int LONG = 7; +- +- /** +- * The sort of the double type. See {@link #getSort getSort}. +- */ +- public static final int DOUBLE = 8; +- +- /** +- * The sort of array reference types. See {@link #getSort getSort}. +- */ +- public static final int ARRAY = 9; +- +- /** +- * The sort of object reference types. See {@link #getSort getSort}. +- */ +- public static final int OBJECT = 10; +- +- /** +- * The sort of method types. See {@link #getSort getSort}. +- */ +- public static final int METHOD = 11; +- +- /** +- * The void type. +- */ +- public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) +- | (5 << 16) | (0 << 8) | 0, 1); +- +- /** +- * The boolean type. +- */ +- public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) +- | (0 << 16) | (5 << 8) | 1, 1); +- +- /** +- * The char type. +- */ +- public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) +- | (0 << 16) | (6 << 8) | 1, 1); +- +- /** +- * The byte type. +- */ +- public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) +- | (0 << 16) | (5 << 8) | 1, 1); +- +- /** +- * The short type. +- */ +- public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) +- | (0 << 16) | (7 << 8) | 1, 1); +- +- /** +- * The int type. +- */ +- public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) +- | (0 << 16) | (0 << 8) | 1, 1); +- +- /** +- * The float type. +- */ +- public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) +- | (2 << 16) | (2 << 8) | 1, 1); +- +- /** +- * The long type. +- */ +- public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) +- | (1 << 16) | (1 << 8) | 2, 1); +- +- /** +- * The double type. +- */ +- public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) +- | (3 << 16) | (3 << 8) | 2, 1); +- +- // ------------------------------------------------------------------------ +- // Fields +- // ------------------------------------------------------------------------ +- +- /** +- * The sort of this Java type. +- */ +- private final int sort; +- +- /** +- * A buffer containing the internal name of this Java type. This field is +- * only used for reference types. +- */ +- private final char[] buf; +- +- /** +- * The offset of the internal name of this Java type in {@link #buf buf} or, +- * for primitive types, the size, descriptor and getOpcode offsets for this +- * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset +- * for IALOAD or IASTORE, byte 3 the offset for all other instructions). +- */ +- private final int off; +- +- /** +- * The length of the internal name of this Java type. +- */ +- private final int len; +- +- // ------------------------------------------------------------------------ +- // Constructors +- // ------------------------------------------------------------------------ +- +- /** +- * Constructs a reference type. +- * +- * @param sort +- * the sort of the reference type to be constructed. +- * @param buf +- * a buffer containing the descriptor of the previous type. +- * @param off +- * the offset of this descriptor in the previous buffer. +- * @param len +- * the length of this descriptor. +- */ +- private Type(final int sort, final char[] buf, final int off, final int len) { +- this.sort = sort; +- this.buf = buf; +- this.off = off; +- this.len = len; +- } +- +- /** +- * Returns the Java type corresponding to the given type descriptor. +- * +- * @param typeDescriptor +- * a field or method type descriptor. +- * @return the Java type corresponding to the given type descriptor. +- */ +- public static Type getType(final String typeDescriptor) { +- return getType(typeDescriptor.toCharArray(), 0); +- } +- +- /** +- * Returns the Java type corresponding to the given internal name. +- * +- * @param internalName +- * an internal name. +- * @return the Java type corresponding to the given internal name. +- */ +- public static Type getObjectType(final String internalName) { +- char[] buf = internalName.toCharArray(); +- return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); +- } +- +- /** +- * Returns the Java type corresponding to the given method descriptor. +- * Equivalent to Type.getType(methodDescriptor). +- * +- * @param methodDescriptor +- * a method descriptor. +- * @return the Java type corresponding to the given method descriptor. +- */ +- public static Type getMethodType(final String methodDescriptor) { +- return getType(methodDescriptor.toCharArray(), 0); +- } +- +- /** +- * Returns the Java method 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 Java 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 Java type corresponding to the given class. +- * +- * @param c +- * a class. +- * @return the Java type corresponding to the given class. +- */ +- public static Type getType(final Class c) { +- if (c.isPrimitive()) { +- if (c == Integer.TYPE) { +- return INT_TYPE; +- } else if (c == Void.TYPE) { +- return VOID_TYPE; +- } else if (c == Boolean.TYPE) { +- return BOOLEAN_TYPE; +- } else if (c == Byte.TYPE) { +- return BYTE_TYPE; +- } else if (c == Character.TYPE) { +- return CHAR_TYPE; +- } else if (c == Short.TYPE) { +- return SHORT_TYPE; +- } else if (c == Double.TYPE) { +- return DOUBLE_TYPE; +- } else if (c == Float.TYPE) { +- return FLOAT_TYPE; +- } else /* if (c == Long.TYPE) */{ +- return LONG_TYPE; +- } +- } else { +- return getType(getDescriptor(c)); +- } +- } +- +- /** +- * Returns the Java method type corresponding to the given constructor. +- * +- * @param c +- * a {@link Constructor Constructor} object. +- * @return the Java method type corresponding to the given constructor. +- */ +- public static Type getType(final Constructor c) { +- return getType(getConstructorDescriptor(c)); +- } +- +- /** +- * Returns the Java method type corresponding to the given method. +- * +- * @param m +- * a {@link Method Method} object. +- * @return the Java method type corresponding to the given method. +- */ +- public static Type getType(final Method m) { +- return getType(getMethodDescriptor(m)); +- } +- +- /** +- * Returns the Java types corresponding to the argument types of the given +- * method descriptor. +- * +- * @param methodDescriptor +- * a method descriptor. +- * @return the Java types corresponding to the argument types of the given +- * method descriptor. +- */ +- public static Type[] getArgumentTypes(final String methodDescriptor) { +- char[] buf = methodDescriptor.toCharArray(); +- int off = 1; +- int size = 0; +- while (true) { +- char car = buf[off++]; +- if (car == ')') { +- break; +- } else if (car == 'L') { +- while (buf[off++] != ';') { +- } +- ++size; +- } else if (car != '[') { +- ++size; +- } +- } +- Type[] args = new Type[size]; +- off = 1; +- size = 0; +- while (buf[off] != ')') { +- args[size] = getType(buf, off); +- off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); +- size += 1; +- } +- return args; +- } +- +- /** +- * Returns the Java types corresponding to the argument types of the given +- * method. +- * +- * @param method +- * a method. +- * @return the Java types 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 Java type corresponding to the return type of the given +- * method descriptor. +- * +- * @param methodDescriptor +- * a method descriptor. +- * @return the Java type corresponding to the return type of the given +- * method descriptor. +- */ +- public static Type getReturnType(final String methodDescriptor) { +- char[] buf = methodDescriptor.toCharArray(); +- return getType(buf, methodDescriptor.indexOf(')') + 1); +- } +- +- /** +- * Returns the Java type corresponding to the return type of the given +- * method. +- * +- * @param method +- * a method. +- * @return the Java type corresponding to the return type of the given +- * method. +- */ +- public static Type getReturnType(final Method method) { +- return getType(method.getReturnType()); +- } +- +- /** +- * Computes the size of the arguments and of the return value of a method. +- * +- * @param desc +- * the descriptor of a method. +- * @return the size of the arguments of the method (plus one for the +- * implicit this argument), argSize, and the size of its return +- * value, retSize, packed into a single int i = +- * (argSize << 2) | retSize (argSize is therefore equal to +- * i >> 2, and retSize to i & 0x03). +- */ +- public static int getArgumentsAndReturnSizes(final String desc) { +- int n = 1; +- int c = 1; +- while (true) { +- char car = desc.charAt(c++); +- if (car == ')') { +- car = desc.charAt(c); +- return n << 2 +- | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); +- } else if (car == 'L') { +- while (desc.charAt(c++) != ';') { +- } +- n += 1; +- } else if (car == '[') { +- while ((car = desc.charAt(c)) == '[') { +- ++c; +- } +- if (car == 'D' || car == 'J') { +- n -= 1; +- } +- } else if (car == 'D' || car == 'J') { +- n += 2; +- } else { +- n += 1; +- } +- } +- } +- +- /** +- * Returns the Java type corresponding to the given type descriptor. For +- * method descriptors, buf is supposed to contain nothing more than the +- * descriptor itself. +- * +- * @param buf +- * a buffer containing a type descriptor. +- * @param off +- * the offset of this descriptor in the previous buffer. +- * @return the Java type corresponding to the given type descriptor. +- */ +- private static Type getType(final char[] buf, final int off) { +- int len; +- switch (buf[off]) { +- 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 '[': +- len = 1; +- while (buf[off + len] == '[') { +- ++len; +- } +- if (buf[off + len] == 'L') { +- ++len; +- while (buf[off + len] != ';') { +- ++len; +- } +- } +- return new Type(ARRAY, buf, off, len + 1); +- case 'L': +- len = 1; +- while (buf[off + len] != ';') { +- ++len; +- } +- return new Type(OBJECT, buf, off + 1, len - 1); +- // case '(': +- default: +- return new Type(METHOD, buf, off, buf.length - off); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Accessors +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the sort of this Java type. +- * +- * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, +- * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, +- * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, +- * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD +- * METHOD}. +- */ +- public int getSort() { +- return 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 i = 1; +- while (buf[off + i] == '[') { +- ++i; +- } +- return i; +- } +- +- /** +- * 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() { +- return getType(buf, off + getDimensions()); +- } +- +- /** +- * 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: +- StringBuffer b = new StringBuffer(getElementType().getClassName()); +- for (int i = getDimensions(); i > 0; --i) { +- b.append("[]"); +- } +- return b.toString(); +- case OBJECT: +- return new String(buf, off, len).replace('/', '.'); +- default: +- return null; +- } +- } +- +- /** +- * 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 new String(buf, off, len); +- } +- +- /** +- * 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 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 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 (plus one for the implicit this +- * argument), argSize, and the size of the return value, retSize, +- * packed into a single +- * int i = (argSize << 2) | retSize +- * (argSize is therefore equal to i >> 2, +- * and retSize to i & 0x03). +- */ +- public int getArgumentsAndReturnSizes() { +- return getArgumentsAndReturnSizes(getDescriptor()); +- } +- +- // ------------------------------------------------------------------------ +- // Conversion to type descriptors +- // ------------------------------------------------------------------------ +- +- /** +- * Returns the descriptor corresponding to this Java type. +- * +- * @return the descriptor corresponding to this Java type. +- */ +- public String getDescriptor() { +- StringBuffer buf = new StringBuffer(); +- getDescriptor(buf); +- return buf.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) { +- StringBuffer buf = new StringBuffer(); +- buf.append('('); +- for (int i = 0; i < argumentTypes.length; ++i) { +- argumentTypes[i].getDescriptor(buf); +- } +- buf.append(')'); +- returnType.getDescriptor(buf); +- return buf.toString(); +- } +- +- /** +- * Appends the descriptor corresponding to this Java type to the given +- * string buffer. +- * +- * @param buf +- * the string buffer to which the descriptor must be appended. +- */ +- private void getDescriptor(final StringBuffer buf) { +- if (this.buf == null) { +- // descriptor is in byte 3 of 'off' for primitive types (buf == +- // null) +- buf.append((char) ((off & 0xFF000000) >>> 24)); +- } else if (sort == OBJECT) { +- buf.append('L'); +- buf.append(this.buf, off, len); +- buf.append(';'); +- } else { // sort == ARRAY || sort == METHOD +- buf.append(this.buf, off, len); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Direct conversion from classes to type descriptors, +- // without intermediate Type objects +- // ------------------------------------------------------------------------ +- +- /** +- * 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 c +- * an object or array class. +- * @return the internal name of the given class. +- */ +- public static String getInternalName(final Class c) { +- return c.getName().replace('.', '/'); +- } +- +- /** +- * Returns the descriptor corresponding to the given Java type. +- * +- * @param c +- * an object class, a primitive class or an array class. +- * @return the descriptor corresponding to the given class. +- */ +- public static String getDescriptor(final Class c) { +- StringBuffer buf = new StringBuffer(); +- getDescriptor(buf, c); +- return buf.toString(); +- } +- +- /** +- * Returns the descriptor corresponding to the given constructor. +- * +- * @param c +- * a {@link Constructor Constructor} object. +- * @return the descriptor of the given constructor. +- */ +- public static String getConstructorDescriptor(final Constructor c) { +- Class[] parameters = c.getParameterTypes(); +- StringBuffer buf = new StringBuffer(); +- buf.append('('); +- for (int i = 0; i < parameters.length; ++i) { +- getDescriptor(buf, parameters[i]); +- } +- return buf.append(")V").toString(); +- } +- +- /** +- * Returns the descriptor corresponding to the given method. +- * +- * @param m +- * a {@link Method Method} object. +- * @return the descriptor of the given method. +- */ +- public static String getMethodDescriptor(final Method m) { +- Class[] parameters = m.getParameterTypes(); +- StringBuffer buf = new StringBuffer(); +- buf.append('('); +- for (int i = 0; i < parameters.length; ++i) { +- getDescriptor(buf, parameters[i]); +- } +- buf.append(')'); +- getDescriptor(buf, m.getReturnType()); +- return buf.toString(); +- } +- +- /** +- * Appends the descriptor of the given class to the given string buffer. +- * +- * @param buf +- * the string buffer to which the descriptor must be appended. +- * @param c +- * the class whose descriptor must be computed. +- */ +- private static void getDescriptor(final StringBuffer buf, final Class c) { +- Class d = c; +- while (true) { +- if (d.isPrimitive()) { +- char car; +- if (d == Integer.TYPE) { +- car = 'I'; +- } else if (d == Void.TYPE) { +- car = 'V'; +- } else if (d == Boolean.TYPE) { +- car = 'Z'; +- } else if (d == Byte.TYPE) { +- car = 'B'; +- } else if (d == Character.TYPE) { +- car = 'C'; +- } else if (d == Short.TYPE) { +- car = 'S'; +- } else if (d == Double.TYPE) { +- car = 'D'; +- } else if (d == Float.TYPE) { +- car = 'F'; +- } else /* if (d == Long.TYPE) */{ +- car = 'J'; +- } +- buf.append(car); +- return; +- } else if (d.isArray()) { +- buf.append('['); +- d = d.getComponentType(); +- } else { +- buf.append('L'); +- String name = d.getName(); +- int len = name.length(); +- for (int i = 0; i < len; ++i) { +- char car = name.charAt(i); +- buf.append(car == '.' ? '/' : car); +- } +- buf.append(';'); +- return; +- } +- } +- } +- +- // ------------------------------------------------------------------------ +- // Corresponding size and opcodes +- // ------------------------------------------------------------------------ +- +- /** +- * 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 long and +- * double, 0 for void and 1 otherwise. +- */ +- public int getSize() { +- // the size is in byte 0 of 'off' for primitive types (buf == null) +- return buf == null ? (off & 0xFF) : 1; +- } +- +- /** +- * Returns a JVM instruction opcode adapted to this Java 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 Java type. For example, if this type is float and +- * opcode is IRETURN, this method returns FRETURN. +- */ +- public int getOpcode(final int opcode) { +- if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { +- // the offset for IALOAD or IASTORE is in byte 1 of 'off' for +- // primitive types (buf == null) +- return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); +- } else { +- // the offset for other instructions is in byte 2 of 'off' for +- // primitive types (buf == null) +- return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); +- } +- } +- +- // ------------------------------------------------------------------------ +- // Equals, hashCode and toString +- // ------------------------------------------------------------------------ +- +- /** +- * Tests if the given object is equal to this type. +- * +- * @param o +- * the object to be compared to this type. +- * @return true if the given object is equal to this type. +- */ +- @Override +- public boolean equals(final Object o) { +- if (this == o) { +- return true; +- } +- if (!(o instanceof Type)) { +- return false; +- } +- Type t = (Type) o; +- if (sort != t.sort) { +- return false; +- } +- if (sort >= ARRAY) { +- if (len != t.len) { +- return false; +- } +- for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { +- if (buf[i] != t.buf[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 hc = 13 * sort; +- if (sort >= ARRAY) { +- for (int i = off, end = i + len; i < end; i++) { +- hc = 17 * (hc + buf[i]); +- } +- } +- return hc; +- } +- +- /** +- * 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 b8d7d10c..00000000 +--- a/src/main/java/org/mvel2/asm/TypePath.java ++++ /dev/null +@@ -1,193 +0,0 @@ +-/*** +- * ASM: a very small and fast Java bytecode manipulation framework +- * Copyright (c) 2000-2013 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 class TypePath { +- +- /** +- * A type path step that steps into the element type of an array type. See +- * {@link #getStep getStep}. +- */ +- public final static int ARRAY_ELEMENT = 0; +- +- /** +- * A type path step that steps into the nested type of a class type. See +- * {@link #getStep getStep}. +- */ +- public final static int INNER_TYPE = 1; +- +- /** +- * A type path step that steps into the bound of a wildcard type. See +- * {@link #getStep getStep}. +- */ +- public final static int WILDCARD_BOUND = 2; +- +- /** +- * A type path step that steps into a type argument of a generic type. See +- * {@link #getStep getStep}. +- */ +- public final static int TYPE_ARGUMENT = 3; +- +- /** +- * The byte array where the path is stored, in Java class file format. +- */ +- byte[] b; +- +- /** +- * The offset of the first byte of the type path in 'b'. +- */ +- int offset; +- +- /** +- * Creates a new type path. +- * +- * @param b +- * the byte array containing the type path in Java class file +- * format. +- * @param offset +- * the offset of the first byte of the type path in 'b'. +- */ +- TypePath(byte[] b, int offset) { +- this.b = b; +- this.offset = offset; +- } +- +- /** +- * Returns the length of this path. +- * +- * @return the length of this path. +- */ +- public int getLength() { +- return b[offset]; +- } +- +- /** +- * Returns the value of the given step of this path. +- * +- * @param index +- * an index between 0 and {@link #getLength()}, exclusive. +- * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE +- * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or +- * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. +- */ +- public int getStep(int index) { +- return b[offset + 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 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(int index) { +- return b[offset + 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 null or empty. +- * @return the corresponding TypePath object, or null if the path is empty. +- */ +- public static TypePath fromString(final String typePath) { +- if (typePath == null || typePath.length() == 0) { +- return null; +- } +- int n = typePath.length(); +- ByteVector out = new ByteVector(n); +- out.putByte(0); +- for (int i = 0; i < n;) { +- char c = typePath.charAt(i++); +- if (c == '[') { +- out.put11(ARRAY_ELEMENT, 0); +- } else if (c == '.') { +- out.put11(INNER_TYPE, 0); +- } else if (c == '*') { +- out.put11(WILDCARD_BOUND, 0); +- } else if (c >= '0' && c <= '9') { +- int typeArg = c - '0'; +- while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { +- typeArg = typeArg * 10 + c - '0'; +- i += 1; +- } +- out.put11(TYPE_ARGUMENT, typeArg); +- } +- } +- out.data[0] = (byte) (out.length / 2); +- return new TypePath(out.data, 0); +- } +- +- /** +- * Returns a string representation of this type path. {@link #ARRAY_ELEMENT +- * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE +- * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps +- * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type +- * argument index in decimal form. +- */ +- @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)); +- break; +- default: +- result.append('_'); +- } +- } +- return result.toString(); +- } +-} +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 7ac917b4..00000000 +--- a/src/main/java/org/mvel2/asm/TypeReference.java ++++ /dev/null +@@ -1,452 +0,0 @@ +-/*** +- * ASM: a very small and fast Java bytecode manipulation framework +- * Copyright (c) 2000-2013 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 getSort}. +- */ +- public final static int CLASS_TYPE_PARAMETER = 0x00; +- +- /** +- * The sort of type references that target a type parameter of a generic +- * method. See {@link #getSort getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static int CLASS_EXTENDS = 0x10; +- +- /** +- * The sort of type references that target a bound of a type parameter of a +- * generic class. See {@link #getSort getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; +- +- /** +- * The sort of type references that target the type of a field. See +- * {@link #getSort getSort}. +- */ +- public final static int FIELD = 0x13; +- +- /** +- * The sort of type references that target the return type of a method. See +- * {@link #getSort getSort}. +- */ +- public final static int METHOD_RETURN = 0x14; +- +- /** +- * The sort of type references that target the receiver type of a method. +- * See {@link #getSort getSort}. +- */ +- public final static int METHOD_RECEIVER = 0x15; +- +- /** +- * The sort of type references that target the type of a formal parameter of +- * a method. See {@link #getSort getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static int THROWS = 0x17; +- +- /** +- * The sort of type references that target the type of a local variable in a +- * method. See {@link #getSort getSort}. +- */ +- public final static int LOCAL_VARIABLE = 0x40; +- +- /** +- * The sort of type references that target the type of a resource variable +- * in a method. See {@link #getSort getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static int EXCEPTION_PARAMETER = 0x42; +- +- /** +- * The sort of type references that target the type declared in an +- * 'instanceof' instruction. See {@link #getSort getSort}. +- */ +- public final static int INSTANCEOF = 0x43; +- +- /** +- * The sort of type references that target the type of the object created by +- * a 'new' instruction. See {@link #getSort getSort}. +- */ +- public final static int NEW = 0x44; +- +- /** +- * The sort of type references that target the receiver type of a +- * constructor reference. See {@link #getSort getSort}. +- */ +- public final static int CONSTRUCTOR_REFERENCE = 0x45; +- +- /** +- * The sort of type references that target the receiver type of a method +- * reference. See {@link #getSort getSort}. +- */ +- public final static int METHOD_REFERENCE = 0x46; +- +- /** +- * The sort of type references that target the type declared in an explicit +- * or implicit cast instruction. See {@link #getSort getSort}. +- */ +- public final static int CAST = 0x47; +- +- /** +- * The sort of type references that target a type parameter of a generic +- * constructor in a constructor call. See {@link #getSort getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static 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 getSort}. +- */ +- public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; +- +- /** +- * The type reference value in Java class file format. +- */ +- private int value; +- +- /** +- * Creates a new TypeReference. +- * +- * @param typeRef +- * the int encoded value of the type reference, as received in a +- * visit method related to type annotations, like +- * visitTypeAnnotation. +- */ +- public TypeReference(int typeRef) { +- this.value = typeRef; +- } +- +- /** +- * Returns a type reference of the given sort. +- * +- * @param sort +- * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, +- * {@link #METHOD_RECEIVER METHOD_RECEIVER}, +- * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, +- * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, +- * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, +- * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or +- * {@link #METHOD_REFERENCE METHOD_REFERENCE}. +- * @return a type reference of the given sort. +- */ +- public static TypeReference newTypeReference(int sort) { +- return new TypeReference(sort << 24); +- } +- +- /** +- * Returns a reference to a type parameter of a generic class or method. +- * +- * @param sort +- * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or +- * {@link #METHOD_TYPE_PARAMETER 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(int sort, +- 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 +- * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or +- * {@link #METHOD_TYPE_PARAMETER 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(int sort, +- int paramIndex, 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(int itfIndex) { +- itfIndex &= 0xFFFF; +- return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 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(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(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(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 +- * {@link #CAST CAST}, +- * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, +- * {@link #METHOD_INVOCATION_TYPE_ARGUMENT +- * METHOD_INVOCATION_TYPE_ARGUMENT}, +- * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or +- * {@link #METHOD_REFERENCE_TYPE_ARGUMENT +- * 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(int sort, int argIndex) { +- return new TypeReference((sort << 24) | argIndex); +- } +- +- /** +- * Returns the sort of this type reference. +- * +- * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, +- * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, +- * {@link #CLASS_EXTENDS CLASS_EXTENDS}, +- * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, +- * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, +- * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, +- * {@link #METHOD_RECEIVER METHOD_RECEIVER}, +- * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, +- * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, +- * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, +- * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, +- * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, +- * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, +- * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, +- * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, +- * {@link #METHOD_INVOCATION_TYPE_ARGUMENT +- * METHOD_INVOCATION_TYPE_ARGUMENT}, +- * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or +- * {@link #METHOD_REFERENCE_TYPE_ARGUMENT +- * METHOD_REFERENCE_TYPE_ARGUMENT}. +- */ +- public int getSort() { +- return value >>> 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 CLASS_TYPE_PARAMETER}, +- * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, +- * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or +- * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. +- * +- * @return a type parameter index. +- */ +- public int getTypeParameterIndex() { +- return (value & 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 CLASS_TYPE_PARAMETER_BOUND} or +- * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. +- * +- * @return a type parameter bound index. +- */ +- public int getTypeParameterBoundIndex() { +- return (value & 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 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) ((value & 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 METHOD_FORMAL_PARAMETER}. +- * +- * @return a formal parameter index. +- */ +- public int getFormalParameterIndex() { +- return (value & 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 THROWS}. +- * +- * @return the index of an exception in the 'throws' clause of a method. +- */ +- public int getExceptionIndex() { +- return (value & 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 EXCEPTION_PARAMETER} . +- * +- * @return the index of an exception in the 'throws' clause of a method. +- */ +- public int getTryCatchBlockIndex() { +- return (value & 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 CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT +- * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, +- * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, +- * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT +- * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or +- * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. +- * +- * @return a type parameter index. +- */ +- public int getTypeArgumentIndex() { +- return value & 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 value; +- } +-} +diff --git a/src/main/java/org/mvel2/asm/attrs/package.html b/src/main/java/org/mvel2/asm/attrs/package.html +deleted file mode 100644 +index 7aae27b9..00000000 +--- a/src/main/java/org/mvel2/asm/attrs/package.html ++++ /dev/null +@@ -1,49 +0,0 @@ +- +- +- +-Provides an implementation for optional class, field and method attributes. +- +-

+- +- By default ASM strips optional attributes, in order to keep them in +- the bytecode that is being readed you should pass an array of required attribute +- instances to {@link org.mvel2.asm.ClassReader#accept(org.mvel2.asm.ClassVisitor, +- org.mvel2.asm.Attribute[], boolean) ClassReader.accept()} method. +- In order to add custom attributes to the manually constructed bytecode concrete +- subclasses of the {@link org.mvel2.asm.Attribute Attribute} can be passed to +- the visitAttribute methods of the +- {@link org.mvel2.asm.ClassVisitor ClassVisitor}, +- {@link org.mvel2.asm.FieldVisitor FieldVisitor} and +- {@link org.mvel2.asm.MethodVisitor MethodVisitor} interfaces. +- +- @since ASM 1.4.1 +- +- +diff --git a/src/main/java/org/mvel2/asm/commons/AdviceAdapter.java b/src/main/java/org/mvel2/asm/commons/AdviceAdapter.java +deleted file mode 100644 +index a434778d..00000000 +--- a/src/main/java/org/mvel2/asm/commons/AdviceAdapter.java ++++ /dev/null +@@ -1,646 +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.commons; +- +-import java.util.ArrayList; +-import java.util.HashMap; +-import java.util.List; +-import java.util.Map; +- +-import org.mvel2.asm.Handle; +-import org.mvel2.asm.Label; +-import org.mvel2.asm.MethodVisitor; +-import org.mvel2.asm.Opcodes; +-import org.mvel2.asm.Type; +- +-/** +- * A {@link org.mvel2.asm.MethodVisitor} to insert before, after and around +- * advices in methods and constructors. +- *

+- * The behavior for constructors is like this: +- *

    +- * +- *
  1. as long as the INVOKESPECIAL for the object initialization has not been +- * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. +- * +- *
  3. when this one is reached, it is only added in the ctor code visitor and a +- * JP invoke is added
  4. +- * +- *
  5. after that, only the other code visitor receives the instructions
  6. +- * +- *
+- * +- * @author Eugene Kuleshov +- * @author Eric Bruneton +- */ +-public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { +- +- private static final Object THIS = new Object(); +- +- private static final Object OTHER = new Object(); +- +- protected int methodAccess; +- +- protected String methodDesc; +- +- private boolean constructor; +- +- private boolean superInitialized; +- +- private List stackFrame; +- +- private Map> branches; +- +- /** +- * Creates a new {@link AdviceAdapter}. +- * +- * @param api +- * the ASM API version implemented by this visitor. Must be one +- * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. +- * @param mv +- * the method visitor to which this adapter delegates calls. +- * @param access +- * the method's access flags (see {@link Opcodes}). +- * @param name +- * the method's name. +- * @param desc +- * the method's descriptor (see {@link Type Type}). +- */ +- protected AdviceAdapter(final int api, final MethodVisitor mv, +- final int access, final String name, final String desc) { +- super(api, mv, access, name, desc); +- methodAccess = access; +- methodDesc = desc; +- constructor = "".equals(name); +- } +- +- @Override +- public void visitCode() { +- mv.visitCode(); +- if (constructor) { +- stackFrame = new ArrayList(); +- branches = new HashMap>(); +- } else { +- superInitialized = true; +- onMethodEnter(); +- } +- } +- +- @Override +- public void visitLabel(final Label label) { +- mv.visitLabel(label); +- if (constructor && branches != null) { +- List frame = branches.get(label); +- if (frame != null) { +- stackFrame = frame; +- branches.remove(label); +- } +- } +- } +- +- @Override +- public void visitInsn(final int opcode) { +- if (constructor) { +- int s; +- switch (opcode) { +- case RETURN: // empty stack +- onMethodExit(opcode); +- break; +- case IRETURN: // 1 before n/a after +- case FRETURN: // 1 before n/a after +- case ARETURN: // 1 before n/a after +- case ATHROW: // 1 before n/a after +- popValue(); +- onMethodExit(opcode); +- break; +- case LRETURN: // 2 before n/a after +- case DRETURN: // 2 before n/a after +- popValue(); +- popValue(); +- onMethodExit(opcode); +- break; +- case NOP: +- case LALOAD: // remove 2 add 2 +- case DALOAD: // remove 2 add 2 +- case LNEG: +- case DNEG: +- case FNEG: +- case INEG: +- case L2D: +- case D2L: +- case F2I: +- case I2B: +- case I2C: +- case I2S: +- case I2F: +- case ARRAYLENGTH: +- break; +- case ACONST_NULL: +- case ICONST_M1: +- case ICONST_0: +- case ICONST_1: +- case ICONST_2: +- case ICONST_3: +- case ICONST_4: +- case ICONST_5: +- case FCONST_0: +- case FCONST_1: +- case FCONST_2: +- case F2L: // 1 before 2 after +- case F2D: +- case I2L: +- case I2D: +- pushValue(OTHER); +- break; +- case LCONST_0: +- case LCONST_1: +- case DCONST_0: +- case DCONST_1: +- pushValue(OTHER); +- pushValue(OTHER); +- break; +- case IALOAD: // remove 2 add 1 +- case FALOAD: // remove 2 add 1 +- case AALOAD: // remove 2 add 1 +- case BALOAD: // remove 2 add 1 +- case CALOAD: // remove 2 add 1 +- case SALOAD: // remove 2 add 1 +- case POP: +- case IADD: +- case FADD: +- case ISUB: +- case LSHL: // 3 before 2 after +- case LSHR: // 3 before 2 after +- case LUSHR: // 3 before 2 after +- case L2I: // 2 before 1 after +- case L2F: // 2 before 1 after +- case D2I: // 2 before 1 after +- case D2F: // 2 before 1 after +- case FSUB: +- case FMUL: +- case FDIV: +- case FREM: +- case FCMPL: // 2 before 1 after +- case FCMPG: // 2 before 1 after +- case IMUL: +- case IDIV: +- case IREM: +- case ISHL: +- case ISHR: +- case IUSHR: +- case IAND: +- case IOR: +- case IXOR: +- case MONITORENTER: +- case MONITOREXIT: +- popValue(); +- break; +- case POP2: +- case LSUB: +- case LMUL: +- case LDIV: +- case LREM: +- case LADD: +- case LAND: +- case LOR: +- case LXOR: +- case DADD: +- case DMUL: +- case DSUB: +- case DDIV: +- case DREM: +- popValue(); +- popValue(); +- break; +- case IASTORE: +- case FASTORE: +- case AASTORE: +- case BASTORE: +- case CASTORE: +- case SASTORE: +- case LCMP: // 4 before 1 after +- case DCMPL: +- case DCMPG: +- popValue(); +- popValue(); +- popValue(); +- break; +- case LASTORE: +- case DASTORE: +- popValue(); +- popValue(); +- popValue(); +- popValue(); +- break; +- case DUP: +- pushValue(peekValue()); +- break; +- case DUP_X1: +- s = stackFrame.size(); +- stackFrame.add(s - 2, stackFrame.get(s - 1)); +- break; +- case DUP_X2: +- s = stackFrame.size(); +- stackFrame.add(s - 3, stackFrame.get(s - 1)); +- break; +- case DUP2: +- s = stackFrame.size(); +- stackFrame.add(s - 2, stackFrame.get(s - 1)); +- stackFrame.add(s - 2, stackFrame.get(s - 1)); +- break; +- case DUP2_X1: +- s = stackFrame.size(); +- stackFrame.add(s - 3, stackFrame.get(s - 1)); +- stackFrame.add(s - 3, stackFrame.get(s - 1)); +- break; +- case DUP2_X2: +- s = stackFrame.size(); +- stackFrame.add(s - 4, stackFrame.get(s - 1)); +- stackFrame.add(s - 4, stackFrame.get(s - 1)); +- break; +- case SWAP: +- s = stackFrame.size(); +- stackFrame.add(s - 2, stackFrame.get(s - 1)); +- stackFrame.remove(s); +- break; +- } +- } else { +- switch (opcode) { +- case RETURN: +- case IRETURN: +- case FRETURN: +- case ARETURN: +- case LRETURN: +- case DRETURN: +- case ATHROW: +- onMethodExit(opcode); +- break; +- } +- } +- mv.visitInsn(opcode); +- } +- +- @Override +- public void visitVarInsn(final int opcode, final int var) { +- super.visitVarInsn(opcode, var); +- if (constructor) { +- switch (opcode) { +- case ILOAD: +- case FLOAD: +- pushValue(OTHER); +- break; +- case LLOAD: +- case DLOAD: +- pushValue(OTHER); +- pushValue(OTHER); +- break; +- case ALOAD: +- pushValue(var == 0 ? THIS : OTHER); +- break; +- case ASTORE: +- case ISTORE: +- case FSTORE: +- popValue(); +- break; +- case LSTORE: +- case DSTORE: +- popValue(); +- popValue(); +- break; +- } +- } +- } +- +- @Override +- public void visitFieldInsn(final int opcode, final String owner, +- final String name, final String desc) { +- mv.visitFieldInsn(opcode, owner, name, desc); +- if (constructor) { +- char c = desc.charAt(0); +- boolean longOrDouble = c == 'J' || c == 'D'; +- switch (opcode) { +- case GETSTATIC: +- pushValue(OTHER); +- if (longOrDouble) { +- pushValue(OTHER); +- } +- break; +- case PUTSTATIC: +- popValue(); +- if (longOrDouble) { +- popValue(); +- } +- break; +- case PUTFIELD: +- popValue(); +- if (longOrDouble) { +- popValue(); +- popValue(); +- } +- break; +- // case GETFIELD: +- default: +- if (longOrDouble) { +- pushValue(OTHER); +- } +- } +- } +- } +- +- @Override +- public void visitIntInsn(final int opcode, final int operand) { +- mv.visitIntInsn(opcode, operand); +- if (constructor && opcode != NEWARRAY) { +- pushValue(OTHER); +- } +- } +- +- @Override +- public void visitLdcInsn(final Object cst) { +- mv.visitLdcInsn(cst); +- if (constructor) { +- pushValue(OTHER); +- if (cst instanceof Double || cst instanceof Long) { +- pushValue(OTHER); +- } +- } +- } +- +- @Override +- public void visitMultiANewArrayInsn(final String desc, final int dims) { +- mv.visitMultiANewArrayInsn(desc, dims); +- if (constructor) { +- for (int i = 0; i < dims; i++) { +- popValue(); +- } +- pushValue(OTHER); +- } +- } +- +- @Override +- public void visitTypeInsn(final int opcode, final String type) { +- mv.visitTypeInsn(opcode, type); +- // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack +- if (constructor && opcode == NEW) { +- pushValue(OTHER); +- } +- } +- +- @Deprecated +- @Override +- public void visitMethodInsn(final int opcode, final String owner, +- final String name, final String desc) { +- if (api >= Opcodes.ASM5) { +- super.visitMethodInsn(opcode, owner, name, desc); +- return; +- } +- doVisitMethodInsn(opcode, owner, name, desc, +- opcode == Opcodes.INVOKEINTERFACE); +- } +- +- @Override +- public void visitMethodInsn(final int opcode, final String owner, +- final String name, final String desc, final boolean itf) { +- if (api < Opcodes.ASM5) { +- super.visitMethodInsn(opcode, owner, name, desc, itf); +- return; +- } +- doVisitMethodInsn(opcode, owner, name, desc, itf); +- } +- +- private void doVisitMethodInsn(int opcode, final String owner, +- final String name, final String desc, final boolean itf) { +- mv.visitMethodInsn(opcode, owner, name, desc, itf); +- if (constructor) { +- Type[] types = Type.getArgumentTypes(desc); +- for (int i = 0; i < types.length; i++) { +- popValue(); +- if (types[i].getSize() == 2) { +- popValue(); +- } +- } +- switch (opcode) { +- // case INVOKESTATIC: +- // break; +- case INVOKEINTERFACE: +- case INVOKEVIRTUAL: +- popValue(); // objectref +- break; +- case INVOKESPECIAL: +- Object type = popValue(); // objectref +- if (type == THIS && !superInitialized) { +- onMethodEnter(); +- superInitialized = true; +- // once super has been initialized it is no longer +- // necessary to keep track of stack state +- constructor = false; +- } +- break; +- } +- +- Type returnType = Type.getReturnType(desc); +- if (returnType != Type.VOID_TYPE) { +- pushValue(OTHER); +- if (returnType.getSize() == 2) { +- pushValue(OTHER); +- } +- } +- } +- } +- +- @Override +- public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, +- Object... bsmArgs) { +- mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); +- if (constructor) { +- Type[] types = Type.getArgumentTypes(desc); +- for (int i = 0; i < types.length; i++) { +- popValue(); +- if (types[i].getSize() == 2) { +- popValue(); +- } +- } +- +- Type returnType = Type.getReturnType(desc); +- if (returnType != Type.VOID_TYPE) { +- pushValue(OTHER); +- if (returnType.getSize() == 2) { +- pushValue(OTHER); +- } +- } +- } +- } +- +- @Override +- public void visitJumpInsn(final int opcode, final Label label) { +- mv.visitJumpInsn(opcode, label); +- if (constructor) { +- switch (opcode) { +- case IFEQ: +- case IFNE: +- case IFLT: +- case IFGE: +- case IFGT: +- case IFLE: +- case IFNULL: +- case IFNONNULL: +- popValue(); +- break; +- case IF_ICMPEQ: +- case IF_ICMPNE: +- case IF_ICMPLT: +- case IF_ICMPGE: +- case IF_ICMPGT: +- case IF_ICMPLE: +- case IF_ACMPEQ: +- case IF_ACMPNE: +- popValue(); +- popValue(); +- break; +- case JSR: +- pushValue(OTHER); +- break; +- } +- addBranch(label); +- } +- } +- +- @Override +- public void visitLookupSwitchInsn(final Label dflt, final int[] keys, +- final Label[] labels) { +- mv.visitLookupSwitchInsn(dflt, keys, labels); +- if (constructor) { +- popValue(); +- addBranches(dflt, labels); +- } +- } +- +- @Override +- public void visitTableSwitchInsn(final int min, final int max, +- final Label dflt, final Label... labels) { +- mv.visitTableSwitchInsn(min, max, dflt, labels); +- if (constructor) { +- popValue(); +- addBranches(dflt, labels); +- } +- } +- +- @Override +- public void visitTryCatchBlock(Label start, Label end, Label handler, +- String type) { +- super.visitTryCatchBlock(start, end, handler, type); +- if (constructor && !branches.containsKey(handler)) { +- List stackFrame = new ArrayList(); +- stackFrame.add(OTHER); +- branches.put(handler, stackFrame); +- } +- } +- +- private void addBranches(final Label dflt, final Label[] labels) { +- addBranch(dflt); +- for (int i = 0; i < labels.length; i++) { +- addBranch(labels[i]); +- } +- } +- +- private void addBranch(final Label label) { +- if (branches.containsKey(label)) { +- return; +- } +- branches.put(label, new ArrayList(stackFrame)); +- } +- +- private Object popValue() { +- return stackFrame.remove(stackFrame.size() - 1); +- } +- +- private Object peekValue() { +- return stackFrame.get(stackFrame.size() - 1); +- } +- +- private void pushValue(final Object o) { +- stackFrame.add(o); +- } +- +- /** +- * Called at the beginning of the method or after super class class call in +- * the constructor.
+- *
+- * +- * Custom code can use or change all the local variables, but should not +- * change state of the stack. +- */ +- protected void onMethodEnter() { +- } +- +- /** +- * Called before explicit exit from the method using either return or throw. +- * Top element on the stack contains the return value or exception instance. +- * For example: +- * +- *
+-     *   public void onMethodExit(int opcode) {
+-     *     if(opcode==RETURN) {
+-     *         visitInsn(ACONST_NULL);
+-     *     } else if(opcode==ARETURN || opcode==ATHROW) {
+-     *         dup();
+-     *     } else {
+-     *         if(opcode==LRETURN || opcode==DRETURN) {
+-     *             dup2();
+-     *         } else {
+-     *             dup();
+-     *         }
+-     *         box(Type.getReturnType(this.methodDesc));
+-     *     }
+-     *     visitIntInsn(SIPUSH, opcode);
+-     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+-     *   }
+-     * 
+-     *   // an actual call back method
+-     *   public static void onExit(Object param, int opcode) {
+-     *     ...
+-     * 
+- * +- *
+- *
+- * +- * Custom code can use or change all the local variables, but should not +- * change state of the stack. +- * +- * @param opcode +- * one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN +- * or ATHROW +- * +- */ +- protected void onMethodExit(int opcode) { +- } +- +- // TODO onException, onMethodCall +-} +diff --git a/src/main/java/org/mvel2/asm/commons/AnalyzerAdapter.java b/src/main/java/org/mvel2/asm/commons/AnalyzerAdapter.java +deleted file mode 100644 +index 7afc2f6b..00000000 +--- a/src/main/java/org/mvel2/asm/commons/AnalyzerAdapter.java ++++ /dev/null +@@ -1,947 +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.commons; +- +-import java.util.ArrayList; +-import java.util.HashMap; +-import java.util.List; +-import java.util.Map; +- +-import org.mvel2.asm.Handle; +-import org.mvel2.asm.Label; +-import org.mvel2.asm.MethodVisitor; +-import org.mvel2.asm.Opcodes; +-import org.mvel2.asm.Type; +- +-/** +- * A {@link MethodVisitor} that keeps track of stack map frame changes between +- * {@link #visitFrame(int, int, Object[], int, Object[]) visitFrame} calls. This +- * adapter must be used with the +- * {@link org.mvel2.asm.ClassReader#EXPAND_FRAMES} option. Each +- * visitX instruction delegates to the next visitor in the chain, if any, +- * and then simulates the effect of this instruction on the stack map frame, +- * represented by {@link #locals} and {@link #stack}. The next visitor in the +- * chain can get the state of the stack map frame before each instruction +- * by reading the value of these fields in its visitX methods (this +- * requires a reference to the AnalyzerAdapter that is before it in the chain). +- * If this adapter is used with a class that does not contain stack map table +- * attributes (i.e., pre Java 6 classes) then this adapter may not be able to +- * compute the stack map frame for each instruction. In this case no exception +- * is thrown but the {@link #locals} and {@link #stack} fields will be null for +- * these instructions. +- * +- * @author Eric Bruneton +- */ +-public class AnalyzerAdapter extends MethodVisitor { +- +- /** +- * List of the local variable slots for current execution +- * frame. 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 +- * two elements, the second one being TOP). 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). This field is null for unreachable +- * instructions. +- */ +- public List locals; +- +- /** +- * List of the operand stack slots for current execution frame. +- * 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 +- * two elements, the second one being TOP). 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). This field is null for unreachable +- * instructions. +- */ +- public List stack; +- +- /** +- * The labels that designate the next instruction to be visited. May be +- * null. +- */ +- private List