/* | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code 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 General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
/* | 
|
 * This file is available under and governed by the GNU General Public | 
|
 * License version 2 only, as published by the Free Software Foundation. | 
|
 * However, the following notice accompanied the original version of this | 
|
 * file: | 
|
 * | 
|
 * ASM: a very small and fast Java bytecode manipulation framework | 
|
 * Copyright (c) 2000-2011 INRIA, France Telecom | 
|
 * All rights reserved. | 
|
 * | 
|
 * Redistribution and use in source and binary forms, with or without | 
|
 * modification, are permitted provided that the following conditions | 
|
 * are met: | 
|
 * 1. Redistributions of source code must retain the above copyright | 
|
 *    notice, this list of conditions and the following disclaimer. | 
|
 * 2. Redistributions in binary form must reproduce the above copyright | 
|
 *    notice, this list of conditions and the following disclaimer in the | 
|
 *    documentation and/or other materials provided with the distribution. | 
|
 * 3. Neither the name of the copyright holders nor the names of its | 
|
 *    contributors may be used to endorse or promote products derived from | 
|
 *    this software without specific prior written permission. | 
|
 * | 
|
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
|
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
|
 * THE POSSIBILITY OF SUCH DAMAGE. | 
|
*/  | 
|
package jdk.internal.org.objectweb.asm.commons;  | 
|
import jdk.internal.org.objectweb.asm.AnnotationVisitor;  | 
|
import jdk.internal.org.objectweb.asm.Label;  | 
|
import jdk.internal.org.objectweb.asm.MethodVisitor;  | 
|
import jdk.internal.org.objectweb.asm.Opcodes;  | 
|
import jdk.internal.org.objectweb.asm.Type;  | 
|
import jdk.internal.org.objectweb.asm.TypePath;  | 
|
/** | 
|
 * A {@link MethodVisitor} that renumbers local variables in their order of | 
|
 * appearance. This adapter allows one to easily add new local variables to a | 
|
 * method. It may be used by inheriting from this class, but the preferred way | 
|
 * of using it is via delegation: the next visitor in the chain can indeed add | 
|
 * new locals when needed by calling {@link #newLocal} on this adapter (this | 
|
 * requires a reference back to this {@link LocalVariablesSorter}). | 
|
 * | 
|
 * @author Chris Nokleberg | 
|
 * @author Eugene Kuleshov | 
|
 * @author Eric Bruneton | 
|
*/  | 
|
public class LocalVariablesSorter extends MethodVisitor {  | 
|
private static final Type OBJECT_TYPE = Type  | 
|
.getObjectType("java/lang/Object");  | 
|
    /** | 
|
     * Mapping from old to new local variable indexes. A local variable at index | 
|
     * i of size 1 is remapped to 'mapping[2*i]', while a local variable at | 
|
     * index i of size 2 is remapped to 'mapping[2*i+1]'. | 
|
*/  | 
|
private int[] mapping = new int[40];  | 
|
    /** | 
|
     * Array used to store stack map local variable types after remapping. | 
|
*/  | 
|
private Object[] newLocals = new Object[20];  | 
|
    /** | 
|
     * Index of the first local variable, after formal parameters. | 
|
*/  | 
|
protected final int firstLocal;  | 
|
    /** | 
|
     * Index of the next local variable to be created by {@link #newLocal}. | 
|
*/  | 
|
protected int nextLocal;  | 
|
    /** | 
|
     * Indicates if at least one local variable has moved due to remapping. | 
|
*/  | 
|
private boolean changed;  | 
|
    /** | 
|
     * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use | 
|
     * this constructor</i>. Instead, they must use the | 
|
     * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. | 
|
     * | 
|
     * @param access | 
|
     *            access flags of the adapted method. | 
|
     * @param desc | 
|
     *            the method's descriptor (see {@link Type Type}). | 
|
     * @param mv | 
|
     *            the method visitor to which this adapter delegates calls. | 
|
     * @throws IllegalStateException | 
|
     *             If a subclass calls this constructor. | 
|
*/  | 
|
public LocalVariablesSorter(final int access, final String desc,  | 
|
final MethodVisitor mv) {  | 
|
this(Opcodes.ASM5, access, desc, mv);  | 
|
if (getClass() != LocalVariablesSorter.class) {  | 
|
throw new IllegalStateException();  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Creates a new {@link LocalVariablesSorter}. | 
|
     * | 
|
     * @param api | 
|
     *            the ASM API version implemented by this visitor. Must be one | 
|
     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. | 
|
     * @param access | 
|
     *            access flags of the adapted method. | 
|
     * @param desc | 
|
     *            the method's descriptor (see {@link Type Type}). | 
|
     * @param mv | 
|
     *            the method visitor to which this adapter delegates calls. | 
|
*/  | 
|
protected LocalVariablesSorter(final int api, final int access,  | 
|
final String desc, final MethodVisitor mv) {  | 
|
super(api, mv);  | 
|
Type[] args = Type.getArgumentTypes(desc);  | 
|
nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;  | 
|
for (int i = 0; i < args.length; i++) {  | 
|
nextLocal += args[i].getSize();  | 
|
}  | 
|
firstLocal = nextLocal;  | 
|
}  | 
|
@Override  | 
|
    public void visitVarInsn(final int opcode, final int var) { | 
|
Type type;  | 
|
switch (opcode) {  | 
|
case Opcodes.LLOAD:  | 
|
case Opcodes.LSTORE:  | 
|
type = Type.LONG_TYPE;  | 
|
break;  | 
|
case Opcodes.DLOAD:  | 
|
case Opcodes.DSTORE:  | 
|
type = Type.DOUBLE_TYPE;  | 
|
break;  | 
|
case Opcodes.FLOAD:  | 
|
case Opcodes.FSTORE:  | 
|
type = Type.FLOAT_TYPE;  | 
|
break;  | 
|
case Opcodes.ILOAD:  | 
|
case Opcodes.ISTORE:  | 
|
type = Type.INT_TYPE;  | 
|
break;  | 
|
default:  | 
|
// case Opcodes.ALOAD:  | 
|
// case Opcodes.ASTORE:  | 
|
            // case RET: | 
|
type = OBJECT_TYPE;  | 
|
break;  | 
|
}  | 
|
mv.visitVarInsn(opcode, remap(var, type));  | 
|
}  | 
|
@Override  | 
|
    public void visitIincInsn(final int var, final int increment) { | 
|
mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);  | 
|
}  | 
|
@Override  | 
|
    public void visitMaxs(final int maxStack, final int maxLocals) { | 
|
mv.visitMaxs(maxStack, nextLocal);  | 
|
}  | 
|
@Override  | 
|
public void visitLocalVariable(final String name, final String desc,  | 
|
final String signature, final Label start, final Label end,  | 
|
            final int index) { | 
|
int newIndex = remap(index, Type.getType(desc));  | 
|
mv.visitLocalVariable(name, desc, signature, start, end, newIndex);  | 
|
}  | 
|
@Override  | 
|
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,  | 
|
TypePath typePath, Label[] start, Label[] end, int[] index,  | 
|
String desc, boolean visible) {  | 
|
Type t = Type.getType(desc);  | 
|
int[] newIndex = new int[index.length];  | 
|
for (int i = 0; i < newIndex.length; ++i) {  | 
|
newIndex[i] = remap(index[i], t);  | 
|
}  | 
|
return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end,  | 
|
newIndex, desc, visible);  | 
|
}  | 
|
@Override  | 
|
public void visitFrame(final int type, final int nLocal,  | 
|
final Object[] local, final int nStack, final Object[] stack) {  | 
|
if (type != Opcodes.F_NEW) { // uncompressed frame  | 
|
throw new IllegalStateException(  | 
|
                    "ClassReader.accept() should be called with EXPAND_FRAMES flag"); | 
|
}  | 
|
if (!changed) { // optimization for the case where mapping = identity  | 
|
mv.visitFrame(type, nLocal, local, nStack, stack);  | 
|
return;  | 
|
}  | 
|
        // creates a copy of newLocals | 
|
Object[] oldLocals = new Object[newLocals.length];  | 
|
System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);  | 
|
updateNewLocals(newLocals);  | 
|
// copies types from 'local' to 'newLocals'  | 
|
// 'newLocals' already contains the variables added with 'newLocal'  | 
|
        int index = 0; // old local variable index | 
