commit e646050846e2da6a3fb1586ee7b2b90512ab1b4b Author: rtlhq Date: Mon Oct 16 01:25:13 2023 +0800 Import Upstream version 2.0b4 diff --git a/BeanShell.iml b/BeanShell.iml new file mode 100644 index 0000000..a38af89 --- /dev/null +++ b/BeanShell.iml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/BeanShell.ipr b/BeanShell.ipr new file mode 100644 index 0000000..0ca064f --- /dev/null +++ b/BeanShell.ipr @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BeanShell.iws b/BeanShell.iws new file mode 100644 index 0000000..e106ebd --- /dev/null +++ b/BeanShell.iws @@ -0,0 +1,770 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/asm/README-asm.txt b/asm/README-asm.txt new file mode 100644 index 0000000..d1b9ba5 --- /dev/null +++ b/asm/README-asm.txt @@ -0,0 +1,12 @@ + +These files are part of the org.objectweb.asm distribution. +(http://asm.objectweb.org) and are included under the terms of the LGPL license. + +ASM is a very light weight, fast, visitor-pattern style Java byte code +reader / generator. + +We have repackaged these classes under a "bsh." prefix for two reasons: +1) BeanShell uses the subset of ASM only for writing classes and +2) Since BeanShell is widely distributed we don't want to break the ability +of script writers to use updated versions of ASM in their scripts. + diff --git a/asm/src/bsh/org/objectweb/asm/ByteVector.java b/asm/src/bsh/org/objectweb/asm/ByteVector.java new file mode 100644 index 0000000..e22ebc7 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/ByteVector.java @@ -0,0 +1,281 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to + * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. + */ + +final 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 put1 (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. + */ + + public 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 put2 (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. + */ + + public 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 put4 (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 put8 (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 a String in UTF format into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param s a String. + * @return this byte vector. + */ + + public ByteVector putUTF (final String s) { + int charLength = s.length(); + int byteLength = 0; + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > 65535) { + throw new IllegalArgumentException(); + } + int length = this.length; + if (length + 2 + byteLength > data.length) { + enlarge(2 + byteLength); + } + byte[] data = this.data; + data[length++] = (byte)(byteLength >>> 8); + data[length++] = (byte)(byteLength); + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + data[length++] = (byte)c; + } else if (c > '\u07FF') { + data[length++] = (byte)(0xE0 | c >> 12 & 0xF); + data[length++] = (byte)(0x80 | c >> 6 & 0x3F); + data[length++] = (byte)(0x80 | c & 0x3F); + } else { + data[length++] = (byte)(0xC0 | c >> 6 & 0x1F); + data[length++] = (byte)(0x80 | c & 0x3F); + } + } + this.length = length; + return this; + } + + /** + * Puts an array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b an array of bytes. May be null to put len null + * bytes into this byte vector. + * @param off index of the fist byte of b that must be copied. + * @param len number of bytes of b that must be copied. + * @return this byte vector. + */ + + public ByteVector putByteArray ( + final byte[] b, + final int off, + final int len) + { + if (length + len > data.length) { + enlarge(len); + } + if (b != null) { + System.arraycopy(b, off, data, length, len); + } + length += len; + return this; + } + + /** + * Enlarge this byte vector so that it can receive n more bytes. + * + * @param size number of additional bytes that this byte vector should be + * able to receive. + */ + + private void enlarge (final int size) { + byte[] newData = new byte[Math.max(2*data.length, length + size)]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } +} diff --git a/asm/src/bsh/org/objectweb/asm/ClassVisitor.java b/asm/src/bsh/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 0000000..4f4def7 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/ClassVisitor.java @@ -0,0 +1,126 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this interface must be called + * in the following order: visit (visitField | + * visitMethod | visitInnerClass)* visitEnd. + */ + +public interface ClassVisitor { + + /** + * Visits the header of the class. + * + * @param access the class's access flags (see {@link Constants}). This + * parameter also indicates if the class is deprecated. + * @param name the internal name of the class (see {@link Type#getInternalName + * getInternalName}). + * @param superName the internal of name of the super class (see {@link + * Type#getInternalName getInternalName}). For interfaces, the super + * class is {@link Object}. May be null, but only for the {@link + * Object java.lang.Object} class. + * @param interfaces the internal names of the class's interfaces (see {@link + * Type#getInternalName getInternalName}). May be null. + * @param sourceFile the name of the source file from which this class was + * compiled. May be null. + */ + + void visit ( + int access, + String name, + String superName, + String[] interfaces, + String sourceFile); + + /** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name the internal name of an inner class (see {@link + * Type#getInternalName getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName getInternalName}). May be + * null. + * @param innerName the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ + + 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 Constants}). 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 value the field's initial value. This parameter, which may be + * null if the field does not have an initial value, must be an + * {@link java.lang.Integer Integer}, a {@link java.lang.Float Float}, a + * {@link java.lang.Long Long}, a {@link java.lang.Double Double} or a + * {@link String String}. + */ + + void visitField (int access, String name, String desc, Object value); + + /** + * Visits a method of the class. This method must return a new + * {@link CodeVisitor CodeVisitor} instance (or null) each time it + * is called, i.e., it should not return a previously returned visitor. + * + * @param access the method's access flags (see {@link Constants}). 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 exceptions the internal names of the method's exception + * classes (see {@link Type#getInternalName getInternalName}). May be + * null. + * @return an object to visit the byte code of the method, or null if + * this class visitor is not interested in visiting the code of this + * method. + */ + + CodeVisitor visitMethod ( + int access, + String name, + String desc, + 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/asm/src/bsh/org/objectweb/asm/ClassWriter.java b/asm/src/bsh/org/objectweb/asm/ClassWriter.java new file mode 100644 index 0000000..34b279a --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/ClassWriter.java @@ -0,0 +1,950 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A {@link ClassVisitor ClassVisitor} that generates Java class files. 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. + */ + +public class ClassWriter implements ClassVisitor { + + /** + * 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; + + /** + * Index of the next item to be added in the constant pool. + */ + + private short index; + + /** + * The constant pool of this class. + */ + + private ByteVector pool; + + /** + * The constant pool's hash table data. + */ + + private Item[] table; + + /** + * The threshold of the constant pool's hash table. + */ + + private int threshold; + + /** + * 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 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 constant pool item that contains the name of the source file from + * which this class was compiled. + */ + + private Item sourceFile; + + /** + * Number of fields of this class. + */ + + private int fieldCount; + + /** + * The fields of this class. + */ + + private ByteVector fields; + + /** + * true if the maximum stack size and number of local variables must + * be automatically computed. + */ + + private boolean computeMaxs; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link CodeWriter CodeWriter} objects, linked to each other by their {@link + * CodeWriter#next} field. This field stores the first element of this list. + */ + + CodeWriter firstMethod; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link CodeWriter CodeWriter} objects, linked to each other by their {@link + * CodeWriter#next} field. This field stores the last element of this list. + */ + + CodeWriter lastMethod; + + /** + * The number of entries in the InnerClasses attribute. + */ + + private int innerClassesCount; + + /** + * The InnerClasses attribute. + */ + + private ByteVector innerClasses; + + /** + * A reusable key used to look for items in the hash {@link #table table}. + */ + + Item key; + + /** + * A reusable key used to look for items in the hash {@link #table table}. + */ + + Item key2; + + /** + * A reusable key used to look for items in the hash {@link #table table}. + */ + + Item key3; + + /** + * The type of instructions without any label. + */ + + final static int NOARG_INSN = 0; + + /** + * The type of instructions with an signed byte label. + */ + + final static int SBYTE_INSN = 1; + + /** + * The type of instructions with an signed short label. + */ + + final static int SHORT_INSN = 2; + + /** + * The type of instructions with a local variable index label. + */ + + final static int VAR_INSN = 3; + + /** + * The type of instructions with an implicit local variable index label. + */ + + 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; + + // -------------------------------------------------------------------------- + // Static initializer + // -------------------------------------------------------------------------- + + /** + * Computes the instruction types of JVM opcodes. + */ + + static { + int i; + byte[] b = new byte[220]; + String s = + "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADDDDDEEEEEEEEE" + + "EEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA" + + "AAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAAAAAAGGGGGGGHAFBFAAFFAAQPIIJJII" + + "IIIIIIIIIIIIIIII"; + 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 CodeWriter + 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 ClassWriter} object. + * + * @param computeMaxs true if the maximum stack size and the maximum + * number of local variables must be automatically computed. If this flag + * is true, then the arguments of the {@link + * CodeVisitor#visitMaxs visitMaxs} method of the {@link CodeVisitor + * CodeVisitor} returned by the {@link #visitMethod visitMethod} method + * will be ignored, and computed automatically from the signature and + * the bytecode of each method. + */ + + public ClassWriter (final boolean computeMaxs) { + index = 1; + pool = new ByteVector(); + table = new Item[64]; + threshold = (int)(0.75d*table.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + this.computeMaxs = computeMaxs; + } + + // -------------------------------------------------------------------------- + // Implementation of the ClassVisitor interface + // -------------------------------------------------------------------------- + + public void visit ( + final int access, + final String name, + final String superName, + final String[] interfaces, + final String sourceFile) + { + this.access = access; + this.name = newClass(name).index; + this.superName = superName == null ? 0 : newClass(superName).index; + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = newClass(interfaces[i]).index; + } + } + if (sourceFile != null) { + newUTF8("SourceFile"); + this.sourceFile = newUTF8(sourceFile); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + newUTF8("Deprecated"); + } + } + + public void visitInnerClass ( + final String name, + final String outerName, + final String innerName, + final int access) + { + if (innerClasses == null) { + newUTF8("InnerClasses"); + innerClasses = new ByteVector(); + } + ++innerClassesCount; + innerClasses.put2(name == null ? 0 : newClass(name).index); + innerClasses.put2(outerName == null ? 0 : newClass(outerName).index); + innerClasses.put2(innerName == null ? 0 : newUTF8(innerName).index); + innerClasses.put2(access); + } + + public void visitField ( + final int access, + final String name, + final String desc, + final Object value) + { + ++fieldCount; + if (fields == null) { + fields = new ByteVector(); + } + fields.put2(access).put2(newUTF8(name).index).put2(newUTF8(desc).index); + int attributeCount = 0; + if (value != null) { + ++attributeCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + ++attributeCount; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + fields.put2(attributeCount); + if (value != null) { + fields.put2(newUTF8("ConstantValue").index); + fields.put4(2).put2(newCst(value).index); + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + fields.put2(newUTF8("Synthetic").index).put4(0); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + fields.put2(newUTF8("Deprecated").index).put4(0); + } + } + + public CodeVisitor visitMethod ( + final int access, + final String name, + final String desc, + final String[] exceptions) + { + CodeWriter cw = new CodeWriter(this, computeMaxs); + cw.init(access, name, desc, exceptions); + return cw; + } + + 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; + if (fields != null) { + size += fields.length; + } + int nbMethods = 0; + CodeWriter cb = firstMethod; + while (cb != null) { + ++nbMethods; + size += cb.getSize(); + cb = cb.next; + } + size += pool.length; + int attributeCount = 0; + if (sourceFile != null) { + ++attributeCount; + size += 8; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + size += 6; + } + if (innerClasses != null) { + ++attributeCount; + size += 8 + innerClasses.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.put4(0xCAFEBABE).put2(3).put2(45); + out.put2(index).putByteArray(pool.data, 0, pool.length); + out.put2(access).put2(name).put2(superName); + out.put2(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + out.put2(interfaces[i]); + } + out.put2(fieldCount); + if (fields != null) { + out.putByteArray(fields.data, 0, fields.length); + } + out.put2(nbMethods); + cb = firstMethod; + while (cb != null) { + cb.put(out); + cb = cb.next; + } + out.put2(attributeCount); + if (sourceFile != null) { + out.put2(newUTF8("SourceFile").index).put4(2).put2(sourceFile.index); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + out.put2(newUTF8("Deprecated").index).put4(0); + } + if (innerClasses != null) { + out.put2(newUTF8("InnerClasses").index); + out.put4(innerClasses.length + 2).put2(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + 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 java.lang.Integer Integer}, a {@link + * java.lang.Float Float}, a {@link java.lang.Long Long}, a {@link + java.lang.Double Double} or a {@link String String}. + * @return a new or already existing constant item with the given value. + */ + + Item newCst (final Object cst) { + if (cst instanceof Integer) { + int val = ((Integer)cst).intValue(); + 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 { + throw new IllegalArgumentException("value " + cst); + } + } + + /** + * Adds an UTF 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 UTF8 item. + */ + + Item newUTF8 (final String value) { + key.set(UTF8, value, null, null); + Item result = get(key); + if (result == null) { + pool.put1(UTF8).putUTF(value); + result = new Item(index++, key); + 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. + * + * @param value the internal name of the class. + * @return a new or already existing class reference item. + */ + + Item newClass (final String value) { + key2.set(CLASS, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(CLASS, newUTF8(value).index); + result = new Item(index++, key2); + 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. + * + * @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 newField ( + 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).index, newNameType(name, desc).index); + 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. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @return a new or already existing method reference item. + */ + + Item newMethod ( + final String owner, + final String name, + final String desc) + { + key3.set(METH, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(METH, newClass(owner).index, newNameType(name, desc).index); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds an interface method reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param ownerItf the internal name of the method's owner interface. + * @param name the method's name. + * @param desc the method's descriptor. + * @return a new or already existing interface method reference item. + */ + + Item newItfMethod ( + final String ownerItf, + final String name, + final String desc) + { + key3.set(IMETH, ownerItf, name, desc); + Item result = get(key3); + if (result == null) { + put122(IMETH, newClass(ownerItf).index, newNameType(name, desc).index); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * 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. + */ + + private Item newInteger (final int value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.put1(INT).put4(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. + */ + + private Item newFloat (final float value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.put1(FLOAT).put4(Float.floatToIntBits(value)); + 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. + */ + + private Item newLong (final long value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.put1(LONG).put8(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. + */ + + private Item newDouble (final double value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.put1(DOUBLE).put8(Double.doubleToLongBits(value)); + 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).index); + 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. + * + * @param name a name. + * @param desc a type descriptor. + * @return a new or already existing name and type item. + */ + + private Item 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).index, newUTF8(desc).index); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or null if there is no such item. + */ + + private Item get (final Item key) { + Item tab[] = table; + int hashCode = key.hashCode; + int index = (hashCode & 0x7FFFFFFF) % tab.length; + for (Item i = tab[index]; i != null; i = i.next) { + if (i.hashCode == hashCode && key.isEqualTo(i)) { + return i; + } + } + return null; + } + + /** + * Puts the given item in the constant pool's hash table. The hash table + * must not already contains this item. + * + * @param i the item to be added to the constant pool's hash table. + */ + + private void put (final Item i) { + if (index > threshold) { + int oldCapacity = table.length; + Item oldMap[] = table; + int newCapacity = oldCapacity * 2 + 1; + Item newMap[] = new Item[newCapacity]; + threshold = (int)(newCapacity * 0.75); + table = newMap; + for (int j = oldCapacity; j-- > 0; ) { + for (Item old = oldMap[j]; old != null; ) { + Item e = old; + old = old.next; + int index = (e.hashCode & 0x7FFFFFFF) % newCapacity; + e.next = newMap[index]; + newMap[index] = e; + } + } + } + int index = (i.hashCode & 0x7FFFFFFF) % table.length; + i.next = table[index]; + table[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).put2(s2); + } +} diff --git a/asm/src/bsh/org/objectweb/asm/CodeVisitor.java b/asm/src/bsh/org/objectweb/asm/CodeVisitor.java new file mode 100644 index 0000000..4f30b12 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/CodeVisitor.java @@ -0,0 +1,287 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A visitor to visit the bytecode instructions of a Java method. The methods + * of this visitor must be called in the sequential order of the bytecode + * instructions of the visited code. The {@link #visitMaxs visitMaxs} method + * must be called after all the instructions have been visited. The {@link + * #visitTryCatchBlock visitTryCatchBlock}, {@link #visitLocalVariable + * visitLocalVariable} and {@link #visitLineNumber visitLineNumber} methods may + * be called in any order, at any time (provided the labels passed as arguments + * have already been visited with {@link #visitLabel visitLabel}). + */ + +public interface CodeVisitor { + + /** + * 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. + */ + + 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 java.lang.Integer Integer}, a {@link java.lang.Float + * Float}, a {@link java.lang.Long Long}, a {@link java.lang.Double + * Double} or a {@link String String}. + */ + + 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. labels[i] is the + * beginning of the handler block for the min + i 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. labels[i] is the + * beginning of the handler block for the keys[i] 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, max stack size and max locals + // ------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param handler beginning of the exception handler's code. + * @param type internal name of the type of exceptions handled by the handler, + * or null to catch any exceptions (for "finally" blocks). + * @throws IllegalArgumentException if one of the labels has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + + void visitTryCatchBlock (Label start, Label end, Label handler, String type); + + /** + * 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); + + // ------------------------------------------------------------------------- + // Debug information + // ------------------------------------------------------------------------- + + /** + * Visits a local variable declaration. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @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, + 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 start has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + + void visitLineNumber (int line, Label start); +} diff --git a/asm/src/bsh/org/objectweb/asm/CodeWriter.java b/asm/src/bsh/org/objectweb/asm/CodeWriter.java new file mode 100644 index 0000000..d9c2b0c --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/CodeWriter.java @@ -0,0 +1,1766 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A {@link CodeVisitor CodeVisitor} that generates Java bytecode instructions. + * 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. + */ + +public class CodeWriter implements CodeVisitor { + + /** + * true if preconditions must be checked at runtime or not. + */ + + final static boolean CHECK = false; + + /** + * Next code writer (see {@link ClassWriter#firstMethod firstMethod}). + */ + + CodeWriter next; + + /** + * The class writer to which this method must be added. + */ + + private ClassWriter cw; + + /** + * The constant pool item that contains the name of this method. + */ + + private Item name; + + /** + * The constant pool item that contains the descriptor of this method. + */ + + private Item desc; + + /** + * Access flags of this method. + */ + + private int access; + + /** + * Maximum stack size of this method. + */ + + private int maxStack; + + /** + * Maximum number of local variables for this method. + */ + + private int maxLocals; + + /** + * The bytecode of this method. + */ + + private ByteVector code = new ByteVector(); + + /** + * Number of entries in the catch table of this method. + */ + + private int catchCount; + + /** + * The catch table of this method. + */ + + private ByteVector catchTable; + + /** + * Number of exceptions that can be thrown by this method. + */ + + private 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. + */ + + private int[] exceptions; + + /** + * Number of entries in the LocalVariableTable attribute. + */ + + private int localVarCount; + + /** + * The LocalVariableTable attribute. + */ + + private ByteVector localVar; + + /** + * Number of entries in the LineNumberTable attribute. + */ + + private int lineNumberCount; + + /** + * The LineNumberTable attribute. + */ + + private ByteVector lineNumber; + + /** + * Indicates if some jump instructions are too small and need to be resized. + */ + + private boolean resize; + + // -------------------------------------------------------------------------- + // 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. + // -------------------------------------------------------------------------- + + /** + * true if the maximum stack size and number of local variables must + * be automatically computed. + */ + + private final boolean computeMaxs; + + /** + * 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#beginStackSize beginStackSize} of the current basic block plus + * stackSize. + */ + + private int stackSize; + + /** + * The (relative) maximum stack size after the last visited instruction. This + * size is relative to the beginning of the current basic block, i.e., the + * true maximum stack size after the last visited instruction is equal to the + * {@link Label#beginStackSize beginStackSize} of the current basic block plus + * stackSize. + */ + + private int maxStackSize; + + /** + * The current basic block. This block is the basic block to which the next + * instruction to be visited must be added. + */ + + private Label currentBlock; + + /** + * The basic block stack used by the control flow analysis algorithm. This + * stack is represented by a linked list of {@link Label Label} objects, + * linked to each other by their {@link Label#next} field. This stack must + * not be confused with the JVM stack used to execute the JVM instructions! + */ + + private Label blockStack; + + /** + * 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. + */ + + private final static int[] SIZE; + + // -------------------------------------------------------------------------- + // Fields to optimize the creation of {@link Edge Edge} objects by using a + // pool of reusable objects. The (shared) pool is a linked list of Edge + // objects, linked to each other by their {@link Edge#poolNext} field. Each + // time a CodeWriter needs to allocate an Edge, it removes the first Edge + // of the pool and adds it to a private list of Edge objects. After the end + // of the control flow analysis algorithm, the Edge objects in the private + // list of the CodeWriter are added back to the pool (by appending this + // private list to the pool list; in order to do this in constant time, both + // head and tail of the private list are stored in this CodeWriter). + // -------------------------------------------------------------------------- + + /** + * The head of the list of {@link Edge Edge} objects used by this {@link + * CodeWriter CodeWriter}. These objects, linked to each other by their + * {@link Edge#poolNext} field, are added back to the shared pool at the + * end of the control flow analysis algorithm. + */ + + private Edge head; + + /** + * The tail of the list of {@link Edge Edge} objects used by this {@link + * CodeWriter CodeWriter}. These objects, linked to each other by their + * {@link Edge#poolNext} field, are added back to the shared pool at the + * end of the control flow analysis algorithm. + */ + + private Edge tail; + + /** + * The shared pool of {@link Edge Edge} objects. This pool is a linked list + * of Edge objects, linked to each other by their {@link Edge#poolNext} field. + */ + + private static Edge pool; + + // -------------------------------------------------------------------------- + // Static initializer + // -------------------------------------------------------------------------- + + /** + * Computes the stack size variation corresponding to each JVM instruction. + */ + + static { + int i; + int[] b = new int[202]; + String s = + "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE" + + "EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF" + + "DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + 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(); + */ + } + + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + + /** + * Constructs a CodeWriter. + * + * @param cw the class writer in which the method must be added. + * @param computeMaxs true if the maximum stack size and number of + * local variables must be automatically computed. + */ + + protected CodeWriter (final ClassWriter cw, final boolean computeMaxs) { + if (cw.firstMethod == null) { + cw.firstMethod = this; + cw.lastMethod = this; + } else { + cw.lastMethod.next = this; + cw.lastMethod = this; + } + this.cw = cw; + this.computeMaxs = computeMaxs; + if (computeMaxs) { + // pushes the first block onto the stack of blocks to be visited + currentBlock = new Label(); + currentBlock.pushed = true; + blockStack = currentBlock; + } + } + + /** + * Initializes this CodeWriter to define the bytecode of the specified method. + * + * @param access the method's access flags (see {@link Constants}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param exceptions the internal names of the method's exceptions. May be + * null. + */ + + protected void init ( + final int access, + final String name, + final String desc, + final String[] exceptions) + { + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + 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]).index; + } + } + if (computeMaxs) { + // updates maxLocals + int size = getArgumentsAndReturnSizes(desc) >> 2; + if ((access & Constants.ACC_STATIC) != 0) { + --size; + } + if (size > maxLocals) { + maxLocals = size; + } + } + } + + // -------------------------------------------------------------------------- + // Implementation of the CodeVisitor interface + // -------------------------------------------------------------------------- + + public void visitInsn (final int opcode) { + if (computeMaxs) { + // updates current and max stack sizes + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN) || + opcode == Constants.ATHROW) + { + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } + } + // adds the instruction to the bytecode of the method + code.put1(opcode); + } + + public void visitIntInsn (final int opcode, final int operand) { + if (computeMaxs && opcode != Constants.NEWARRAY) { + // updates current and max stack sizes only if opcode == 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 == Constants.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + } + + public void visitVarInsn (final int opcode, final int var) { + if (computeMaxs) { + // updates current and max stack sizes + if (opcode == Constants.RET) { + // no stack change, but end of current block (no successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } else { // xLOAD or xSTORE + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // updates max locals + int n; + if (opcode == Constants.LLOAD || opcode == Constants.DLOAD || + opcode == Constants.LSTORE || opcode == Constants.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 != Constants.RET) { + int opt; + if (opcode < Constants.ISTORE) { + opt = 26 /*ILOAD_0*/ + ((opcode - Constants.ILOAD) << 2) + var; + } else { + opt = 59 /*ISTORE_0*/ + ((opcode - Constants.ISTORE) << 2) + var; + } + code.put1(opt); + } else if (var >= 256) { + code.put1(196 /*WIDE*/).put12(opcode, var); + } else { + code.put11(opcode, var); + } + } + + public void visitTypeInsn (final int opcode, final String desc) { + if (computeMaxs && opcode == Constants.NEW) { + // updates current and max stack sizes only if opcode == NEW + // (stack size variation = 0 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, cw.newClass(desc).index); + } + + public void visitFieldInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + if (computeMaxs) { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch (opcode) { + case Constants.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Constants.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Constants.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, cw.newField(owner, name, desc).index); + } + + public void visitMethodInsn ( + final int opcode, + final String owner, + final String name, + final String desc) + { + Item i; + if (opcode == Constants.INVOKEINTERFACE) { + i = cw.newItfMethod(owner, name, desc); + } else { + i = cw.newMethod(owner, name, desc); + } + int argSize = i.intVal; + if (computeMaxs) { + // 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 == Constants.INVOKESTATIC) { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } else { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + if (opcode == Constants.INVOKEINTERFACE) { + if (!computeMaxs) { + if (argSize == 0) { + argSize = getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + } + code.put12(Constants.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } else { + code.put12(opcode, i.index); + } + } + + public void visitJumpInsn (final int opcode, final Label label) { + if (CHECK) { + if (label.owner == null) { + label.owner = this; + } else if (label.owner != this) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + if (opcode == Constants.GOTO) { + // no stack change, but end of current block (with one new successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + currentBlock = null; + } + } else if (opcode == Constants.JSR) { + if (currentBlock != null) { + addSuccessor(stackSize + 1, label); + } + } else { + // updates current stack size (max stack size unchanged because stack + // size variation always negative in this case) + stackSize += SIZE[opcode]; + if (currentBlock != null) { + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if (label.resolved && label.position - code.length < Short.MIN_VALUE) { + // case of a backward jump with an offset < -32768. In this case we + // automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + // with IFNOTxxx GOTO_W , where IFNOTxxx is the "opposite" opcode + // of IFxxx (i.e., IFNE for IFEQ) and where designates the + // instruction just after the GOTO_W. + if (opcode == Constants.GOTO) { + code.put1(200); // GOTO_W + } else if (opcode == Constants.JSR) { + code.put1(201); // JSR_W + } else { + code.put1(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1); + code.put2(8); // jump offset + code.put1(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.put1(opcode); + label.put(this, code, code.length - 1, false); + } + } + + public void visitLabel (final Label label) { + if (CHECK) { + if (label.owner == null) { + label.owner = this; + } else if (label.owner != this) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + if (currentBlock != null) { + // ends current block (with one new successor) + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block, + // resets the relative current and max stack sizes + currentBlock = label; + stackSize = 0; + maxStackSize = 0; + } + // resolves previous forward references to label, if any + resize |= label.resolve(this, code.length, code.data); + } + + public void visitLdcInsn (final Object cst) { + Item i = cw.newCst(cst); + if (computeMaxs) { + 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(Constants.LDC, index); + } + } + + public void visitIincInsn (final int var, final int increment) { + if (computeMaxs) { + // updates max locals only (no stack change) + 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.put1(196 /*WIDE*/).put12(Constants.IINC, var).put2(increment); + } else { + code.put1(Constants.IINC).put11(var, increment); + } + } + + public void visitTableSwitchInsn ( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.put1(Constants.TABLESWITCH); + while (code.length % 4 != 0) { + code.put1(0); + } + dflt.put(this, code, source, true); + code.put4(min).put4(max); + for (int i = 0; i < labels.length; ++i) { + labels[i].put(this, code, source, true); + } + } + + public void visitLookupSwitchInsn ( + final Label dflt, + final int keys[], + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.put1(Constants.LOOKUPSWITCH); + while (code.length % 4 != 0) { + code.put1(0); + } + dflt.put(this, code, source, true); + code.put4(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.put4(keys[i]); + labels[i].put(this, code, source, true); + } + } + + public void visitMultiANewArrayInsn (final String desc, final int dims) { + if (computeMaxs) { + // 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 + Item classItem = cw.newClass(desc); + code.put12(Constants.MULTIANEWARRAY, classItem.index).put1(dims); + } + + public void visitTryCatchBlock ( + final Label start, + final Label end, + final Label handler, + final String type) + { + if (CHECK) { + if (start.owner != this || end.owner != this || handler.owner != this) { + throw new IllegalArgumentException(); + } + if (!start.resolved || !end.resolved || !handler.resolved) { + throw new IllegalArgumentException(); + } + } + if (computeMaxs) { + // pushes handler block onto the stack of blocks to be visited + if (!handler.pushed) { + handler.beginStackSize = 1; + handler.pushed = true; + handler.next = blockStack; + blockStack = handler; + } + } + ++catchCount; + if (catchTable == null) { + catchTable = new ByteVector(); + } + catchTable.put2(start.position); + catchTable.put2(end.position); + catchTable.put2(handler.position); + catchTable.put2(type != null ? cw.newClass(type).index : 0); + } + + public void visitMaxs (final int maxStack, final int maxLocals) { + if (computeMaxs) { + // true (non relative) max stack size + int max = 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#beginStackSize} of the blocks in the block stack are the true + // (non relative) beginning stack sizes of these blocks. + Label stack = blockStack; + 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.beginStackSize; + int blockMax = start + l.maxStackSize; + // updates the global max stack size + if (blockMax > max) { + max = blockMax; + } + // analyses the successors of the block + Edge b = l.successors; + while (b != null) { + l = b.successor; + // if this successor has not already been pushed onto the stack... + if (!l.pushed) { + // computes the true beginning stack size of this successor block + l.beginStackSize = start + b.stackSize; + // pushes this successor onto the stack + l.pushed = true; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = max; + // releases all the Edge objects used by this CodeWriter + synchronized (SIZE) { + // appends the [head ... tail] list at the beginning of the pool list + if (tail != null) { + tail.poolNext = pool; + pool = head; + } + } + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + public void visitLocalVariable ( + final String name, + final String desc, + final Label start, + final Label end, + final int index) + { + if (CHECK) { + if (start.owner != this || !start.resolved) { + throw new IllegalArgumentException(); + } + if (end.owner != this || !end.resolved) { + throw new IllegalArgumentException(); + } + } + if (localVar == null) { + cw.newUTF8("LocalVariableTable"); + localVar = new ByteVector(); + } + ++localVarCount; + localVar.put2(start.position); + localVar.put2(end.position - start.position); + localVar.put2(cw.newUTF8(name).index); + localVar.put2(cw.newUTF8(desc).index); + localVar.put2(index); + } + + public void visitLineNumber (final int line, final Label start) { + if (CHECK) { + if (start.owner != this || !start.resolved) { + throw new IllegalArgumentException(); + } + } + if (lineNumber == null) { + cw.newUTF8("LineNumberTable"); + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.put2(start.position); + lineNumber.put2(line); + } + + // -------------------------------------------------------------------------- + // 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 = (argSize << 2) | retSize + * (argSize is therefore equal to i >> 2, and retSize to + * i & 0x03). + */ + + private 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 stackSize the current (relative) stack size in the current block. + * @param successor the successor block to be added to the current block. + */ + + private void addSuccessor (final int stackSize, final Label successor) { + Edge b; + // creates a new Edge object or reuses one from the shared pool + synchronized (SIZE) { + if (pool == null) { + b = new Edge(); + } else { + b = pool; + // removes b from the pool + pool = pool.poolNext; + } + } + // adds the previous Edge to the list of Edges used by this CodeWriter + if (tail == null) { + tail = b; + } + b.poolNext = head; + head = b; + // initializes the previous Edge object... + b.stackSize = stackSize; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; + } + + // -------------------------------------------------------------------------- + // 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 (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + resizeInstructions(new int[0], new int[0], 0); + } + int size = 8; + if (code.length > 0) { + cw.newUTF8("Code"); + size += 18 + code.length + 8 * catchCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + } + if (exceptionCount > 0) { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + 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.put2(access).put2(name.index).put2(desc.index); + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (exceptionCount > 0) { + ++attributeCount; + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + ++attributeCount; + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + out.put2(attributeCount); + if (code.length > 0) { + int size = 12 + code.length + 8 * catchCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + out.put2(cw.newUTF8("Code").index).put4(size); + out.put2(maxStack).put2(maxLocals); + out.put4(code.length).putByteArray(code.data, 0, code.length); + out.put2(catchCount); + if (catchCount > 0) { + out.putByteArray(catchTable.data, 0, catchTable.length); + } + attributeCount = 0; + if (localVar != null) { + ++attributeCount; + } + if (lineNumber != null) { + ++attributeCount; + } + out.put2(attributeCount); + if (localVar != null) { + out.put2(cw.newUTF8("LocalVariableTable").index); + out.put4(localVar.length + 2).put2(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if (lineNumber != null) { + out.put2(cw.newUTF8("LineNumberTable").index); + out.put4(lineNumber.length + 2).put2(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + } + if (exceptionCount > 0) { + out.put2(cw.newUTF8("Exceptions").index).put4(2 * exceptionCount + 2); + out.put2(exceptionCount); + for (int i = 0; i < exceptionCount; ++i) { + out.put2(exceptions[i]); + } + } + if ((access & Constants.ACC_SYNTHETIC) != 0) { + out.put2(cw.newUTF8("Synthetic").index).put4(0); + } + if ((access & Constants.ACC_DEPRECATED) != 0) { + out.put2(cw.newUTF8("Deprecated").index).put4(0); + } + } + + // -------------------------------------------------------------------------- + // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) + // -------------------------------------------------------------------------- + + /** + * Resizes the designated instructions, while keeping jump offsets and + * instruction addresses consistent. This may require to resize other existing + * instructions, or even to introduce new instructions: for example, + * increasing the size of an instruction by 2 at the middle of a method can + * increases the offset of an IFEQ instruction from 32766 to 32768, in which + * case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 32765. This, in turn, + * may require to increase the size of another jump instruction, and so on... + * All these operations are handled automatically by this method. + *

+ * This method must be called after all the method that is being built has + * been visited. In particular, the {@link Label Label} objects used to + * construct the method are no longer valid after this method has been called. + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last byte, + * plus one (or, in other words, by the index of the first byte of + * the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the instruction + * designated by indexes[i] or, if sizes[i] is + * negative, the last |sizes[i]| bytes of the instruction + * will be removed (the instruction size must not become negative + * or null). The gaps introduced by this method must be filled in + * "manually" in the array returned by the {@link #getCode getCode} + * method. + * @param len the number of instruction to be resized. Must be smaller than or + * equal to indexes.length and sizes.length. + * @return the indexes array, which now contains the new positions of + * the resized instructions (designated as above). + */ + + protected int[] resizeInstructions ( + final int[] indexes, + final int[] sizes, + final int len) + { + 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[len]; // copy of indexes + int[] allSizes = new int[len]; // copy of sizes + boolean[] resize; // instructions to be resized + int newOffset; // future offset of a jump instruction + + System.arraycopy(indexes, 0, allIndexes, 0, len); + System.arraycopy(sizes, 0, allSizes, 0, len); + resize = new boolean[code.length]; + + int state = 3; // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done + 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 (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 (newOffset < Short.MIN_VALUE || newOffset > Short.MAX_VALUE) { + if (!resize[u]) { + if (opcode == Constants.GOTO || opcode == Constants.JSR) { + // two additional bytes will be required to replace this + // GOTO or JSR instruction with a GOTO_W or a JSR_W + insert = 2; + } else { + // five additional bytes will be required to replace this + // IFxxx instruction with IFNOTxxx GOTO_W , where + // IFNOTxxx is the "opposite" opcode of IFxxx (i.e., IFNE for + // IFEQ) and where designates the instruction just after + // the GOTO_W. + insert = 5; + } + resize[u] = true; + } + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + u += 5; + break; + case ClassWriter.TABL_INSN: + if (state == 1) { + // true number of bytes to be added (or removed) from this + // instruction = (future number of padding bytes - current number + // of padding byte) - previously over estimated variation = + // = ((3 - newOffset%4) - (3 - u%4)) - u%4 + // = (-newOffset%4 + u%4) - u%4 + // = -(newOffset & 3) + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // over estimation of the number of bytes to be added to this + // instruction = 3 - current number of padding bytes = 3 - (3 - + // u%4) = u%4 = u & 3 + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 4*(readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; + break; + case ClassWriter.LOOK_INSN: + if (state == 1) { + // like TABL_INSN + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // like TABL_INSN + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 8*readInt(b, u + 4) + 8; + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Constants.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) { + for (i = allIndexes.length - 1; i >= 0; --i) { + if (allIndexes[i] == u) { + if (i < len) { + if (sizes[i] > 0) { + newCode.putByteArray(null, 0, sizes[i]); + } else { + newCode.length += sizes[i]; + } + indexes[i] = newCode.length; + } + } + } + int opcode = b[u] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + newCode.put1(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 (newOffset < Short.MIN_VALUE || newOffset > Short.MAX_VALUE) { + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx with + // IFNOTxxx GOTO_W , where IFNOTxxx is the "opposite" opcode + // of IFxxx (i.e., IFNE for IFEQ) and where designates the + // instruction just after the GOTO_W. + if (opcode == Constants.GOTO) { + newCode.put1(200); // GOTO_W + } else if (opcode == Constants.JSR) { + newCode.put1(201); // JSR_W + } else { + newCode.put1(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1); + newCode.put2(8); // jump offset + newCode.put1(200); // GOTO_W + newOffset -= 3; // newOffset now computed from start of GOTO_W + } + newCode.put4(newOffset); + } else { + newCode.put1(opcode); + newCode.put2(newOffset); + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + label = u + readInt(b, u + 1); + newOffset = getNewOffset(allIndexes, allSizes, u, label); + newCode.put1(opcode); + newCode.put4(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 + int source = newCode.length; + newCode.put1(Constants.TABLESWITCH); + while (newCode.length % 4 != 0) { + newCode.put1(0); + } + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.put4(newOffset); + j = readInt(b, u); u += 4; + newCode.put4(j); + j = readInt(b, u) - j + 1; u += 4; + newCode.put4(readInt(b, u - 4)); + for ( ; j > 0; --j) { + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.put4(newOffset); + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + source = newCode.length; + newCode.put1(Constants.LOOKUPSWITCH); + while (newCode.length % 4 != 0) { + newCode.put1(0); + } + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.put4(newOffset); + j = readInt(b, u); u += 4; + newCode.put4(j); + for ( ; j > 0; --j) { + newCode.put4(readInt(b, u)); u += 4; + label = v + readInt(b, u); u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.put4(newOffset); + } + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Constants.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; + } + } + + // updates the instructions addresses in the + // catch, local var and line number tables + if (catchTable != null) { + b = catchTable.data; + u = 0; + while (u < catchTable.length) { + writeShort(b, u, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u))); + writeShort(b, u + 2, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u + 2))); + writeShort(b, u + 4, getNewOffset( + allIndexes, allSizes, 0, readUnsignedShort(b, u + 4))); + u += 8; + } + } + if (localVar != null) { + b = localVar.data; + u = 0; + while (u < localVar.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, 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; + } + } + + // replaces old bytecodes with new ones + code = newCode; + + // returns the positions of the resized instructions + return indexes; + } + + /** + * Reads an unsigned short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static int readUnsignedShort (final byte[] b, final int index) { + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static short readShort (final byte[] b, final int index) { + return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ + + static int readInt (final byte[] b, final int index) { + return ((b[index] & 0xFF) << 24) | + ((b[index + 1] & 0xFF) << 16) | + ((b[index + 2] & 0xFF) << 8) | + (b[index + 3] & 0xFF); + } + + /** + * Writes a short value in the given byte array. + * + * @param b a byte array. + * @param index where the first byte of the short value must be written. + * @param s the value to be written in the given byte array. + */ + + static void writeShort (final byte[] b, final int index, final int s) { + b[index] = (byte)(s >>> 8); + b[index + 1] = (byte)s; + } + + /** + * Computes the future value of a bytecode offset. + *

+ * Note: it is possible to have several entries for the same instruction + * in the indexes and sizes: two entries (index=a,size=b) + * and (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last byte, + * plus one (or, in other words, by the index of the first byte of + * the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the instruction + * designated by indexes[i] or, if sizes[i] is + * negative, the last |sizes[i]| bytes of the instruction + * will be removed (the instruction size must not become negative + * or null). + * @param begin index of the first byte of the source instruction. + * @param end index of the first byte of the target instruction. + * @return the future value of the given bytecode offset. + */ + + static int getNewOffset ( + final int[] indexes, + final int[] sizes, + final int begin, + final int end) + { + int offset = end - begin; + for (int i = 0; i < indexes.length; ++i) { + if (begin < indexes[i] && indexes[i] <= end) { // forward jump + offset += sizes[i]; + } else if (end < indexes[i] && indexes[i] <= begin) { // backward jump + offset -= sizes[i]; + } + } + return offset; + } + + /** + * Returns the current size of the bytecode of this method. This size just + * includes the size of the bytecode instructions: it does not include the + * size of the Exceptions, LocalVariableTable, LineNumberTable, Synthetic + * and Deprecated attributes, if present. + * + * @return the current size of the bytecode of this method. + */ + + protected int getCodeSize () { + return code.length; + } + + /** + * Returns the current bytecode of this method. This bytecode only contains + * the instructions: it does not include the Exceptions, LocalVariableTable, + * LineNumberTable, Synthetic and Deprecated attributes, if present. + * + * @return the current bytecode of this method. The bytecode is contained + * between the index 0 (inclusive) and the index {@link #getCodeSize + * getCodeSize} (exclusive). + */ + + protected byte[] getCode () { + return code.data; + } +} diff --git a/asm/src/bsh/org/objectweb/asm/Constants.java b/asm/src/bsh/org/objectweb/asm/Constants.java new file mode 100644 index 0000000..14a8855 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/Constants.java @@ -0,0 +1,273 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.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. + */ + +public interface Constants { + + // access flags + + int ACC_PUBLIC = 1; + int ACC_PRIVATE = 2; + int ACC_PROTECTED = 4; + int ACC_STATIC = 8; + int ACC_FINAL = 16; + int ACC_SYNCHRONIZED = 32; + int ACC_VOLATILE = 64; + int ACC_TRANSIENT = 128; + int ACC_NATIVE = 256; + int ACC_INTERFACE = 512; + int ACC_ABSTRACT = 1024; + int ACC_STRICT = 2048; + int ACC_SUPER = 32; + + int ACC_SYNTHETIC = 65536; + int ACC_DEPRECATED = 131072; + + // 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; + + // 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/asm/src/bsh/org/objectweb/asm/Edge.java b/asm/src/bsh/org/objectweb/asm/Edge.java new file mode 100644 index 0000000..9b371c9 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/Edge.java @@ -0,0 +1,60 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + */ + +class Edge { + + /** + * 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. + */ + + int stackSize; + + /** + * 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; + + /** + * The next available edge in the pool. See {@link CodeWriter#pool pool}. + */ + + Edge poolNext; +} diff --git a/asm/src/bsh/org/objectweb/asm/Item.java b/asm/src/bsh/org/objectweb/asm/Item.java new file mode 100644 index 0000000..359751f --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/Item.java @@ -0,0 +1,256 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + */ + +final class Item { + + /** + * Index of this item in the constant pool. + */ + + short 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 the constants defined in the + * {@link ClassWriter ClassWriter} class. + */ + + int type; + + /** + * Value of this item, for a {@link ClassWriter#INT INT} item. + */ + + int intVal; + + /** + * Value of this item, for a {@link ClassWriter#LONG LONG} item. + */ + + long longVal; + + /** + * Value of this item, for a {@link ClassWriter#FLOAT FLOAT} item. + */ + + float floatVal; + + /** + * Value of this item, for a {@link ClassWriter#DOUBLE DOUBLE} item. + */ + + double doubleVal; + + /** + * 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} object. + */ + + Item () { + } + + /** + * 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 short index, final Item i) { + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + floatVal = i.floatVal; + doubleVal = i.doubleVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; + } + + /** + * Sets this item to an {@link ClassWriter#INT INT} item. + * + * @param intVal the value of this item. + */ + + void set (final int intVal) { + this.type = ClassWriter.INT; + this.intVal = intVal; + this.hashCode = type + intVal; + } + + /** + * Sets this item to a {@link ClassWriter#LONG LONG} item. + * + * @param longVal the value of this item. + */ + + void set (final long longVal) { + this.type = ClassWriter.LONG; + this.longVal = longVal; + this.hashCode = type + (int)longVal; + } + + /** + * Sets this item to a {@link ClassWriter#FLOAT FLOAT} item. + * + * @param floatVal the value of this item. + */ + + void set (final float floatVal) { + this.type = ClassWriter.FLOAT; + this.floatVal = floatVal; + this.hashCode = type + (int)floatVal; + } + + /** + * Sets this item to a {@link ClassWriter#DOUBLE DOUBLE} item. + * + * @param doubleVal the value of this item. + */ + + void set (final double doubleVal) { + this.type = ClassWriter.DOUBLE; + this.doubleVal = doubleVal; + this.hashCode = 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: + hashCode = type + strVal1.hashCode(); + return; + case ClassWriter.NAME_TYPE: + hashCode = type + strVal1.hashCode()*strVal2.hashCode(); + return; + //case ClassWriter.FIELD: + //case ClassWriter.METH: + //case ClassWriter.IMETH: + default: + hashCode = type + strVal1.hashCode()*strVal2.hashCode()*strVal3.hashCode(); + return; + } + } + + /** + * Indicates if the given item is equal to this one. + * + * @param i the item to be compared to this one. + * @return true if the given item if equal to this one, + * false otherwise. + */ + + boolean isEqualTo (final Item i) { + if (i.type == type) { + switch (type) { + case ClassWriter.INT: + return i.intVal == intVal; + case ClassWriter.LONG: + return i.longVal == longVal; + case ClassWriter.FLOAT: + return i.floatVal == floatVal; + case ClassWriter.DOUBLE: + return i.doubleVal == doubleVal; + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + return i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) && + i.strVal2.equals(strVal2); + //case ClassWriter.FIELD: + //case ClassWriter.METH: + //case ClassWriter.IMETH: + default: + return i.strVal1.equals(strVal1) && + i.strVal2.equals(strVal2) && + i.strVal3.equals(strVal3); + } + } + return false; + } +} diff --git a/asm/src/bsh/org/objectweb/asm/Label.java b/asm/src/bsh/org/objectweb/asm/Label.java new file mode 100644 index 0000000..2b294e7 --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/Label.java @@ -0,0 +1,279 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.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. + */ + +public class Label { + + /** + * The code writer to which this label belongs, or null if unknown. + */ + + CodeWriter owner; + + /** + * Indicates if the position of this label is known. + */ + + boolean resolved; + + /** + * 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 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 it successors in the graph, as a linked list of Edge + // objects. + // -------------------------------------------------------------------------- + + /** + * The stack size at the beginning of this basic block. + * This size is initially unknown. It is computed by the control flow + * analysis algorithm (see {@link CodeWriter#visitMaxs visitMaxs}). + */ + + int beginStackSize; + + /** + * The (relative) maximum stack size corresponding to this basic block. This + * size is relative to the stack size at the beginning of the basic block, + * i.e., the true maximum stack size is equal to {@link #beginStackSize + * beginStackSize} + {@link #maxStackSize maxStackSize}. + */ + + int maxStackSize; + + /** + * 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. + * See {@link CodeWriter#visitMaxs visitMaxs}. + */ + + Label next; + + /** + * true if this basic block has been pushed in the basic block stack. + * See {@link CodeWriter#visitMaxs visitMaxs}. + */ + + boolean pushed; + + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + + /** + * Constructs a new label. + */ + + public Label () { + } + + // -------------------------------------------------------------------------- + // Methods to compute offsets and to manage forward references + // -------------------------------------------------------------------------- + + /** + * Puts a reference to this label in the bytecode of a method. If the position + * of the label is known, the offset is computed and written directly. + * Otherwise, a null offset is written and a new forward reference is declared + * for this label. + * + * @param owner the code writer that calls this method. + * @param out the bytecode of the method. + * @param source the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset true if the reference must be stored in 4 bytes, + * or false if it must be stored with 2 bytes. + * @throws IllegalArgumentException if this label has not been created by the + * given code writer. + */ + + void put ( + final CodeWriter owner, + final ByteVector out, + final int source, + final boolean wideOffset) + { + if (CodeWriter.CHECK) { + if (this.owner == null) { + this.owner = owner; + } else if (this.owner != owner) { + throw new IllegalArgumentException(); + } + } + if (resolved) { + if (wideOffset) { + out.put4(position - source); + } else { + out.put2(position - source); + } + } else { + if (wideOffset) { + addReference(-1 - source, out.length); + out.put4(-1); + } else { + addReference(source, out.length); + out.put2(-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 true if a blank that was left for this label was to small + * to store the offset. In such a case the corresponding jump instruction + * is replaced with a pseudo instruction (using unused opcodes) using an + * unsigned two bytes offset. These pseudo instructions will need to be + * replaced with true instructions with wider offsets (4 bytes instead of + * 2). This is done in {@link CodeWriter#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 CodeWriter owner, + final int position, + final byte[] data) + { + if (CodeWriter.CHECK) { + if (this.owner == null) { + this.owner = owner; + } + if (resolved || this.owner != owner) { + throw new IllegalArgumentException(); + } + } + boolean needUpdate = false; + this.resolved = true; + 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 CodeWriter). 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 <= Constants.JSR) { + // changes IFEQ ... JSR to opcodes 202 to 217 (inclusive) + data[reference - 1] = (byte)(opcode + 49); + } else { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 (inclusive) + 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; + } +} diff --git a/asm/src/bsh/org/objectweb/asm/Type.java b/asm/src/bsh/org/objectweb/asm/Type.java new file mode 100644 index 0000000..b02b66d --- /dev/null +++ b/asm/src/bsh/org/objectweb/asm/Type.java @@ -0,0 +1,693 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (C) 2000 INRIA, France Telecom + * Copyright (C) 2002 France Telecom + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact: Eric.Bruneton@rd.francetelecom.com + * + * Author: Eric Bruneton + */ + +package bsh.org.objectweb.asm; + +import java.lang.reflect.Method; + +/** + * A Java type. This class can be used to make it easier to manipulate type + * and method descriptors. + */ + +public class Type { + + /** + * The sort of the void type. See {@link #getSort getSort}. + */ + + public final static int VOID = 0; + + /** + * The sort of the boolean type. See {@link #getSort getSort}. + */ + + public final static int BOOLEAN = 1; + + /** + * The sort of the char type. See {@link #getSort getSort}. + */ + + public final static int CHAR = 2; + + /** + * The sort of the byte type. See {@link #getSort getSort}. + */ + + public final static int BYTE = 3; + + /** + * The sort of the short type. See {@link #getSort getSort}. + */ + + public final static int SHORT = 4; + + /** + * The sort of the int type. See {@link #getSort getSort}. + */ + + public final static int INT = 5; + + /** + * The sort of the float type. See {@link #getSort getSort}. + */ + + public final static int FLOAT = 6; + + /** + * The sort of the long type. See {@link #getSort getSort}. + */ + + public final static int LONG = 7; + + /** + * The sort of the double 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 void type. + */ + + public final static Type VOID_TYPE = new Type(VOID); + + /** + * The boolean type. + */ + + public final static Type BOOLEAN_TYPE = new Type(BOOLEAN); + + /** + * The char type. + */ + + public final static Type CHAR_TYPE = new Type(CHAR); + + /** + * The byte type. + */ + + public final static Type BYTE_TYPE = new Type(BYTE); + + /** + * The short type. + */ + + public final static Type SHORT_TYPE = new Type(SHORT); + + /** + * The int type. + */ + + public final static Type INT_TYPE = new Type(INT); + + /** + * The float type. + */ + + public final static Type FLOAT_TYPE = new Type(FLOAT); + + /** + * The long type. + */ + + public final static Type LONG_TYPE = new Type(LONG); + + /** + * The double 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 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 object type. + * This method should only be used for an object type. + * + * @return the fully qualified name of the class corresponding to this object + * type. + */ + + public String getClassName () { + 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 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 long and + * double, 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 float and + * opcode is IRETURN, this method returns FRETURN. + */ + + public int getOpcode (final int opcode) { + if (opcode == Constants.IALOAD || opcode == Constants.IASTORE) { + switch (sort) { + case VOID: + return opcode + 5; + case BOOLEAN: + case BYTE: + return opcode + 6; + case CHAR: + return opcode + 7; + case SHORT: + return opcode + 8; + 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; + } + } + } +} diff --git a/bsf/README b/bsf/README new file mode 100644 index 0000000..9d34c6c --- /dev/null +++ b/bsf/README @@ -0,0 +1,18 @@ + +This is the BeanShell adapter for IBM's Bean Scripting Famework. +It is an implementation of the BSFEngine class, allowing BSF aware +applications to use BeanShell as a scripting language. + +I believe this implementation is complete (with some hesitation about the +the usefullness of the compileXXX() style methods - provided by the base +utility class). + + +Note: + +Changed over to Apache Jakarta BSF 2.3 with BeanShell 1.3. +The changes are package name changes only. +See http://www.beanshell.org/manual/bsf.html for some more info. + + +@author Pat Niemeyer diff --git a/bsf/src/TestBshBSF.java b/bsf/src/TestBshBSF.java new file mode 100644 index 0000000..f89f6fd --- /dev/null +++ b/bsf/src/TestBshBSF.java @@ -0,0 +1,61 @@ + +import org.apache.bsf.*; +import java.util.Vector; + +public class TestBshBSF +{ + public static void main( String [] args ) + throws BSFException + { + BSFManager mgr = new BSFManager(); + + // register beanshell with the BSF framework + String [] extensions = { "bsh" }; + mgr.registerScriptingEngine( + "beanshell", "bsh.util.BeanShellBSFEngine", extensions ); + + mgr.declareBean("foo", "fooString", String.class); + mgr.declareBean("bar", "barString", String.class); + mgr.registerBean("gee", "geeString"); + + BSFEngine beanshellEngine = mgr.loadScriptingEngine("beanshell"); + + String script = "foo + bar + bsf.lookupBean(\"gee\")"; + Object result = beanshellEngine.eval( "Test eval...", -1, -1, script ); + + assertTrue( result.equals("fooStringbarStringgeeString" ) ); + + // test apply() + Vector names = new Vector(); + names.addElement("name"); + Vector vals = new Vector(); + vals.addElement("Pat"); + + script = "name + name"; + + result = beanshellEngine.apply( + "source string...", -1, -1, script, names, vals ); + + assertTrue( result.equals("PatPat" ) ); + + result = beanshellEngine.eval( "Test eval...", -1, -1, "name" ); + + // name should not be set + assertTrue( result == null ); + + // Verify the primitives are unwrapped + result = beanshellEngine.eval( "Test eval...", -1, -1, "1+1"); + + assertTrue( result instanceof Integer + && ((Integer)result).intValue() == 2 ); + } + + static void assertTrue( boolean cond ) { + if ( cond ) + System.out.println("Passed..."); + else + throw new Error("assert failed..."); + } + +} + diff --git a/bsf/src/bsh/util/BeanShellBSFEngine.java b/bsf/src/bsh/util/BeanShellBSFEngine.java new file mode 100644 index 0000000..ef9a188 --- /dev/null +++ b/bsf/src/bsh/util/BeanShellBSFEngine.java @@ -0,0 +1,268 @@ +package bsh.util; + +/* + This file is associated with the BeanShell Java Scripting language + distribution (http://www.beanshell.org/). + + This file is hereby placed into the public domain... You may copy, + modify, and redistribute it without restriction. +*/ + +import java.util.Vector; +import org.apache.bsf.*; +import org.apache.bsf.util.*; +import bsh.Interpreter; +import bsh.InterpreterError; +import bsh.EvalError; +import bsh.TargetError; +import bsh.Primitive; + +/** + This is the BeanShell adapter for IBM's Bean Scripting Famework. + It is an implementation of the BSFEngine class, allowing BSF aware + applications to use BeanShell as a scripting language. +

+ + I believe this implementation is complete (with some hesitation about the + the usefullness of the compileXXX() style methods - provided by the base + utility class). +

+ + @author Pat Niemeyer +*/ +public class BeanShellBSFEngine extends BSFEngineImpl +{ + Interpreter interpreter; + boolean installedApplyMethod; + + public void initialize ( BSFManager mgr, String lang, Vector declaredBeans) + throws BSFException + { + super.initialize( mgr, lang, declaredBeans ); + + interpreter = new Interpreter(); + + // declare the bsf manager for callbacks, etc. + try { + interpreter.set( "bsf", mgr ); + } catch ( EvalError e ) { + throw new BSFException("bsh internal error: "+e.toString()); + } + + for(int i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/faq/build.xml b/docs/faq/build.xml new file mode 100644 index 0000000..4725ec8 --- /dev/null +++ b/docs/faq/build.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + +