/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.stackmap;
import javassist.
ClassPool;
import javassist.
CtClass;
import javassist.
NotFoundException;
import javassist.bytecode.
ConstPool;
import javassist.bytecode.
StackMapTable;
import javassist.bytecode.
BadBytecode;
import java.util.
ArrayList;
public abstract class
TypeData {
/* Memo:
* array type is a subtype of Cloneable and Serializable
*/
protected
TypeData() {}
public abstract void
merge(
TypeData neighbor);
/**
* Sets the type name of this object type. If the given type name is
* a subclass of the current type name, then the given name becomes
* the name of this object type.
*
* @param className dot-separated name unless the type is an array type.
*/
static void
setType(
TypeData td,
String className,
ClassPool cp) throws
BadBytecode {
if (
td ==
TypeTag.
TOP)
throw new
BadBytecode("unset variable");
else
td.
setType(
className,
cp);
}
public abstract boolean
equals(
Object obj);
public abstract int
getTypeTag();
public abstract int
getTypeData(
ConstPool cp);
/*
* See UninitData.getSelf().
*/
public
TypeData getSelf() { return this; }
/* An operand value is copied when it is stored in a
* local variable.
*/
public abstract
TypeData copy();
public abstract boolean
isObjectType();
public boolean
is2WordType() { return false; }
public boolean
isNullType() { return false; }
public abstract
String getName() throws
BadBytecode;
protected abstract void
setType(
String s,
ClassPool cp) throws
BadBytecode;
public abstract void
evalExpectedType(
ClassPool cp) throws
BadBytecode;
public abstract
String getExpected() throws
BadBytecode;
/**
* Primitive types.
*/
protected static class
BasicType extends
TypeData {
private
String name;
private int
typeTag;
public
BasicType(
String type, int
tag) {
name =
type;
typeTag =
tag;
}
public void
merge(
TypeData neighbor) {}
public boolean
equals(
Object obj) {
return this ==
obj;
}
public int
getTypeTag() { return
typeTag; }
public int
getTypeData(
ConstPool cp) { return 0; }
public boolean
isObjectType() { return false; }
public boolean
is2WordType() {
return
typeTag ==
StackMapTable.
LONG
||
typeTag ==
StackMapTable.
DOUBLE;
}
public
TypeData copy() {
return this;
}
public void
evalExpectedType(
ClassPool cp) throws
BadBytecode {}
public
String getExpected() throws
BadBytecode {
return
name;
}
public
String getName() {
return
name;
}
protected void
setType(
String s,
ClassPool cp) throws
BadBytecode {
throw new
BadBytecode("conflict: " +
name + " and " +
s);
}
public
String toString() { return
name; }
}
protected static abstract class
TypeName extends
TypeData {
protected
ArrayList equivalences;
protected
String expectedName;
private
CtClass cache;
private boolean
evalDone;
protected
TypeName() {
equivalences = new
ArrayList();
equivalences.
add(this);
expectedName = null;
cache = null;
evalDone = false;
}
public void
merge(
TypeData neighbor) {
if (this ==
neighbor)
return;
if (!(
neighbor instanceof
TypeName))
return; // neighbor might be UninitData
TypeName neighbor2 = (
TypeName)
neighbor;
ArrayList list =
equivalences;
ArrayList list2 =
neighbor2.
equivalences;
if (
list ==
list2)
return;
int
n =
list2.
size();
for (int
i = 0;
i <
n;
i++) {
TypeName tn = (
TypeName)
list2.
get(
i);
add(
list,
tn);
tn.
equivalences =
list;
}
}
private static void
add(
ArrayList list,
TypeData td) {
int
n =
list.
size();
for (int
i = 0;
i <
n;
i++)
if (
list.
get(
i) ==
td)
return;
list.
add(
td);
}
/* NullType overrides this method.
*/
public int
getTypeTag() { return
StackMapTable.
OBJECT; }
public int
getTypeData(
ConstPool cp) {
String type;
try {
type =
getExpected();
} catch (
BadBytecode e) {
throw new
RuntimeException("fatal error: ",
e);
}
return
getTypeData2(
cp,
type);
}
/* NullType overrides this method.
*/
protected int
getTypeData2(
ConstPool cp,
String type) {
return
cp.
addClassInfo(
type);
}
public boolean
equals(
Object obj) {
if (
obj instanceof
TypeName) {
try {
TypeName tn = (
TypeName)
obj;
return
getExpected().
equals(
tn.
getExpected());
}
catch (
BadBytecode e) {}
}
return false;
}
public boolean
isObjectType() { return true; }
protected void
setType(
String typeName,
ClassPool cp) throws
BadBytecode {
if (
update(
cp,
expectedName,
typeName))
expectedName =
typeName;
}
public void
evalExpectedType(
ClassPool cp) throws
BadBytecode {
if (this.
evalDone)
return;
ArrayList equiv = this.
equivalences;
int
n =
equiv.
size();
String name =
evalExpectedType2(
equiv,
n);
if (
name == null) {
name = this.
expectedName;
for (int
i = 0;
i <
n;
i++) {
TypeData td = (
TypeData)
equiv.
get(
i);
if (
td instanceof
TypeName) {
TypeName tn = (
TypeName)
td;
if (
update(
cp,
name,
tn.
expectedName))
name =
tn.
expectedName;
}
}
}
for (int
i = 0;
i <
n;
i++) {
TypeData td = (
TypeData)
equiv.
get(
i);
if (
td instanceof
TypeName) {
TypeName tn = (
TypeName)
td;
tn.
expectedName =
name;
tn.
cache = null;
tn.
evalDone = true;
}
}
}
private
String evalExpectedType2(
ArrayList equiv, int
n) throws
BadBytecode {
String origName = null;
for (int
i = 0;
i <
n;
i++) {
TypeData td = (
TypeData)
equiv.
get(
i);
if (!
td.
isNullType())
if (
origName == null)
origName =
td.
getName();
else if (!
origName.
equals(
td.
getName()))
return null;
}
return
origName;
}
protected boolean
isTypeName() { return true; }
private boolean
update(
ClassPool cp,
String oldName,
String typeName) throws
BadBytecode {
if (
typeName == null)
return false;
else if (
oldName == null)
return true;
else if (
oldName.
equals(
typeName))
return false;
else if (
typeName.
charAt(0) == '['
&&
oldName.
equals("[Ljava.lang.Object;")) {
/* this rule is not correct but Tracer class sets the type
of the operand of arraylength to java.lang.Object[].
Thus, int[] etc. must be a subtype of java.lang.Object[].
*/
return true;
}
try {
if (
cache == null)
cache =
cp.
get(
oldName);
CtClass cache2 =
cp.
get(
typeName);
if (
cache2.
subtypeOf(
cache)) {
cache =
cache2;
return true;
}
else
return false;
}
catch (
NotFoundException e) {
throw new
BadBytecode("cannot find " +
e.
getMessage());
}
}
/* See also NullType.getExpected().
*/
public
String getExpected() throws
BadBytecode {
ArrayList equiv =
equivalences;
if (
equiv.
size() == 1)
return
getName();
else {
String en =
expectedName;
if (
en == null)
return "java.lang.Object";
else
return
en;
}
}
public
String toString() {
try {
String en =
expectedName;
if (
en != null)
return
en;
String name =
getName();
if (
equivalences.
size() == 1)
return
name;
else
return
name + "?";
}
catch (
BadBytecode e) {
return "<" +
e.
getMessage() + ">";
}
}
}
/**
* Type data for OBJECT.
*/
public static class
ClassName extends
TypeName {
private
String name; // dot separated. null if this object is a copy of another.
public
ClassName(
String n) {
name =
n;
}
public
TypeData copy() {
return new
ClassName(
name);
}
public
String getName() { // never returns null.
return
name;
}
}
/**
* Type data for NULL or OBJECT.
* The types represented by the instances of this class are
* initially NULL but will be OBJECT.
*/
public static class
NullType extends
ClassName {
public
NullType() {
super("null"); // type name
}
public
TypeData copy() {
return new
NullType();
}
public boolean
isNullType() { return true; }
public int
getTypeTag() {
try {
if ("null".
equals(
getExpected()))
return
StackMapTable.
NULL;
else
return super.getTypeTag();
}
catch (
BadBytecode e) {
throw new
RuntimeException("fatal error: ",
e);
}
}
protected int
getTypeData2(
ConstPool cp,
String type) {
if ("null".
equals(
type))
return 0;
else
return super.getTypeData2(
cp,
type);
}
public
String getExpected() throws
BadBytecode {
String en =
expectedName;
if (
en == null) {
// ArrayList equiv = equivalences;
// if (equiv.size() == 1)
// return getName();
// else
return "java.lang.Object";
}
else
return
en;
}
}
/**
* Type data for OBJECT if the type is an object type and is
* derived as an element type from an array type by AALOAD.
*/
public static class
ArrayElement extends
TypeName {
TypeData array;
public
ArrayElement(
TypeData a) { // a is never null
array =
a;
}
public
TypeData copy() {
return new
ArrayElement(
array);
}
protected void
setType(
String typeName,
ClassPool cp) throws
BadBytecode {
super.setType(
typeName,
cp);
array.
setType(
getArrayType(
typeName),
cp);
}
public
String getName() throws
BadBytecode {
String name =
array.
getName();
if (
name.
length() > 1 &&
name.
charAt(0) == '[') {
char
c =
name.
charAt(1);
if (
c == 'L')
return
name.
substring(2,
name.
length() - 1).
replace('/', '.');
else if (
c == '[')
return
name.
substring(1);
}
throw new
BadBytecode("bad array type for AALOAD: "
+
name);
}
public static
String getArrayType(
String elementType) {
if (
elementType.
charAt(0) == '[')
return "[" +
elementType;
else
return "[L" +
elementType.
replace('.', '/') + ";";
}
public static
String getElementType(
String arrayType) {
char
c =
arrayType.
charAt(1);
if (
c == 'L')
return
arrayType.
substring(2,
arrayType.
length() - 1).
replace('/', '.');
else if (
c == '[')
return
arrayType.
substring(1);
else
return
arrayType;
}
}
/**
* Type data for UNINIT.
*/
public static class
UninitData extends
TypeData {
String className;
int
offset;
boolean
initialized;
UninitData(int
offset,
String className) {
this.
className =
className;
this.
offset =
offset;
this.
initialized = false;
}
public void
merge(
TypeData neighbor) {}
public int
getTypeTag() { return
StackMapTable.
UNINIT; }
public int
getTypeData(
ConstPool cp) { return
offset; }
public boolean
equals(
Object obj) {
if (
obj instanceof
UninitData) {
UninitData ud = (
UninitData)
obj;
return
offset ==
ud.
offset &&
className.
equals(
ud.
className);
}
else
return false;
}
public
TypeData getSelf() {
if (
initialized)
return
copy();
else
return this;
}
public
TypeData copy() {
return new
ClassName(
className);
}
public boolean
isObjectType() { return true; }
protected void
setType(
String typeName,
ClassPool cp) throws
BadBytecode {
initialized = true;
}
public void
evalExpectedType(
ClassPool cp) throws
BadBytecode {}
public
String getName() {
return
className;
}
public
String getExpected() { return
className; }
public
String toString() { return "uninit:" +
className + "@" +
offset; }
}
public static class
UninitThis extends
UninitData {
UninitThis(
String className) {
super(-1,
className);
}
public int
getTypeTag() { return
StackMapTable.
THIS; }
public int
getTypeData(
ConstPool cp) { return 0; }
public boolean
equals(
Object obj) {
return
obj instanceof
UninitThis;
}
public
String toString() { return "uninit:this"; }
}
}