|
        int number = 0; // old local variable number | 
|
for (; number < nLocal; ++number) {  | 
|
Object t = local[number];  | 
|
int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;  | 
|
if (t != Opcodes.TOP) {  | 
|
Type typ = OBJECT_TYPE;  | 
|
if (t == Opcodes.INTEGER) {  | 
|
typ = Type.INT_TYPE;  | 
|
} else if (t == Opcodes.FLOAT) {  | 
|
typ = Type.FLOAT_TYPE;  | 
|
} else if (t == Opcodes.LONG) {  | 
|
typ = Type.LONG_TYPE;  | 
|
} else if (t == Opcodes.DOUBLE) {  | 
|
typ = Type.DOUBLE_TYPE;  | 
|
} else if (t instanceof String) {  | 
|
typ = Type.getObjectType((String) t);  | 
|
}  | 
|
setFrameLocal(remap(index, typ), t);  | 
|
}  | 
|
index += size;  | 
|
}  | 
|
// removes TOP after long and double types as well as trailing TOPs  | 
|
index = 0;  | 
|
number = 0;  | 
|
for (int i = 0; index < newLocals.length; ++i) {  | 
|
Object t = newLocals[index++];  | 
|
if (t != null && t != Opcodes.TOP) {  | 
|
newLocals[i] = t;  | 
|
number = i + 1;  | 
|
if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {  | 
|
index += 1;  | 
|
}  | 
|
            } else { | 
|
newLocals[i] = Opcodes.TOP;  | 
|
}  | 
|
}  | 
|
        // visits remapped frame | 
