summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jvm/clojure/asm/AnnotationVisitor.java97
-rw-r--r--src/jvm/clojure/asm/AnnotationWriter.java357
-rw-r--r--src/jvm/clojure/asm/Attribute.java253
-rw-r--r--src/jvm/clojure/asm/ByteVector.java318
-rw-r--r--src/jvm/clojure/asm/ClassAdapter.java115
-rw-r--r--src/jvm/clojure/asm/ClassReader.java2224
-rw-r--r--src/jvm/clojure/asm/ClassVisitor.java196
-rw-r--r--src/jvm/clojure/asm/ClassWriter.java1415
-rw-r--r--src/jvm/clojure/asm/Edge.java75
-rw-r--r--src/jvm/clojure/asm/FieldVisitor.java64
-rw-r--r--src/jvm/clojure/asm/FieldWriter.java290
-rw-r--r--src/jvm/clojure/asm/Frame.java1506
-rw-r--r--src/jvm/clojure/asm/Handler.java70
-rw-r--r--src/jvm/clojure/asm/Item.java258
-rw-r--r--src/jvm/clojure/asm/Label.java437
-rw-r--r--src/jvm/clojure/asm/MethodAdapter.java186
-rw-r--r--src/jvm/clojure/asm/MethodVisitor.java396
-rw-r--r--src/jvm/clojure/asm/MethodWriter.java3029
-rw-r--r--src/jvm/clojure/asm/Opcodes.java341
-rw-r--r--src/jvm/clojure/asm/Type.java872
-rw-r--r--src/jvm/clojure/asm/commons/AdviceAdapter.java681
-rw-r--r--src/jvm/clojure/asm/commons/AnalyzerAdapter.java938
-rw-r--r--src/jvm/clojure/asm/commons/CodeSizeEvaluator.java234
-rw-r--r--src/jvm/clojure/asm/commons/EmptyVisitor.java221
-rw-r--r--src/jvm/clojure/asm/commons/GeneratorAdapter.java1533
-rw-r--r--src/jvm/clojure/asm/commons/LocalVariablesSorter.java330
-rw-r--r--src/jvm/clojure/asm/commons/Method.java267
-rw-r--r--src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java508
-rw-r--r--src/jvm/clojure/asm/commons/StaticInitMerger.java102
-rw-r--r--src/jvm/clojure/asm/commons/TableSwitchGenerator.java55
-rw-r--r--src/jvm/clojure/asm/commons/package.html48
-rw-r--r--src/jvm/clojure/asm/package.html87
32 files changed, 17503 insertions, 0 deletions
diff --git a/src/jvm/clojure/asm/AnnotationVisitor.java b/src/jvm/clojure/asm/AnnotationVisitor.java
new file mode 100644
index 00000000..2b0d2d63
--- /dev/null
+++ b/src/jvm/clojure/asm/AnnotationVisitor.java
@@ -0,0 +1,97 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * A visitor to visit a Java annotation. The methods of this interface must be
+ * called in the following order: (<tt>visit<tt> | <tt>visitEnum<tt> |
+ * <tt>visitAnnotation<tt> | <tt>visitArray<tt>)* <tt>visitEnd<tt>.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public interface AnnotationVisitor{
+
+/**
+ * Visits a primitive value of the annotation.
+ *
+ * @param name the value name.
+ * @param value the actual value, whose type must be {@link Byte},
+ * {@link Boolean}, {@link Character}, {@link Short},
+ * {@link Integer}, {@link Long}, {@link Float}, {@link Double},
+ * {@link String} or {@link Type}. 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).
+ */
+void visit(String name, Object 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.
+ */
+void visitEnum(String name, String desc, String 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
+ * <tt>null</tt> if this visitor is not interested in visiting
+ * this nested annotation. <i>The nested annotation value must be
+ * fully visited before calling other methods on this annotation
+ * visitor</i>.
+ */
+AnnotationVisitor visitAnnotation(String name, String desc);
+
+/**
+ * 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
+ * <tt>null</tt> if this visitor is not interested in visiting
+ * these values. The 'name' parameters passed to the methods of this
+ * visitor are ignored. <i>All the array values must be visited
+ * before calling other methods on this annotation visitor</i>.
+ */
+AnnotationVisitor visitArray(String name);
+
+/**
+ * Visits the end of the annotation.
+ */
+void visitEnd();
+}
diff --git a/src/jvm/clojure/asm/AnnotationWriter.java b/src/jvm/clojure/asm/AnnotationWriter.java
new file mode 100644
index 00000000..3097ac50
--- /dev/null
+++ b/src/jvm/clojure/asm/AnnotationWriter.java
@@ -0,0 +1,357 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * An {@link AnnotationVisitor} that generates annotations in bytecode form.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+final class AnnotationWriter implements 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;
+
+/**
+ * <tt>true<tt> if values are named, <tt>false</tt> 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 <tt>true<tt> if values are named, <tt>false</tt> 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 <tt>parent</tt> the number of annotation values must
+ * be stored.
+ */
+AnnotationWriter(
+ final ClassWriter cw,
+ final boolean named,
+ final ByteVector bv,
+ final ByteVector parent,
+ final int offset){
+ this.cw = cw;
+ this.named = named;
+ this.bv = bv;
+ this.parent = parent;
+ this.offset = offset;
+}
+
+// ------------------------------------------------------------------------
+// Implementation of the AnnotationVisitor interface
+// ------------------------------------------------------------------------
+
+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);
+ }
+}
+
+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));
+}
+
+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);
+}
+
+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);
+}
+
+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 out where the annotations must be put.
+ */
+static void put(final AnnotationWriter[] panns, final ByteVector out){
+ int size = 1 + 2 * panns.length;
+ for(int i = 0; i < panns.length; ++i)
+ {
+ size += panns[i] == null ? 0 : panns[i].getSize();
+ }
+ out.putInt(size).putByte(panns.length);
+ for(int i = 0; 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;
+ }
+ }
+}
+}
diff --git a/src/jvm/clojure/asm/Attribute.java b/src/jvm/clojure/asm/Attribute.java
new file mode 100644
index 00000000..d08762f9
--- /dev/null
+++ b/src/jvm/clojure/asm/Attribute.java
@@ -0,0 +1,253 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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 <tt>null</tt>.
+ */
+Attribute next;
+
+/**
+ * Constructs a new empty attribute.
+ *
+ * @param type the type of the attribute.
+ */
+protected Attribute(final String type){
+ this.type = type;
+}
+
+/**
+ * Returns <tt>true</tt> if this type of attribute is unknown. The default
+ * implementation of this method always returns <tt>true</tt>.
+ *
+ * @return <tt>true</tt> if this type of attribute is unknown.
+ */
+public boolean isUnknown(){
+ return true;
+}
+
+/**
+ * Returns <tt>true</tt> if this type of attribute is a code attribute.
+ *
+ * @return <tt>true</tt> 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 <tt>null</tt> 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 <i>new</i>
+ * {@link Attribute} object, of type {@link #type type}, corresponding to
+ * the <tt>len</tt> 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 <tt>null</tt> if the
+ * attribute to be read is not a code attribute.
+ * @return a <i>new</i> {@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 <tt>null</tt> if this attribute is not a code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to this
+ * code attribute, or <tt>null</tt> 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 <tt>null</tt> if these attributes are not code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to
+ * these code attributes, or <tt>null</tt> 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 <tt>null</tt> if these attributes are not code
+ * attributes.
+ * @param len the length of the bytecode of the method corresponding to
+ * these code attributes, or <tt>null</tt> 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/jvm/clojure/asm/ByteVector.java b/src/jvm/clojure/asm/ByteVector.java
new file mode 100644
index 00000000..5181e7b8
--- /dev/null
+++ b/src/jvm/clojure/asm/ByteVector.java
@@ -0,0 +1,318 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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.
+ * @return this byte vector.
+ */
+public ByteVector putUTF8(final String s){
+ int charLength = s.length();
+ if(length + 2 + charLength > data.length)
+ {
+ enlarge(2 + charLength);
+ }
+ int len = length;
+ 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
+ {
+ int byteLength = i;
+ 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;
+ }
+ }
+ data[length] = (byte) (byteLength >>> 8);
+ data[length + 1] = (byte) byteLength;
+ if(length + 2 + byteLength > data.length)
+ {
+ length = len;
+ enlarge(2 + byteLength);
+ data = this.data;
+ }
+ 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);
+ }
+ }
+ break;
+ }
+ }
+ 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 <tt>null</tt> to put <tt>len</tt>
+ * 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/jvm/clojure/asm/ClassAdapter.java b/src/jvm/clojure/asm/ClassAdapter.java
new file mode 100644
index 00000000..d9b0d786
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassAdapter.java
@@ -0,0 +1,115 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}.
+ * This class can be used as a super class to quickly implement usefull class
+ * adapter classes, just by overriding the necessary methods.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassAdapter implements ClassVisitor{
+
+/**
+ * The {@link ClassVisitor} to which this adapter delegates calls.
+ */
+protected ClassVisitor cv;
+
+/**
+ * Constructs a new {@link ClassAdapter} object.
+ *
+ * @param cv the class visitor to which this adapter must delegate calls.
+ */
+public ClassAdapter(final ClassVisitor cv){
+ this.cv = cv;
+}
+
+public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces){
+ cv.visit(version, access, name, signature, superName, interfaces);
+}
+
+public void visitSource(final String source, final String debug){
+ cv.visitSource(source, debug);
+}
+
+public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc){
+ cv.visitOuterClass(owner, name, desc);
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ return cv.visitAnnotation(desc, visible);
+}
+
+public void visitAttribute(final Attribute attr){
+ cv.visitAttribute(attr);
+}
+
+public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access){
+ cv.visitInnerClass(name, outerName, innerName, access);
+}
+
+public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value){
+ return cv.visitField(access, name, desc, signature, value);
+}
+
+public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions){
+ return cv.visitMethod(access, name, desc, signature, exceptions);
+}
+
+public void visitEnd(){
+ cv.visitEnd();
+}
+}
diff --git a/src/jvm/clojure/asm/ClassReader.java b/src/jvm/clojure/asm/ClassReader.java
new file mode 100644
index 00000000..c9bec91f
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassReader.java
@@ -0,0 +1,2224 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * 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{
+
+/**
+ * Flag to skip method code. If this class is set <code>CODE</code>
+ * attribute won't be visited. This can be used, for example, to retrieve
+ * annotations for methods and method parameters.
+ */
+public final static 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 final static 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 final static 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 final static int EXPAND_FRAMES = 8;
+
+/**
+ * The class to be parsed. <i>The content of this array must not be
+ * modified. This field is intended for {@link Attribute} sub classes, and
+ * is normally not needed by class generators or adapters.</i>
+ */
+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;
+ // 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:
+ 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.CLASS:
+ // case ClassWriter.STR:
+ 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 <tt>null</tt> for
+ * {@link Object} class.
+ * @see ClassVisitor#visit(int,int,String,String,String,String[])
+ */
+public String getSuperName(){
+ int n = items[readUnsignedShort(header + 4)];
+ return n == 0 ? null : readUTF8(n, 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
+ * <tt>null</tt>.
+ * @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.STR:
+ // case ClassWriter.CLASS:
+ 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;
+}
+
+/**
+ * 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));
+}
+
+/**
+ * Constructs a new {@link ClassReader} object.
+ *
+ * @param name the fully qualified name of the class to be read.
+ * @throws IOException if an exception occurs during reading.
+ */
+public ClassReader(final String name) throws IOException{
+ this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
+ + ".class"));
+}
+
+/**
+ * Reads the bytecode of a class.
+ *
+ * @param is an input stream from which to read the class.
+ * @return the bytecode read from the given input stream.
+ * @throws IOException if a problem occurs during reading.
+ */
+private static byte[] readClass(final InputStream is) throws IOException{
+ if(is == null)
+ {
+ throw new IOException("Class not found");
+ }
+ 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)
+ {
+ byte[] c = new byte[b.length + 1000];
+ System.arraycopy(b, 0, c, 0, len);
+ b = c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+// 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}.
+ */
+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. <i>This may
+ * corrupt it if this value contains references to the constant pool,
+ * or has syntactic or semantic links with a class element that has
+ * been transformed by a class adapter between the reader and the
+ * writer</i>.
+ * @param flags option flags that can be used to modify the default behavior
+ * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}.
+ */
+public void accept(
+ final ClassVisitor classVisitor,
+ final Attribute[] attrs,
+ final int flags){
+ byte[] b = this.b; // the bytecode array
+ char[] c = new char[maxStringLength]; // buffer used to read strings
+ int i, j, k; // loop variables
+ int u, v, w; // indexes in b
+ Attribute attr;
+
+ int access;
+ String name;
+ String desc;
+ String attrName;
+ String signature;
+ int anns = 0;
+ int ianns = 0;
+ Attribute cattrs = null;
+
+ // visits the header
+ u = header;
+ access = readUnsignedShort(u);
+ name = readClass(u + 2, c);
+ v = items[readUnsignedShort(u + 4)];
+ String superClassName = v == 0 ? null : readUTF8(v, c);
+ String[] implementedItfs = new String[readUnsignedShort(u + 6)];
+ w = 0;
+ u += 8;
+ for(i = 0; i < implementedItfs.length; ++i)
+ {
+ implementedItfs[i] = readClass(u, c);
+ u += 2;
+ }
+
+ boolean skipCode = (flags & SKIP_CODE) != 0;
+ boolean skipDebug = (flags & SKIP_DEBUG) != 0;
+ boolean unzip = (flags & EXPAND_FRAMES) != 0;
+
+ // skips fields and methods
+ v = u;
+ i = readUnsignedShort(v);
+ v += 2;
+ for(; i > 0; --i)
+ {
+ j = readUnsignedShort(v + 6);
+ v += 8;
+ for(; j > 0; --j)
+ {
+ v += 6 + readInt(v + 2);
+ }
+ }
+ i = readUnsignedShort(v);
+ v += 2;
+ for(; i > 0; --i)
+ {
+ j = readUnsignedShort(v + 6);
+ v += 8;
+ for(; j > 0; --j)
+ {
+ v += 6 + readInt(v + 2);
+ }
+ }
+ // reads the class's attributes
+ signature = null;
+ String sourceFile = null;
+ String sourceDebug = null;
+ String enclosingOwner = null;
+ String enclosingName = null;
+ String enclosingDesc = null;
+
+ i = readUnsignedShort(v);
+ v += 2;
+ for(; i > 0; --i)
+ {
+ attrName = readUTF8(v, c);
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if(attrName.equals("SourceFile"))
+ {
+ sourceFile = readUTF8(v + 6, c);
+ }
+ else if(attrName.equals("InnerClasses"))
+ {
+ w = v + 6;
+ }
+ else if(attrName.equals("EnclosingMethod"))
+ {
+ enclosingOwner = readClass(v + 6, c);
+ int item = readUnsignedShort(v + 8);
+ if(item != 0)
+ {
+ enclosingName = readUTF8(items[item], c);
+ enclosingDesc = readUTF8(items[item] + 2, c);
+ }
+ }
+ else if(attrName.equals("Signature"))
+ {
+ signature = readUTF8(v + 6, c);
+ }
+ else if(attrName.equals("RuntimeVisibleAnnotations"))
+ {
+ anns = v + 6;
+ }
+ else if(attrName.equals("Deprecated"))
+ {
+ access |= Opcodes.ACC_DEPRECATED;
+ }
+ else if(attrName.equals("Synthetic"))
+ {
+ access |= Opcodes.ACC_SYNTHETIC;
+ }
+ else if(attrName.equals("SourceDebugExtension"))
+ {
+ int len = readInt(v + 2);
+ sourceDebug = readUTF(v + 6, len, new char[len]);
+ }
+ else if(attrName.equals("RuntimeInvisibleAnnotations"))
+ {
+ ianns = v + 6;
+ }
+ else
+ {
+ attr = readAttribute(attrs,
+ attrName,
+ v + 6,
+ readInt(v + 2),
+ c,
+ -1,
+ null);
+ if(attr != null)
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ v += 6 + readInt(v + 2);
+ }
+ // calls the visit method
+ classVisitor.visit(readInt(4),
+ access,
+ name,
+ signature,
+ superClassName,
+ implementedItfs);
+
+ // calls the visitSource method
+ if(!skipDebug && (sourceFile != null || sourceDebug != null))
+ {
+ classVisitor.visitSource(sourceFile, sourceDebug);
+ }
+
+ // calls the visitOuterClass method
+ if(enclosingOwner != null)
+ {
+ classVisitor.visitOuterClass(enclosingOwner,
+ enclosingName,
+ enclosingDesc);
+ }
+
+ // visits the class annotations
+ for(i = 1; i >= 0; --i)
+ {
+ v = i == 0 ? ianns : anns;
+ if(v != 0)
+ {
+ j = readUnsignedShort(v);
+ v += 2;
+ for(; j > 0; --j)
+ {
+ v = readAnnotationValues(v + 2,
+ c,
+ true,
+ classVisitor.visitAnnotation(readUTF8(v, c), i != 0));
+ }
+ }
+ }
+
+ // visits the class attributes
+ while(cattrs != null)
+ {
+ attr = cattrs.next;
+ cattrs.next = null;
+ classVisitor.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+
+ // calls the visitInnerClass method
+ if(w != 0)
+ {
+ i = readUnsignedShort(w);
+ w += 2;
+ for(; i > 0; --i)
+ {
+ classVisitor.visitInnerClass(readUnsignedShort(w) == 0
+ ? null
+ : readClass(w, c), readUnsignedShort(w + 2) == 0
+ ? null
+ : readClass(w + 2, c), readUnsignedShort(w + 4) == 0
+ ? null
+ : readUTF8(w + 4, c),
+ readUnsignedShort(w + 6));
+ w += 8;
+ }
+ }
+
+ // visits the fields
+ i = readUnsignedShort(u);
+ u += 2;
+ for(; i > 0; --i)
+ {
+ access = readUnsignedShort(u);
+ name = readUTF8(u + 2, c);
+ desc = readUTF8(u + 4, c);
+ // visits the field's attributes and looks for a ConstantValue
+ // attribute
+ int fieldValueItem = 0;
+ signature = null;
+ anns = 0;
+ ianns = 0;
+ cattrs = null;
+
+ j = readUnsignedShort(u + 6);
+ u += 8;
+ for(; j > 0; --j)
+ {
+ attrName = readUTF8(u, c);
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if(attrName.equals("ConstantValue"))
+ {
+ fieldValueItem = readUnsignedShort(u + 6);
+ }
+ else if(attrName.equals("Signature"))
+ {
+ signature = readUTF8(u + 6, c);
+ }
+ else if(attrName.equals("Deprecated"))
+ {
+ access |= Opcodes.ACC_DEPRECATED;
+ }
+ else if(attrName.equals("Synthetic"))
+ {
+ access |= Opcodes.ACC_SYNTHETIC;
+ }
+ else if(attrName.equals("RuntimeVisibleAnnotations"))
+ {
+ anns = u + 6;
+ }
+ else if(attrName.equals("RuntimeInvisibleAnnotations"))
+ {
+ ianns = u + 6;
+ }
+ else
+ {
+ attr = readAttribute(attrs,
+ attrName,
+ u + 6,
+ readInt(u + 2),
+ c,
+ -1,
+ null);
+ if(attr != null)
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ u += 6 + readInt(u + 2);
+ }
+ // visits the field
+ FieldVisitor fv = classVisitor.visitField(access,
+ name,
+ desc,
+ signature,
+ fieldValueItem == 0 ? null : readConst(fieldValueItem, c));
+ // visits the field annotations and attributes
+ if(fv != null)
+ {
+ for(j = 1; j >= 0; --j)
+ {
+ v = j == 0 ? ianns : anns;
+ if(v != 0)
+ {
+ k = readUnsignedShort(v);
+ v += 2;
+ for(; k > 0; --k)
+ {
+ v = readAnnotationValues(v + 2,
+ c,
+ true,
+ fv.visitAnnotation(readUTF8(v, c), j != 0));
+ }
+ }
+ }
+ while(cattrs != null)
+ {
+ attr = cattrs.next;
+ cattrs.next = null;
+ fv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ fv.visitEnd();
+ }
+ }
+
+ // visits the methods
+ i = readUnsignedShort(u);
+ u += 2;
+ for(; i > 0; --i)
+ {
+ int u0 = u + 6;
+ access = readUnsignedShort(u);
+ name = readUTF8(u + 2, c);
+ desc = readUTF8(u + 4, c);
+ signature = null;
+ anns = 0;
+ ianns = 0;
+ int dann = 0;
+ int mpanns = 0;
+ int impanns = 0;
+ cattrs = null;
+ v = 0;
+ w = 0;
+
+ // looks for Code and Exceptions attributes
+ j = readUnsignedShort(u + 6);
+ u += 8;
+ for(; j > 0; --j)
+ {
+ attrName = readUTF8(u, c);
+ int attrSize = readInt(u + 2);
+ u += 6;
+ // tests are sorted in decreasing frequency order
+ // (based on frequencies observed on typical classes)
+ if(attrName.equals("Code"))
+ {
+ if(!skipCode)
+ {
+ v = u;
+ }
+ }
+ else if(attrName.equals("Exceptions"))
+ {
+ w = u;
+ }
+ else if(attrName.equals("Signature"))
+ {
+ signature = readUTF8(u, c);
+ }
+ else if(attrName.equals("Deprecated"))
+ {
+ access |= Opcodes.ACC_DEPRECATED;
+ }
+ else if(attrName.equals("RuntimeVisibleAnnotations"))
+ {
+ anns = u;
+ }
+ else if(attrName.equals("AnnotationDefault"))
+ {
+ dann = u;
+ }
+ else if(attrName.equals("Synthetic"))
+ {
+ access |= Opcodes.ACC_SYNTHETIC;
+ }
+ else if(attrName.equals("RuntimeInvisibleAnnotations"))
+ {
+ ianns = u;
+ }
+ else if(attrName.equals("RuntimeVisibleParameterAnnotations"))
+ {
+ mpanns = u;
+ }
+ else if(attrName.equals("RuntimeInvisibleParameterAnnotations"))
+ {
+ impanns = u;
+ }
+ else
+ {
+ attr = readAttribute(attrs,
+ attrName,
+ u,
+ attrSize,
+ c,
+ -1,
+ null);
+ if(attr != null)
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ u += attrSize;
+ }
+ // reads declared exceptions
+ String[] exceptions;
+ if(w == 0)
+ {
+ exceptions = null;
+ }
+ else
+ {
+ exceptions = new String[readUnsignedShort(w)];
+ w += 2;
+ for(j = 0; j < exceptions.length; ++j)
+ {
+ exceptions[j] = readClass(w, c);
+ w += 2;
+ }
+ }
+
+ // visits the method's code, if any
+ MethodVisitor mv = classVisitor.visitMethod(access,
+ name,
+ desc,
+ signature,
+ exceptions);
+
+ if(mv != null)
+ {
+ /*
+ * 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(mv instanceof MethodWriter)
+ {
+ MethodWriter mw = (MethodWriter) mv;
+ if(mw.cw.cr == this)
+ {
+ if(signature == mw.signature)
+ {
+ boolean sameExceptions = false;
+ if(exceptions == null)
+ {
+ sameExceptions = mw.exceptionCount == 0;
+ }
+ else
+ {
+ if(exceptions.length == mw.exceptionCount)
+ {
+ sameExceptions = true;
+ for(j = exceptions.length - 1; j >= 0; --j)
+ {
+ w -= 2;
+ if(mw.exceptions[j] != readUnsignedShort(w))
+ {
+ 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 = u0;
+ mw.classReaderLength = u - u0;
+ continue;
+ }
+ }
+ }
+ }
+
+ if(dann != 0)
+ {
+ AnnotationVisitor dv = mv.visitAnnotationDefault();
+ readAnnotationValue(dann, c, null, dv);
+ if(dv != null)
+ {
+ dv.visitEnd();
+ }
+ }
+ for(j = 1; j >= 0; --j)
+ {
+ w = j == 0 ? ianns : anns;
+ if(w != 0)
+ {
+ k = readUnsignedShort(w);
+ w += 2;
+ for(; k > 0; --k)
+ {
+ w = readAnnotationValues(w + 2,
+ c,
+ true,
+ mv.visitAnnotation(readUTF8(w, c), j != 0));
+ }
+ }
+ }
+ if(mpanns != 0)
+ {
+ readParameterAnnotations(mpanns, c, true, mv);
+ }
+ if(impanns != 0)
+ {
+ readParameterAnnotations(impanns, c, false, mv);
+ }
+ while(cattrs != null)
+ {
+ attr = cattrs.next;
+ cattrs.next = null;
+ mv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ }
+
+ if(mv != null && v != 0)
+ {
+ int maxStack = readUnsignedShort(v);
+ int maxLocals = readUnsignedShort(v + 2);
+ int codeLength = readInt(v + 4);
+ v += 8;
+
+ int codeStart = v;
+ int codeEnd = v + codeLength;
+
+ mv.visitCode();
+
+ // 1st phase: finds the labels
+ int label;
+ Label[] labels = new Label[codeLength + 1];
+ while(v < codeEnd)
+ {
+ int opcode = b[v] & 0xFF;
+ switch(ClassWriter.TYPE[opcode])
+ {
+ case ClassWriter.NOARG_INSN:
+ case ClassWriter.IMPLVAR_INSN:
+ v += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ label = v - codeStart + readShort(v + 1);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ v += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ label = v - codeStart + readInt(v + 1);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ v += 5;
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[v + 1] & 0xFF;
+ if(opcode == Opcodes.IINC)
+ {
+ v += 6;
+ }
+ else
+ {
+ v += 4;
+ }
+ break;
+ case ClassWriter.TABL_INSN:
+ // skips 0 to 3 padding bytes
+ w = v - codeStart;
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ j = readInt(v + 8) - readInt(v + 4) + 1;
+ v += 12;
+ for(; j > 0; --j)
+ {
+ label = w + readInt(v);
+ v += 4;
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ }
+ break;
+ case ClassWriter.LOOK_INSN:
+ // skips 0 to 3 padding bytes
+ w = v - codeStart;
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ j = readInt(v + 4);
+ v += 8;
+ for(; j > 0; --j)
+ {
+ label = w + readInt(v + 4);
+ v += 8;
+ if(labels[label] == null)
+ {
+ labels[label] = new Label();
+ }
+ }
+ break;
+ case ClassWriter.VAR_INSN:
+ case ClassWriter.SBYTE_INSN:
+ case ClassWriter.LDC_INSN:
+ v += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ case ClassWriter.LDCW_INSN:
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.TYPE_INSN:
+ case ClassWriter.IINC_INSN:
+ v += 3;
+ break;
+ case ClassWriter.ITFMETH_INSN:
+ v += 5;
+ break;
+ // case MANA_INSN:
+ default:
+ v += 4;
+ break;
+ }
+ }
+ // parses the try catch entries
+ j = readUnsignedShort(v);
+ v += 2;
+ for(; j > 0; --j)
+ {
+ label = readUnsignedShort(v);
+ Label start = labels[label];
+ if(start == null)
+ {
+ labels[label] = start = new Label();
+ }
+ label = readUnsignedShort(v + 2);
+ Label end = labels[label];
+ if(end == null)
+ {
+ labels[label] = end = new Label();
+ }
+ label = readUnsignedShort(v + 4);
+ Label handler = labels[label];
+ if(handler == null)
+ {
+ labels[label] = handler = new Label();
+ }
+ int type = readUnsignedShort(v + 6);
+ if(type == 0)
+ {
+ mv.visitTryCatchBlock(start, end, handler, null);
+ }
+ else
+ {
+ mv.visitTryCatchBlock(start,
+ end,
+ handler,
+ readUTF8(items[type], c));
+ }
+ v += 8;
+ }
+ // parses the local variable, line number tables, and code
+ // attributes
+ int varTable = 0;
+ int varTypeTable = 0;
+ int stackMap = 0;
+ int frameCount = 0;
+ int frameMode = 0;
+ int frameOffset = 0;
+ int frameLocalCount = 0;
+ int frameLocalDiff = 0;
+ int frameStackCount = 0;
+ Object[] frameLocal = null;
+ Object[] frameStack = null;
+ boolean zip = true;
+ cattrs = null;
+ j = readUnsignedShort(v);
+ v += 2;
+ for(; j > 0; --j)
+ {
+ attrName = readUTF8(v, c);
+ if(attrName.equals("LocalVariableTable"))
+ {
+ if(!skipDebug)
+ {
+ varTable = v + 6;
+ k = readUnsignedShort(v + 6);
+ w = v + 8;
+ for(; k > 0; --k)
+ {
+ label = readUnsignedShort(w);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label(true);
+ }
+ label += readUnsignedShort(w + 2);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label(true);
+ }
+ w += 10;
+ }
+ }
+ }
+ else if(attrName.equals("LocalVariableTypeTable"))
+ {
+ varTypeTable = v + 6;
+ }
+ else if(attrName.equals("LineNumberTable"))
+ {
+ if(!skipDebug)
+ {
+ k = readUnsignedShort(v + 6);
+ w = v + 8;
+ for(; k > 0; --k)
+ {
+ label = readUnsignedShort(w);
+ if(labels[label] == null)
+ {
+ labels[label] = new Label(true);
+ }
+ labels[label].line = readUnsignedShort(w + 2);
+ w += 4;
+ }
+ }
+ }
+ else if(attrName.equals("StackMapTable"))
+ {
+ if((flags & SKIP_FRAMES) == 0)
+ {
+ stackMap = v + 8;
+ frameCount = readUnsignedShort(v + 6);
+ }
+ /*
+ * 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.
+ */
+ // TODO true for frame offsets,
+ // but for UNINITIALIZED type offsets?
+ }
+ else if(attrName.equals("StackMap"))
+ {
+ if((flags & SKIP_FRAMES) == 0)
+ {
+ stackMap = v + 8;
+ frameCount = readUnsignedShort(v + 6);
+ zip = false;
+ }
+ /*
+ * 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(k = 0; k < attrs.length; ++k)
+ {
+ if(attrs[k].type.equals(attrName))
+ {
+ attr = attrs[k].read(this,
+ v + 6,
+ readInt(v + 2),
+ c,
+ codeStart - 8,
+ labels);
+ if(attr != null)
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ }
+ }
+ }
+ v += 6 + readInt(v + 2);
+ }
+
+ // 2nd phase: visits each instruction
+ if(stackMap != 0)
+ {
+ // creates the very first (implicit) frame from the method
+ // descriptor
+ frameLocal = new Object[maxLocals];
+ frameStack = new Object[maxStack];
+ if(unzip)
+ {
+ int local = 0;
+ if((access & Opcodes.ACC_STATIC) == 0)
+ {
+ if(name.equals("<init>"))
+ {
+ frameLocal[local++] = Opcodes.UNINITIALIZED_THIS;
+ }
+ else
+ {
+ frameLocal[local++] = readClass(header + 2, c);
+ }
+ }
+ j = 1;
+ loop:
+ while(true)
+ {
+ k = j;
+ switch(desc.charAt(j++))
+ {
+ case'Z':
+ case'C':
+ case'B':
+ case'S':
+ case'I':
+ frameLocal[local++] = Opcodes.INTEGER;
+ break;
+ case'F':
+ frameLocal[local++] = Opcodes.FLOAT;
+ break;
+ case'J':
+ frameLocal[local++] = Opcodes.LONG;
+ break;
+ case'D':
+ frameLocal[local++] = Opcodes.DOUBLE;
+ break;
+ case'[':
+ while(desc.charAt(j) == '[')
+ {
+ ++j;
+ }
+ if(desc.charAt(j) == 'L')
+ {
+ ++j;
+ while(desc.charAt(j) != ';')
+ {
+ ++j;
+ }
+ }
+ frameLocal[local++] = desc.substring(k, ++j);
+ break;
+ case'L':
+ while(desc.charAt(j) != ';')
+ {
+ ++j;
+ }
+ frameLocal[local++] = desc.substring(k + 1,
+ j++);
+ break;
+ default:
+ break loop;
+ }
+ }
+ frameLocalCount = local;
+ }
+ /*
+ * 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
+ */
+ frameOffset = -1;
+ }
+ v = codeStart;
+ Label l;
+ while(v < codeEnd)
+ {
+ w = v - codeStart;
+
+ l = labels[w];
+ if(l != null)
+ {
+ mv.visitLabel(l);
+ if(!skipDebug && l.line > 0)
+ {
+ mv.visitLineNumber(l.line, l);
+ }
+ }
+
+ while(frameLocal != null
+ && (frameOffset == w || frameOffset == -1))
+ {
+ // if there is a frame for this offset,
+ // makes the visitor visit it,
+ // and reads the next frame if there is one.
+ if(!zip || unzip)
+ {
+ mv.visitFrame(Opcodes.F_NEW,
+ frameLocalCount,
+ frameLocal,
+ frameStackCount,
+ frameStack);
+ }
+ else if(frameOffset != -1)
+ {
+ mv.visitFrame(frameMode,
+ frameLocalDiff,
+ frameLocal,
+ frameStackCount,
+ frameStack);
+ }
+
+ if(frameCount > 0)
+ {
+ int tag, delta, n;
+ if(zip)
+ {
+ tag = b[stackMap++] & 0xFF;
+ }
+ else
+ {
+ tag = MethodWriter.FULL_FRAME;
+ frameOffset = -1;
+ }
+ frameLocalDiff = 0;
+ if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME)
+ {
+ delta = tag;
+ frameMode = Opcodes.F_SAME;
+ frameStackCount = 0;
+ }
+ else if(tag < MethodWriter.RESERVED)
+ {
+ delta = tag
+ - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME;
+ stackMap = readFrameType(frameStack,
+ 0,
+ stackMap,
+ c,
+ labels);
+ frameMode = Opcodes.F_SAME1;
+ frameStackCount = 1;
+ }
+ else
+ {
+ delta = readUnsignedShort(stackMap);
+ stackMap += 2;
+ if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+ {
+ stackMap = readFrameType(frameStack,
+ 0,
+ stackMap,
+ c,
+ labels);
+ frameMode = Opcodes.F_SAME1;
+ frameStackCount = 1;
+ }
+ else if(tag >= MethodWriter.CHOP_FRAME
+ && tag < MethodWriter.SAME_FRAME_EXTENDED)
+ {
+ frameMode = Opcodes.F_CHOP;
+ frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED
+ - tag;
+ frameLocalCount -= frameLocalDiff;
+ frameStackCount = 0;
+ }
+ else if(tag == MethodWriter.SAME_FRAME_EXTENDED)
+ {
+ frameMode = Opcodes.F_SAME;
+ frameStackCount = 0;
+ }
+ else if(tag < MethodWriter.FULL_FRAME)
+ {
+ j = unzip ? frameLocalCount : 0;
+ for(k = tag
+ - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--)
+ {
+ stackMap = readFrameType(frameLocal,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ frameMode = Opcodes.F_APPEND;
+ frameLocalDiff = tag
+ - MethodWriter.SAME_FRAME_EXTENDED;
+ frameLocalCount += frameLocalDiff;
+ frameStackCount = 0;
+ }
+ else
+ { // if (tag == FULL_FRAME) {
+ frameMode = Opcodes.F_FULL;
+ n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap);
+ stackMap += 2;
+ for(j = 0; n > 0; n--)
+ {
+ stackMap = readFrameType(frameLocal,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ n = frameStackCount = readUnsignedShort(stackMap);
+ stackMap += 2;
+ for(j = 0; n > 0; n--)
+ {
+ stackMap = readFrameType(frameStack,
+ j++,
+ stackMap,
+ c,
+ labels);
+ }
+ }
+ }
+ frameOffset += delta + 1;
+ if(labels[frameOffset] == null)
+ {
+ labels[frameOffset] = new Label();
+ }
+
+ --frameCount;
+ }
+ else
+ {
+ frameLocal = null;
+ }
+ }
+
+ int opcode = b[v] & 0xFF;
+ switch(ClassWriter.TYPE[opcode])
+ {
+ case ClassWriter.NOARG_INSN:
+ mv.visitInsn(opcode);
+ v += 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);
+ }
+ v += 1;
+ break;
+ case ClassWriter.LABEL_INSN:
+ mv.visitJumpInsn(opcode, labels[w
+ + readShort(v + 1)]);
+ v += 3;
+ break;
+ case ClassWriter.LABELW_INSN:
+ mv.visitJumpInsn(opcode - 33, labels[w
+ + readInt(v + 1)]);
+ v += 5;
+ break;
+ case ClassWriter.WIDE_INSN:
+ opcode = b[v + 1] & 0xFF;
+ if(opcode == Opcodes.IINC)
+ {
+ mv.visitIincInsn(readUnsignedShort(v + 2),
+ readShort(v + 4));
+ v += 6;
+ }
+ else
+ {
+ mv.visitVarInsn(opcode,
+ readUnsignedShort(v + 2));
+ v += 4;
+ }
+ break;
+ case ClassWriter.TABL_INSN:
+ // skips 0 to 3 padding bytes
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ int min = readInt(v + 4);
+ int max = readInt(v + 8);
+ v += 12;
+ Label[] table = new Label[max - min + 1];
+ for(j = 0; j < table.length; ++j)
+ {
+ table[j] = labels[w + readInt(v)];
+ v += 4;
+ }
+ mv.visitTableSwitchInsn(min,
+ max,
+ labels[label],
+ table);
+ break;
+ case ClassWriter.LOOK_INSN:
+ // skips 0 to 3 padding bytes
+ v = v + 4 - (w & 3);
+ // reads instruction
+ label = w + readInt(v);
+ j = readInt(v + 4);
+ v += 8;
+ int[] keys = new int[j];
+ Label[] values = new Label[j];
+ for(j = 0; j < keys.length; ++j)
+ {
+ keys[j] = readInt(v);
+ values[j] = labels[w + readInt(v + 4)];
+ v += 8;
+ }
+ mv.visitLookupSwitchInsn(labels[label],
+ keys,
+ values);
+ break;
+ case ClassWriter.VAR_INSN:
+ mv.visitVarInsn(opcode, b[v + 1] & 0xFF);
+ v += 2;
+ break;
+ case ClassWriter.SBYTE_INSN:
+ mv.visitIntInsn(opcode, b[v + 1]);
+ v += 2;
+ break;
+ case ClassWriter.SHORT_INSN:
+ mv.visitIntInsn(opcode, readShort(v + 1));
+ v += 3;
+ break;
+ case ClassWriter.LDC_INSN:
+ mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c));
+ v += 2;
+ break;
+ case ClassWriter.LDCW_INSN:
+ mv.visitLdcInsn(readConst(readUnsignedShort(v + 1),
+ c));
+ v += 3;
+ break;
+ case ClassWriter.FIELDORMETH_INSN:
+ case ClassWriter.ITFMETH_INSN:
+ int cpIndex = items[readUnsignedShort(v + 1)];
+ 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);
+ }
+ if(opcode == Opcodes.INVOKEINTERFACE)
+ {
+ v += 5;
+ }
+ else
+ {
+ v += 3;
+ }
+ break;
+ case ClassWriter.TYPE_INSN:
+ mv.visitTypeInsn(opcode, readClass(v + 1, c));
+ v += 3;
+ break;
+ case ClassWriter.IINC_INSN:
+ mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]);
+ v += 3;
+ break;
+ // case MANA_INSN:
+ default:
+ mv.visitMultiANewArrayInsn(readClass(v + 1, c),
+ b[v + 3] & 0xFF);
+ v += 4;
+ break;
+ }
+ }
+ l = labels[codeEnd - codeStart];
+ if(l != null)
+ {
+ mv.visitLabel(l);
+ }
+ // visits the local variable tables
+ if(!skipDebug && varTable != 0)
+ {
+ int[] typeTable = null;
+ if(varTypeTable != 0)
+ {
+ k = readUnsignedShort(varTypeTable) * 3;
+ w = varTypeTable + 2;
+ typeTable = new int[k];
+ while(k > 0)
+ {
+ typeTable[--k] = w + 6; // signature
+ typeTable[--k] = readUnsignedShort(w + 8); // index
+ typeTable[--k] = readUnsignedShort(w); // start
+ w += 10;
+ }
+ }
+ k = readUnsignedShort(varTable);
+ w = varTable + 2;
+ for(; k > 0; --k)
+ {
+ int start = readUnsignedShort(w);
+ int length = readUnsignedShort(w + 2);
+ int index = readUnsignedShort(w + 8);
+ String vsignature = null;
+ if(typeTable != null)
+ {
+ for(int a = 0; a < typeTable.length; a += 3)
+ {
+ if(typeTable[a] == start
+ && typeTable[a + 1] == index)
+ {
+ vsignature = readUTF8(typeTable[a + 2], c);
+ break;
+ }
+ }
+ }
+ mv.visitLocalVariable(readUTF8(w + 4, c),
+ readUTF8(w + 6, c),
+ vsignature,
+ labels[start],
+ labels[start + length],
+ index);
+ w += 10;
+ }
+ }
+ // visits the other attributes
+ while(cattrs != null)
+ {
+ attr = cattrs.next;
+ cattrs.next = null;
+ mv.visitAttribute(cattrs);
+ cattrs = attr;
+ }
+ // visits the max stack and max locals values
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+
+ if(mv != null)
+ {
+ mv.visitEnd();
+ }
+ }
+
+ // visits the end of the class
+ classVisitor.visitEnd();
+}
+
+/**
+ * Reads parameter annotations and makes the given visitor visit them.
+ *
+ * @param v start offset in {@link #b b} of the annotations to be read.
+ * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
+ * {@link #readClass(int,char[]) readClass} or
+ * {@link #readConst readConst}.
+ * @param visible <tt>true</tt> if the annotations to be read are visible
+ * at runtime.
+ * @param mv the visitor that must visit the annotations.
+ */
+private void readParameterAnnotations(
+ int v,
+ final char[] buf,
+ final boolean visible,
+ final MethodVisitor mv){
+ int n = b[v++] & 0xFF;
+ for(int i = 0; i < n; ++i)
+ {
+ int j = readUnsignedShort(v);
+ v += 2;
+ for(; j > 0; --j)
+ {
+ v = readAnnotationValues(v + 2,
+ buf,
+ true,
+ mv.visitParameterAnnotation(i,
+ readUTF8(v, buf),
+ visible));
+ }
+ }
+}
+
+/**
+ * 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 (<i>not
+ * including the value name constant pool index</i>).
+ * @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;
+}
+
+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
+ int offset = readUnsignedShort(v);
+ if(labels[offset] == null)
+ {
+ labels[offset] = new Label();
+ }
+ frame[index] = labels[offset];
+ v += 2;
+ }
+ return v;
+}
+
+/**
+ * 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 <tt>null</tt> if the
+ * attribute to be read is not a code attribute.
+ * @return the attribute that has been read, or <tt>null</tt> 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 start index of the constant pool item in {@link #b b}, plus
+ * one. <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @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];
+}
+
+/**
+ * Reads a byte value in {@link #b b}. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @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}. <i>This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.</i>
+ *
+ * @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}. <i>This method is intended
+ * for {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @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}. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @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}. <i>This method is intended
+ * for {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @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}. <i>This method
+ * is intended for {@link Attribute} sub classes, and is normally not needed
+ * by class generators or adapters.</i>
+ *
+ * @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);
+ 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, d, e;
+ while(index < endIndex)
+ {
+ c = b[index++] & 0xFF;
+ switch(c >> 4)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // 0xxxxxxx
+ buf[strLen++] = (char) c;
+ break;
+ case 12:
+ case 13:
+ // 110x xxxx 10xx xxxx
+ d = b[index++];
+ buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F));
+ break;
+ default:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ d = b[index++];
+ e = b[index++];
+ buf[strLen++] = (char) (((c & 0x0F) << 12)
+ | ((d & 0x3F) << 6) | (e & 0x3F));
+ break;
+ }
+ }
+ return new String(buf, 0, strLen);
+}
+
+/**
+ * Reads a class constant pool item in {@link #b b}. <i>This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.</i>
+ *
+ * @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}. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @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} or {@link Type} 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:
+ String s = readUTF8(index, buf);
+ return s.charAt(0) == '['
+ ? Type.getType(s)
+ : Type.getObjectType(s);
+ // case ClassWriter.STR:
+ default:
+ return readUTF8(index, buf);
+ }
+}
+}
diff --git a/src/jvm/clojure/asm/ClassVisitor.java b/src/jvm/clojure/asm/ClassVisitor.java
new file mode 100644
index 00000000..c150e31e
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassVisitor.java
@@ -0,0 +1,196 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * A visitor to visit a Java class. The methods of this interface must be called
+ * in the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
+ * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* (<tt>visitInnerClass</tt> |
+ * <tt>visitField</tt> | <tt>visitMethod</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public interface ClassVisitor{
+
+/**
+ * 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 <tt>null</tt> 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 <tt>null</tt>, but
+ * only for the {@link Object} class.
+ * @param interfaces the internal names of the class's interfaces (see
+ * {@link Type#getInternalName() getInternalName}). May be
+ * <tt>null</tt>.
+ */
+void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces);
+
+/**
+ * Visits the source of the class.
+ *
+ * @param source the name of the source file from which the class was
+ * compiled. May be <tt>null</tt>.
+ * @param debug additional debug information to compute the correspondance
+ * between source and compiled elements of the class. May be
+ * <tt>null</tt>.
+ */
+void visitSource(String source, String 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
+ * <tt>null</tt> 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
+ * <tt>null</tt> if the class is not enclosed in a method of its
+ * enclosing class.
+ */
+void visitOuterClass(String owner, String name, String desc);
+
+/**
+ * Visits an annotation of the class.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+/**
+ * Visits a non standard attribute of the class.
+ *
+ * @param attr an attribute.
+ */
+void visitAttribute(Attribute 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 <tt>null</tt> for not member classes.
+ * @param innerName the (simple) name of the inner class inside its
+ * enclosing class. May be <tt>null</tt> for anonymous inner
+ * classes.
+ * @param access the access flags of the inner class as originally declared
+ * in the enclosing class.
+ */
+void visitInnerClass(
+ String name,
+ String outerName,
+ String innerName,
+ int 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 <tt>null</tt> if the
+ * field's type does not use generic types.
+ * @param value the field's initial value. This parameter, which may be
+ * <tt>null</tt> 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 <tt>int</tt>,
+ * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
+ * respectively). <i>This parameter is only used for static fields</i>.
+ * Its value is ignored for non static fields, which must be
+ * initialized through bytecode instructions in constructors or
+ * methods.
+ * @return a visitor to visit field annotations and attributes, or
+ * <tt>null</tt> if this class visitor is not interested in
+ * visiting these annotations and attributes.
+ */
+FieldVisitor visitField(
+ int access,
+ String name,
+ String desc,
+ String signature,
+ Object value);
+
+/**
+ * Visits a method of the class. This method <i>must</i> return a new
+ * {@link MethodVisitor} instance (or <tt>null</tt>) 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 <tt>null</tt> 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
+ * <tt>null</tt>.
+ * @return an object to visit the byte code of the method, or <tt>null</tt>
+ * if this class visitor is not interested in visiting the code of
+ * this method.
+ */
+MethodVisitor visitMethod(
+ int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions);
+
+/**
+ * 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.
+ */
+void visitEnd();
+}
diff --git a/src/jvm/clojure/asm/ClassWriter.java b/src/jvm/clojure/asm/ClassWriter.java
new file mode 100644
index 00000000..3bc76dc8
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassWriter.java
@@ -0,0 +1,1415 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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 implements 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 final static 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 final static int COMPUTE_FRAMES = 2;
+
+/**
+ * The type of instructions without any argument.
+ */
+final static int NOARG_INSN = 0;
+
+/**
+ * The type of instructions with an signed byte argument.
+ */
+final static int SBYTE_INSN = 1;
+
+/**
+ * The type of instructions with an signed short argument.
+ */
+final static int SHORT_INSN = 2;
+
+/**
+ * The type of instructions with a local variable index argument.
+ */
+final static int VAR_INSN = 3;
+
+/**
+ * The type of instructions with an implicit local variable index argument.
+ */
+final static int IMPLVAR_INSN = 4;
+
+/**
+ * The type of instructions with a type descriptor argument.
+ */
+final static int TYPE_INSN = 5;
+
+/**
+ * The type of field and method invocations instructions.
+ */
+final static int FIELDORMETH_INSN = 6;
+
+/**
+ * The type of the INVOKEINTERFACE instruction.
+ */
+final static int ITFMETH_INSN = 7;
+
+/**
+ * The type of instructions with a 2 bytes bytecode offset label.
+ */
+final static int LABEL_INSN = 8;
+
+/**
+ * The type of instructions with a 4 bytes bytecode offset label.
+ */
+final static int LABELW_INSN = 9;
+
+/**
+ * The type of the LDC instruction.
+ */
+final static int LDC_INSN = 10;
+
+/**
+ * The type of the LDC_W and LDC2_W instructions.
+ */
+final static int LDCW_INSN = 11;
+
+/**
+ * The type of the IINC instruction.
+ */
+final static int IINC_INSN = 12;
+
+/**
+ * The type of the TABLESWITCH instruction.
+ */
+final static int TABL_INSN = 13;
+
+/**
+ * The type of the LOOKUPSWITCH instruction.
+ */
+final static int LOOK_INSN = 14;
+
+/**
+ * The type of the MULTIANEWARRAY instruction.
+ */
+final static int MANA_INSN = 15;
+
+/**
+ * The type of the WIDE instruction.
+ */
+final static int WIDE_INSN = 16;
+
+/**
+ * The instruction types of all JVM opcodes.
+ */
+static byte[] TYPE;
+
+/**
+ * The type of CONSTANT_Class constant pool items.
+ */
+final static int CLASS = 7;
+
+/**
+ * The type of CONSTANT_Fieldref constant pool items.
+ */
+final static int FIELD = 9;
+
+/**
+ * The type of CONSTANT_Methodref constant pool items.
+ */
+final static int METH = 10;
+
+/**
+ * The type of CONSTANT_InterfaceMethodref constant pool items.
+ */
+final static int IMETH = 11;
+
+/**
+ * The type of CONSTANT_String constant pool items.
+ */
+final static int STR = 8;
+
+/**
+ * The type of CONSTANT_Integer constant pool items.
+ */
+final static int INT = 3;
+
+/**
+ * The type of CONSTANT_Float constant pool items.
+ */
+final static int FLOAT = 4;
+
+/**
+ * The type of CONSTANT_Long constant pool items.
+ */
+final static int LONG = 5;
+
+/**
+ * The type of CONSTANT_Double constant pool items.
+ */
+final static int DOUBLE = 6;
+
+/**
+ * The type of CONSTANT_NameAndType constant pool items.
+ */
+final static int NAME_TYPE = 12;
+
+/**
+ * The type of CONSTANT_Utf8 constant pool items.
+ */
+final static int UTF8 = 1;
+
+/**
+ * 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.
+ */
+final static int TYPE_NORMAL = 13;
+
+/**
+ * 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.
+ */
+final static int TYPE_UNINIT = 14;
+
+/**
+ * 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.
+ */
+final static int TYPE_MERGED = 15;
+
+/**
+ * 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.
+ */
+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.
+ */
+Item key;
+
+/**
+ * A reusable key used to look for items in the {@link #items} hash table.
+ */
+Item key2;
+
+/**
+ * A reusable key used to look for items in the {@link #items} hash table.
+ */
+Item key3;
+
+/**
+ * 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 <tt>i</tt>
+ * the Item whose index is <tt>i</tt>. 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, conversly, 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; // TODO int?
+
+/**
+ * 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 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 fields of this class. These fields are stored in a linked list of
+ * {@link FieldWriter} objects, linked to each other by their
+ * {@link FieldWriter#next} 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#next} 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#next} 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#next} field. This field stores the last element of
+ * this list.
+ */
+MethodWriter lastMethod;
+
+/**
+ * <tt>true</tt> if the maximum stack size and number of local variables
+ * must be automatically computed.
+ */
+private boolean computeMaxs;
+
+/**
+ * <tt>true</tt> if the stack map frames must be recomputed from scratch.
+ */
+private boolean computeFrames;
+
+/**
+ * <tt>true</tt> 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 = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA"
+ + "AAAAGGGGGGGHAFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII";
+ 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;
+ //
+ // // 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){
+ index = 1;
+ pool = new ByteVector();
+ items = new Item[256];
+ threshold = (int) (0.75d * items.length);
+ key = new Item();
+ key2 = new Item();
+ key3 = 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:
+ * <p/>
+ * <ul> <li>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 <i>won't be
+ * removed</i>.</li> <li>Methods that are not transformed are copied as
+ * is in the new class, directly from the original class bytecode (i.e.
+ * without emitting visit events for all the method instructions), which
+ * saves a <i>lot</i> of time. Untransformed methods are detected by the
+ * fact that the {@link ClassReader} receives {@link MethodVisitor} objects
+ * that come from a {@link ClassWriter} (and not from a custom
+ * {@link ClassAdapter} or any other {@link ClassVisitor} instance).</li>
+ * </ul>
+ *
+ * @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. 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 interface
+// ------------------------------------------------------------------------
+
+public 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(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]);
+ }
+ }
+}
+
+public void visitSource(final String file, final String debug){
+ if(file != null)
+ {
+ sourceFile = newUTF8(file);
+ }
+ if(debug != null)
+ {
+ sourceDebug = new ByteVector().putUTF8(debug);
+ }
+}
+
+public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc){
+ enclosingMethodOwner = newClass(owner);
+ if(name != null && desc != null)
+ {
+ enclosingMethod = newNameType(name, desc);
+ }
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ 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;
+}
+
+public void visitAttribute(final Attribute attr){
+ attr.next = attrs;
+ attrs = attr;
+}
+
+public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access){
+ if(innerClasses == null)
+ {
+ innerClasses = new ByteVector();
+ }
+ ++innerClassesCount;
+ innerClasses.putShort(name == null ? 0 : newClass(name));
+ innerClasses.putShort(outerName == null ? 0 : newClass(outerName));
+ innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName));
+ innerClasses.putShort(access);
+}
+
+public 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);
+}
+
+public 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);
+}
+
+public 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(){
+ // 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 = fb.next;
+ }
+ int nbMethods = 0;
+ MethodWriter mb = firstMethod;
+ while(mb != null)
+ {
+ ++nbMethods;
+ size += mb.getSize();
+ mb = mb.next;
+ }
+ int attributeCount = 0;
+ if(signature != 0)
+ {
+ ++attributeCount;
+ size += 8;
+ newUTF8("Signature");
+ }
+ if(sourceFile != 0)
+ {
+ ++attributeCount;
+ size += 8;
+ newUTF8("SourceFile");
+ }
+ if(sourceDebug != null)
+ {
+ ++attributeCount;
+ size += sourceDebug.length + 4;
+ 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
+ && (version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ size += 6;
+ newUTF8("Synthetic");
+ }
+ if(innerClasses != null)
+ {
+ ++attributeCount;
+ size += 8 + innerClasses.length;
+ newUTF8("InnerClasses");
+ }
+ if(anns != null)
+ {
+ ++attributeCount;
+ size += 8 + anns.getSize();
+ newUTF8("RuntimeVisibleAnnotations");
+ }
+ if(ianns != null)
+ {
+ ++attributeCount;
+ size += 8 + ianns.getSize();
+ newUTF8("RuntimeInvisibleAnnotations");
+ }
+ 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);
+ out.putShort(access).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 = fb.next;
+ }
+ out.putShort(nbMethods);
+ mb = firstMethod;
+ while(mb != null)
+ {
+ mb.put(out);
+ mb = mb.next;
+ }
+ out.putShort(attributeCount);
+ if(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 - 2;
+ out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
+ out.putByteArray(sourceDebug.data, 2, 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
+ && (version & 0xffff) < Opcodes.V1_5)
+ {
+ 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(anns != null)
+ {
+ out.putShort(newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if(ianns != null)
+ {
+ out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if(attrs != null)
+ {
+ attrs.put(this, null, 0, -1, -1, out);
+ }
+ if(invalidFrames)
+ {
+ ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
+ new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES);
+ return cw.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;
+ return newClassItem(t.getSort() == Type.OBJECT
+ ? t.getInternalName()
+ : t.getDescriptor());
+ }
+ 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.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @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. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @param value the String value.
+ * @return the index of a new or already existing UTF8 item.
+ */
+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.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param value the internal name of the class.
+ * @return 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.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param value the internal name of the class.
+ * @return the index of a new or already existing class reference item.
+ */
+public int newClass(final String value){
+ return newClassItem(value).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.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param owner the internal name of the field's owner class.
+ * @param name the field's name.
+ * @param 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 <tt>true</tt> if <tt>owner</tt> 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.
+ * <i>This method is intended for {@link Attribute} sub classes, and is
+ * normally not needed by class generators or adapters.</i>
+ *
+ * @param owner the internal name of the method's owner class.
+ * @param name the method's name.
+ * @param desc the method's descriptor.
+ * @param itf <tt>true</tt> if <tt>owner</tt> 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);
+ put(result);
+ index += 2;
+ }
+ 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);
+ put(result);
+ index += 2;
+ }
+ 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. <i>This
+ * method is intended for {@link Attribute} sub classes, and is normally not
+ * needed by class generators or adapters.</i>
+ *
+ * @param name a name.
+ * @param 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){
+ 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.index;
+}
+
+/**
+ * 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 <i>loads<i> the two given classes and uses
+ * the java.lang.Class methods to find the common super class. It can be
+ * overriden 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;
+ try
+ {
+ c = Class.forName(type1.replace('/', '.'));
+ d = Class.forName(type2.replace('/', '.'));
+ }
+ catch(ClassNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+ 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 <tt>null</tt> if there is no such item.
+ */
+private Item get(final Item key){
+ Item i = items[key.hashCode % items.length];
+ while(i != null && !key.isEqualTo(i))
+ {
+ i = i.next;
+ }
+ return i;
+}
+
+/**
+ * Puts the given item in the constant pool's hash table. The hash table
+ * <i>must</i> 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 > 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);
+}
+}
diff --git a/src/jvm/clojure/asm/Edge.java b/src/jvm/clojure/asm/Edge.java
new file mode 100644
index 00000000..f549cfbb
--- /dev/null
+++ b/src/jvm/clojure/asm/Edge.java
@@ -0,0 +1,75 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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.
+ */
+final static 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.
+ */
+final static 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/jvm/clojure/asm/FieldVisitor.java b/src/jvm/clojure/asm/FieldVisitor.java
new file mode 100644
index 00000000..649fd6aa
--- /dev/null
+++ b/src/jvm/clojure/asm/FieldVisitor.java
@@ -0,0 +1,64 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * A visitor to visit a Java field. The methods of this interface must be called
+ * in the following order: ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public interface FieldVisitor{
+
+/**
+ * Visits an annotation of the field.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+/**
+ * Visits a non standard attribute of the field.
+ *
+ * @param attr an attribute.
+ */
+void visitAttribute(Attribute 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.
+ */
+void visitEnd();
+}
diff --git a/src/jvm/clojure/asm/FieldWriter.java b/src/jvm/clojure/asm/FieldWriter.java
new file mode 100644
index 00000000..6ac63f7f
--- /dev/null
+++ b/src/jvm/clojure/asm/FieldWriter.java
@@ -0,0 +1,290 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * An {@link FieldVisitor} that generates Java fields in bytecode form.
+ *
+ * @author Eric Bruneton
+ */
+final class FieldWriter implements FieldVisitor{
+
+/**
+ * Next field writer (see {@link ClassWriter#firstField firstField}).
+ */
+FieldWriter next;
+
+/**
+ * The class writer to which this field must be added.
+ */
+private ClassWriter cw;
+
+/**
+ * Access flags of this field.
+ */
+private int access;
+
+/**
+ * The index of the constant pool item that contains the name of this
+ * method.
+ */
+private int name;
+
+/**
+ * The index of the constant pool item that contains the descriptor of this
+ * field.
+ */
+private 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 <tt>null</tt>.
+ */
+private AnnotationWriter anns;
+
+/**
+ * The runtime invisible annotations of this field. May be <tt>null</tt>.
+ */
+private AnnotationWriter ianns;
+
+/**
+ * The non standard attributes of this field. May be <tt>null</tt>.
+ */
+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 <tt>null</tt>.
+ * @param value the field's constant value. May be <tt>null</tt>.
+ */
+protected FieldWriter(
+ final ClassWriter cw,
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value){
+ if(cw.firstField == null)
+ {
+ cw.firstField = this;
+ }
+ else
+ {
+ cw.lastField.next = this;
+ }
+ cw.lastField = this;
+ this.cw = cw;
+ this.access = access;
+ this.name = cw.newUTF8(name);
+ this.desc = cw.newUTF8(desc);
+ if(signature != null)
+ {
+ this.signature = cw.newUTF8(signature);
+ }
+ if(value != null)
+ {
+ this.value = cw.newConstItem(value).index;
+ }
+}
+
+// ------------------------------------------------------------------------
+// Implementation of the FieldVisitor interface
+// ------------------------------------------------------------------------
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ 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;
+}
+
+public void visitAttribute(final Attribute attr){
+ attr.next = attrs;
+ attrs = attr;
+}
+
+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
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ cw.newUTF8("Synthetic");
+ size += 6;
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ cw.newUTF8("Deprecated");
+ size += 6;
+ }
+ if(signature != 0)
+ {
+ cw.newUTF8("Signature");
+ size += 8;
+ }
+ if(anns != null)
+ {
+ cw.newUTF8("RuntimeVisibleAnnotations");
+ size += 8 + anns.getSize();
+ }
+ if(ianns != null)
+ {
+ cw.newUTF8("RuntimeInvisibleAnnotations");
+ size += 8 + ianns.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){
+ out.putShort(access).putShort(name).putShort(desc);
+ int attributeCount = 0;
+ if(value != 0)
+ {
+ ++attributeCount;
+ }
+ if((access & Opcodes.ACC_SYNTHETIC) != 0
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ ++attributeCount;
+ }
+ if(signature != 0)
+ {
+ ++attributeCount;
+ }
+ if(anns != null)
+ {
+ ++attributeCount;
+ }
+ if(ianns != 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
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+ }
+ if(signature != 0)
+ {
+ out.putShort(cw.newUTF8("Signature"));
+ out.putInt(2).putShort(signature);
+ }
+ if(anns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if(ianns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if(attrs != null)
+ {
+ attrs.put(cw, null, 0, -1, -1, out);
+ }
+}
+}
diff --git a/src/jvm/clojure/asm/Frame.java b/src/jvm/clojure/asm/Frame.java
new file mode 100644
index 00000000..810c82b1
--- /dev/null
+++ b/src/jvm/clojure/asm/Frame.java
@@ -0,0 +1,1506 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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 begining 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 in FrameVisitor, 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.
+ */
+final static int DIM = 0xF0000000;
+
+/**
+ * Constant to be added to a type to get a type with one more dimension.
+ */
+final static int ARRAY_OF = 0x10000000;
+
+/**
+ * Constant to be added to a type to get a type with one less dimension.
+ */
+final static int ELEMENT_OF = 0xF0000000;
+
+/**
+ * Mask to get the kind of a frame type.
+ *
+ * @see #BASE
+ * @see #LOCAL
+ * @see #STACK
+ */
+final static int KIND = 0xF000000;
+
+/**
+ * Mask to get the value of a frame type.
+ */
+final static int VALUE = 0xFFFFFF;
+
+/**
+ * Mask to get the kind of base types.
+ */
+final static int BASE_KIND = 0xFF00000;
+
+/**
+ * Mask to get the value of base types.
+ */
+final static int BASE_VALUE = 0xFFFFF;
+
+/**
+ * Kind of the types that are not relative to an input stack map frame.
+ */
+final static int BASE = 0x1000000;
+
+/**
+ * Base kind of the base reference types. The BASE_VALUE of such types is an
+ * index into the type table.
+ */
+final static 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).
+ */
+final static 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 final static 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 final static int STACK = 0x3000000;
+
+/**
+ * The TOP type. This is a BASE type.
+ */
+final static int TOP = BASE | 0;
+
+/**
+ * The BOOLEAN type. This is a BASE type mainly used for array types.
+ */
+final static int BOOLEAN = BASE | 9;
+
+/**
+ * The BYTE type. This is a BASE type mainly used for array types.
+ */
+final static int BYTE = BASE | 10;
+
+/**
+ * The CHAR type. This is a BASE type mainly used for array types.
+ */
+final static int CHAR = BASE | 11;
+
+/**
+ * The SHORT type. This is a BASE type mainly used for array types.
+ */
+final static int SHORT = BASE | 12;
+
+/**
+ * The INTEGER type. This is a BASE type.
+ */
+final static int INTEGER = BASE | 1;
+
+/**
+ * The FLOAT type. This is a BASE type.
+ */
+final static int FLOAT = BASE | 2;
+
+/**
+ * The DOUBLE type. This is a BASE type.
+ */
+final static int DOUBLE = BASE | 3;
+
+/**
+ * The LONG type. This is a BASE type.
+ */
+final static int LONG = BASE | 4;
+
+/**
+ * The NULL type. This is a BASE type.
+ */
+final static int NULL = BASE | 5;
+
+/**
+ * The UNINITIALIZED_THIS type. This is a BASE type.
+ */
+final static 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.
+ */
+final static 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, //UNUSED, // NOT VISITED
+ // 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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
+ * <i>every occurence</i> 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 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((MethodWriter.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:
+ default:
+ push(OBJECT | cw.addType("java/lang/String"));
+ }
+ 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);
+ }
+ }
+ 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);
+ }
+ }
+ 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.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 <tt>true</tt> 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 <tt>true</tt> 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 == LOCAL)
+ {
+ t = dim + inputLocals[s & VALUE];
+ }
+ else if(kind == STACK)
+ {
+ t = dim + inputStack[nStack - (s & VALUE)];
+ }
+ else
+ {
+ t = s;
+ }
+ }
+ }
+ 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 == LOCAL)
+ {
+ t = dim + inputLocals[s & VALUE];
+ }
+ else if(kind == STACK)
+ {
+ t = dim + inputStack[nStack - (s & VALUE)];
+ }
+ else
+ {
+ t = s;
+ }
+ 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 <tt>true</tt> 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 <tt>true</tt> if the type array has been modified by this
+ * operation.
+ */
+private 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((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,
+ // merge(u,t)=java/lang/Object
+ v = 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/jvm/clojure/asm/Handler.java b/src/jvm/clojure/asm/Handler.java
new file mode 100644
index 00000000..8e98a0bb
--- /dev/null
+++ b/src/jvm/clojure/asm/Handler.java
@@ -0,0 +1,70 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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
+ * <tt>null</tt> 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;
+}
diff --git a/src/jvm/clojure/asm/Item.java b/src/jvm/clojure/asm/Item.java
new file mode 100644
index 00000000..19923d72
--- /dev/null
+++ b/src/jvm/clojure/asm/Item.java
@@ -0,0 +1,258 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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}.
+ * <p/>
+ * 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.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:
+ default:
+ hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+ * strVal2.hashCode() * strVal3.hashCode());
+ }
+}
+
+/**
+ * Indicates if the given item is equal to this one.
+ *
+ * @param i the item to be compared to this one.
+ * @return <tt>true</tt> if the given item if equal to this one,
+ * <tt>false</tt> otherwise.
+ */
+boolean isEqualTo(final Item i){
+ if(i.type == type)
+ {
+ switch(type)
+ {
+ case ClassWriter.INT:
+ case ClassWriter.FLOAT:
+ return i.intVal == intVal;
+ case ClassWriter.TYPE_MERGED:
+ case ClassWriter.LONG:
+ case ClassWriter.DOUBLE:
+ return i.longVal == longVal;
+ case ClassWriter.UTF8:
+ case ClassWriter.STR:
+ case ClassWriter.CLASS:
+ case ClassWriter.TYPE_NORMAL:
+ return i.strVal1.equals(strVal1);
+ 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);
+ // ClassWriter.FIELD:
+ // ClassWriter.METH:
+ // ClassWriter.IMETH:
+ default:
+ return i.strVal1.equals(strVal1)
+ && i.strVal2.equals(strVal2)
+ && i.strVal3.equals(strVal3);
+ }
+ }
+ return false;
+}
+}
diff --git a/src/jvm/clojure/asm/Label.java b/src/jvm/clojure/asm/Label.java
new file mode 100644
index 00000000..a26020b4
--- /dev/null
+++ b/src/jvm/clojure/asm/Label.java
@@ -0,0 +1,437 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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.
+ *
+ * @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).
+ */
+final static int DEBUG = 1;
+
+/**
+ * Indicates if the position of this label is known.
+ */
+final static int RESOLVED = 2;
+
+/**
+ * Indicates if this label has been updated, after instruction resizing.
+ */
+final static int RESIZED = 4;
+
+/**
+ * Indicates if this basic block has been pushed in the basic block stack.
+ * See {@link MethodWriter#visitMaxs visitMaxs}.
+ */
+final static int PUSHED = 8;
+
+/**
+ * Indicates if this label is the target of a jump instruction, or the start
+ * of an exception handler.
+ */
+final static int TARGET = 16;
+
+/**
+ * Indicates if a stack map frame must be stored for this label.
+ */
+final static int STORE = 32;
+
+/**
+ * Indicates if this label corresponds to a reachable basic block.
+ */
+final static int REACHABLE = 64;
+
+/**
+ * Indicates if this basic block ends with a JSR instruction.
+ */
+final static int JSR = 128;
+
+/**
+ * Indicates if this basic block ends with a RET instruction.
+ */
+final static int RET = 256;
+
+/**
+ * Field used to associate user information to a label.
+ */
+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.
+ */
+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", <i>relatively</i> to the frame
+ * state at the beginning of the basic block, which is called the "input
+ * frame", and which is <i>unknown</i> during this step. The second step,
+ * in {@link MethodWriter#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.
+ * <p/>
+ * When only the maximum stack size is computed, this field is the number of
+ * elements in the input stack.
+ * <p/>
+ * 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.
+ *
+ * @see MethodWriter#visitMaxs
+ */
+Label next;
+
+// ------------------------------------------------------------------------
+// Constructor
+// ------------------------------------------------------------------------
+
+/**
+ * Constructs a new label.
+ */
+public Label(){
+}
+
+/**
+ * Constructs a new label.
+ *
+ * @param debug if this label is only used for debug attributes.
+ */
+Label(final boolean debug){
+ this.status = debug ? DEBUG : 0;
+}
+
+// ------------------------------------------------------------------------
+// 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. <i>This method is intended for
+ * {@link Attribute} sub classes, and is normally not needed by class
+ * generators or adapters.</i>
+ *
+ * @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 <tt>true</tt> if the reference must be stored in 4
+ * bytes, or <tt>false</tt> 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)
+ {
+ out.putInt(position - source);
+ }
+ else
+ {
+ out.putShort(position - source);
+ }
+ }
+ else
+ {
+ if(wideOffset)
+ {
+ addReference(-1 - source, out.length);
+ out.putInt(-1);
+ }
+ else
+ {
+ addReference(source, out.length);
+ out.putShort(-1);
+ }
+ }
+}
+
+/**
+ * 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 <tt>true</tt> 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 frame == null ? this : frame.owner;
+}
+
+// ------------------------------------------------------------------------
+// Overriden Object methods
+// ------------------------------------------------------------------------
+
+/**
+ * Returns a string representation of this label.
+ *
+ * @return a string representation of this label.
+ */
+public String toString(){
+ return "L" + System.identityHashCode(this);
+}
+}
diff --git a/src/jvm/clojure/asm/MethodAdapter.java b/src/jvm/clojure/asm/MethodAdapter.java
new file mode 100644
index 00000000..5970862a
--- /dev/null
+++ b/src/jvm/clojure/asm/MethodAdapter.java
@@ -0,0 +1,186 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * An empty {@link MethodVisitor} that delegates to another
+ * {@link MethodVisitor}. This class can be used as a super class to quickly
+ * implement usefull method adapter classes, just by overriding the necessary
+ * methods.
+ *
+ * @author Eric Bruneton
+ */
+public class MethodAdapter implements MethodVisitor{
+
+/**
+ * The {@link MethodVisitor} to which this adapter delegates calls.
+ */
+protected MethodVisitor mv;
+
+/**
+ * Constructs a new {@link MethodAdapter} object.
+ *
+ * @param mv the code visitor to which this adapter must delegate calls.
+ */
+public MethodAdapter(final MethodVisitor mv){
+ this.mv = mv;
+}
+
+public AnnotationVisitor visitAnnotationDefault(){
+ return mv.visitAnnotationDefault();
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ return mv.visitAnnotation(desc, visible);
+}
+
+public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible){
+ return mv.visitParameterAnnotation(parameter, desc, visible);
+}
+
+public void visitAttribute(final Attribute attr){
+ mv.visitAttribute(attr);
+}
+
+public void visitCode(){
+ mv.visitCode();
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+}
+
+public void visitInsn(final int opcode){
+ mv.visitInsn(opcode);
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ mv.visitIntInsn(opcode, operand);
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ mv.visitVarInsn(opcode, var);
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+ mv.visitTypeInsn(opcode, desc);
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ mv.visitFieldInsn(opcode, owner, name, desc);
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ mv.visitMethodInsn(opcode, owner, name, desc);
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+ mv.visitJumpInsn(opcode, label);
+}
+
+public void visitLabel(final Label label){
+ mv.visitLabel(label);
+}
+
+public void visitLdcInsn(final Object cst){
+ mv.visitLdcInsn(cst);
+}
+
+public void visitIincInsn(final int var, final int increment){
+ mv.visitIincInsn(var, increment);
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label labels[]){
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int keys[],
+ final Label labels[]){
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+}
+
+public void visitMultiANewArrayInsn(final String desc, final int dims){
+ mv.visitMultiANewArrayInsn(desc, dims);
+}
+
+public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type){
+ mv.visitTryCatchBlock(start, end, handler, type);
+}
+
+public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index){
+ mv.visitLocalVariable(name, desc, signature, start, end, index);
+}
+
+public void visitLineNumber(final int line, final Label start){
+ mv.visitLineNumber(line, start);
+}
+
+public void visitMaxs(final int maxStack, final int maxLocals){
+ mv.visitMaxs(maxStack, maxLocals);
+}
+
+public void visitEnd(){
+ mv.visitEnd();
+}
+}
diff --git a/src/jvm/clojure/asm/MethodVisitor.java b/src/jvm/clojure/asm/MethodVisitor.java
new file mode 100644
index 00000000..f77a92d3
--- /dev/null
+++ b/src/jvm/clojure/asm/MethodVisitor.java
@@ -0,0 +1,396 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+/**
+ * A visitor to visit a Java method. The methods of this interface must be
+ * called in the following order: [ <tt>visitAnnotationDefault</tt> ] (
+ * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
+ * <tt>visit</tt><i>X</i>Insn</tt> | <tt>visitLabel</tt> | <tt>visitTryCatchBlock</tt> |
+ * <tt>visitLocalVariable</tt> | <tt>visitLineNumber</tt>)* <tt>visitMaxs</tt> ]
+ * <tt>visitEnd</tt>. In addition, the <tt>visit</tt><i>X</i>Insn</tt>
+ * and <tt>visitLabel</tt> methods must be called in the sequential order of
+ * the bytecode instructions of the visited code, <tt>visitTryCatchBlock</tt>
+ * must be called <i>before</i> the labels passed as arguments have been
+ * visited, and the <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt>
+ * methods must be called <i>after</i> the labels passed as arguments have been
+ * visited.
+ *
+ * @author Eric Bruneton
+ */
+public interface MethodVisitor{
+
+// -------------------------------------------------------------------------
+// Annotations and non standard attributes
+// -------------------------------------------------------------------------
+
+/**
+ * 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 <tt>null</tt> 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.
+ */
+AnnotationVisitor visitAnnotationDefault();
+
+/**
+ * Visits an annotation of this method.
+ *
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+/**
+ * Visits an annotation of a parameter this method.
+ *
+ * @param parameter the parameter index.
+ * @param desc the class descriptor of the annotation class.
+ * @param visible <tt>true</tt> if the annotation is visible at runtime.
+ * @return a visitor to visit the annotation values, or <tt>null</tt> if
+ * this visitor is not interested in visiting this annotation.
+ */
+AnnotationVisitor visitParameterAnnotation(
+ int parameter,
+ String desc,
+ boolean visible);
+
+/**
+ * Visits a non standard attribute of this method.
+ *
+ * @param attr an attribute.
+ */
+void visitAttribute(Attribute attr);
+
+/**
+ * Starts the visit of the method's code, if any (i.e. non abstract method).
+ */
+void visitCode();
+
+/**
+ * Visits the current state of the local variables and operand stack
+ * elements. This method must(*) be called <i>just before</i> any
+ * instruction <b>i</b> that follows an unconditionnal branch instruction
+ * such as GOTO or THROW, that is the target of a jump instruction, or that
+ * starts an exception handler block. The visited types must describe the
+ * values of the local variables and of the operand stack elements <i>just
+ * before</i> <b>i</b> is executed. <br> <br> (*) this is mandatory only
+ * for classes whose version is greater than or equal to
+ * {@link Opcodes#V1_6 V1_6}. <br> <br> Packed frames are basically
+ * "deltas" from the state of the previous frame (very first frame is
+ * implicitly defined by the method's parameters and access flags): <ul>
+ * <li>{@link Opcodes#F_SAME} representing frame with exactly the same
+ * locals as the previous frame and with the empty stack.</li> <li>{@link Opcodes#F_SAME1}
+ * representing frame with exactly the same locals as the previous frame and
+ * with single value on the stack (<code>nStack</code> is 1 and
+ * <code>stack[0]</code> contains value for the type of the stack item).</li>
+ * <li>{@link Opcodes#F_APPEND} representing frame with current locals are
+ * the same as the locals in the previous frame, except that additional
+ * locals are defined (<code>nLocal</code> is 1, 2 or 3 and
+ * <code>local</code> elements contains values representing added types).</li>
+ * <li>{@link Opcodes#F_CHOP} representing frame with current locals are
+ * the same as the locals in the previous frame, except that the last 1-3
+ * locals are absent and with the empty stack (<code>nLocals</code> is 1,
+ * 2 or 3). </li> <li>{@link Opcodes#F_FULL} representing complete frame
+ * data.</li> </li> </ul>
+ *
+ * @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, or type
+ * descriptors for array types), 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.
+ */
+void visitFrame(
+ int type,
+ int nLocal,
+ Object[] local,
+ int nStack,
+ Object[] 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.
+ */
+void visitInsn(int opcode);
+
+/**
+ * Visits an instruction with a single int operand.
+ *
+ * @param opcode the opcode of the instruction to be visited. This opcode is
+ * either BIPUSH, SIPUSH or NEWARRAY.
+ * @param operand the operand of the instruction to be visited.<br> When
+ * opcode is BIPUSH, operand value should be between Byte.MIN_VALUE
+ * and Byte.MAX_VALUE.<br> When opcode is SIPUSH, operand value
+ * should be between Short.MIN_VALUE and Short.MAX_VALUE.<br> When
+ * opcode is NEWARRAY, operand value should be one of
+ * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR},
+ * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE},
+ * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT},
+ * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
+ */
+void visitIntInsn(int opcode, int 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.
+ */
+void visitVarInsn(int opcode, int var);
+
+/**
+ * Visits a type instruction. A type instruction is an instruction that
+ * takes a type descriptor as parameter.
+ *
+ * @param opcode the opcode of the type instruction to be visited. This
+ * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+ * @param desc the operand of the instruction to be visited. This operand is
+ * must be a fully qualified class name in internal form, or the type
+ * descriptor of an array type (see {@link Type Type}).
+ */
+void visitTypeInsn(int opcode, String desc);
+
+/**
+ * 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}).
+ */
+void visitFieldInsn(int opcode, String owner, String name, String 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}).
+ */
+void visitMethodInsn(int opcode, String owner, String name, String desc);
+
+/**
+ * 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.
+ */
+void visitJumpInsn(int opcode, Label label);
+
+/**
+ * Visits a label. A label designates the instruction that will be visited
+ * just after it.
+ *
+ * @param label a {@link Label Label} object.
+ */
+void visitLabel(Label label);
+
+// -------------------------------------------------------------------------
+// Special instructions
+// -------------------------------------------------------------------------
+
+/**
+ * Visits a LDC instruction.
+ *
+ * @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} (or a {@link Type} for
+ * <tt>.class</tt> constants, for classes whose version is 49.0 or
+ * more).
+ */
+void visitLdcInsn(Object cst);
+
+/**
+ * Visits an IINC instruction.
+ *
+ * @param var index of the local variable to be incremented.
+ * @param increment amount to increment the local variable by.
+ */
+void visitIincInsn(int var, int 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. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>min + i</tt> key.
+ */
+void visitTableSwitchInsn(int min, int max, Label dflt, Label 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. <tt>labels[i]</tt> is
+ * the beginning of the handler block for the <tt>keys[i]</tt> key.
+ */
+void visitLookupSwitchInsn(Label dflt, int keys[], Label 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.
+ */
+void visitMultiANewArrayInsn(String desc, int dims);
+
+// -------------------------------------------------------------------------
+// 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 <tt>null</tt> 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).
+ */
+void visitTryCatchBlock(Label start, Label end, Label handler, String type);
+
+/**
+ * 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
+ * <tt>null</tt> 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).
+ */
+void visitLocalVariable(
+ String name,
+ String desc,
+ String signature,
+ Label start,
+ Label end,
+ int index);
+
+/**
+ * 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 <tt>start</tt> has not already been
+ * visited by this visitor (by the {@link #visitLabel visitLabel}
+ * method).
+ */
+void visitLineNumber(int line, Label 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.
+ */
+void visitMaxs(int maxStack, int 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.
+ */
+void visitEnd();
+}
diff --git a/src/jvm/clojure/asm/MethodWriter.java b/src/jvm/clojure/asm/MethodWriter.java
new file mode 100644
index 00000000..19239738
--- /dev/null
+++ b/src/jvm/clojure/asm/MethodWriter.java
@@ -0,0 +1,3029 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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 implements MethodVisitor{
+
+/**
+ * Pseudo access flag used to denote constructors.
+ */
+final static int ACC_CONSTRUCTOR = 262144;
+
+/**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is zero.
+ */
+final static 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
+ */
+final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
+
+/**
+ * Reserved for future use
+ */
+final static 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;
+ */
+final static 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.
+ */
+final static 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;
+ */
+final static 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.
+ */
+final static int APPEND_FRAME = 252; // to 254 // fc-fe
+
+/**
+ * Full frame
+ */
+final static 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 final static int FRAMES = 0;
+
+/**
+ * Indicates that the maximum stack size and number of local variables must
+ * be automatically computed.
+ *
+ * @see #compute
+ */
+private final static int MAXS = 1;
+
+/**
+ * Indicates that nothing must be automatically computed.
+ *
+ * @see #compute
+ */
+private final static int NOTHING = 2;
+
+/**
+ * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
+ */
+MethodWriter next;
+
+/**
+ * The class writer to which this method must be added.
+ */
+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 int name;
+
+/**
+ * The index of the constant pool item that contains the descriptor of this
+ * method.
+ */
+private int desc;
+
+/**
+ * The descriptor of this method.
+ */
+private 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 <code>cw.cr</code>. More
+ * precisely, this field gives the index of the first byte to copied from
+ * <code>cw.cr.b</code>.
+ */
+int classReaderOffset;
+
+/**
+ * If not zero, indicates that the code of this method must be copied from
+ * the ClassReader associated to this writer in <code>cw.cr</code>. More
+ * precisely, this field gives the number of bytes to copied from
+ * <code>cw.cr.b</code>.
+ */
+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 <tt>null</tt>.
+ */
+private ByteVector annd;
+
+/**
+ * The runtime visible annotations of this method. May be <tt>null</tt>.
+ */
+private AnnotationWriter anns;
+
+/**
+ * The runtime invisible annotations of this method. May be <tt>null</tt>.
+ */
+private AnnotationWriter ianns;
+
+/**
+ * The runtime visible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+private AnnotationWriter[] panns;
+
+/**
+ * The runtime invisible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+private AnnotationWriter[] ipanns;
+
+/**
+ * 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 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;
+
+/**
+ * Index of the next element to be added in {@link #frame}.
+ */
+private int frameIndex;
+
+/**
+ * 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 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 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;
+
+/**
+ * Indicates if the instructions contain at least one JSR instruction.
+ */
+private boolean jsr;
+
+// ------------------------------------------------------------------------
+
+/*
+ * 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 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 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 <tt>stackSize</tt>.
+ */
+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 <tt>stackSize</tt>.
+ */
+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 <tt>null</tt>.
+ * @param exceptions the internal names of the method's exceptions. May be
+ * <tt>null</tt>.
+ * @param computeMaxs <tt>true</tt> if the maximum stack size and number
+ * of local variables must be automatically computed.
+ * @param computeFrames <tt>true</tt> 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){
+ if(cw.firstMethod == null)
+ {
+ cw.firstMethod = this;
+ }
+ else
+ {
+ cw.lastMethod.next = this;
+ }
+ cw.lastMethod = this;
+ this.cw = cw;
+ this.access = access;
+ this.name = cw.newUTF8(name);
+ this.desc = cw.newUTF8(desc);
+ this.descriptor = desc;
+ 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)
+ {
+ if(computeFrames && name.equals("<init>"))
+ {
+ this.access |= ACC_CONSTRUCTOR;
+ }
+ // updates maxLocals
+ int size = getArgumentsAndReturnSizes(descriptor) >> 2;
+ if((access & Opcodes.ACC_STATIC) != 0)
+ {
+ --size;
+ }
+ maxLocals = size;
+ // creates and visits the label for the first basic block
+ labels = new Label();
+ labels.status |= Label.PUSHED;
+ visitLabel(labels);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Implementation of the MethodVisitor interface
+// ------------------------------------------------------------------------
+
+public AnnotationVisitor visitAnnotationDefault(){
+ annd = new ByteVector();
+ return new AnnotationWriter(cw, false, annd, null, 0);
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ 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;
+}
+
+public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible){
+ 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)
+ {
+ 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;
+}
+
+public void visitAttribute(final Attribute attr){
+ if(attr.isCodeAttribute())
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ else
+ {
+ attr.next = attrs;
+ attrs = attr;
+ }
+}
+
+public void visitCode(){
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+ if(compute == FRAMES)
+ {
+ return;
+ }
+
+ if(type == Opcodes.F_NEW)
+ {
+ 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;
+ }
+
+ switch(type)
+ {
+ case Opcodes.F_FULL:
+ 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:
+ stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)
+ .putShort(delta);
+ for(int i = 0; i < nLocal; ++i)
+ {
+ writeFrameType(local[i]);
+ }
+ break;
+ case Opcodes.F_CHOP:
+ 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;
+ }
+}
+
+public void visitInsn(final int opcode){
+ // 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();
+ }
+ }
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ // 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);
+ }
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ // 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());
+ }
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+ Item i = cw.newClassItem(desc);
+ // 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);
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ 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);
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+ 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 = 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(itf)
+ {
+ if(argSize == 0)
+ {
+ argSize = getArgumentsAndReturnSizes(desc);
+ i.intVal = argSize;
+ }
+ code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
+ }
+ else
+ {
+ code.put12(opcode, i.index);
+ }
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+ 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)
+ {
+ jsr = true;
+ 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
+ * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
+ * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
+ * 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();
+ }
+ }
+}
+
+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;
+ }
+}
+
+public void visitLdcInsn(final Object cst){
+ 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);
+ }
+}
+
+public void visitIincInsn(final int var, final int increment){
+ 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);
+ }
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label labels[]){
+ // adds the instruction to the bytecode of the method
+ int source = code.length;
+ code.putByte(Opcodes.TABLESWITCH);
+ code.length += (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);
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int keys[],
+ final Label labels[]){
+ // adds the instruction to the bytecode of the method
+ int source = code.length;
+ code.putByte(Opcodes.LOOKUPSWITCH);
+ code.length += (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();
+ }
+}
+
+public void visitMultiANewArrayInsn(final String desc, final int dims){
+ 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);
+}
+
+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;
+}
+
+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;
+ }
+ }
+}
+
+public void visitLineNumber(final int line, final Label start){
+ if(lineNumber == null)
+ {
+ lineNumber = new ByteVector();
+ }
+ ++lineNumberCount;
+ lineNumber.putShort(start.position);
+ lineNumber.putShort(line);
+}
+
+public void visitMaxs(final int maxStack, final int maxLocals){
+ if(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 reacheable jump target must be stored in the stack map
+ if((l.status & Label.TARGET) != 0)
+ {
+ l.status |= Label.STORE;
+ }
+ // all visited labels are reacheable, 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;
+ }
+ }
+ this.maxStack = max;
+
+ // 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)
+ {
+ // 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
+ startFrame(start, 0, 1);
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType("java/lang/Throwable");
+ endFrame();
+ }
+ }
+ l = l.successor;
+ }
+ }
+ 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)
+ {
+ // 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;
+ }
+ else
+ {
+ b.next = l.successors;
+ l.successors = b;
+ }
+ // goes to the next label
+ l = l.successor;
+ }
+ handler = handler.next;
+ }
+
+ if(jsr)
+ {
+ // 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, and
+ * stores this set as a bit set in the {@link Label#status}
+ * field. Subroutines are numbered with powers of two, from
+ * 0x1000 to 0x80000000 (so there must be at most 20 subroutines
+ * in a method).
+ */
+ // finds the basic blocks that belong to the "main" subroutine
+ int id = 0x1000;
+ findSubroutine(labels, id);
+ // 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 does not have an id yet...
+ if((subroutine.status & ~0xFFF) == 0)
+ {
+ // ...assigns it a new id and finds its basic blocks
+ id = id << 1;
+ findSubroutine(subroutine, id);
+ }
+ }
+ l = l.successor;
+ }
+ // second step: finds the successors of RET blocks
+ findSubroutineSuccessors(0x1000, new Label[10], 0);
+ }
+
+ /*
+ * 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;
+ }
+ // analyses 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 = max;
+ }
+ else
+ {
+ this.maxStack = maxStack;
+ this.maxLocals = maxLocals;
+ }
+}
+
+public void visitEnd(){
+}
+
+// ------------------------------------------------------------------------
+// Utility methods: control flow analysis algorithm
+// ------------------------------------------------------------------------
+
+/**
+ * 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 =
+ * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
+ * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
+ */
+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;
+ }
+ }
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Finds the basic blocks that belong to a given subroutine, and marks these
+ * blocks as belonging to this subroutine (by using {@link Label#status} as
+ * a bit set (see {@link #visitMaxs}). This recursive method follows the
+ * control flow graph to find all the blocks that are reachable from the
+ * given block WITHOUT following any JSR target.
+ *
+ * @param block a block that belongs to the subroutine
+ * @param id the id of this subroutine
+ */
+private void findSubroutine(final Label block, final int id){
+ // if 'block' is already marked as belonging to subroutine 'id', returns
+ if((block.status & id) != 0)
+ {
+ return;
+ }
+ // marks 'block' as belonging to subroutine 'id'
+ block.status |= id;
+ // calls this method recursively on each successor, except JSR targets
+ Edge e = block.successors;
+ while(e != null)
+ {
+ // if 'block' is a JSR block, then 'block.successors.next' leads
+ // to the JSR target (see {@link #visitJumpInsn}) and must therefore
+ // not be followed
+ if((block.status & Label.JSR) == 0 || e != block.successors.next)
+ {
+ findSubroutine(e.successor, id);
+ }
+ e = e.next;
+ }
+}
+
+/**
+ * Finds the successors of the RET blocks of the specified subroutine, and
+ * of any nested subroutine it calls.
+ *
+ * @param id id of the subroutine whose RET block successors must be found.
+ * @param JSRs the JSR blocks that were followed to reach this subroutine.
+ * @param nJSRs number of JSR blocks in the JSRs array.
+ */
+private void findSubroutineSuccessors(
+ final int id,
+ final Label[] JSRs,
+ final int nJSRs){
+ // iterates over all the basic blocks...
+ Label l = labels;
+ while(l != null)
+ {
+ // for those that belong to subroutine 'id'...
+ if((l.status & id) != 0)
+ {
+ if((l.status & Label.JSR) != 0)
+ {
+ // finds the subroutine to which 'l' leads by following the
+ // second edge of l.successors (see {@link #visitJumpInsn})
+ int nId = l.successors.next.successor.status & ~0xFFF;
+ if(nId != id)
+ {
+ // calls this method recursively with l pushed onto the
+ // JSRs stack to find the successors of the RET blocks
+ // of this nested subroutine 'nId'
+ JSRs[nJSRs] = l;
+ findSubroutineSuccessors(nId, JSRs, nJSRs + 1);
+ }
+ }
+ else if((l.status & Label.RET) != 0)
+ {
+ /*
+ * finds the JSR block in the JSRs stack that corresponds to
+ * this RET block, and updates the successors of this RET
+ * block accordingly. This corresponding JSR is the one that
+ * leads to the subroutine to which the RET block belongs.
+ * But the RET block can belong to several subroutines (if a
+ * nested subroutine returns to its parent subroutine
+ * implicitely, without a RET). So, in fact, the JSR that
+ * corresponds to this RET is the first block in the JSRs
+ * stack, starting from the bottom of the stack, that leads
+ * to a subroutine to which the RET block belongs.
+ */
+ for(int i = 0; i < nJSRs; ++i)
+ {
+ int JSRstatus = JSRs[i].successors.next.successor.status;
+ if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0)
+ {
+ Edge e = new Edge();
+ e.info = l.inputStackTop;
+ e.successor = JSRs[i].successors.successor;
+ e.next = l.successors;
+ l.successors = e;
+ break;
+ }
+ }
+ }
+ }
+ l = l.successor;
+ }
+}
+
+// ------------------------------------------------------------------------
+// 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
+ 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();
+}
+
+/**
+ * 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.
+ */
+private void 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;
+ frameIndex = 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;
+ }
+ if(resize)
+ {
+ // replaces the temporary jump opcodes introduced by Label.resolve.
+ resizeInstructions();
+ }
+ int size = 8;
+ if(code.length > 0)
+ {
+ 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(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
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ cw.newUTF8("Synthetic");
+ size += 6;
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ cw.newUTF8("Deprecated");
+ size += 6;
+ }
+ if(signature != null)
+ {
+ cw.newUTF8("Signature");
+ cw.newUTF8(signature);
+ size += 8;
+ }
+ if(annd != null)
+ {
+ cw.newUTF8("AnnotationDefault");
+ size += 6 + annd.length;
+ }
+ if(anns != null)
+ {
+ cw.newUTF8("RuntimeVisibleAnnotations");
+ size += 8 + anns.getSize();
+ }
+ if(ianns != null)
+ {
+ cw.newUTF8("RuntimeInvisibleAnnotations");
+ size += 8 + ianns.getSize();
+ }
+ if(panns != null)
+ {
+ cw.newUTF8("RuntimeVisibleParameterAnnotations");
+ size += 7 + 2 * panns.length;
+ for(int i = panns.length - 1; i >= 0; --i)
+ {
+ size += panns[i] == null ? 0 : panns[i].getSize();
+ }
+ }
+ if(ipanns != null)
+ {
+ cw.newUTF8("RuntimeInvisibleParameterAnnotations");
+ size += 7 + 2 * ipanns.length;
+ for(int i = ipanns.length - 1; i >= 0; --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){
+ out.putShort(access).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
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ ++attributeCount;
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ ++attributeCount;
+ }
+ if(signature != null)
+ {
+ ++attributeCount;
+ }
+ if(annd != null)
+ {
+ ++attributeCount;
+ }
+ if(anns != null)
+ {
+ ++attributeCount;
+ }
+ if(ianns != null)
+ {
+ ++attributeCount;
+ }
+ if(panns != null)
+ {
+ ++attributeCount;
+ }
+ if(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(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(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(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
+ && (cw.version & 0xffff) < Opcodes.V1_5)
+ {
+ out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+ }
+ if((access & Opcodes.ACC_DEPRECATED) != 0)
+ {
+ out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+ }
+ if(signature != null)
+ {
+ out.putShort(cw.newUTF8("Signature"))
+ .putInt(2)
+ .putShort(cw.newUTF8(signature));
+ }
+ if(annd != null)
+ {
+ out.putShort(cw.newUTF8("AnnotationDefault"));
+ out.putInt(annd.length);
+ out.putByteArray(annd.data, 0, annd.length);
+ }
+ if(anns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+ anns.put(out);
+ }
+ if(ianns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+ ianns.put(out);
+ }
+ if(panns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
+ AnnotationWriter.put(panns, out);
+ }
+ if(ipanns != null)
+ {
+ out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
+ AnnotationWriter.put(ipanns, 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. <p> <i>This method must be called after all the method
+ * that is being built has been visited</i>. 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 <l> instruction with
+ // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
+ // is the "opposite" opcode of IFxxx (i.e.,
+ // IFNE for IFEQ) and where <l'> 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:
+ 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
+ // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
+ // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
+ // and where <l'> 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.length += (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.length += (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:
+ newCode.putByteArray(b, u, 5);
+ u += 5;
+ break;
+ // case MANA_INSN:
+ default:
+ newCode.putByteArray(b, u, 4);
+ u += 4;
+ break;
+ }
+ }
+
+ // recomputes the stack map frames
+ if(frameCount > 0)
+ {
+ if(compute == FRAMES)
+ {
+ frameCount = 0;
+ stackMap = null;
+ previousFrame = null;
+ frame = null;
+ Frame f = new Frame();
+ f.owner = labels;
+ Type[] args = Type.getArgumentTypes(descriptor);
+ f.initInputFrame(cw, access, args, maxLocals);
+ visitFrame(f);
+ Label l = labels;
+ while(l != null)
+ {
+ /*
+ * here we need the original label position. getNewOffset
+ * must therefore never have been called for this label.
+ */
+ u = l.position - 3;
+ if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u]))
+ {
+ getNewOffset(allIndexes, allSizes, l);
+ // TODO update offsets in UNINITIALIZED values
+ visitFrame(l.frame);
+ }
+ l = l.successor;
+ }
+ }
+ else
+ {
+ /*
+ * 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. <p> Note: it is possible
+ * to have several entries for the same instruction in the <tt>indexes</tt>
+ * and <tt>sizes</tt>: 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 <i>last</i>
+ * byte, plus one (or, in other words, by the index of the <i>first</i>
+ * byte of the <i>next</i> instruction).
+ * @param sizes the number of bytes to be <i>added</i> to the above
+ * instructions. More precisely, for each i < <tt>len</tt>,
+ * <tt>sizes</tt>[i] bytes will be added at the end of the
+ * instruction designated by <tt>indexes</tt>[i] or, if
+ * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
+ * bytes of the instruction will be removed (the instruction size
+ * <i>must not</i> 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 <i>last</i>
+ * byte, plus one (or, in other words, by the index of the <i>first</i>
+ * byte of the <i>next</i> instruction).
+ * @param sizes the number of bytes to be <i>added</i> to the above
+ * instructions. More precisely, for each i < <tt>len</tt>,
+ * <tt>sizes</tt>[i] bytes will be added at the end of the
+ * instruction designated by <tt>indexes</tt>[i] or, if
+ * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
+ * bytes of the instruction will be removed (the instruction size
+ * <i>must not</i> 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/jvm/clojure/asm/Opcodes.java b/src/jvm/clojure/asm/Opcodes.java
new file mode 100644
index 00000000..9c42750f
--- /dev/null
+++ b/src/jvm/clojure/asm/Opcodes.java
@@ -0,0 +1,341 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.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{
+
+// 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;
+
+// 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
+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
+int ACC_ANNOTATION = 0x2000; // class
+int ACC_ENUM = 0x4000; // class(?) field inner
+
+// ASM specific pseudo access flags
+
+int ACC_DEPRECATED = 131072; // 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;
+
+// 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 UNUSED = 186; // NOT VISITED
+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/jvm/clojure/asm/Type.java b/src/jvm/clojure/asm/Type.java
new file mode 100644
index 00000000..4cde14a1
--- /dev/null
+++ b/src/jvm/clojure/asm/Type.java
@@ -0,0 +1,872 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * A Java 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 <tt>void</tt> type. See {@link #getSort getSort}.
+ */
+public final static int VOID = 0;
+
+/**
+ * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
+ */
+public final static int BOOLEAN = 1;
+
+/**
+ * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
+ */
+public final static int CHAR = 2;
+
+/**
+ * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
+ */
+public final static int BYTE = 3;
+
+/**
+ * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
+ */
+public final static int SHORT = 4;
+
+/**
+ * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
+ */
+public final static int INT = 5;
+
+/**
+ * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
+ */
+public final static int FLOAT = 6;
+
+/**
+ * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
+ */
+public final static int LONG = 7;
+
+/**
+ * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
+ */
+public final static int DOUBLE = 8;
+
+/**
+ * The sort of array reference types. See {@link #getSort getSort}.
+ */
+public final static int ARRAY = 9;
+
+/**
+ * The sort of object reference type. See {@link #getSort getSort}.
+ */
+public final static int OBJECT = 10;
+
+/**
+ * The <tt>void</tt> type.
+ */
+public final static Type VOID_TYPE = new Type(VOID);
+
+/**
+ * The <tt>boolean</tt> type.
+ */
+public final static Type BOOLEAN_TYPE = new Type(BOOLEAN);
+
+/**
+ * The <tt>char</tt> type.
+ */
+public final static Type CHAR_TYPE = new Type(CHAR);
+
+/**
+ * The <tt>byte</tt> type.
+ */
+public final static Type BYTE_TYPE = new Type(BYTE);
+
+/**
+ * The <tt>short</tt> type.
+ */
+public final static Type SHORT_TYPE = new Type(SHORT);
+
+/**
+ * The <tt>int</tt> type.
+ */
+public final static Type INT_TYPE = new Type(INT);
+
+/**
+ * The <tt>float</tt> type.
+ */
+public final static Type FLOAT_TYPE = new Type(FLOAT);
+
+/**
+ * The <tt>long</tt> type.
+ */
+public final static Type LONG_TYPE = new Type(LONG);
+
+/**
+ * The <tt>double</tt> type.
+ */
+public final static Type DOUBLE_TYPE = new Type(DOUBLE);
+
+// ------------------------------------------------------------------------
+// Fields
+// ------------------------------------------------------------------------
+
+/**
+ * The sort of this Java type.
+ */
+private final int sort;
+
+/**
+ * A buffer containing the descriptor of this Java type. This field is only
+ * used for reference types.
+ */
+private char[] buf;
+
+/**
+ * The offset of the descriptor of this Java type in {@link #buf buf}. This
+ * field is only used for reference types.
+ */
+private int off;
+
+/**
+ * The length of the descriptor of this Java type.
+ */
+private int len;
+
+// ------------------------------------------------------------------------
+// Constructors
+// ------------------------------------------------------------------------
+
+/**
+ * Constructs a primitive type.
+ *
+ * @param sort the sort of the primitive type to be constructed.
+ */
+private Type(final int sort){
+ this.sort = sort;
+ this.len = 1;
+}
+
+/**
+ * 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 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 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 {@link Type#OBJECT} type for the given internal class name.
+ * This is a shortcut method for <code>Type.getType("L"+name+";")</code>.
+ * <i>Note that opposed to {@link Type#getType(String)}, this method takes
+ * internal class names and not class descriptor.</i>
+ *
+ * @param name an internal class name.
+ * @return the the {@link Type#OBJECT} type for the given class name.
+ */
+public static Type getObjectType(String name){
+ int l = name.length();
+ char[] buf = new char[l + 2];
+ buf[0] = 'L';
+ buf[l + 1] = ';';
+ name.getChars(0, l, buf, 1);
+ return new Type(OBJECT, buf, 0, l + 2);
+}
+
+/**
+ * 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;
+ 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());
+}
+
+/**
+ * Returns the Java type corresponding to the given type descriptor.
+ *
+ * @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':
+ default:
+ len = 1;
+ while(buf[off + len] != ';')
+ {
+ ++len;
+ }
+ return new Type(OBJECT, buf, off, len + 1);
+ }
+}
+
+// ------------------------------------------------------------------------
+// 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} or
+ * {@link #OBJECT OBJECT}.
+ */
+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 name of the class corresponding to this type.
+ *
+ * @return the fully qualified 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:
+ default:
+ return new String(buf, off + 1, len - 2).replace('/', '.');
+ }
+}
+
+/**
+ * Returns the internal name of the class corresponding to this object type.
+ * The internal name of a class is its fully qualified name, where '.' are
+ * replaced by '/'. This method should only be used for an object type.
+ *
+ * @return the internal name of the class corresponding to this object type.
+ */
+public String getInternalName(){
+ return new String(buf, off + 1, len - 2);
+}
+
+// ------------------------------------------------------------------------
+// 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){
+ switch(sort)
+ {
+ case VOID:
+ buf.append('V');
+ return;
+ case BOOLEAN:
+ buf.append('Z');
+ return;
+ case CHAR:
+ buf.append('C');
+ return;
+ case BYTE:
+ buf.append('B');
+ return;
+ case SHORT:
+ buf.append('S');
+ return;
+ case INT:
+ buf.append('I');
+ return;
+ case FLOAT:
+ buf.append('F');
+ return;
+ case LONG:
+ buf.append('J');
+ return;
+ case DOUBLE:
+ buf.append('D');
+ return;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ 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, where '.' are replaced by '/'.
+ *
+ * @param c an object 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.
+ *
+ * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
+ * <tt>double</tt>, and 1 otherwise.
+ */
+public int getSize(){
+ return sort == LONG || sort == DOUBLE ? 2 : 1;
+}
+
+/**
+ * Returns a JVM instruction opcode adapted to this Java type.
+ *
+ * @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 <tt>float</tt> and
+ * <tt>opcode</tt> is IRETURN, this method returns FRETURN.
+ */
+public int getOpcode(final int opcode){
+ if(opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE)
+ {
+ switch(sort)
+ {
+ case BOOLEAN:
+ case BYTE:
+ return opcode + 5;
+ case CHAR:
+ return opcode + 6;
+ case SHORT:
+ return opcode + 7;
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 4;
+ }
+ }
+ else
+ {
+ switch(sort)
+ {
+ case VOID:
+ return opcode + 5;
+ case BOOLEAN:
+ case CHAR:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return opcode;
+ case FLOAT:
+ return opcode + 2;
+ case LONG:
+ return opcode + 1;
+ case DOUBLE:
+ return opcode + 3;
+ // case ARRAY:
+ // case OBJECT:
+ default:
+ return opcode + 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 <tt>true</tt> if the given object is equal to this type.
+ */
+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 == Type.OBJECT || sort == Type.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.
+ */
+public int hashCode(){
+ int hc = 13 * sort;
+ if(sort == Type.OBJECT || sort == Type.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.
+ */
+public String toString(){
+ return getDescriptor();
+}
+}
diff --git a/src/jvm/clojure/asm/commons/AdviceAdapter.java b/src/jvm/clojure/asm/commons/AdviceAdapter.java
new file mode 100644
index 00000000..a03ccb0d
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/AdviceAdapter.java
@@ -0,0 +1,681 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link clojure.asm.MethodAdapter} to insert before, after and around
+ * advices in methods and constructors. <p> The behavior for constructors is
+ * like this: <ol>
+ * <p/>
+ * <li>as long as the INVOKESPECIAL for the object initialization has not been
+ * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
+ * <p/>
+ * <li>when this one is reached, it is only added in the ctor code visitor and
+ * a JP invoke is added</li>
+ * <p/>
+ * <li>after that, only the other code visitor receives the instructions</li>
+ * <p/>
+ * </ol>
+ *
+ * @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 ArrayList stackFrame;
+private HashMap branches;
+
+/**
+ * Creates a new {@link AdviceAdapter}.
+ *
+ * @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}).
+ */
+public AdviceAdapter(
+ final MethodVisitor mv,
+ final int access,
+ final String name,
+ final String desc){
+ super(mv, access, name, desc);
+ methodAccess = access;
+ methodDesc = desc;
+
+ constructor = "<init>".equals(name);
+}
+
+public void visitCode(){
+ mv.visitCode();
+ if(!constructor)
+ {
+ superInitialized = true;
+ onMethodEnter();
+ }
+ else
+ {
+ stackFrame = new ArrayList();
+ branches = new HashMap();
+ }
+}
+
+public void visitLabel(final Label label){
+ mv.visitLabel(label);
+
+ if(constructor && branches != null)
+ {
+ ArrayList frame = (ArrayList) branches.get(label);
+ if(frame != null)
+ {
+ stackFrame = frame;
+ branches.remove(label);
+ }
+ }
+}
+
+public void visitInsn(final int opcode){
+ if(constructor)
+ {
+ 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();
+ 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 Opcodes.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:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o1);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP_X2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ pushValue(o1);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2_X1:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case DUP2_X2:
+ // TODO optimize this
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ Object o3 = popValue();
+ Object o4 = popValue();
+ pushValue(o2);
+ pushValue(o1);
+ pushValue(o4);
+ pushValue(o3);
+ pushValue(o2);
+ pushValue(o1);
+ }
+ break;
+
+ case SWAP:
+ {
+ Object o1 = popValue();
+ Object o2 = popValue();
+ pushValue(o1);
+ pushValue(o2);
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch(opcode)
+ {
+ case RETURN:
+ case IRETURN:
+ case FRETURN:
+ case ARETURN:
+ case LRETURN:
+ case DRETURN:
+ case ATHROW:
+ onMethodExit(opcode);
+ break;
+ }
+ }
+ mv.visitInsn(opcode);
+}
+
+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;
+ }
+ }
+}
+
+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);
+ }
+ }
+ }
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ mv.visitIntInsn(opcode, operand);
+
+ if(constructor && opcode != NEWARRAY)
+ {
+ pushValue(OTHER);
+ }
+}
+
+public void visitLdcInsn(final Object cst){
+ mv.visitLdcInsn(cst);
+
+ if(constructor)
+ {
+ pushValue(OTHER);
+ if(cst instanceof Double || cst instanceof Long)
+ {
+ pushValue(OTHER);
+ }
+ }
+}
+
+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);
+ }
+}
+
+public void visitTypeInsn(final int opcode, final String name){
+ mv.visitTypeInsn(opcode, name);
+
+ // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
+ if(constructor && opcode == NEW)
+ {
+ pushValue(OTHER);
+ }
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ mv.visitMethodInsn(opcode, owner, name, desc);
+
+ 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);
+ }
+ }
+ }
+}
+
+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);
+ }
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels){
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+
+ if(constructor)
+ {
+ popValue();
+ addBranches(dflt, labels);
+ }
+}
+
+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);
+ }
+}
+
+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;
+ }
+ ArrayList frame = new ArrayList();
+ frame.addAll(stackFrame);
+ branches.put(label, frame);
+}
+
+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. <br><br>
+ * <p/>
+ * <i>Custom code can use or change all the local variables, but should not
+ * change state of the stack.</i>
+ */
+protected abstract 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:
+ * <p/>
+ * <pre>
+ * 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");
+ * }
+ * <p/>
+ * // an actual call back method
+ * public static void onExit(int opcode, Object param) {
+ * ...
+ * </pre>
+ * <p/>
+ * <br><br>
+ * <p/>
+ * <i>Custom code can use or change all the local variables, but should not
+ * change state of the stack.</i>
+ *
+ * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
+ * DRETURN or ATHROW
+ */
+protected abstract void onMethodExit(int opcode);
+
+// TODO onException, onMethodCall
+
+}
diff --git a/src/jvm/clojure/asm/commons/AnalyzerAdapter.java b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java
new file mode 100644
index 00000000..3b62d754
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java
@@ -0,0 +1,938 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import clojure.asm.Label;
+import clojure.asm.MethodAdapter;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link MethodAdapter} 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 clojure.asm.ClassReader#EXPAND_FRAMES} option. Each visit<i>XXX</i>
+ * 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 <i>before</i> each instruction by reading
+ * the value of these fields in its visit<i>XXX</i> methods (this requires a
+ * reference to the AnalyzerAdapter that is before it in the chain).
+ *
+ * @author Eric Bruneton
+ */
+public class AnalyzerAdapter extends MethodAdapter{
+
+/**
+ * <code>List</code> 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 a
+ * two elements, the second one being TOP). Reference types are represented
+ * by String objects (representing internal names, or type descriptors for
+ * array types), and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized value).
+ * This field is <tt>null</tt> for unreacheable instructions.
+ */
+public List locals;
+
+/**
+ * <code>List</code> 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 a
+ * two elements, the second one being TOP). Reference types are represented
+ * by String objects (representing internal names, or type descriptors for
+ * array types), and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized value).
+ * This field is <tt>null</tt> for unreacheable instructions.
+ */
+public List stack;
+
+/**
+ * The labels that designate the next instruction to be visited. May be
+ * <tt>null</tt>.
+ */
+private List labels;
+
+/**
+ * Information about uninitialized types in the current execution frame.
+ * This map associates internal names to Label objects. Each label
+ * designates a NEW instruction that created the currently uninitialized
+ * types, and the associated internal name represents the NEW operand, i.e.
+ * the final, initialized type value.
+ */
+private Map uninitializedTypes;
+
+/**
+ * The maximum stack size of this method.
+ */
+private int maxStack;
+
+/**
+ * The maximum number of local variables of this method.
+ */
+private int maxLocals;
+
+/**
+ * Creates a new {@link AnalyzerAdapter}.
+ *
+ * @param owner the owner's class name.
+ * @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}).
+ * @param mv the method visitor to which this adapter delegates calls. May
+ * be <tt>null</tt>.
+ */
+public AnalyzerAdapter(
+ final String owner,
+ final int access,
+ final String name,
+ final String desc,
+ final MethodVisitor mv){
+ super(mv);
+ locals = new ArrayList();
+ stack = new ArrayList();
+ uninitializedTypes = new HashMap();
+
+ if((access & Opcodes.ACC_STATIC) == 0)
+ {
+ if(name.equals("<init>"))
+ {
+ locals.add(Opcodes.UNINITIALIZED_THIS);
+ }
+ else
+ {
+ locals.add(owner);
+ }
+ }
+ Type[] types = Type.getArgumentTypes(desc);
+ for(int i = 0; i < types.length; ++i)
+ {
+ Type type = types[i];
+ switch(type.getSort())
+ {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ locals.add(Opcodes.INTEGER);
+ break;
+ case Type.FLOAT:
+ locals.add(Opcodes.FLOAT);
+ break;
+ case Type.LONG:
+ locals.add(Opcodes.LONG);
+ locals.add(Opcodes.TOP);
+ break;
+ case Type.DOUBLE:
+ locals.add(Opcodes.DOUBLE);
+ locals.add(Opcodes.TOP);
+ break;
+ case Type.ARRAY:
+ locals.add(types[i].getDescriptor());
+ break;
+ // case Type.OBJECT:
+ default:
+ locals.add(types[i].getInternalName());
+ }
+ }
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+ if(type != Opcodes.F_NEW)
+ { // uncompressed frame
+ throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
+ }
+
+ if(mv != null)
+ {
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ }
+
+ if(this.locals != null)
+ {
+ this.locals.clear();
+ this.stack.clear();
+ }
+ else
+ {
+ this.locals = new ArrayList();
+ this.stack = new ArrayList();
+ }
+ visitFrameTypes(nLocal, local, this.locals);
+ visitFrameTypes(nStack, stack, this.stack);
+ maxStack = Math.max(maxStack, this.stack.size());
+}
+
+private void visitFrameTypes(
+ final int n,
+ final Object[] types,
+ final List result){
+ for(int i = 0; i < n; ++i)
+ {
+ Object type = types[i];
+ result.add(type);
+ if(type == Opcodes.LONG || type == Opcodes.DOUBLE)
+ {
+ result.add(Opcodes.TOP);
+ }
+ }
+}
+
+public void visitInsn(final int opcode){
+ if(mv != null)
+ {
+ mv.visitInsn(opcode);
+ }
+ execute(opcode, 0, null);
+ if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
+ || opcode == Opcodes.ATHROW)
+ {
+ this.locals = null;
+ this.stack = null;
+ }
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ if(mv != null)
+ {
+ mv.visitIntInsn(opcode, operand);
+ }
+ execute(opcode, operand, null);
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ if(mv != null)
+ {
+ mv.visitVarInsn(opcode, var);
+ }
+ execute(opcode, var, null);
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+ if(opcode == Opcodes.NEW)
+ {
+ if(labels == null)
+ {
+ Label l = new Label();
+ labels = new ArrayList(3);
+ labels.add(l);
+ if(mv != null)
+ {
+ mv.visitLabel(l);
+ }
+ }
+ for(int i = 0; i < labels.size(); ++i)
+ {
+ uninitializedTypes.put(labels.get(i), desc);
+ }
+ }
+ if(mv != null)
+ {
+ mv.visitTypeInsn(opcode, desc);
+ }
+ execute(opcode, 0, desc);
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ if(mv != null)
+ {
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+ execute(opcode, 0, desc);
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ if(mv != null)
+ {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+ pop(desc);
+ if(opcode != Opcodes.INVOKESTATIC)
+ {
+ Object t = pop();
+ if(opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<')
+ {
+ Object u;
+ if(t == Opcodes.UNINITIALIZED_THIS)
+ {
+ u = owner;
+ }
+ else
+ {
+ u = uninitializedTypes.get(t);
+ }
+ for(int i = 0; i < locals.size(); ++i)
+ {
+ if(locals.get(i) == t)
+ {
+ locals.set(i, u);
+ }
+ }
+ for(int i = 0; i < stack.size(); ++i)
+ {
+ if(stack.get(i) == t)
+ {
+ stack.set(i, u);
+ }
+ }
+ }
+ }
+ pushDesc(desc);
+ labels = null;
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+ if(mv != null)
+ {
+ mv.visitJumpInsn(opcode, label);
+ }
+ execute(opcode, 0, null);
+ if(opcode == Opcodes.GOTO)
+ {
+ this.locals = null;
+ this.stack = null;
+ }
+}
+
+public void visitLabel(final Label label){
+ if(mv != null)
+ {
+ mv.visitLabel(label);
+ }
+ if(labels == null)
+ {
+ labels = new ArrayList(3);
+ }
+ labels.add(label);
+}
+
+public void visitLdcInsn(final Object cst){
+ if(mv != null)
+ {
+ mv.visitLdcInsn(cst);
+ }
+ if(cst instanceof Integer)
+ {
+ push(Opcodes.INTEGER);
+ }
+ else if(cst instanceof Long)
+ {
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ }
+ else if(cst instanceof Float)
+ {
+ push(Opcodes.FLOAT);
+ }
+ else if(cst instanceof Double)
+ {
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ }
+ else if(cst instanceof String)
+ {
+ push("java/lang/String");
+ }
+ else if(cst instanceof Type)
+ {
+ push("java/lang/Class");
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+ labels = null;
+}
+
+public void visitIincInsn(final int var, final int increment){
+ if(mv != null)
+ {
+ mv.visitIincInsn(var, increment);
+ }
+ execute(Opcodes.IINC, var, null);
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label labels[]){
+ if(mv != null)
+ {
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+ execute(Opcodes.TABLESWITCH, 0, null);
+ this.locals = null;
+ this.stack = null;
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int keys[],
+ final Label labels[]){
+ if(mv != null)
+ {
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+ execute(Opcodes.LOOKUPSWITCH, 0, null);
+ this.locals = null;
+ this.stack = null;
+}
+
+public void visitMultiANewArrayInsn(final String desc, final int dims){
+ if(mv != null)
+ {
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+ execute(Opcodes.MULTIANEWARRAY, dims, desc);
+}
+
+public void visitMaxs(final int maxStack, final int maxLocals){
+ if(mv != null)
+ {
+ this.maxStack = Math.max(this.maxStack, maxStack);
+ this.maxLocals = Math.max(this.maxLocals, maxLocals);
+ mv.visitMaxs(this.maxStack, this.maxLocals);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+private Object get(final int local){
+ maxLocals = Math.max(maxLocals, local);
+ return local < locals.size() ? locals.get(local) : Opcodes.TOP;
+}
+
+private void set(final int local, final Object type){
+ maxLocals = Math.max(maxLocals, local);
+ while(local >= locals.size())
+ {
+ locals.add(Opcodes.TOP);
+ }
+ locals.set(local, type);
+}
+
+private void push(final Object type){
+ stack.add(type);
+ maxStack = Math.max(maxStack, stack.size());
+}
+
+private void pushDesc(final String desc){
+ int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
+ switch(desc.charAt(index))
+ {
+ case'V':
+ return;
+ case'Z':
+ case'C':
+ case'B':
+ case'S':
+ case'I':
+ push(Opcodes.INTEGER);
+ return;
+ case'F':
+ push(Opcodes.FLOAT);
+ return;
+ case'J':
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ return;
+ case'D':
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ return;
+ case'[':
+ if(index == 0)
+ {
+ push(desc);
+ }
+ else
+ {
+ push(desc.substring(index, desc.length()));
+ }
+ break;
+ // case 'L':
+ default:
+ if(index == 0)
+ {
+ push(desc.substring(1, desc.length() - 1));
+ }
+ else
+ {
+ push(desc.substring(index + 1, desc.length() - 1));
+ }
+ return;
+ }
+}
+
+private Object pop(){
+ return stack.remove(stack.size() - 1);
+}
+
+private void pop(final int n){
+ int size = stack.size();
+ int end = size - n;
+ for(int i = size - 1; i >= end; --i)
+ {
+ stack.remove(i);
+ }
+}
+
+private void pop(final String desc){
+ char c = desc.charAt(0);
+ if(c == '(')
+ {
+ int n = 0;
+ Type[] types = Type.getArgumentTypes(desc);
+ for(int i = 0; i < types.length; ++i)
+ {
+ n += types[i].getSize();
+ }
+ pop(n);
+ }
+ else if(c == 'J' || c == 'D')
+ {
+ pop(2);
+ }
+ else
+ {
+ pop(1);
+ }
+}
+
+private void execute(final int opcode, final int iarg, final String sarg){
+ if(this.locals == null)
+ {
+ return;
+ }
+ Object 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(Opcodes.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:
+ push(Opcodes.INTEGER);
+ break;
+ case Opcodes.LCONST_0:
+ case Opcodes.LCONST_1:
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.FCONST_0:
+ case Opcodes.FCONST_1:
+ case Opcodes.FCONST_2:
+ push(Opcodes.FLOAT);
+ break;
+ case Opcodes.DCONST_0:
+ case Opcodes.DCONST_1:
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.ILOAD:
+ case Opcodes.FLOAD:
+ case Opcodes.ALOAD:
+ push(get(iarg));
+ break;
+ case Opcodes.LLOAD:
+ case Opcodes.DLOAD:
+ push(get(iarg));
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.IALOAD:
+ case Opcodes.BALOAD:
+ case Opcodes.CALOAD:
+ case Opcodes.SALOAD:
+ pop(2);
+ push(Opcodes.INTEGER);
+ break;
+ case Opcodes.LALOAD:
+ case Opcodes.D2L:
+ pop(2);
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.FALOAD:
+ pop(2);
+ push(Opcodes.FLOAT);
+ break;
+ case Opcodes.DALOAD:
+ case Opcodes.L2D:
+ pop(2);
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.AALOAD:
+ pop(1);
+ t1 = pop();
+ pushDesc(((String) t1).substring(1));
+ break;
+ case Opcodes.ISTORE:
+ case Opcodes.FSTORE:
+ case Opcodes.ASTORE:
+ t1 = pop();
+ set(iarg, t1);
+ if(iarg > 0)
+ {
+ t2 = get(iarg - 1);
+ if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE)
+ {
+ set(iarg - 1, Opcodes.TOP);
+ }
+ }
+ break;
+ case Opcodes.LSTORE:
+ case Opcodes.DSTORE:
+ pop(1);
+ t1 = pop();
+ set(iarg, t1);
+ set(iarg + 1, Opcodes.TOP);
+ if(iarg > 0)
+ {
+ t2 = get(iarg - 1);
+ if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE)
+ {
+ set(iarg - 1, Opcodes.TOP);
+ }
+ }
+ 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(Opcodes.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(Opcodes.LONG);
+ push(Opcodes.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(Opcodes.FLOAT);
+ break;
+ case Opcodes.DADD:
+ case Opcodes.DSUB:
+ case Opcodes.DMUL:
+ case Opcodes.DDIV:
+ case Opcodes.DREM:
+ pop(4);
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.LSHL:
+ case Opcodes.LSHR:
+ case Opcodes.LUSHR:
+ pop(3);
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.IINC:
+ set(iarg, Opcodes.INTEGER);
+ break;
+ case Opcodes.I2L:
+ case Opcodes.F2L:
+ pop(1);
+ push(Opcodes.LONG);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.I2F:
+ pop(1);
+ push(Opcodes.FLOAT);
+ break;
+ case Opcodes.I2D:
+ case Opcodes.F2D:
+ pop(1);
+ push(Opcodes.DOUBLE);
+ push(Opcodes.TOP);
+ break;
+ case Opcodes.F2I:
+ case Opcodes.ARRAYLENGTH:
+ case Opcodes.INSTANCEOF:
+ pop(1);
+ push(Opcodes.INTEGER);
+ break;
+ case Opcodes.LCMP:
+ case Opcodes.DCMPL:
+ case Opcodes.DCMPG:
+ pop(4);
+ push(Opcodes.INTEGER);
+ break;
+ case Opcodes.JSR:
+ case Opcodes.RET:
+ throw new RuntimeException("JSR/RET are not supported");
+ case Opcodes.GETSTATIC:
+ pushDesc(sarg);
+ break;
+ case Opcodes.PUTSTATIC:
+ pop(sarg);
+ break;
+ case Opcodes.GETFIELD:
+ pop(1);
+ pushDesc(sarg);
+ break;
+ case Opcodes.PUTFIELD:
+ pop(sarg);
+ pop();
+ break;
+ case Opcodes.NEW:
+ push(labels.get(0));
+ break;
+ case Opcodes.NEWARRAY:
+ pop();
+ switch(iarg)
+ {
+ case Opcodes.T_BOOLEAN:
+ pushDesc("[Z");
+ break;
+ case Opcodes.T_CHAR:
+ pushDesc("[C");
+ break;
+ case Opcodes.T_BYTE:
+ pushDesc("[B");
+ break;
+ case Opcodes.T_SHORT:
+ pushDesc("[S");
+ break;
+ case Opcodes.T_INT:
+ pushDesc("[I");
+ break;
+ case Opcodes.T_FLOAT:
+ pushDesc("[F");
+ break;
+ case Opcodes.T_DOUBLE:
+ pushDesc("[D");
+ break;
+ // case Opcodes.T_LONG:
+ default:
+ pushDesc("[J");
+ break;
+ }
+ break;
+ case Opcodes.ANEWARRAY:
+ pop();
+ if(sarg.charAt(0) == '[')
+ {
+ pushDesc("[" + sarg);
+ }
+ else
+ {
+ pushDesc("[L" + sarg + ";");
+ }
+ break;
+ case Opcodes.CHECKCAST:
+ pop();
+ if(sarg.charAt(0) == '[')
+ {
+ pushDesc(sarg);
+ }
+ else
+ {
+ push(sarg);
+ }
+ break;
+ // case Opcodes.MULTIANEWARRAY:
+ default:
+ pop(iarg);
+ pushDesc(sarg);
+ break;
+ }
+ labels = null;
+}
+}
diff --git a/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java
new file mode 100644
index 00000000..234db417
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java
@@ -0,0 +1,234 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import clojure.asm.Label;
+import clojure.asm.MethodAdapter;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link MethodAdapter} that can be used to approximate method size.
+ *
+ * @author Eugene Kuleshov
+ */
+public class CodeSizeEvaluator extends MethodAdapter implements Opcodes{
+
+private int minSize;
+
+private int maxSize;
+
+public CodeSizeEvaluator(final MethodVisitor mv){
+ super(mv);
+}
+
+public int getMinSize(){
+ return this.minSize;
+}
+
+public int getMaxSize(){
+ return this.maxSize;
+}
+
+public void visitInsn(final int opcode){
+ minSize += 1;
+ maxSize += 1;
+ if(mv != null)
+ {
+ mv.visitInsn(opcode);
+ }
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ if(opcode == SIPUSH)
+ {
+ minSize += 3;
+ maxSize += 3;
+ }
+ else
+ {
+ minSize += 2;
+ maxSize += 2;
+ }
+ if(mv != null)
+ {
+ mv.visitIntInsn(opcode, operand);
+ }
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ if(var < 4 && opcode != Opcodes.RET)
+ {
+ minSize += 1;
+ maxSize += 1;
+ }
+ else if(var >= 256)
+ {
+ minSize += 4;
+ maxSize += 4;
+ }
+ else
+ {
+ minSize += 2;
+ maxSize += 2;
+ }
+ if(mv != null)
+ {
+ mv.visitVarInsn(opcode, var);
+ }
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+ minSize += 3;
+ maxSize += 3;
+ if(mv != null)
+ {
+ mv.visitTypeInsn(opcode, desc);
+ }
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ minSize += 3;
+ maxSize += 3;
+ if(mv != null)
+ {
+ mv.visitFieldInsn(opcode, owner, name, desc);
+ }
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ if(opcode == INVOKEINTERFACE)
+ {
+ minSize += 5;
+ maxSize += 5;
+ }
+ else
+ {
+ minSize += 3;
+ maxSize += 3;
+ }
+ if(mv != null)
+ {
+ mv.visitMethodInsn(opcode, owner, name, desc);
+ }
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+ minSize += 3;
+ if(opcode == GOTO || opcode == JSR)
+ {
+ maxSize += 5;
+ }
+ else
+ {
+ maxSize += 8;
+ }
+ if(mv != null)
+ {
+ mv.visitJumpInsn(opcode, label);
+ }
+}
+
+public void visitLdcInsn(final Object cst){
+ if(cst instanceof Long || cst instanceof Double)
+ {
+ minSize += 3;
+ maxSize += 3;
+ }
+ else
+ {
+ minSize += 2;
+ maxSize += 3;
+ }
+ if(mv != null)
+ {
+ mv.visitLdcInsn(cst);
+ }
+}
+
+public void visitIincInsn(final int var, final int increment){
+ if(var > 255 || increment > 127 || increment < -128)
+ {
+ minSize += 6;
+ maxSize += 6;
+ }
+ else
+ {
+ minSize += 3;
+ maxSize += 3;
+ }
+ if(mv != null)
+ {
+ mv.visitIincInsn(var, increment);
+ }
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label[] labels){
+ minSize += 13 + labels.length * 4;
+ maxSize += 16 + labels.length * 4;
+ if(mv != null)
+ {
+ mv.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int[] keys,
+ final Label[] labels){
+ minSize += 9 + keys.length * 8;
+ maxSize += 12 + keys.length * 8;
+ if(mv != null)
+ {
+ mv.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+}
+
+public void visitMultiANewArrayInsn(final String desc, final int dims){
+ minSize += 4;
+ maxSize += 4;
+ if(mv != null)
+ {
+ mv.visitMultiANewArrayInsn(desc, dims);
+ }
+}
+}
diff --git a/src/jvm/clojure/asm/commons/EmptyVisitor.java b/src/jvm/clojure/asm/commons/EmptyVisitor.java
new file mode 100644
index 00000000..b721b08f
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/EmptyVisitor.java
@@ -0,0 +1,221 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import clojure.asm.AnnotationVisitor;
+import clojure.asm.Attribute;
+import clojure.asm.ClassVisitor;
+import clojure.asm.FieldVisitor;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+
+/**
+ * An empty implementation of the ASM visitor interfaces.
+ *
+ * @author Eric Bruneton
+ */
+public class EmptyVisitor implements
+ ClassVisitor,
+ FieldVisitor,
+ MethodVisitor,
+ AnnotationVisitor{
+
+public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces){
+}
+
+public void visitSource(final String source, final String debug){
+}
+
+public void visitOuterClass(
+ final String owner,
+ final String name,
+ final String desc){
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ return this;
+}
+
+public void visitAttribute(final Attribute attr){
+}
+
+public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access){
+}
+
+public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value){
+ return this;
+}
+
+public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions){
+ return this;
+}
+
+public void visitEnd(){
+}
+
+public AnnotationVisitor visitAnnotationDefault(){
+ return this;
+}
+
+public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible){
+ return this;
+}
+
+public void visitCode(){
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+}
+
+public void visitInsn(final int opcode){
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+}
+
+public void visitVarInsn(final int opcode, final int var){
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+}
+
+public void visitLabel(final Label label){
+}
+
+public void visitLdcInsn(final Object cst){
+}
+
+public void visitIincInsn(final int var, final int increment){
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label labels[]){
+}
+
+public void visitLookupSwitchInsn(
+ final Label dflt,
+ final int keys[],
+ final Label labels[]){
+}
+
+public void visitMultiANewArrayInsn(final String desc, final int dims){
+}
+
+public void visitTryCatchBlock(
+ final Label start,
+ final Label end,
+ final Label handler,
+ final String type){
+}
+
+public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index){
+}
+
+public void visitLineNumber(final int line, final Label start){
+}
+
+public void visitMaxs(final int maxStack, final int maxLocals){
+}
+
+public void visit(final String name, final Object value){
+}
+
+public void visitEnum(
+ final String name,
+ final String desc,
+ final String value){
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String name,
+ final String desc){
+ return this;
+}
+
+public AnnotationVisitor visitArray(final String name){
+ return this;
+}
+}
diff --git a/src/jvm/clojure/asm/commons/GeneratorAdapter.java b/src/jvm/clojure/asm/commons/GeneratorAdapter.java
new file mode 100644
index 00000000..18596413
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/GeneratorAdapter.java
@@ -0,0 +1,1533 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import clojure.asm.ClassVisitor;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link clojure.asm.MethodAdapter} with convenient methods to generate
+ * code. For example, using this adapter, the class below
+ * <p/>
+ * <pre>
+ * public class Example {
+ * public static void main(String[] args) {
+ * System.out.println(&quot;Hello world!&quot;);
+ * }
+ * }
+ * </pre>
+ * <p/>
+ * can be generated as follows:
+ * <p/>
+ * <pre>
+ * ClassWriter cw = new ClassWriter(true);
+ * cw.visit(V1_1, ACC_PUBLIC, &quot;Example&quot;, null, &quot;java/lang/Object&quot;, null);
+ * <p/>
+ * Method m = Method.getMethod(&quot;void &lt;init&gt; ()&quot;);
+ * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
+ * mg.loadThis();
+ * mg.invokeConstructor(Type.getType(Object.class), m);
+ * mg.returnValue();
+ * mg.endMethod();
+ * <p/>
+ * m = Method.getMethod(&quot;void main (String[])&quot;);
+ * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
+ * mg.getStatic(Type.getType(System.class), &quot;out&quot;, Type.getType(PrintStream.class));
+ * mg.push(&quot;Hello world!&quot;);
+ * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod(&quot;void println (String)&quot;));
+ * mg.returnValue();
+ * mg.endMethod();
+ * <p/>
+ * cw.visitEnd();
+ * </pre>
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class GeneratorAdapter extends LocalVariablesSorter{
+
+private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
+
+private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
+
+private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
+
+private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
+
+private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
+
+private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
+
+private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
+
+private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
+
+private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
+
+private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
+private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
+
+private final static Method CHAR_VALUE = Method.getMethod("char charValue()");
+
+private final static Method INT_VALUE = Method.getMethod("int intValue()");
+
+private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()");
+
+private final static Method LONG_VALUE = Method.getMethod("long longValue()");
+
+private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int ADD = Opcodes.IADD;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int SUB = Opcodes.ISUB;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int MUL = Opcodes.IMUL;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int DIV = Opcodes.IDIV;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int REM = Opcodes.IREM;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int NEG = Opcodes.INEG;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int SHL = Opcodes.ISHL;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int SHR = Opcodes.ISHR;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int USHR = Opcodes.IUSHR;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int AND = Opcodes.IAND;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int OR = Opcodes.IOR;
+
+/**
+ * Constant for the {@link #math math} method.
+ */
+public final static int XOR = Opcodes.IXOR;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int EQ = Opcodes.IFEQ;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int NE = Opcodes.IFNE;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int LT = Opcodes.IFLT;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int GE = Opcodes.IFGE;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int GT = Opcodes.IFGT;
+
+/**
+ * Constant for the {@link #ifCmp ifCmp} method.
+ */
+public final static int LE = Opcodes.IFLE;
+
+/**
+ * Access flags of the method visited by this adapter.
+ */
+private final int access;
+
+/**
+ * Return type of the method visited by this adapter.
+ */
+private final Type returnType;
+
+/**
+ * Argument types of the method visited by this adapter.
+ */
+private final Type[] argumentTypes;
+
+/**
+ * Types of the local variables of the method visited by this adapter.
+ */
+private final List localTypes = new ArrayList();
+
+/**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @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}).
+ */
+public GeneratorAdapter(
+ final MethodVisitor mv,
+ final int access,
+ final String name,
+ final String desc){
+ super(access, desc, mv);
+ this.access = access;
+ this.returnType = Type.getReturnType(desc);
+ this.argumentTypes = Type.getArgumentTypes(desc);
+}
+
+/**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param method the adapted method.
+ * @param mv the method visitor to which this adapter delegates calls.
+ */
+public GeneratorAdapter(
+ final int access,
+ final Method method,
+ final MethodVisitor mv){
+ super(access, method.getDescriptor(), mv);
+ this.access = access;
+ this.returnType = method.getReturnType();
+ this.argumentTypes = method.getArgumentTypes();
+}
+
+/**
+ * Creates a new {@link GeneratorAdapter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param method the adapted method.
+ * @param signature the signature of the adapted method (may be
+ * <tt>null</tt>).
+ * @param exceptions the exceptions thrown by the adapted method (may be
+ * <tt>null</tt>).
+ * @param cv the class visitor to which this adapter delegates calls.
+ */
+public GeneratorAdapter(
+ final int access,
+ final Method method,
+ final String signature,
+ final Type[] exceptions,
+ final ClassVisitor cv){
+ this(access, method, cv.visitMethod(access,
+ method.getName(),
+ method.getDescriptor(),
+ signature,
+ getInternalNames(exceptions)));
+}
+
+/**
+ * Returns the internal names of the given types.
+ *
+ * @param types a set of types.
+ * @return the internal names of the given types.
+ */
+private static String[] getInternalNames(final Type[] types){
+ if(types == null)
+ {
+ return null;
+ }
+ String[] names = new String[types.length];
+ for(int i = 0; i < names.length; ++i)
+ {
+ names[i] = types[i].getInternalName();
+ }
+ return names;
+}
+
+// ------------------------------------------------------------------------
+// Instructions to push constants on the stack
+// ------------------------------------------------------------------------
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final boolean value){
+ push(value ? 1 : 0);
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final int value){
+ if(value >= -1 && value <= 5)
+ {
+ mv.visitInsn(Opcodes.ICONST_0 + value);
+ }
+ else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
+ {
+ mv.visitIntInsn(Opcodes.BIPUSH, value);
+ }
+ else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
+ {
+ mv.visitIntInsn(Opcodes.SIPUSH, value);
+ }
+ else
+ {
+ mv.visitLdcInsn(new Integer(value));
+ }
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final long value){
+ if(value == 0L || value == 1L)
+ {
+ mv.visitInsn(Opcodes.LCONST_0 + (int) value);
+ }
+ else
+ {
+ mv.visitLdcInsn(new Long(value));
+ }
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final float value){
+ int bits = Float.floatToIntBits(value);
+ if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000)
+ { // 0..2
+ mv.visitInsn(Opcodes.FCONST_0 + (int) value);
+ }
+ else
+ {
+ mv.visitLdcInsn(new Float(value));
+ }
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final double value){
+ long bits = Double.doubleToLongBits(value);
+ if(bits == 0L || bits == 0x3ff0000000000000L)
+ { // +0.0d and 1.0d
+ mv.visitInsn(Opcodes.DCONST_0 + (int) value);
+ }
+ else
+ {
+ mv.visitLdcInsn(new Double(value));
+ }
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack. May be <tt>null</tt>.
+ */
+public void push(final String value){
+ if(value == null)
+ {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ }
+ else
+ {
+ mv.visitLdcInsn(value);
+ }
+}
+
+/**
+ * Generates the instruction to push the given value on the stack.
+ *
+ * @param value the value to be pushed on the stack.
+ */
+public void push(final Type value){
+ if(value == null)
+ {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ }
+ else
+ {
+ mv.visitLdcInsn(value);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Instructions to load and store method arguments
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the index of the given method argument in the frame's local
+ * variables array.
+ *
+ * @param arg the index of a method argument.
+ * @return the index of the given method argument in the frame's local
+ * variables array.
+ */
+private int getArgIndex(final int arg){
+ int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
+ for(int i = 0; i < arg; i++)
+ {
+ index += argumentTypes[i].getSize();
+ }
+ return index;
+}
+
+/**
+ * Generates the instruction to push a local variable on the stack.
+ *
+ * @param type the type of the local variable to be loaded.
+ * @param index an index in the frame's local variables array.
+ */
+private void loadInsn(final Type type, final int index){
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
+}
+
+/**
+ * Generates the instruction to store the top stack value in a local
+ * variable.
+ *
+ * @param type the type of the local variable to be stored.
+ * @param index an index in the frame's local variables array.
+ */
+private void storeInsn(final Type type, final int index){
+ mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
+}
+
+/**
+ * Generates the instruction to load 'this' on the stack.
+ */
+public void loadThis(){
+ if((access & Opcodes.ACC_STATIC) != 0)
+ {
+ throw new IllegalStateException("no 'this' pointer within static method");
+ }
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+}
+
+/**
+ * Generates the instruction to load the given method argument on the stack.
+ *
+ * @param arg the index of a method argument.
+ */
+public void loadArg(final int arg){
+ loadInsn(argumentTypes[arg], getArgIndex(arg));
+}
+
+/**
+ * Generates the instructions to load the given method arguments on the
+ * stack.
+ *
+ * @param arg the index of the first method argument to be loaded.
+ * @param count the number of method arguments to be loaded.
+ */
+public void loadArgs(final int arg, final int count){
+ int index = getArgIndex(arg);
+ for(int i = 0; i < count; ++i)
+ {
+ Type t = argumentTypes[arg + i];
+ loadInsn(t, index);
+ index += t.getSize();
+ }
+}
+
+/**
+ * Generates the instructions to load all the method arguments on the stack.
+ */
+public void loadArgs(){
+ loadArgs(0, argumentTypes.length);
+}
+
+/**
+ * Generates the instructions to load all the method arguments on the stack,
+ * as a single object array.
+ */
+public void loadArgArray(){
+ push(argumentTypes.length);
+ newArray(OBJECT_TYPE);
+ for(int i = 0; i < argumentTypes.length; i++)
+ {
+ dup();
+ push(i);
+ loadArg(i);
+ box(argumentTypes[i]);
+ arrayStore(OBJECT_TYPE);
+ }
+}
+
+/**
+ * Generates the instruction to store the top stack value in the given
+ * method argument.
+ *
+ * @param arg the index of a method argument.
+ */
+public void storeArg(final int arg){
+ storeInsn(argumentTypes[arg], getArgIndex(arg));
+}
+
+// ------------------------------------------------------------------------
+// Instructions to load and store local variables
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the type of the given local variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @return the type of the given local variable.
+ */
+public Type getLocalType(final int local){
+ return (Type) localTypes.get(local - firstLocal);
+}
+
+protected void setLocalType(final int local, final Type type){
+ int index = local - firstLocal;
+ while(localTypes.size() < index + 1)
+ {
+ localTypes.add(null);
+ }
+ localTypes.set(index, type);
+}
+
+/**
+ * Generates the instruction to load the given local variable on the stack.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+public void loadLocal(final int local){
+ loadInsn(getLocalType(local), local);
+}
+
+/**
+ * Generates the instruction to load the given local variable on the stack.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @param type the type of this local variable.
+ */
+public void loadLocal(final int local, final Type type){
+ setLocalType(local, type);
+ loadInsn(type, local);
+}
+
+/**
+ * Generates the instruction to store the top stack value in the given local
+ * variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+public void storeLocal(final int local){
+ storeInsn(getLocalType(local), local);
+}
+
+/**
+ * Generates the instruction to store the top stack value in the given local
+ * variable.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ * @param type the type of this local variable.
+ */
+public void storeLocal(final int local, final Type type){
+ setLocalType(local, type);
+ storeInsn(type, local);
+}
+
+/**
+ * Generates the instruction to load an element from an array.
+ *
+ * @param type the type of the array element to be loaded.
+ */
+public void arrayLoad(final Type type){
+ mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
+}
+
+/**
+ * Generates the instruction to store an element in an array.
+ *
+ * @param type the type of the array element to be stored.
+ */
+public void arrayStore(final Type type){
+ mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
+}
+
+// ------------------------------------------------------------------------
+// Instructions to manage the stack
+// ------------------------------------------------------------------------
+
+/**
+ * Generates a POP instruction.
+ */
+public void pop(){
+ mv.visitInsn(Opcodes.POP);
+}
+
+/**
+ * Generates a POP2 instruction.
+ */
+public void pop2(){
+ mv.visitInsn(Opcodes.POP2);
+}
+
+/**
+ * Generates a DUP instruction.
+ */
+public void dup(){
+ mv.visitInsn(Opcodes.DUP);
+}
+
+/**
+ * Generates a DUP2 instruction.
+ */
+public void dup2(){
+ mv.visitInsn(Opcodes.DUP2);
+}
+
+/**
+ * Generates a DUP_X1 instruction.
+ */
+public void dupX1(){
+ mv.visitInsn(Opcodes.DUP_X1);
+}
+
+/**
+ * Generates a DUP_X2 instruction.
+ */
+public void dupX2(){
+ mv.visitInsn(Opcodes.DUP_X2);
+}
+
+/**
+ * Generates a DUP2_X1 instruction.
+ */
+public void dup2X1(){
+ mv.visitInsn(Opcodes.DUP2_X1);
+}
+
+/**
+ * Generates a DUP2_X2 instruction.
+ */
+public void dup2X2(){
+ mv.visitInsn(Opcodes.DUP2_X2);
+}
+
+/**
+ * Generates a SWAP instruction.
+ */
+public void swap(){
+ mv.visitInsn(Opcodes.SWAP);
+}
+
+/**
+ * Generates the instructions to swap the top two stack values.
+ *
+ * @param prev type of the top - 1 stack value.
+ * @param type type of the top stack value.
+ */
+public void swap(final Type prev, final Type type){
+ if(type.getSize() == 1)
+ {
+ if(prev.getSize() == 1)
+ {
+ swap(); // same as dupX1(), pop();
+ }
+ else
+ {
+ dupX2();
+ pop();
+ }
+ }
+ else
+ {
+ if(prev.getSize() == 1)
+ {
+ dup2X1();
+ pop2();
+ }
+ else
+ {
+ dup2X2();
+ pop2();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+// Instructions to do mathematical and logical operations
+// ------------------------------------------------------------------------
+
+/**
+ * Generates the instruction to do the specified mathematical or logical
+ * operation.
+ *
+ * @param op a mathematical or logical operation. Must be one of ADD, SUB,
+ * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
+ * @param type the type of the operand(s) for this operation.
+ */
+public void math(final int op, final Type type){
+ mv.visitInsn(type.getOpcode(op));
+}
+
+/**
+ * Generates the instructions to compute the bitwise negation of the top
+ * stack value.
+ */
+public void not(){
+ mv.visitInsn(Opcodes.ICONST_1);
+ mv.visitInsn(Opcodes.IXOR);
+}
+
+/**
+ * Generates the instruction to increment the given local variable.
+ *
+ * @param local the local variable to be incremented.
+ * @param amount the amount by which the local variable must be incremented.
+ */
+public void iinc(final int local, final int amount){
+ mv.visitIincInsn(local, amount);
+}
+
+/**
+ * Generates the instructions to cast a numerical value from one type to
+ * another.
+ *
+ * @param from the type of the top stack value
+ * @param to the type into which this value must be cast.
+ */
+public void cast(final Type from, final Type to){
+ if(from != to)
+ {
+ if(from == Type.DOUBLE_TYPE)
+ {
+ if(to == Type.FLOAT_TYPE)
+ {
+ mv.visitInsn(Opcodes.D2F);
+ }
+ else if(to == Type.LONG_TYPE)
+ {
+ mv.visitInsn(Opcodes.D2L);
+ }
+ else
+ {
+ mv.visitInsn(Opcodes.D2I);
+ cast(Type.INT_TYPE, to);
+ }
+ }
+ else if(from == Type.FLOAT_TYPE)
+ {
+ if(to == Type.DOUBLE_TYPE)
+ {
+ mv.visitInsn(Opcodes.F2D);
+ }
+ else if(to == Type.LONG_TYPE)
+ {
+ mv.visitInsn(Opcodes.F2L);
+ }
+ else
+ {
+ mv.visitInsn(Opcodes.F2I);
+ cast(Type.INT_TYPE, to);
+ }
+ }
+ else if(from == Type.LONG_TYPE)
+ {
+ if(to == Type.DOUBLE_TYPE)
+ {
+ mv.visitInsn(Opcodes.L2D);
+ }
+ else if(to == Type.FLOAT_TYPE)
+ {
+ mv.visitInsn(Opcodes.L2F);
+ }
+ else
+ {
+ mv.visitInsn(Opcodes.L2I);
+ cast(Type.INT_TYPE, to);
+ }
+ }
+ else
+ {
+ if(to == Type.BYTE_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2B);
+ }
+ else if(to == Type.CHAR_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2C);
+ }
+ else if(to == Type.DOUBLE_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2D);
+ }
+ else if(to == Type.FLOAT_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2F);
+ }
+ else if(to == Type.LONG_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2L);
+ }
+ else if(to == Type.SHORT_TYPE)
+ {
+ mv.visitInsn(Opcodes.I2S);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+// Instructions to do boxing and unboxing operations
+// ------------------------------------------------------------------------
+
+/**
+ * Generates the instructions to box the top stack value. This value is
+ * replaced by its boxed equivalent on top of the stack.
+ *
+ * @param type the type of the top stack value.
+ */
+public void box(final Type type){
+ if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY)
+ {
+ return;
+ }
+ if(type == Type.VOID_TYPE)
+ {
+ push((String) null);
+ }
+ else
+ {
+ Type boxed = type;
+ switch(type.getSort())
+ {
+ case Type.BYTE:
+ boxed = BYTE_TYPE;
+ break;
+ case Type.BOOLEAN:
+ boxed = BOOLEAN_TYPE;
+ break;
+ case Type.SHORT:
+ boxed = SHORT_TYPE;
+ break;
+ case Type.CHAR:
+ boxed = CHARACTER_TYPE;
+ break;
+ case Type.INT:
+ boxed = INTEGER_TYPE;
+ break;
+ case Type.FLOAT:
+ boxed = FLOAT_TYPE;
+ break;
+ case Type.LONG:
+ boxed = LONG_TYPE;
+ break;
+ case Type.DOUBLE:
+ boxed = DOUBLE_TYPE;
+ break;
+ }
+ newInstance(boxed);
+ if(type.getSize() == 2)
+ {
+ // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
+ dupX2();
+ dupX2();
+ pop();
+ }
+ else
+ {
+ // p -> po -> opo -> oop -> o
+ dupX1();
+ swap();
+ }
+ invokeConstructor(boxed, new Method("<init>",
+ Type.VOID_TYPE,
+ new Type[]{type}));
+ }
+}
+
+/**
+ * Generates the instructions to unbox the top stack value. This value is
+ * replaced by its unboxed equivalent on top of the stack.
+ *
+ * @param type the type of the top stack value.
+ */
+public void unbox(final Type type){
+ Type t = NUMBER_TYPE;
+ Method sig = null;
+ switch(type.getSort())
+ {
+ case Type.VOID:
+ return;
+ case Type.CHAR:
+ t = CHARACTER_TYPE;
+ sig = CHAR_VALUE;
+ break;
+ case Type.BOOLEAN:
+ t = BOOLEAN_TYPE;
+ sig = BOOLEAN_VALUE;
+ break;
+ case Type.DOUBLE:
+ sig = DOUBLE_VALUE;
+ break;
+ case Type.FLOAT:
+ sig = FLOAT_VALUE;
+ break;
+ case Type.LONG:
+ sig = LONG_VALUE;
+ break;
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ sig = INT_VALUE;
+ }
+ if(sig == null)
+ {
+ checkCast(type);
+ }
+ else
+ {
+ checkCast(t);
+ invokeVirtual(t, sig);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Instructions to jump to other instructions
+// ------------------------------------------------------------------------
+
+/**
+ * Creates a new {@link Label}.
+ *
+ * @return a new {@link Label}.
+ */
+public Label newLabel(){
+ return new Label();
+}
+
+/**
+ * Marks the current code position with the given label.
+ *
+ * @param label a label.
+ */
+public void mark(final Label label){
+ mv.visitLabel(label);
+}
+
+/**
+ * Marks the current code position with a new label.
+ *
+ * @return the label that was created to mark the current code position.
+ */
+public Label mark(){
+ Label label = new Label();
+ mv.visitLabel(label);
+ return label;
+}
+
+/**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top two stack values.
+ *
+ * @param type the type of the top two stack values.
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+public void ifCmp(final Type type, final int mode, final Label label){
+ int intOp = -1;
+ switch(type.getSort())
+ {
+ case Type.LONG:
+ mv.visitInsn(Opcodes.LCMP);
+ break;
+ case Type.DOUBLE:
+ mv.visitInsn(Opcodes.DCMPG);
+ break;
+ case Type.FLOAT:
+ mv.visitInsn(Opcodes.FCMPG);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ switch(mode)
+ {
+ case EQ:
+ mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
+ return;
+ case NE:
+ mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
+ return;
+ }
+ throw new IllegalArgumentException("Bad comparison for type "
+ + type);
+ default:
+ switch(mode)
+ {
+ case EQ:
+ intOp = Opcodes.IF_ICMPEQ;
+ break;
+ case NE:
+ intOp = Opcodes.IF_ICMPNE;
+ break;
+ case GE:
+ intOp = Opcodes.IF_ICMPGE;
+ break;
+ case LT:
+ intOp = Opcodes.IF_ICMPLT;
+ break;
+ case LE:
+ intOp = Opcodes.IF_ICMPLE;
+ break;
+ case GT:
+ intOp = Opcodes.IF_ICMPGT;
+ break;
+ }
+ mv.visitJumpInsn(intOp, label);
+ return;
+ }
+ int jumpMode = mode;
+ switch(mode)
+ {
+ case GE:
+ jumpMode = LT;
+ break;
+ case LE:
+ jumpMode = GT;
+ break;
+ }
+ mv.visitJumpInsn(jumpMode, label);
+}
+
+/**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top two integer stack values.
+ *
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+public void ifICmp(final int mode, final Label label){
+ ifCmp(Type.INT_TYPE, mode, label);
+}
+
+/**
+ * Generates the instructions to jump to a label based on the comparison of
+ * the top integer stack value with zero.
+ *
+ * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
+ * LE.
+ * @param label where to jump if the comparison result is <tt>true</tt>.
+ */
+public void ifZCmp(final int mode, final Label label){
+ mv.visitJumpInsn(mode, label);
+}
+
+/**
+ * Generates the instruction to jump to the given label if the top stack
+ * value is null.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+public void ifNull(final Label label){
+ mv.visitJumpInsn(Opcodes.IFNULL, label);
+}
+
+/**
+ * Generates the instruction to jump to the given label if the top stack
+ * value is not null.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+public void ifNonNull(final Label label){
+ mv.visitJumpInsn(Opcodes.IFNONNULL, label);
+}
+
+/**
+ * Generates the instruction to jump to the given label.
+ *
+ * @param label where to jump if the condition is <tt>true</tt>.
+ */
+public void goTo(final Label label){
+ mv.visitJumpInsn(Opcodes.GOTO, label);
+}
+
+/**
+ * Generates a RET instruction.
+ *
+ * @param local a local variable identifier, as returned by
+ * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+ */
+public void ret(final int local){
+ mv.visitVarInsn(Opcodes.RET, local);
+}
+
+/**
+ * Generates the instructions for a switch statement.
+ *
+ * @param keys the switch case keys.
+ * @param generator a generator to generate the code for the switch cases.
+ */
+public void tableSwitch(
+ final int[] keys,
+ final TableSwitchGenerator generator){
+ float density;
+ if(keys.length == 0)
+ {
+ density = 0;
+ }
+ else
+ {
+ density = (float) keys.length
+ / (keys[keys.length - 1] - keys[0] + 1);
+ }
+ tableSwitch(keys, generator, density >= 0.5f);
+}
+
+/**
+ * Generates the instructions for a switch statement.
+ *
+ * @param keys the switch case keys.
+ * @param generator a generator to generate the code for the switch cases.
+ * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
+ * <tt>false</tt> to use a LOOKUPSWITCH instruction.
+ */
+public void tableSwitch(
+ final int[] keys,
+ final TableSwitchGenerator generator,
+ final boolean useTable){
+ for(int i = 1; i < keys.length; ++i)
+ {
+ if(keys[i] < keys[i - 1])
+ {
+ throw new IllegalArgumentException("keys must be sorted ascending");
+ }
+ }
+ Label def = newLabel();
+ Label end = newLabel();
+ if(keys.length > 0)
+ {
+ int len = keys.length;
+ int min = keys[0];
+ int max = keys[len - 1];
+ int range = max - min + 1;
+ if(useTable)
+ {
+ Label[] labels = new Label[range];
+ Arrays.fill(labels, def);
+ for(int i = 0; i < len; ++i)
+ {
+ labels[keys[i] - min] = newLabel();
+ }
+ mv.visitTableSwitchInsn(min, max, def, labels);
+ for(int i = 0; i < range; ++i)
+ {
+ Label label = labels[i];
+ if(label != def)
+ {
+ mark(label);
+ generator.generateCase(i + min, end);
+ }
+ }
+ }
+ else
+ {
+ Label[] labels = new Label[len];
+ for(int i = 0; i < len; ++i)
+ {
+ labels[i] = newLabel();
+ }
+ mv.visitLookupSwitchInsn(def, keys, labels);
+ for(int i = 0; i < len; ++i)
+ {
+ mark(labels[i]);
+ generator.generateCase(keys[i], end);
+ }
+ }
+ }
+ mark(def);
+ generator.generateDefault();
+ mark(end);
+}
+
+/**
+ * Generates the instruction to return the top stack value to the caller.
+ */
+public void returnValue(){
+ mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+}
+
+// ------------------------------------------------------------------------
+// Instructions to load and store fields
+// ------------------------------------------------------------------------
+
+/**
+ * Generates a get field or set field instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param ownerType the class in which the field is defined.
+ * @param name the name of the field.
+ * @param fieldType the type of the field.
+ */
+private void fieldInsn(
+ final int opcode,
+ final Type ownerType,
+ final String name,
+ final Type fieldType){
+ mv.visitFieldInsn(opcode,
+ ownerType.getInternalName(),
+ name,
+ fieldType.getDescriptor());
+}
+
+/**
+ * Generates the instruction to push the value of a static field on the
+ * stack.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+public void getStatic(final Type owner, final String name, final Type type){
+ fieldInsn(Opcodes.GETSTATIC, owner, name, type);
+}
+
+/**
+ * Generates the instruction to store the top stack value in a static field.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+public void putStatic(final Type owner, final String name, final Type type){
+ fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
+}
+
+/**
+ * Generates the instruction to push the value of a non static field on the
+ * stack.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+public void getField(final Type owner, final String name, final Type type){
+ fieldInsn(Opcodes.GETFIELD, owner, name, type);
+}
+
+/**
+ * Generates the instruction to store the top stack value in a non static
+ * field.
+ *
+ * @param owner the class in which the field is defined.
+ * @param name the name of the field.
+ * @param type the type of the field.
+ */
+public void putField(final Type owner, final String name, final Type type){
+ fieldInsn(Opcodes.PUTFIELD, owner, name, type);
+}
+
+// ------------------------------------------------------------------------
+// Instructions to invoke methods
+// ------------------------------------------------------------------------
+
+/**
+ * Generates an invoke method instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param type the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+private void invokeInsn(
+ final int opcode,
+ final Type type,
+ final Method method){
+ String owner = type.getSort() == Type.ARRAY
+ ? type.getDescriptor()
+ : type.getInternalName();
+ mv.visitMethodInsn(opcode,
+ owner,
+ method.getName(),
+ method.getDescriptor());
+}
+
+/**
+ * Generates the instruction to invoke a normal method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+public void invokeVirtual(final Type owner, final Method method){
+ invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
+}
+
+/**
+ * Generates the instruction to invoke a constructor.
+ *
+ * @param type the class in which the constructor is defined.
+ * @param method the constructor to be invoked.
+ */
+public void invokeConstructor(final Type type, final Method method){
+ invokeInsn(Opcodes.INVOKESPECIAL, type, method);
+}
+
+/**
+ * Generates the instruction to invoke a static method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+public void invokeStatic(final Type owner, final Method method){
+ invokeInsn(Opcodes.INVOKESTATIC, owner, method);
+}
+
+/**
+ * Generates the instruction to invoke an interface method.
+ *
+ * @param owner the class in which the method is defined.
+ * @param method the method to be invoked.
+ */
+public void invokeInterface(final Type owner, final Method method){
+ invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
+}
+
+// ------------------------------------------------------------------------
+// Instructions to create objects and arrays
+// ------------------------------------------------------------------------
+
+/**
+ * Generates a type dependent instruction.
+ *
+ * @param opcode the instruction's opcode.
+ * @param type the instruction's operand.
+ */
+private void typeInsn(final int opcode, final Type type){
+ String desc;
+ if(type.getSort() == Type.ARRAY)
+ {
+ desc = type.getDescriptor();
+ }
+ else
+ {
+ desc = type.getInternalName();
+ }
+ mv.visitTypeInsn(opcode, desc);
+}
+
+/**
+ * Generates the instruction to create a new object.
+ *
+ * @param type the class of the object to be created.
+ */
+public void newInstance(final Type type){
+ typeInsn(Opcodes.NEW, type);
+}
+
+/**
+ * Generates the instruction to create a new array.
+ *
+ * @param type the type of the array elements.
+ */
+public void newArray(final Type type){
+ int typ;
+ switch(type.getSort())
+ {
+ case Type.BOOLEAN:
+ typ = Opcodes.T_BOOLEAN;
+ break;
+ case Type.CHAR:
+ typ = Opcodes.T_CHAR;
+ break;
+ case Type.BYTE:
+ typ = Opcodes.T_BYTE;
+ break;
+ case Type.SHORT:
+ typ = Opcodes.T_SHORT;
+ break;
+ case Type.INT:
+ typ = Opcodes.T_INT;
+ break;
+ case Type.FLOAT:
+ typ = Opcodes.T_FLOAT;
+ break;
+ case Type.LONG:
+ typ = Opcodes.T_LONG;
+ break;
+ case Type.DOUBLE:
+ typ = Opcodes.T_DOUBLE;
+ break;
+ default:
+ typeInsn(Opcodes.ANEWARRAY, type);
+ return;
+ }
+ mv.visitIntInsn(Opcodes.NEWARRAY, typ);
+}
+
+// ------------------------------------------------------------------------
+// Miscelaneous instructions
+// ------------------------------------------------------------------------
+
+/**
+ * Generates the instruction to compute the length of an array.
+ */
+public void arrayLength(){
+ mv.visitInsn(Opcodes.ARRAYLENGTH);
+}
+
+/**
+ * Generates the instruction to throw an exception.
+ */
+public void throwException(){
+ mv.visitInsn(Opcodes.ATHROW);
+}
+
+/**
+ * Generates the instructions to create and throw an exception. The
+ * exception class must have a constructor with a single String argument.
+ *
+ * @param type the class of the exception to be thrown.
+ * @param msg the detailed message of the exception.
+ */
+public void throwException(final Type type, final String msg){
+ newInstance(type);
+ dup();
+ push(msg);
+ invokeConstructor(type, Method.getMethod("void <init> (String)"));
+ throwException();
+}
+
+/**
+ * Generates the instruction to check that the top stack value is of the
+ * given type.
+ *
+ * @param type a class or interface type.
+ */
+public void checkCast(final Type type){
+ if(!type.equals(OBJECT_TYPE))
+ {
+ typeInsn(Opcodes.CHECKCAST, type);
+ }
+}
+
+/**
+ * Generates the instruction to test if the top stack value is of the given
+ * type.
+ *
+ * @param type a class or interface type.
+ */
+public void instanceOf(final Type type){
+ typeInsn(Opcodes.INSTANCEOF, type);
+}
+
+/**
+ * Generates the instruction to get the monitor of the top stack value.
+ */
+public void monitorEnter(){
+ mv.visitInsn(Opcodes.MONITORENTER);
+}
+
+/**
+ * Generates the instruction to release the monitor of the top stack value.
+ */
+public void monitorExit(){
+ mv.visitInsn(Opcodes.MONITOREXIT);
+}
+
+// ------------------------------------------------------------------------
+// Non instructions
+// ------------------------------------------------------------------------
+
+/**
+ * Marks the end of the visited method.
+ */
+public void endMethod(){
+ if((access & Opcodes.ACC_ABSTRACT) == 0)
+ {
+ mv.visitMaxs(0, 0);
+ }
+ mv.visitEnd();
+}
+
+/**
+ * Marks the start of an exception handler.
+ *
+ * @param start beginning of the exception handler's scope (inclusive).
+ * @param end end of the exception handler's scope (exclusive).
+ * @param exception internal name of the type of exceptions handled by the
+ * handler.
+ */
+public void catchException(
+ final Label start,
+ final Label end,
+ final Type exception){
+ mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName());
+}
+}
diff --git a/src/jvm/clojure/asm/commons/LocalVariablesSorter.java b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java
new file mode 100644
index 00000000..13d4c743
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java
@@ -0,0 +1,330 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import clojure.asm.Label;
+import clojure.asm.MethodAdapter;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link MethodAdapter} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method. It may be used by inheriting from this class, but the preferred way
+ * of using it is via delegation: the next visitor in the chain can indeed add
+ * new locals when needed by calling {@link #newLocal} on this adapter (this
+ * requires a reference back to this {@link LocalVariablesSorter}).
+ *
+ * @author Chris Nokleberg
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodAdapter{
+
+private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
+/**
+ * Mapping from old to new local variable indexes. A local variable at index
+ * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
+ * index i of size 2 is remapped to 'mapping[2*i+1]'.
+ */
+private int[] mapping = new int[40];
+
+/**
+ * Array used to store stack map local variable types after remapping.
+ */
+private Object[] newLocals = new Object[20];
+
+/**
+ * Index of the first local variable, after formal parameters.
+ */
+protected final int firstLocal;
+
+/**
+ * Index of the next local variable to be created by {@link #newLocal}.
+ */
+protected int nextLocal;
+
+/**
+ * Indicates if at least one local variable has moved due to remapping.
+ */
+private boolean changed;
+
+/**
+ * Creates a new {@link LocalVariablesSorter}.
+ *
+ * @param access access flags of the adapted method.
+ * @param desc the method's descriptor (see {@link Type Type}).
+ * @param mv the method visitor to which this adapter delegates calls.
+ */
+public LocalVariablesSorter(
+ final int access,
+ final String desc,
+ final MethodVisitor mv){
+ super(mv);
+ Type[] args = Type.getArgumentTypes(desc);
+ nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
+ for(int i = 0; i < args.length; i++)
+ {
+ nextLocal += args[i].getSize();
+ }
+ firstLocal = nextLocal;
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ Type type;
+ switch(opcode)
+ {
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ type = Type.LONG_TYPE;
+ break;
+
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ type = Type.DOUBLE_TYPE;
+ break;
+
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ type = Type.FLOAT_TYPE;
+ break;
+
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ type = Type.INT_TYPE;
+ break;
+
+ case Opcodes.ALOAD:
+ case Opcodes.ASTORE:
+ type = OBJECT_TYPE;
+ break;
+
+ // case RET:
+ default:
+ type = Type.VOID_TYPE;
+ }
+ mv.visitVarInsn(opcode, remap(var, type));
+}
+
+public void visitIincInsn(final int var, final int increment){
+ mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
+}
+
+public void visitMaxs(final int maxStack, final int maxLocals){
+ mv.visitMaxs(maxStack, nextLocal);
+}
+
+public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index){
+ int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
+ int newIndex = remap(index, size);
+ mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+ if(type != Opcodes.F_NEW)
+ { // uncompressed frame
+ throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
+ }
+
+ if(!changed)
+ { // optimization for the case where mapping = identity
+ mv.visitFrame(type, nLocal, local, nStack, stack);
+ return;
+ }
+
+ // creates a copy of newLocals
+ Object[] oldLocals = new Object[newLocals.length];
+ System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
+
+ // copies types from 'local' to 'newLocals'
+ // 'newLocals' already contains the variables added with 'newLocal'
+
+ int index = 0; // old local variable index
+ int number = 0; // old local variable number
+ for(; number < nLocal; ++number)
+ {
+ Object t = local[number];
+ int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
+ if(t != Opcodes.TOP)
+ {
+ setFrameLocal(remap(index, size), t);
+ }
+ index += size;
+ }
+
+ // removes TOP after long and double types as well as trailing TOPs
+
+ index = 0;
+ number = 0;
+ for(int i = 0; index < newLocals.length; ++i)
+ {
+ Object t = newLocals[index++];
+ if(t != null && t != Opcodes.TOP)
+ {
+ newLocals[i] = t;
+ number = i + 1;
+ if(t == Opcodes.LONG || t == Opcodes.DOUBLE)
+ {
+ index += 1;
+ }
+ }
+ else
+ {
+ newLocals[i] = Opcodes.TOP;
+ }
+ }
+
+ // visits remapped frame
+ mv.visitFrame(type, number, newLocals, nStack, stack);
+
+ // restores original value of 'newLocals'
+ newLocals = oldLocals;
+}
+
+// -------------
+
+/**
+ * Creates a new local variable of the given type.
+ *
+ * @param type the type of the local variable to be created.
+ * @return the identifier of the newly created local variable.
+ */
+public int newLocal(final Type type){
+ Object t;
+ switch(type.getSort())
+ {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ t = Opcodes.INTEGER;
+ break;
+ case Type.FLOAT:
+ t = Opcodes.FLOAT;
+ break;
+ case Type.LONG:
+ t = Opcodes.LONG;
+ break;
+ case Type.DOUBLE:
+ t = Opcodes.DOUBLE;
+ break;
+ case Type.ARRAY:
+ t = type.getDescriptor();
+ break;
+ // case Type.OBJECT:
+ default:
+ t = type.getInternalName();
+ break;
+ }
+ int local = nextLocal;
+ setLocalType(local, type);
+ setFrameLocal(local, t);
+ nextLocal += type.getSize();
+ return local;
+}
+
+/**
+ * Sets the current type of the given local variable. The default
+ * implementation of this method does nothing.
+ *
+ * @param local a local variable identifier, as returned by {@link #newLocal
+ * newLocal()}.
+ * @param type the type of the value being stored in the local variable
+ */
+protected void setLocalType(final int local, final Type type){
+}
+
+private void setFrameLocal(final int local, final Object type){
+ int l = newLocals.length;
+ if(local >= l)
+ {
+ Object[] a = new Object[Math.max(2 * l, local + 1)];
+ System.arraycopy(newLocals, 0, a, 0, l);
+ newLocals = a;
+ }
+ newLocals[local] = type;
+}
+
+private int remap(final int var, final Type type){
+ if(var < firstLocal)
+ {
+ return var;
+ }
+ int key = 2 * var + type.getSize() - 1;
+ int size = mapping.length;
+ if(key >= size)
+ {
+ int[] newMapping = new int[Math.max(2 * size, key + 1)];
+ System.arraycopy(mapping, 0, newMapping, 0, size);
+ mapping = newMapping;
+ }
+ int value = mapping[key];
+ if(value == 0)
+ {
+ value = nextLocal + 1;
+ mapping[key] = value;
+ setLocalType(nextLocal, type);
+ nextLocal += type.getSize();
+ }
+ if(value - 1 != var)
+ {
+ changed = true;
+ }
+ return value - 1;
+}
+
+private int remap(final int var, final int size){
+ if(var < firstLocal || !changed)
+ {
+ return var;
+ }
+ int key = 2 * var + size - 1;
+ int value = key < mapping.length ? mapping[key] : 0;
+ if(value == 0)
+ {
+ throw new IllegalStateException("Unknown local variable " + var);
+ }
+ return value - 1;
+}
+}
diff --git a/src/jvm/clojure/asm/commons/Method.java b/src/jvm/clojure/asm/commons/Method.java
new file mode 100644
index 00000000..fb5b8e9e
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/Method.java
@@ -0,0 +1,267 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import clojure.asm.Type;
+
+/**
+ * A named method descriptor.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class Method{
+
+/**
+ * The method name.
+ */
+private final String name;
+
+/**
+ * The method descriptor.
+ */
+private final String desc;
+
+/**
+ * Maps primitive Java type names to their descriptors.
+ */
+private final static Map DESCRIPTORS;
+
+static
+ {
+ DESCRIPTORS = new HashMap();
+ DESCRIPTORS.put("void", "V");
+ DESCRIPTORS.put("byte", "B");
+ DESCRIPTORS.put("char", "C");
+ DESCRIPTORS.put("double", "D");
+ DESCRIPTORS.put("float", "F");
+ DESCRIPTORS.put("int", "I");
+ DESCRIPTORS.put("long", "J");
+ DESCRIPTORS.put("short", "S");
+ DESCRIPTORS.put("boolean", "Z");
+ }
+
+/**
+ * Creates a new {@link Method}.
+ *
+ * @param name the method's name.
+ * @param desc the method's descriptor.
+ */
+public Method(final String name, final String desc){
+ this.name = name;
+ this.desc = desc;
+}
+
+/**
+ * Creates a new {@link Method}.
+ *
+ * @param name the method's name.
+ * @param returnType the method's return type.
+ * @param argumentTypes the method's argument types.
+ */
+public Method(
+ final String name,
+ final Type returnType,
+ final Type[] argumentTypes){
+ this(name, Type.getMethodDescriptor(returnType, argumentTypes));
+}
+
+/**
+ * Returns a {@link Method} corresponding to the given Java method
+ * declaration.
+ *
+ * @param method a Java method declaration, without argument names, of the
+ * form "returnType name (argumentType1, ... argumentTypeN)", where
+ * the types are in plain Java (e.g. "int", "float",
+ * "java.util.List", ...). Classes of the java.lang package can be
+ * specified by their unqualified name; all other classes names must
+ * be fully qualified.
+ * @return a {@link Method} corresponding to the given Java method
+ * declaration.
+ * @throws IllegalArgumentException if <code>method</code> could not get
+ * parsed.
+ */
+public static Method getMethod(final String method)
+ throws IllegalArgumentException{
+ return getMethod(method, false);
+}
+
+/**
+ * Returns a {@link Method} corresponding to the given Java method
+ * declaration.
+ *
+ * @param method a Java method declaration, without argument names, of the
+ * form "returnType name (argumentType1, ... argumentTypeN)", where
+ * the types are in plain Java (e.g. "int", "float",
+ * "java.util.List", ...). Classes of the java.lang package may be
+ * specified by their unqualified name, depending on the
+ * defaultPackage argument; all other classes names must be fully
+ * qualified.
+ * @param defaultPackage true if unqualified class names belong to the
+ * default package, or false if they correspond to java.lang classes.
+ * For instance "Object" means "Object" if this option is true, or
+ * "java.lang.Object" otherwise.
+ * @return a {@link Method} corresponding to the given Java method
+ * declaration.
+ * @throws IllegalArgumentException if <code>method</code> could not get
+ * parsed.
+ */
+public static Method getMethod(
+ final String method,
+ final boolean defaultPackage) throws IllegalArgumentException{
+ int space = method.indexOf(' ');
+ int start = method.indexOf('(', space) + 1;
+ int end = method.indexOf(')', start);
+ if(space == -1 || start == -1 || end == -1)
+ {
+ throw new IllegalArgumentException();
+ }
+ // TODO: Check validity of returnType, methodName and arguments.
+ String returnType = method.substring(0, space);
+ String methodName = method.substring(space + 1, start - 1).trim();
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ int p;
+ do
+ {
+ String s;
+ p = method.indexOf(',', start);
+ if(p == -1)
+ {
+ s = map(method.substring(start, end).trim(), defaultPackage);
+ }
+ else
+ {
+ s = map(method.substring(start, p).trim(), defaultPackage);
+ start = p + 1;
+ }
+ sb.append(s);
+ } while(p != -1);
+ sb.append(')');
+ sb.append(map(returnType, defaultPackage));
+ return new Method(methodName, sb.toString());
+}
+
+private static String map(final String type, final boolean defaultPackage){
+ if(type.equals(""))
+ {
+ return type;
+ }
+
+ StringBuffer sb = new StringBuffer();
+ int index = 0;
+ while((index = type.indexOf("[]", index) + 1) > 0)
+ {
+ sb.append('[');
+ }
+
+ String t = type.substring(0, type.length() - sb.length() * 2);
+ String desc = (String) DESCRIPTORS.get(t);
+ if(desc != null)
+ {
+ sb.append(desc);
+ }
+ else
+ {
+ sb.append('L');
+ if(t.indexOf('.') < 0)
+ {
+ if(!defaultPackage)
+ {
+ sb.append("java/lang/");
+ }
+ sb.append(t);
+ }
+ else
+ {
+ sb.append(t.replace('.', '/'));
+ }
+ sb.append(';');
+ }
+ return sb.toString();
+}
+
+/**
+ * Returns the name of the method described by this object.
+ *
+ * @return the name of the method described by this object.
+ */
+public String getName(){
+ return name;
+}
+
+/**
+ * Returns the descriptor of the method described by this object.
+ *
+ * @return the descriptor of the method described by this object.
+ */
+public String getDescriptor(){
+ return desc;
+}
+
+/**
+ * Returns the return type of the method described by this object.
+ *
+ * @return the return type of the method described by this object.
+ */
+public Type getReturnType(){
+ return Type.getReturnType(desc);
+}
+
+/**
+ * Returns the argument types of the method described by this object.
+ *
+ * @return the argument types of the method described by this object.
+ */
+public Type[] getArgumentTypes(){
+ return Type.getArgumentTypes(desc);
+}
+
+public String toString(){
+ return name + desc;
+}
+
+public boolean equals(final Object o){
+ if(!(o instanceof Method))
+ {
+ return false;
+ }
+ Method other = (Method) o;
+ return name.equals(other.name) && desc.equals(other.desc);
+}
+
+public int hashCode(){
+ return name.hashCode() ^ desc.hashCode();
+}
+} \ No newline at end of file
diff --git a/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java
new file mode 100644
index 00000000..62f381bd
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java
@@ -0,0 +1,508 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import clojure.asm.ClassAdapter;
+import clojure.asm.ClassVisitor;
+import clojure.asm.FieldVisitor;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link ClassAdapter} that adds a serial version unique identifier to a
+ * class if missing. Here is typical usage of this class:
+ * <p/>
+ * <pre>
+ * ClassWriter cw = new ClassWriter(...);
+ * ClassVisitor sv = new SerialVersionUIDAdder(cw);
+ * ClassVisitor ca = new MyClassAdapter(sv);
+ * new ClassReader(orginalClass).accept(ca, false);
+ * </pre>
+ * <p/>
+ * The SVUID algorithm can be found <a href=
+ * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
+ * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
+ * <p/>
+ * <pre>
+ * The serialVersionUID is computed using the signature of a stream of bytes
+ * that reflect the class definition. The National Institute of Standards and
+ * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
+ * signature for the stream. The first two 32-bit quantities are used to form a
+ * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
+ * types to a sequence of bytes. The values input to the stream are defined by
+ * the Java Virtual Machine (VM) specification for classes.
+ * <p/>
+ * The sequence of items in the stream is as follows:
+ * <p/>
+ * 1. The class name written using UTF encoding.
+ * 2. The class modifiers written as a 32-bit integer.
+ * 3. The name of each interface sorted by name written using UTF encoding.
+ * 4. For each field of the class sorted by field name (except private static
+ * and private transient fields):
+ * 1. The name of the field in UTF encoding.
+ * 2. The modifiers of the field written as a 32-bit integer.
+ * 3. The descriptor of the field in UTF encoding
+ * 5. If a class initializer exists, write out the following:
+ * 1. The name of the method, &lt;clinit&gt;, in UTF encoding.
+ * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
+ * written as a 32-bit integer.
+ * 3. The descriptor of the method, ()V, in UTF encoding.
+ * 6. For each non-private constructor sorted by method name and signature:
+ * 1. The name of the method, &lt;init&gt;, in UTF encoding.
+ * 2. The modifiers of the method written as a 32-bit integer.
+ * 3. The descriptor of the method in UTF encoding.
+ * 7. For each non-private method sorted by method name and signature:
+ * 1. The name of the method in UTF encoding.
+ * 2. The modifiers of the method written as a 32-bit integer.
+ * 3. The descriptor of the method in UTF encoding.
+ * 8. The SHA-1 algorithm is executed on the stream of bytes produced by
+ * DataOutputStream and produces five 32-bit values sha[0..4].
+ * <p/>
+ * 9. The hash value is assembled from the first and second 32-bit values of
+ * the SHA-1 message digest. If the result of the message digest, the five
+ * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
+ * sha, the hash value would be computed as follows:
+ * <p/>
+ * long hash = ((sha[0] &gt;&gt;&gt; 24) &amp; 0xFF) |
+ * ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
+ * ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
+ * ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
+ * ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
+ * ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
+ * ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
+ * ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
+ * </pre>
+ *
+ * @author Rajendra Inamdar, Vishal Vishnoi
+ */
+public class SerialVersionUIDAdder extends ClassAdapter{
+
+/**
+ * Flag that indicates if we need to compute SVUID.
+ */
+protected boolean computeSVUID;
+
+/**
+ * Set to true if the class already has SVUID.
+ */
+protected boolean hasSVUID;
+
+/**
+ * Classes access flags.
+ */
+protected int access;
+
+/**
+ * Internal name of the class
+ */
+protected String name;
+
+/**
+ * Interfaces implemented by the class.
+ */
+protected String[] interfaces;
+
+/**
+ * Collection of fields. (except private static and private transient
+ * fields)
+ */
+protected Collection svuidFields;
+
+/**
+ * Set to true if the class has static initializer.
+ */
+protected boolean hasStaticInitializer;
+
+/**
+ * Collection of non-private constructors.
+ */
+protected Collection svuidConstructors;
+
+/**
+ * Collection of non-private methods.
+ */
+protected Collection svuidMethods;
+
+/**
+ * Creates a new {@link SerialVersionUIDAdder}.
+ *
+ * @param cv a {@link ClassVisitor} to which this visitor will delegate
+ * calls.
+ */
+public SerialVersionUIDAdder(final ClassVisitor cv){
+ super(cv);
+ svuidFields = new ArrayList();
+ svuidConstructors = new ArrayList();
+ svuidMethods = new ArrayList();
+}
+
+// ------------------------------------------------------------------------
+// Overriden methods
+// ------------------------------------------------------------------------
+
+/*
+ * Visit class header and get class name, access , and intefraces
+ * informatoin (step 1,2, and 3) for SVUID computation.
+ */
+
+public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces){
+ computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
+
+ if(computeSVUID)
+ {
+ this.name = name;
+ this.access = access;
+ this.interfaces = interfaces;
+ }
+
+ super.visit(version, access, name, signature, superName, interfaces);
+}
+
+/*
+ * Visit the methods and get constructor and method information (step 5 and
+ * 7). Also determince if there is a class initializer (step 6).
+ */
+public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions){
+ if(computeSVUID)
+ {
+ if(name.equals("<clinit>"))
+ {
+ hasStaticInitializer = true;
+ }
+ /*
+ * Remembers non private constructors and methods for SVUID
+ * computation For constructor and method modifiers, only the
+ * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
+ * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
+ * are used.
+ */
+ int mods = access
+ & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
+ | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
+ | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
+
+ // all non private methods
+ if((access & Opcodes.ACC_PRIVATE) == 0)
+ {
+ if(name.equals("<init>"))
+ {
+ svuidConstructors.add(new Item(name, mods, desc));
+ }
+ else if(!name.equals("<clinit>"))
+ {
+ svuidMethods.add(new Item(name, mods, desc));
+ }
+ }
+ }
+
+ return cv.visitMethod(access, name, desc, signature, exceptions);
+}
+
+/*
+ * Gets class field information for step 4 of the alogrithm. Also determines
+ * if the class already has a SVUID.
+ */
+public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value){
+ if(computeSVUID)
+ {
+ if(name.equals("serialVersionUID"))
+ {
+ // since the class already has SVUID, we won't be computing it.
+ computeSVUID = false;
+ hasSVUID = true;
+ }
+ /*
+ * Remember field for SVUID computation For field modifiers, only
+ * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
+ * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
+ * computing serialVersionUID values.
+ */
+ int mods = access
+ & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
+ | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
+
+ if((access & Opcodes.ACC_PRIVATE) == 0
+ || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0)
+ {
+ svuidFields.add(new Item(name, mods, desc));
+ }
+ }
+
+ return super.visitField(access, name, desc, signature, value);
+}
+
+/*
+ * Add the SVUID if class doesn't have one
+ */
+public void visitEnd(){
+ // compute SVUID and add it to the class
+ if(computeSVUID && !hasSVUID)
+ {
+ try
+ {
+ cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
+ "serialVersionUID",
+ "J",
+ null,
+ new Long(computeSVUID()));
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException("Error while computing SVUID for "
+ + name, e);
+ }
+ }
+
+ super.visitEnd();
+}
+
+// ------------------------------------------------------------------------
+// Utility methods
+// ------------------------------------------------------------------------
+
+/**
+ * Returns the value of SVUID if the class doesn't have one already. Please
+ * note that 0 is returned if the class already has SVUID, thus use
+ * <code>isHasSVUID</code> to determine if the class already had an SVUID.
+ *
+ * @return Returns the serial version UID
+ * @throws IOException
+ */
+protected long computeSVUID() throws IOException{
+ ByteArrayOutputStream bos = null;
+ DataOutputStream dos = null;
+ long svuid = 0;
+
+ try
+ {
+ bos = new ByteArrayOutputStream();
+ dos = new DataOutputStream(bos);
+
+ /*
+ * 1. The class name written using UTF encoding.
+ */
+ dos.writeUTF(name.replace('/', '.'));
+
+ /*
+ * 2. The class modifiers written as a 32-bit integer.
+ */
+ dos.writeInt(access
+ & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
+ | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
+
+ /*
+ * 3. The name of each interface sorted by name written using UTF
+ * encoding.
+ */
+ Arrays.sort(interfaces);
+ for(int i = 0; i < interfaces.length; i++)
+ {
+ dos.writeUTF(interfaces[i].replace('/', '.'));
+ }
+
+ /*
+ * 4. For each field of the class sorted by field name (except
+ * private static and private transient fields):
+ *
+ * 1. The name of the field in UTF encoding. 2. The modifiers of the
+ * field written as a 32-bit integer. 3. The descriptor of the field
+ * in UTF encoding
+ *
+ * Note that field signatutes are not dot separated. Method and
+ * constructor signatures are dot separated. Go figure...
+ */
+ writeItems(svuidFields, dos, false);
+
+ /*
+ * 5. If a class initializer exists, write out the following: 1. The
+ * name of the method, <clinit>, in UTF encoding. 2. The modifier of
+ * the method, java.lang.reflect.Modifier.STATIC, written as a
+ * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
+ * encoding.
+ */
+ if(hasStaticInitializer)
+ {
+ dos.writeUTF("<clinit>");
+ dos.writeInt(Opcodes.ACC_STATIC);
+ dos.writeUTF("()V");
+ } // if..
+
+ /*
+ * 6. For each non-private constructor sorted by method name and
+ * signature: 1. The name of the method, <init>, in UTF encoding. 2.
+ * The modifiers of the method written as a 32-bit integer. 3. The
+ * descriptor of the method in UTF encoding.
+ */
+ writeItems(svuidConstructors, dos, true);
+
+ /*
+ * 7. For each non-private method sorted by method name and
+ * signature: 1. The name of the method in UTF encoding. 2. The
+ * modifiers of the method written as a 32-bit integer. 3. The
+ * descriptor of the method in UTF encoding.
+ */
+ writeItems(svuidMethods, dos, true);
+
+ dos.flush();
+
+ /*
+ * 8. The SHA-1 algorithm is executed on the stream of bytes
+ * produced by DataOutputStream and produces five 32-bit values
+ * sha[0..4].
+ */
+ byte[] hashBytes = computeSHAdigest(bos.toByteArray());
+
+ /*
+ * 9. The hash value is assembled from the first and second 32-bit
+ * values of the SHA-1 message digest. If the result of the message
+ * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
+ * five int values named sha, the hash value would be computed as
+ * follows:
+ *
+ * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) <<
+ * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
+ * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
+ * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
+ * 56;
+ */
+ for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--)
+ {
+ svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
+ }
+ }
+ finally
+ {
+ // close the stream (if open)
+ if(dos != null)
+ {
+ dos.close();
+ }
+ }
+
+ return svuid;
+}
+
+/**
+ * Returns the SHA-1 message digest of the given value.
+ *
+ * @param value the value whose SHA message digest must be computed.
+ * @return the SHA-1 message digest of the given value.
+ */
+protected byte[] computeSHAdigest(final byte[] value){
+ try
+ {
+ return MessageDigest.getInstance("SHA").digest(value);
+ }
+ catch(Exception e)
+ {
+ throw new UnsupportedOperationException(e);
+ }
+}
+
+/**
+ * Sorts the items in the collection and writes it to the data output stream
+ *
+ * @param itemCollection collection of items
+ * @param dos a <code>DataOutputStream</code> value
+ * @param dotted a <code>boolean</code> value
+ * @throws IOException if an error occurs
+ */
+private void writeItems(
+ final Collection itemCollection,
+ final DataOutputStream dos,
+ final boolean dotted) throws IOException{
+ int size = itemCollection.size();
+ Item items[] = (Item[]) itemCollection.toArray(new Item[size]);
+ Arrays.sort(items);
+ for(int i = 0; i < size; i++)
+ {
+ dos.writeUTF(items[i].name);
+ dos.writeInt(items[i].access);
+ dos.writeUTF(dotted
+ ? items[i].desc.replace('/', '.')
+ : items[i].desc);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Inner classes
+// ------------------------------------------------------------------------
+
+static class Item implements Comparable{
+
+ String name;
+
+ int access;
+
+ String desc;
+
+ Item(final String name, final int access, final String desc){
+ this.name = name;
+ this.access = access;
+ this.desc = desc;
+ }
+
+ public int compareTo(final Object o){
+ Item other = (Item) o;
+ int retVal = name.compareTo(other.name);
+ if(retVal == 0)
+ {
+ retVal = desc.compareTo(other.desc);
+ }
+ return retVal;
+ }
+}
+}
diff --git a/src/jvm/clojure/asm/commons/StaticInitMerger.java b/src/jvm/clojure/asm/commons/StaticInitMerger.java
new file mode 100644
index 00000000..84fa45aa
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/StaticInitMerger.java
@@ -0,0 +1,102 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import clojure.asm.ClassAdapter;
+import clojure.asm.ClassVisitor;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link ClassAdapter} that merges clinit methods into a single one.
+ *
+ * @author Eric Bruneton
+ */
+public class StaticInitMerger extends ClassAdapter{
+
+private String name;
+
+private MethodVisitor clinit;
+
+private String prefix;
+
+private int counter;
+
+public StaticInitMerger(final String prefix, final ClassVisitor cv){
+ super(cv);
+ this.prefix = prefix;
+}
+
+public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces){
+ cv.visit(version, access, name, signature, superName, interfaces);
+ this.name = name;
+}
+
+public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions){
+ MethodVisitor mv;
+ if(name.equals("<clinit>"))
+ {
+ int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC;
+ String n = prefix + counter++;
+ mv = cv.visitMethod(a, n, desc, signature, exceptions);
+
+ if(clinit == null)
+ {
+ clinit = cv.visitMethod(a, name, desc, null, null);
+ }
+ clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc);
+ }
+ else
+ {
+ mv = cv.visitMethod(access, name, desc, signature, exceptions);
+ }
+ return mv;
+}
+
+public void visitEnd(){
+ if(clinit != null)
+ {
+ clinit.visitInsn(Opcodes.RETURN);
+ clinit.visitMaxs(0, 0);
+ }
+ cv.visitEnd();
+}
+}
diff --git a/src/jvm/clojure/asm/commons/TableSwitchGenerator.java b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java
new file mode 100644
index 00000000..8818e3c6
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java
@@ -0,0 +1,55 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 clojure.asm.commons;
+
+import clojure.asm.Label;
+
+/**
+ * A code generator for switch statements.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public interface TableSwitchGenerator{
+
+/**
+ * Generates the code for a switch case.
+ *
+ * @param key the switch case key.
+ * @param end a label that corresponds to the end of the switch statement.
+ */
+void generateCase(int key, Label end);
+
+/**
+ * Generates the code for the default switch case.
+ */
+void generateDefault();
+}
diff --git a/src/jvm/clojure/asm/commons/package.html b/src/jvm/clojure/asm/commons/package.html
new file mode 100644
index 00000000..7050bf22
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/package.html
@@ -0,0 +1,48 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides some useful class and method adapters. <i>The preferred way of using
+ these adapters is by chaining them together and to custom adapters (instead of
+ inheriting from them)</i>. Indeed this approach provides more combination
+possibilities than inheritance. For instance, suppose you want to implement an
+adapter MyAdapter than needs sorted local variables and intermediate stack map
+frame values taking into account the local variables sort. By using inheritance,
+this would require MyAdapter to extend AnalyzerAdapter, itself extending
+LocalVariablesSorter. But AnalyzerAdapter is not a subclass of
+LocalVariablesSorter, so this is not possible. On the contrary, by using
+delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter,
+itself delegating to MyAdapter. In this case AnalyzerAdapter computes
+intermediate frames based on the output of LocalVariablesSorter, and MyAdapter
+can add new locals by calling the newLocal method on LocalVariablesSorter, and
+can get the stack map frame state before each instruction by reading the locals
+and stack fields in AnalyzerAdapter (this requires references from MyAdapter
+back to LocalVariablesSorter and AnalyzerAdapter).
+</body> \ No newline at end of file
diff --git a/src/jvm/clojure/asm/package.html b/src/jvm/clojure/asm/package.html
new file mode 100644
index 00000000..bb251b10
--- /dev/null
+++ b/src/jvm/clojure/asm/package.html
@@ -0,0 +1,87 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides a small and fast bytecode manipulation framework.
+
+<p>
+ The <a href="http://www.objectweb.org/asm">ASM</a> framework is organized
+ around the {@link clojure.asm.ClassVisitor ClassVisitor},
+ {@link clojure.asm.FieldVisitor FieldVisitor} and
+ {@link clojure.asm.MethodVisitor MethodVisitor} interfaces, which allow
+ one to visit the fields and methods of a class, including the bytecode
+ instructions of each method.
+
+<p>
+ In addition to these main interfaces, ASM provides a {@link
+ clojure.asm.ClassReader ClassReader} class, that can parse an
+ existing class and make a given visitor visit it. ASM also provides
+ a {@link clojure.asm.ClassWriter ClassWriter} class, which is
+ a visitor that generates Java class files.
+
+<p>
+ In order to generate a class from scratch, only the {@link
+ clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed,
+ in order to generate a class, one must just call its visit<i>XXX</i>
+ methods with the appropriate arguments to generate the desired fields
+ and methods. See the "helloworld" example in the ASM distribution for
+ more details about class generation.
+
+<p>
+ In order to modify existing classes, one must use a {@link
+ clojure.asm.ClassReader ClassReader} class to analyze
+ the original class, a class modifier, and a {@link clojure.asm.ClassWriter
+ ClassWriter} to construct the modified class. The class modifier
+ is just a {@link clojure.asm.ClassVisitor ClassVisitor}
+ that delegates most of the work to another {@link clojure.asm.ClassVisitor
+ ClassVisitor}, but that sometimes changes some parameter values,
+ or call additional methods, in order to implement the desired
+ modification process. In order to make it easier to implement such
+ class modifiers, ASM provides the {@link clojure.asm.ClassAdapter
+ ClassAdapter} and {@link clojure.asm.MethodAdapter MethodAdapter}
+ classes, which implement the {@link clojure.asm.ClassVisitor ClassVisitor}
+ and {@link clojure.asm.MethodVisitor MethodVisitor} interfaces by
+ delegating all work to other visitors. See the "adapt" example in the ASM
+ distribution for more details about class modification.
+
+<p>
+ The size of the core ASM library, <tt>asm.jar</tt>, is only 42KB, which is much
+ smaller than the size of the
+ <a href="http://jakarta.apache.org/bcel">BCEL</a> library (504KB), and than the
+ size of the
+ <a href="http://serp.sourceforge.net">SERP</a> library (150KB). ASM is also
+ much faster than these tools. Indeed the overhead of a load time class
+ transformation process is of the order of 60% with ASM, 700% or more with BCEL,
+ and 1100% or more with SERP (see the <tt>test/perf</tt> directory in the ASM
+ distribution)!
+
+ @since ASM 1.3
+</body>
+</html>