diff src/clojure/asm/Label.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/asm/Label.java	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,437 @@
     1.4 +/***
     1.5 + * ASM: a very small and fast Java bytecode manipulation framework
     1.6 + * Copyright (c) 2000-2005 INRIA, France Telecom
     1.7 + * All rights reserved.
     1.8 + *
     1.9 + * Redistribution and use in source and binary forms, with or without
    1.10 + * modification, are permitted provided that the following conditions
    1.11 + * are met:
    1.12 + * 1. Redistributions of source code must retain the above copyright
    1.13 + *    notice, this list of conditions and the following disclaimer.
    1.14 + * 2. Redistributions in binary form must reproduce the above copyright
    1.15 + *    notice, this list of conditions and the following disclaimer in the
    1.16 + *    documentation and/or other materials provided with the distribution.
    1.17 + * 3. Neither the name of the copyright holders nor the names of its
    1.18 + *    contributors may be used to endorse or promote products derived from
    1.19 + *    this software without specific prior written permission.
    1.20 + *
    1.21 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    1.22 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    1.23 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    1.24 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    1.25 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    1.26 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    1.27 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    1.28 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    1.29 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    1.30 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    1.31 + * THE POSSIBILITY OF SUCH DAMAGE.
    1.32 + */
    1.33 +package clojure.asm;
    1.34 +
    1.35 +/**
    1.36 + * A label represents a position in the bytecode of a method. Labels are used
    1.37 + * for jump, goto, and switch instructions, and for try catch blocks.
    1.38 + *
    1.39 + * @author Eric Bruneton
    1.40 + */
    1.41 +public class Label{
    1.42 +
    1.43 +/**
    1.44 + * Indicates if this label is only used for debug attributes. Such a label
    1.45 + * is not the start of a basic block, the target of a jump instruction, or
    1.46 + * an exception handler. It can be safely ignored in control flow graph
    1.47 + * analysis algorithms (for optimization purposes).
    1.48 + */
    1.49 +final static int DEBUG = 1;
    1.50 +
    1.51 +/**
    1.52 + * Indicates if the position of this label is known.
    1.53 + */
    1.54 +final static int RESOLVED = 2;
    1.55 +
    1.56 +/**
    1.57 + * Indicates if this label has been updated, after instruction resizing.
    1.58 + */
    1.59 +final static int RESIZED = 4;
    1.60 +
    1.61 +/**
    1.62 + * Indicates if this basic block has been pushed in the basic block stack.
    1.63 + * See {@link MethodWriter#visitMaxs visitMaxs}.
    1.64 + */
    1.65 +final static int PUSHED = 8;
    1.66 +
    1.67 +/**
    1.68 + * Indicates if this label is the target of a jump instruction, or the start
    1.69 + * of an exception handler.
    1.70 + */
    1.71 +final static int TARGET = 16;
    1.72 +
    1.73 +/**
    1.74 + * Indicates if a stack map frame must be stored for this label.
    1.75 + */
    1.76 +final static int STORE = 32;
    1.77 +
    1.78 +/**
    1.79 + * Indicates if this label corresponds to a reachable basic block.
    1.80 + */
    1.81 +final static int REACHABLE = 64;
    1.82 +
    1.83 +/**
    1.84 + * Indicates if this basic block ends with a JSR instruction.
    1.85 + */
    1.86 +final static int JSR = 128;
    1.87 +
    1.88 +/**
    1.89 + * Indicates if this basic block ends with a RET instruction.
    1.90 + */
    1.91 +final static int RET = 256;
    1.92 +
    1.93 +/**
    1.94 + * Field used to associate user information to a label.
    1.95 + */
    1.96 +public Object info;
    1.97 +
    1.98 +/**
    1.99 + * Flags that indicate the status of this label.
   1.100 + *
   1.101 + * @see #DEBUG
   1.102 + * @see #RESOLVED
   1.103 + * @see #RESIZED
   1.104 + * @see #PUSHED
   1.105 + * @see #TARGET
   1.106 + * @see #STORE
   1.107 + * @see #REACHABLE
   1.108 + * @see #JSR
   1.109 + * @see #RET
   1.110 + */
   1.111 +int status;
   1.112 +
   1.113 +/**
   1.114 + * The line number corresponding to this label, if known.
   1.115 + */
   1.116 +int line;
   1.117 +
   1.118 +/**
   1.119 + * The position of this label in the code, if known.
   1.120 + */
   1.121 +int position;
   1.122 +
   1.123 +/**
   1.124 + * Number of forward references to this label, times two.
   1.125 + */
   1.126 +private int referenceCount;
   1.127 +
   1.128 +/**
   1.129 + * Informations about forward references. Each forward reference is
   1.130 + * described by two consecutive integers in this array: the first one is the
   1.131 + * position of the first byte of the bytecode instruction that contains the
   1.132 + * forward reference, while the second is the position of the first byte of
   1.133 + * the forward reference itself. In fact the sign of the first integer
   1.134 + * indicates if this reference uses 2 or 4 bytes, and its absolute value
   1.135 + * gives the position of the bytecode instruction.
   1.136 + */
   1.137 +private int[] srcAndRefPositions;
   1.138 +
   1.139 +// ------------------------------------------------------------------------
   1.140 +
   1.141 +/*
   1.142 +	 * Fields for the control flow and data flow graph analysis algorithms (used
   1.143 +	 * to compute the maximum stack size or the stack map frames). A control
   1.144 +	 * flow graph contains one node per "basic block", and one edge per "jump"
   1.145 +	 * from one basic block to another. Each node (i.e., each basic block) is
   1.146 +	 * represented by the Label object that corresponds to the first instruction
   1.147 +	 * of this basic block. Each node also stores the list of its successors in
   1.148 +	 * the graph, as a linked list of Edge objects.
   1.149 +	 *
   1.150 +	 * The control flow analysis algorithms used to compute the maximum stack
   1.151 +	 * size or the stack map frames are similar and use two steps. The first
   1.152 +	 * step, during the visit of each instruction, builds information about the
   1.153 +	 * state of the local variables and the operand stack at the end of each
   1.154 +	 * basic block, called the "output frame", <i>relatively</i> to the frame
   1.155 +	 * state at the beginning of the basic block, which is called the "input
   1.156 +	 * frame", and which is <i>unknown</i> during this step. The second step,
   1.157 +	 * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that
   1.158 +	 * computes information about the input frame of each basic block, from the
   1.159 +	 * input state of the first basic block (known from the method signature),
   1.160 +	 * and by the using the previously computed relative output frames.
   1.161 +	 *
   1.162 +	 * The algorithm used to compute the maximum stack size only computes the
   1.163 +	 * relative output and absolute input stack heights, while the algorithm
   1.164 +	 * used to compute stack map frames computes relative output frames and
   1.165 +	 * absolute input frames.
   1.166 +	 */
   1.167 +
   1.168 +/**
   1.169 + * Start of the output stack relatively to the input stack. The exact
   1.170 + * semantics of this field depends on the algorithm that is used.
   1.171 + * <p/>
   1.172 + * When only the maximum stack size is computed, this field is the number of
   1.173 + * elements in the input stack.
   1.174 + * <p/>
   1.175 + * When the stack map frames are completely computed, this field is the
   1.176 + * offset of the first output stack element relatively to the top of the
   1.177 + * input stack. This offset is always negative or null. A null offset means
   1.178 + * that the output stack must be appended to the input stack. A -n offset
   1.179 + * means that the first n output stack elements must replace the top n input
   1.180 + * stack elements, and that the other elements must be appended to the input
   1.181 + * stack.
   1.182 + */
   1.183 +int inputStackTop;
   1.184 +
   1.185 +/**
   1.186 + * Maximum height reached by the output stack, relatively to the top of the
   1.187 + * input stack. This maximum is always positive or null.
   1.188 + */
   1.189 +int outputStackMax;
   1.190 +
   1.191 +/**
   1.192 + * Information about the input and output stack map frames of this basic
   1.193 + * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
   1.194 + * option is used.
   1.195 + */
   1.196 +Frame frame;
   1.197 +
   1.198 +/**
   1.199 + * The successor of this label, in the order they are visited. This linked
   1.200 + * list does not include labels used for debug info only. If
   1.201 + * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
   1.202 + * does not contain successive labels that denote the same bytecode position
   1.203 + * (in this case only the first label appears in this list).
   1.204 + */
   1.205 +Label successor;
   1.206 +
   1.207 +/**
   1.208 + * The successors of this node in the control flow graph. These successors
   1.209 + * are stored in a linked list of {@link Edge Edge} objects, linked to each
   1.210 + * other by their {@link Edge#next} field.
   1.211 + */
   1.212 +Edge successors;
   1.213 +
   1.214 +/**
   1.215 + * The next basic block in the basic block stack. This stack is used in the
   1.216 + * main loop of the fix point algorithm used in the second step of the
   1.217 + * control flow analysis algorithms.
   1.218 + *
   1.219 + * @see MethodWriter#visitMaxs
   1.220 + */
   1.221 +Label next;
   1.222 +
   1.223 +// ------------------------------------------------------------------------
   1.224 +// Constructor
   1.225 +// ------------------------------------------------------------------------
   1.226 +
   1.227 +/**
   1.228 + * Constructs a new label.
   1.229 + */
   1.230 +public Label(){
   1.231 +}
   1.232 +
   1.233 +/**
   1.234 + * Constructs a new label.
   1.235 + *
   1.236 + * @param debug if this label is only used for debug attributes.
   1.237 + */
   1.238 +Label(final boolean debug){
   1.239 +	this.status = debug ? DEBUG : 0;
   1.240 +}
   1.241 +
   1.242 +// ------------------------------------------------------------------------
   1.243 +// Methods to compute offsets and to manage forward references
   1.244 +// ------------------------------------------------------------------------
   1.245 +
   1.246 +/**
   1.247 + * Returns the offset corresponding to this label. This offset is computed
   1.248 + * from the start of the method's bytecode. <i>This method is intended for
   1.249 + * {@link Attribute} sub classes, and is normally not needed by class
   1.250 + * generators or adapters.</i>
   1.251 + *
   1.252 + * @return the offset corresponding to this label.
   1.253 + * @throws IllegalStateException if this label is not resolved yet.
   1.254 + */
   1.255 +public int getOffset(){
   1.256 +	if((status & RESOLVED) == 0)
   1.257 +		{
   1.258 +		throw new IllegalStateException("Label offset position has not been resolved yet");
   1.259 +		}
   1.260 +	return position;
   1.261 +}
   1.262 +
   1.263 +/**
   1.264 + * Puts a reference to this label in the bytecode of a method. If the
   1.265 + * position of the label is known, the offset is computed and written
   1.266 + * directly. Otherwise, a null offset is written and a new forward reference
   1.267 + * is declared for this label.
   1.268 + *
   1.269 + * @param owner      the code writer that calls this method.
   1.270 + * @param out        the bytecode of the method.
   1.271 + * @param source     the position of first byte of the bytecode instruction that
   1.272 + *                   contains this label.
   1.273 + * @param wideOffset <tt>true</tt> if the reference must be stored in 4
   1.274 + *                   bytes, or <tt>false</tt> if it must be stored with 2 bytes.
   1.275 + * @throws IllegalArgumentException if this label has not been created by
   1.276 + *                                  the given code writer.
   1.277 + */
   1.278 +void put(
   1.279 +		final MethodWriter owner,
   1.280 +		final ByteVector out,
   1.281 +		final int source,
   1.282 +		final boolean wideOffset){
   1.283 +	if((status & RESOLVED) != 0)
   1.284 +		{
   1.285 +		if(wideOffset)
   1.286 +			{
   1.287 +			out.putInt(position - source);
   1.288 +			}
   1.289 +		else
   1.290 +			{
   1.291 +			out.putShort(position - source);
   1.292 +			}
   1.293 +		}
   1.294 +	else
   1.295 +		{
   1.296 +		if(wideOffset)
   1.297 +			{
   1.298 +			addReference(-1 - source, out.length);
   1.299 +			out.putInt(-1);
   1.300 +			}
   1.301 +		else
   1.302 +			{
   1.303 +			addReference(source, out.length);
   1.304 +			out.putShort(-1);
   1.305 +			}
   1.306 +		}
   1.307 +}
   1.308 +
   1.309 +/**
   1.310 + * Adds a forward reference to this label. This method must be called only
   1.311 + * for a true forward reference, i.e. only if this label is not resolved
   1.312 + * yet. For backward references, the offset of the reference can be, and
   1.313 + * must be, computed and stored directly.
   1.314 + *
   1.315 + * @param sourcePosition    the position of the referencing instruction. This
   1.316 + *                          position will be used to compute the offset of this forward
   1.317 + *                          reference.
   1.318 + * @param referencePosition the position where the offset for this forward
   1.319 + *                          reference must be stored.
   1.320 + */
   1.321 +private void addReference(
   1.322 +		final int sourcePosition,
   1.323 +		final int referencePosition){
   1.324 +	if(srcAndRefPositions == null)
   1.325 +		{
   1.326 +		srcAndRefPositions = new int[6];
   1.327 +		}
   1.328 +	if(referenceCount >= srcAndRefPositions.length)
   1.329 +		{
   1.330 +		int[] a = new int[srcAndRefPositions.length + 6];
   1.331 +		System.arraycopy(srcAndRefPositions,
   1.332 +		                 0,
   1.333 +		                 a,
   1.334 +		                 0,
   1.335 +		                 srcAndRefPositions.length);
   1.336 +		srcAndRefPositions = a;
   1.337 +		}
   1.338 +	srcAndRefPositions[referenceCount++] = sourcePosition;
   1.339 +	srcAndRefPositions[referenceCount++] = referencePosition;
   1.340 +}
   1.341 +
   1.342 +/**
   1.343 + * Resolves all forward references to this label. This method must be called
   1.344 + * when this label is added to the bytecode of the method, i.e. when its
   1.345 + * position becomes known. This method fills in the blanks that where left
   1.346 + * in the bytecode by each forward reference previously added to this label.
   1.347 + *
   1.348 + * @param owner    the code writer that calls this method.
   1.349 + * @param position the position of this label in the bytecode.
   1.350 + * @param data     the bytecode of the method.
   1.351 + * @return <tt>true</tt> if a blank that was left for this label was to
   1.352 + *         small to store the offset. In such a case the corresponding jump
   1.353 + *         instruction is replaced with a pseudo instruction (using unused
   1.354 + *         opcodes) using an unsigned two bytes offset. These pseudo
   1.355 + *         instructions will need to be replaced with true instructions with
   1.356 + *         wider offsets (4 bytes instead of 2). This is done in
   1.357 + *         {@link MethodWriter#resizeInstructions}.
   1.358 + * @throws IllegalArgumentException if this label has already been resolved,
   1.359 + *                                  or if it has not been created by the given code writer.
   1.360 + */
   1.361 +boolean resolve(
   1.362 +		final MethodWriter owner,
   1.363 +		final int position,
   1.364 +		final byte[] data){
   1.365 +	boolean needUpdate = false;
   1.366 +	this.status |= RESOLVED;
   1.367 +	this.position = position;
   1.368 +	int i = 0;
   1.369 +	while(i < referenceCount)
   1.370 +		{
   1.371 +		int source = srcAndRefPositions[i++];
   1.372 +		int reference = srcAndRefPositions[i++];
   1.373 +		int offset;
   1.374 +		if(source >= 0)
   1.375 +			{
   1.376 +			offset = position - source;
   1.377 +			if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)
   1.378 +				{
   1.379 +				/*
   1.380 +									 * changes the opcode of the jump instruction, in order to
   1.381 +									 * be able to find it later (see resizeInstructions in
   1.382 +									 * MethodWriter). These temporary opcodes are similar to
   1.383 +									 * jump instruction opcodes, except that the 2 bytes offset
   1.384 +									 * is unsigned (and can therefore represent values from 0 to
   1.385 +									 * 65535, which is sufficient since the size of a method is
   1.386 +									 * limited to 65535 bytes).
   1.387 +									 */
   1.388 +				int opcode = data[reference - 1] & 0xFF;
   1.389 +				if(opcode <= Opcodes.JSR)
   1.390 +					{
   1.391 +					// changes IFEQ ... JSR to opcodes 202 to 217
   1.392 +					data[reference - 1] = (byte) (opcode + 49);
   1.393 +					}
   1.394 +				else
   1.395 +					{
   1.396 +					// changes IFNULL and IFNONNULL to opcodes 218 and 219
   1.397 +					data[reference - 1] = (byte) (opcode + 20);
   1.398 +					}
   1.399 +				needUpdate = true;
   1.400 +				}
   1.401 +			data[reference++] = (byte) (offset >>> 8);
   1.402 +			data[reference] = (byte) offset;
   1.403 +			}
   1.404 +		else
   1.405 +			{
   1.406 +			offset = position + source + 1;
   1.407 +			data[reference++] = (byte) (offset >>> 24);
   1.408 +			data[reference++] = (byte) (offset >>> 16);
   1.409 +			data[reference++] = (byte) (offset >>> 8);
   1.410 +			data[reference] = (byte) offset;
   1.411 +			}
   1.412 +		}
   1.413 +	return needUpdate;
   1.414 +}
   1.415 +
   1.416 +/**
   1.417 + * Returns the first label of the series to which this label belongs. For an
   1.418 + * isolated label or for the first label in a series of successive labels,
   1.419 + * this method returns the label itself. For other labels it returns the
   1.420 + * first label of the series.
   1.421 + *
   1.422 + * @return the first label of the series to which this label belongs.
   1.423 + */
   1.424 +Label getFirst(){
   1.425 +	return frame == null ? this : frame.owner;
   1.426 +}
   1.427 +
   1.428 +// ------------------------------------------------------------------------
   1.429 +// Overriden Object methods
   1.430 +// ------------------------------------------------------------------------
   1.431 +
   1.432 +/**
   1.433 + * Returns a string representation of this label.
   1.434 + *
   1.435 + * @return a string representation of this label.
   1.436 + */
   1.437 +public String toString(){
   1.438 +	return "L" + System.identityHashCode(this);
   1.439 +}
   1.440 +}