|
mv.visitFrame(type, number, newLocals, nStack, stack);  | 
|
        // restores original value of 'newLocals' | 
|
newLocals = oldLocals;  | 
|
}  | 
|
// -------------  | 
|
    /** | 
|
     * Creates a new local variable of the given type. | 
|
     * | 
|
     * @param type | 
|
     *            the type of the local variable to be created. | 
|
     * @return the identifier of the newly created local variable. | 
|
*/  | 
|
public int newLocal(final Type type) {  | 
|
Object t;  | 
|
switch (type.getSort()) {  | 
|
case Type.BOOLEAN:  | 
|
case Type.CHAR:  | 
|
case Type.BYTE:  | 
|
case Type.SHORT:  | 
|
case Type.INT:  | 
|
t = Opcodes.INTEGER;  | 
|
break;  | 
|
case Type.FLOAT:  | 
|
t = Opcodes.FLOAT;  | 
|
break;  | 
|
case Type.LONG:  | 
|
t = Opcodes.LONG;  | 
|
break;  | 
|
case Type.DOUBLE:  | 
|
t = Opcodes.DOUBLE;  | 
|
break;  | 
|
case Type.ARRAY:  | 
|
t = type.getDescriptor();  | 
|
break;  | 
|
        // case Type.OBJECT: | 
|
default:  | 
|
t = type.getInternalName();  | 
|
break;  | 
|
}  | 
|
int local = newLocalMapping(type);  | 
|
setLocalType(local, type);  | 
|
setFrameLocal(local, t);  | 
|
changed = true;  | 
|
return local;  | 
|
}  | 
|
    /** | 
|
     * Notifies subclasses that a new stack map frame is being visited. The | 
|
     * array argument contains the stack map frame types corresponding to the | 
|
     * local variables added with {@link #newLocal}. This method can update | 
|
     * these types in place for the stack map frame being visited. The default | 
|
     * implementation of this method does nothing, i.e. a local variable added | 
|
     * with {@link #newLocal} will have the same type in all stack map frames. | 
|
     * But this behavior is not always the desired one, for instance if a local | 
|
     * variable is added in the middle of a try/catch block: the frame for the | 
|
     * exception handler should have a TOP type for this new local. | 
|
     * | 
|
     * @param newLocals | 
|
     *            the stack map frame types corresponding to the local variables | 
|
     *            added with {@link #newLocal} (and null for the others). The | 
|
     *            format of this array is the same as in | 
|
     *            {@link MethodVisitor#visitFrame}, except that long and double | 
|
     *            types use two slots. The types for the current stack map frame | 
|
     *            must be updated in place in this array. | 
|
*/  | 
|
protected void updateNewLocals(Object[] newLocals) {  | 
|
}  | 
|
    /** | 
|
     * Notifies subclasses that a local variable has been added or remapped. The | 
|
     * default implementation of this method does nothing. | 
|
     * | 
|
     * @param local | 
|
     *            a local variable identifier, as returned by {@link #newLocal | 
|
     *            newLocal()}. | 
|
     * @param type | 
|
     *            the type of the value being stored in the local variable. | 
|
*/  | 
|
protected void setLocalType(final int local, final Type type) {  | 
|
}  | 
|
private void setFrameLocal(final int local, final Object type) {  | 
|
int l = newLocals.length;  | 
|
if (local >= l) {  | 
|
Object[] a = new Object[Math.max(2 * l, local + 1)];  | 
|
System.arraycopy(newLocals, 0, a, 0, l);  | 
|
newLocals = a;  | 
|
}  | 
|
newLocals[local] = type;  | 
|
}  | 
|
private int remap(final int var, final Type type) {  | 
|
if (var + type.getSize() <= firstLocal) {  | 
|
return var;  | 
|
}  | 
|
int key = 2 * var + type.getSize() - 1;  | 
|
int size = mapping.length;  | 
|
if (key >= size) {  | 
|
int[] newMapping = new int[Math.max(2 * size, key + 1)];  | 
|
System.arraycopy(mapping, 0, newMapping, 0, size);  | 
|
mapping = newMapping;  | 
|
}  | 
|
int value = mapping[key];  | 
|
if (value == 0) {  | 
|
value = newLocalMapping(type);  | 
|
setLocalType(value, type);  | 
|
mapping[key] = value + 1;  | 
|
        } else { | 
|
value--;  | 
|
}  | 
|
if (value != var) {  | 
|
changed = true;  | 
|
}  | 
|
return value;  | 
|
}  | 
|
protected int newLocalMapping(final Type type) {  | 
|
int local = nextLocal;  | 
|
nextLocal += type.getSize();  | 
|
return local;  | 
|
}  | 
|
}  |