diff src/clojure/asm/commons/LocalVariablesSorter.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/commons/LocalVariablesSorter.java	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,330 @@
     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.commons;
    1.34 +
    1.35 +import clojure.asm.Label;
    1.36 +import clojure.asm.MethodAdapter;
    1.37 +import clojure.asm.MethodVisitor;
    1.38 +import clojure.asm.Opcodes;
    1.39 +import clojure.asm.Type;
    1.40 +
    1.41 +/**
    1.42 + * A {@link MethodAdapter} that renumbers local variables in their order of
    1.43 + * appearance. This adapter allows one to easily add new local variables to a
    1.44 + * method. It may be used by inheriting from this class, but the preferred way
    1.45 + * of using it is via delegation: the next visitor in the chain can indeed add
    1.46 + * new locals when needed by calling {@link #newLocal} on this adapter (this
    1.47 + * requires a reference back to this {@link LocalVariablesSorter}).
    1.48 + *
    1.49 + * @author Chris Nokleberg
    1.50 + * @author Eugene Kuleshov
    1.51 + * @author Eric Bruneton
    1.52 + */
    1.53 +public class LocalVariablesSorter extends MethodAdapter{
    1.54 +
    1.55 +private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
    1.56 +
    1.57 +/**
    1.58 + * Mapping from old to new local variable indexes. A local variable at index
    1.59 + * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
    1.60 + * index i of size 2 is remapped to 'mapping[2*i+1]'.
    1.61 + */
    1.62 +private int[] mapping = new int[40];
    1.63 +
    1.64 +/**
    1.65 + * Array used to store stack map local variable types after remapping.
    1.66 + */
    1.67 +private Object[] newLocals = new Object[20];
    1.68 +
    1.69 +/**
    1.70 + * Index of the first local variable, after formal parameters.
    1.71 + */
    1.72 +protected final int firstLocal;
    1.73 +
    1.74 +/**
    1.75 + * Index of the next local variable to be created by {@link #newLocal}.
    1.76 + */
    1.77 +protected int nextLocal;
    1.78 +
    1.79 +/**
    1.80 + * Indicates if at least one local variable has moved due to remapping.
    1.81 + */
    1.82 +private boolean changed;
    1.83 +
    1.84 +/**
    1.85 + * Creates a new {@link LocalVariablesSorter}.
    1.86 + *
    1.87 + * @param access access flags of the adapted method.
    1.88 + * @param desc   the method's descriptor (see {@link Type Type}).
    1.89 + * @param mv     the method visitor to which this adapter delegates calls.
    1.90 + */
    1.91 +public LocalVariablesSorter(
    1.92 +		final int access,
    1.93 +		final String desc,
    1.94 +		final MethodVisitor mv){
    1.95 +	super(mv);
    1.96 +	Type[] args = Type.getArgumentTypes(desc);
    1.97 +	nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
    1.98 +	for(int i = 0; i < args.length; i++)
    1.99 +		{
   1.100 +		nextLocal += args[i].getSize();
   1.101 +		}
   1.102 +	firstLocal = nextLocal;
   1.103 +}
   1.104 +
   1.105 +public void visitVarInsn(final int opcode, final int var){
   1.106 +	Type type;
   1.107 +	switch(opcode)
   1.108 +		{
   1.109 +		case Opcodes.LLOAD:
   1.110 +		case Opcodes.LSTORE:
   1.111 +			type = Type.LONG_TYPE;
   1.112 +			break;
   1.113 +
   1.114 +		case Opcodes.DLOAD:
   1.115 +		case Opcodes.DSTORE:
   1.116 +			type = Type.DOUBLE_TYPE;
   1.117 +			break;
   1.118 +
   1.119 +		case Opcodes.FLOAD:
   1.120 +		case Opcodes.FSTORE:
   1.121 +			type = Type.FLOAT_TYPE;
   1.122 +			break;
   1.123 +
   1.124 +		case Opcodes.ILOAD:
   1.125 +		case Opcodes.ISTORE:
   1.126 +			type = Type.INT_TYPE;
   1.127 +			break;
   1.128 +
   1.129 +		case Opcodes.ALOAD:
   1.130 +		case Opcodes.ASTORE:
   1.131 +			type = OBJECT_TYPE;
   1.132 +			break;
   1.133 +
   1.134 +			// case RET:
   1.135 +		default:
   1.136 +			type = Type.VOID_TYPE;
   1.137 +		}
   1.138 +	mv.visitVarInsn(opcode, remap(var, type));
   1.139 +}
   1.140 +
   1.141 +public void visitIincInsn(final int var, final int increment){
   1.142 +	mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
   1.143 +}
   1.144 +
   1.145 +public void visitMaxs(final int maxStack, final int maxLocals){
   1.146 +	mv.visitMaxs(maxStack, nextLocal);
   1.147 +}
   1.148 +
   1.149 +public void visitLocalVariable(
   1.150 +		final String name,
   1.151 +		final String desc,
   1.152 +		final String signature,
   1.153 +		final Label start,
   1.154 +		final Label end,
   1.155 +		final int index){
   1.156 +	int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
   1.157 +	int newIndex = remap(index, size);
   1.158 +	mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
   1.159 +}
   1.160 +
   1.161 +public void visitFrame(
   1.162 +		final int type,
   1.163 +		final int nLocal,
   1.164 +		final Object[] local,
   1.165 +		final int nStack,
   1.166 +		final Object[] stack){
   1.167 +	if(type != Opcodes.F_NEW)
   1.168 +		{ // uncompressed frame
   1.169 +		throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
   1.170 +		}
   1.171 +
   1.172 +	if(!changed)
   1.173 +		{ // optimization for the case where mapping = identity
   1.174 +		mv.visitFrame(type, nLocal, local, nStack, stack);
   1.175 +		return;
   1.176 +		}
   1.177 +
   1.178 +	// creates a copy of newLocals
   1.179 +	Object[] oldLocals = new Object[newLocals.length];
   1.180 +	System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
   1.181 +
   1.182 +	// copies types from 'local' to 'newLocals'
   1.183 +	// 'newLocals' already contains the variables added with 'newLocal'
   1.184 +
   1.185 +	int index = 0; // old local variable index
   1.186 +	int number = 0; // old local variable number
   1.187 +	for(; number < nLocal; ++number)
   1.188 +		{
   1.189 +		Object t = local[number];
   1.190 +		int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
   1.191 +		if(t != Opcodes.TOP)
   1.192 +			{
   1.193 +			setFrameLocal(remap(index, size), t);
   1.194 +			}
   1.195 +		index += size;
   1.196 +		}
   1.197 +
   1.198 +	// removes TOP after long and double types as well as trailing TOPs
   1.199 +
   1.200 +	index = 0;
   1.201 +	number = 0;
   1.202 +	for(int i = 0; index < newLocals.length; ++i)
   1.203 +		{
   1.204 +		Object t = newLocals[index++];
   1.205 +		if(t != null && t != Opcodes.TOP)
   1.206 +			{
   1.207 +			newLocals[i] = t;
   1.208 +			number = i + 1;
   1.209 +			if(t == Opcodes.LONG || t == Opcodes.DOUBLE)
   1.210 +				{
   1.211 +				index += 1;
   1.212 +				}
   1.213 +			}
   1.214 +		else
   1.215 +			{
   1.216 +			newLocals[i] = Opcodes.TOP;
   1.217 +			}
   1.218 +		}
   1.219 +
   1.220 +	// visits remapped frame
   1.221 +	mv.visitFrame(type, number, newLocals, nStack, stack);
   1.222 +
   1.223 +	// restores original value of 'newLocals'
   1.224 +	newLocals = oldLocals;
   1.225 +}
   1.226 +
   1.227 +// -------------
   1.228 +
   1.229 +/**
   1.230 + * Creates a new local variable of the given type.
   1.231 + *
   1.232 + * @param type the type of the local variable to be created.
   1.233 + * @return the identifier of the newly created local variable.
   1.234 + */
   1.235 +public int newLocal(final Type type){
   1.236 +	Object t;
   1.237 +	switch(type.getSort())
   1.238 +		{
   1.239 +		case Type.BOOLEAN:
   1.240 +		case Type.CHAR:
   1.241 +		case Type.BYTE:
   1.242 +		case Type.SHORT:
   1.243 +		case Type.INT:
   1.244 +			t = Opcodes.INTEGER;
   1.245 +			break;
   1.246 +		case Type.FLOAT:
   1.247 +			t = Opcodes.FLOAT;
   1.248 +			break;
   1.249 +		case Type.LONG:
   1.250 +			t = Opcodes.LONG;
   1.251 +			break;
   1.252 +		case Type.DOUBLE:
   1.253 +			t = Opcodes.DOUBLE;
   1.254 +			break;
   1.255 +		case Type.ARRAY:
   1.256 +			t = type.getDescriptor();
   1.257 +			break;
   1.258 +			// case Type.OBJECT:
   1.259 +		default:
   1.260 +			t = type.getInternalName();
   1.261 +			break;
   1.262 +		}
   1.263 +	int local = nextLocal;
   1.264 +	setLocalType(local, type);
   1.265 +	setFrameLocal(local, t);
   1.266 +	nextLocal += type.getSize();
   1.267 +	return local;
   1.268 +}
   1.269 +
   1.270 +/**
   1.271 + * Sets the current type of the given local variable. The default
   1.272 + * implementation of this method does nothing.
   1.273 + *
   1.274 + * @param local a local variable identifier, as returned by {@link #newLocal
   1.275 + *              newLocal()}.
   1.276 + * @param type  the type of the value being stored in the local variable
   1.277 + */
   1.278 +protected void setLocalType(final int local, final Type type){
   1.279 +}
   1.280 +
   1.281 +private void setFrameLocal(final int local, final Object type){
   1.282 +	int l = newLocals.length;
   1.283 +	if(local >= l)
   1.284 +		{
   1.285 +		Object[] a = new Object[Math.max(2 * l, local + 1)];
   1.286 +		System.arraycopy(newLocals, 0, a, 0, l);
   1.287 +		newLocals = a;
   1.288 +		}
   1.289 +	newLocals[local] = type;
   1.290 +}
   1.291 +
   1.292 +private int remap(final int var, final Type type){
   1.293 +	if(var < firstLocal)
   1.294 +		{
   1.295 +		return var;
   1.296 +		}
   1.297 +	int key = 2 * var + type.getSize() - 1;
   1.298 +	int size = mapping.length;
   1.299 +	if(key >= size)
   1.300 +		{
   1.301 +		int[] newMapping = new int[Math.max(2 * size, key + 1)];
   1.302 +		System.arraycopy(mapping, 0, newMapping, 0, size);
   1.303 +		mapping = newMapping;
   1.304 +		}
   1.305 +	int value = mapping[key];
   1.306 +	if(value == 0)
   1.307 +		{
   1.308 +		value = nextLocal + 1;
   1.309 +		mapping[key] = value;
   1.310 +		setLocalType(nextLocal, type);
   1.311 +		nextLocal += type.getSize();
   1.312 +		}
   1.313 +	if(value - 1 != var)
   1.314 +		{
   1.315 +		changed = true;
   1.316 +		}
   1.317 +	return value - 1;
   1.318 +}
   1.319 +
   1.320 +private int remap(final int var, final int size){
   1.321 +	if(var < firstLocal || !changed)
   1.322 +		{
   1.323 +		return var;
   1.324 +		}
   1.325 +	int key = 2 * var + size - 1;
   1.326 +	int value = key < mapping.length ? mapping[key] : 0;
   1.327 +	if(value == 0)
   1.328 +		{
   1.329 +		throw new IllegalStateException("Unknown local variable " + var);
   1.330 +		}
   1.331 +	return value - 1;
   1.332 +}
   1.333 +}