WALA/com.ibm.wala.shrike/src/com/ibm/wala/shrikeBT/ConstantInstruction.java

886 lines
20 KiB
Java

/*******************************************************************************
* Copyright (c) 2002,2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.shrikeBT;
import com.ibm.wala.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrikeCT.ConstantPoolParser;
/**
* A ConstantInstruction pushes some constant value onto the stack.
*/
public abstract class ConstantInstruction extends Instruction {
public static class InvokeDynamicToken {
private final BootstrapMethod bootstrapMethod;
private final String name;
private final String type;
public InvokeDynamicToken(BootstrapMethod bootstrapMethod, String name, String type) {
this.bootstrapMethod = bootstrapMethod;
this.name = name;
this.type = type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bootstrapMethod == null) ? 0 : bootstrapMethod.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InvokeDynamicToken other = (InvokeDynamicToken) obj;
if (bootstrapMethod == null) {
if (other.bootstrapMethod != null)
return false;
} else if (!bootstrapMethod.equals(other.bootstrapMethod))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}
public static class ClassToken {
private final String typeName;
ClassToken(String typeName) {
this.typeName = typeName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ClassToken other = (ClassToken) obj;
if (typeName == null) {
if (other.typeName != null)
return false;
} else if (!typeName.equals(other.typeName))
return false;
return true;
}
public String getTypeName() {
return typeName;
}
}
public ConstantInstruction(short opcode) {
super(opcode);
}
ConstantPoolReader getLazyConstantPool() {
return null;
}
int getCPIndex() {
return 0;
}
final static class ConstNull extends ConstantInstruction {
protected ConstNull() {
super(OP_aconst_null);
}
private final static ConstNull preallocated = new ConstNull();
static ConstNull makeInternal() {
return preallocated;
}
@Override
public Object getValue() {
return null;
}
@Override
public String getType() {
return TYPE_null;
}
}
static class ConstInt extends ConstantInstruction {
protected int value;
private final static ConstInt[] preallocated = preallocate();
protected ConstInt(short opcode, int value) {
super(opcode);
this.value = value;
}
private static ConstInt[] preallocate() {
ConstInt[] r = new ConstInt[256];
for (int i = 0; i < r.length; i++) {
r[i] = new ConstInt(OP_bipush, i - 128);
}
for (int i = -1; i <= 5; i++) {
r[i + 128] = new ConstInt((short) (i - (-1) + OP_iconst_m1), i);
}
return r;
}
static ConstInt makeInternal(int i) {
if (((byte) i) == i) {
return preallocated[i + 128];
} else if (((short) i) == i) {
return new ConstInt(OP_sipush, i);
} else {
return new ConstInt(OP_ldc_w, i);
}
}
@Override
final public Object getValue() {
return Integer.valueOf(getIntValue());
}
@Override
final public String getType() {
return TYPE_int;
}
public int getIntValue() {
return value;
}
}
final static class LazyInt extends ConstInt {
final private ConstantPoolReader cp;
final private int index;
private boolean isSet;
protected LazyInt(short opcode, ConstantPoolReader cp, int index) {
super(opcode, 0);
this.cp = cp;
this.index = index;
this.isSet = false;
}
@Override
public int getIntValue() {
if (!isSet) {
value = cp.getConstantPoolInteger(index);
isSet = true;
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstLong extends ConstantInstruction {
protected long value;
private final static ConstLong[] preallocated = preallocate();
protected ConstLong(short opcode, long value) {
super(opcode);
this.value = value;
}
private static ConstLong[] preallocate() {
ConstLong[] r = { new ConstLong(OP_lconst_0, 0), new ConstLong(OP_lconst_1, 1) };
return r;
}
static ConstLong makeInternal(long v) {
if (v == 0 || v == 1) {
return preallocated[(int) v];
} else {
return new ConstLong(OP_ldc2_w, v);
}
}
@Override
final public Object getValue() {
return Long.valueOf(getLongValue());
}
@Override
final public String getType() {
return TYPE_long;
}
public long getLongValue() {
return value;
}
}
final static class LazyLong extends ConstLong {
final private ConstantPoolReader cp;
final private int index;
private boolean isSet;
protected LazyLong(short opcode, ConstantPoolReader cp, int index) {
super(opcode, 0);
this.cp = cp;
this.index = index;
this.isSet = false;
}
@Override
public long getLongValue() {
if (!isSet) {
value = cp.getConstantPoolLong(index);
isSet = true;
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstFloat extends ConstantInstruction {
protected float value;
private final static ConstFloat[] preallocated = preallocate();
protected ConstFloat(short opcode, float value) {
super(opcode);
this.value = value;
}
private static ConstFloat[] preallocate() {
ConstFloat[] r = { new ConstFloat(OP_fconst_0, 0), new ConstFloat(OP_fconst_1, 1), new ConstFloat(OP_fconst_2, 2) };
return r;
}
static ConstFloat makeInternal(float v) {
if (v == 0.0 || v == 1.0 || v == 2.0) {
return preallocated[(int) v];
} else {
return new ConstFloat(OP_ldc_w, v);
}
}
@Override
final public Object getValue() {
return Float.valueOf(getFloatValue());
}
@Override
final public String getType() {
return TYPE_float;
}
public float getFloatValue() {
return value;
}
}
final static class LazyFloat extends ConstFloat {
final private ConstantPoolReader cp;
final private int index;
private boolean isSet;
protected LazyFloat(short opcode, ConstantPoolReader cp, int index) {
super(opcode, 0.0f);
this.cp = cp;
this.index = index;
this.isSet = false;
}
@Override
public float getFloatValue() {
if (!isSet) {
value = cp.getConstantPoolFloat(index);
isSet = true;
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstDouble extends ConstantInstruction {
protected double value;
private final static ConstDouble[] preallocated = preallocate();
protected ConstDouble(short opcode, double value) {
super(opcode);
this.value = value;
}
private static ConstDouble[] preallocate() {
ConstDouble[] r = { new ConstDouble(OP_dconst_0, 0), new ConstDouble(OP_dconst_1, 1) };
return r;
}
static ConstDouble makeInternal(double v) {
if (v == 0.0 || v == 1.0) {
return preallocated[(int) v];
} else {
return new ConstDouble(OP_ldc2_w, v);
}
}
@Override
final public Object getValue() {
return Double.valueOf(getDoubleValue());
}
@Override
final public String getType() {
return TYPE_double;
}
public double getDoubleValue() {
return value;
}
}
final static class LazyDouble extends ConstDouble {
final private ConstantPoolReader cp;
final private int index;
private boolean isSet;
protected LazyDouble(short opcode, ConstantPoolReader cp, int index) {
super(opcode, 0.0);
this.cp = cp;
this.index = index;
this.isSet = false;
}
@Override
public double getDoubleValue() {
if (!isSet) {
value = cp.getConstantPoolDouble(index);
isSet = true;
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstString extends ConstantInstruction {
protected String value;
protected ConstString(short opcode, String value) {
super(opcode);
this.value = value;
}
static ConstString makeInternal(String v) {
return new ConstString(OP_ldc_w, v);
}
@Override
public Object getValue() {
return value;
}
@Override
final public String getType() {
return TYPE_String;
}
}
final static class LazyString extends ConstString {
final private ConstantPoolReader cp;
final private int index;
protected LazyString(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (value == null) {
value = cp.getConstantPoolString(index);
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstClass extends ConstantInstruction {
protected String typeName;
protected ConstClass(short opcode, String typeName) {
super(opcode);
this.typeName = typeName;
}
static ConstClass makeInternal(String v) {
return new ConstClass(OP_ldc_w, v);
}
@Override
public Object getValue() {
return new ClassToken(typeName);
}
@Override
final public String getType() {
return TYPE_Class;
}
@Override
public boolean isPEI() {
// load of a class constant may trigger a ClassNotFoundException
return true;
}
}
final static class LazyClass extends ConstClass {
final private ConstantPoolReader cp;
final private int index;
protected LazyClass(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (typeName == null) {
typeName = cp.getConstantPoolClassType(index);
}
return new ClassToken(typeName);
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstMethodType extends ConstantInstruction {
protected String descriptor;
ConstMethodType(short opcode, String descriptor) {
super(opcode);
this.descriptor = descriptor;
}
@Override
public Object getValue() {
return descriptor;
}
@Override
public String getType() {
return TYPE_MethodType;
}
}
static class LazyMethodType extends ConstMethodType {
final private ConstantPoolReader cp;
final private int index;
LazyMethodType(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (descriptor == null) {
descriptor = cp.getConstantPoolMethodType(index);
}
return descriptor;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstMethodHandle extends ConstantInstruction {
protected Object value;
public ConstMethodHandle(short opcode, Object value) {
super(opcode);
this.value = value;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getType() {
return TYPE_MethodHandle;
}
}
static class LazyMethodHandle extends ConstMethodHandle {
final private ConstantPoolReader cp;
final private int index;
LazyMethodHandle(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (value == null) {
String className = cp.getConstantPoolHandleClassType(getCPIndex());
String eltName = cp.getConstantPoolHandleName(getCPIndex());
String eltDesc = cp.getConstantPoolHandleType(getCPIndex());
byte kind = cp.getConstantPoolHandleKind(getCPIndex());
value = new ConstantPoolParser.ReferenceToken(kind, className, eltName, eltDesc);
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
static class ConstInvokeDynamic extends ConstantInstruction {
protected Object value;
public ConstInvokeDynamic(short opcode, Object value) {
super(opcode);
this.value = value;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getType() {
return null;
}
}
static class LazyInvokeDynamic extends ConstMethodHandle {
final private ConstantPoolReader cp;
final private int index;
LazyInvokeDynamic(short opcode, ConstantPoolReader cp, int index) {
super(opcode, null);
this.cp = cp;
this.index = index;
}
@Override
public Object getValue() {
if (value == null) {
BootstrapMethod bootstrap = cp.getConstantPoolDynamicBootstrap(index);
String name = cp.getConstantPoolDynamicName(index);
String type = cp.getConstantPoolDynamicType(index);
value = new InvokeDynamicToken(bootstrap, name, type);
}
return value;
}
@Override
public ConstantPoolReader getLazyConstantPool() {
return cp;
}
@Override
public int getCPIndex() {
return index;
}
}
/**
* @return the constant value pushed: an Integer, a Long, a Float, a Double, a String, or null
*/
public abstract Object getValue();
/**
* @return the type of the value pushed
*/
public abstract String getType();
public static ConstantInstruction make(String type, Object constant) throws IllegalArgumentException {
if (type == null && constant != null) {
throw new IllegalArgumentException("(type == null) and (constant != null)");
}
if (constant == null) {
return ConstNull.makeInternal();
} else if (type.equals(TYPE_String)) {
return makeString((String) constant);
} else if (type.equals(TYPE_Class)) {
return makeClass((String) constant);
} else {
try {
switch (Util.getTypeIndex(type)) {
case TYPE_int_index:
return make(((Number) constant).intValue());
case TYPE_long_index:
return make(((Number) constant).longValue());
case TYPE_float_index:
return make(((Number) constant).floatValue());
case TYPE_double_index:
return make(((Number) constant).doubleValue());
default:
throw new IllegalArgumentException("Invalid type for constant: " + type);
}
} catch (ClassCastException e) {
throw new IllegalArgumentException(e);
}
}
}
public static ConstantInstruction make(int i) {
return ConstInt.makeInternal(i);
}
public static ConstantInstruction make(long l) {
return ConstLong.makeInternal(l);
}
public static ConstantInstruction make(float f) {
return ConstFloat.makeInternal(f);
}
public static ConstantInstruction make(double d) {
return ConstDouble.makeInternal(d);
}
public static ConstantInstruction makeString(String s) {
return s == null ? (ConstantInstruction) ConstNull.makeInternal() : (ConstantInstruction) ConstString.makeInternal(s);
}
public static ConstantInstruction makeClass(String s) {
return ConstClass.makeInternal(s);
}
public static ConstantInstruction make(ConstantPoolReader cp, int index) {
switch (cp.getConstantPoolItemType(index)) {
case CONSTANT_Integer:
return new LazyInt(OP_ldc_w, cp, index);
case CONSTANT_Long:
return new LazyLong(OP_ldc2_w, cp, index);
case CONSTANT_Float:
return new LazyFloat(OP_ldc_w, cp, index);
case CONSTANT_Double:
return new LazyDouble(OP_ldc2_w, cp, index);
case CONSTANT_String:
return new LazyString(OP_ldc_w, cp, index);
case CONSTANT_Class:
return new LazyClass(OP_ldc_w, cp, index);
case CONSTANT_MethodHandle:
return new LazyMethodHandle(OP_ldc_w, cp, index);
case CONSTANT_MethodType:
return new LazyMethodType(OP_ldc_w, cp, index);
case CONSTANT_InvokeDynamic:
return new LazyInvokeDynamic(OP_ldc_w, cp, index);
default:
return null;
}
}
@Override
final public boolean equals(Object o) {
if (o instanceof ConstantInstruction) {
ConstantInstruction i = (ConstantInstruction) o;
if (!i.getType().equals(getType())) {
return false;
}
if (i.getValue() == null) {
if (getValue() == null) {
return true;
} else {
return false;
}
} else {
if (getValue() == null) {
return false;
} else {
return i.getValue().equals(getValue());
}
}
} else {
return false;
}
}
@Override
final public String getPushedType(String[] types) {
return getType();
}
@Override
final public byte getPushedWordSize() {
return Util.getWordSize(getType());
}
@Override
final public int hashCode() {
int v = getValue() == null ? 0 : getValue().hashCode();
return getType().hashCode() + 14411 * v;
}
@Override
final public void visit(IInstruction.Visitor v) throws NullPointerException {
v.visitConstant(this);
}
private static String quote(Object o) {
if (o instanceof String) {
String s = (String) o;
StringBuffer buf = new StringBuffer("\"");
int len = s.length();
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
switch (ch) {
case '"':
buf.append('\\');
buf.append(ch);
break;
case '\n':
buf.append("\\\n");
break;
case '\t':
buf.append("\\\t");
break;
default:
buf.append(ch);
}
}
buf.append("\"");
return buf.toString();
} else if (o == null) {
return "null";
} else {
return o.toString();
}
}
@Override
final public String toString() {
return "Constant(" + getType() + "," + quote(getValue()) + ")";
}
@Override
public boolean isPEI() {
return false;
}
}