diff options
Diffstat (limited to 'src/main/javassist/bytecode/stackmap/TypeData.java')
-rw-r--r-- | src/main/javassist/bytecode/stackmap/TypeData.java | 1107 |
1 files changed, 801 insertions, 306 deletions
diff --git a/src/main/javassist/bytecode/stackmap/TypeData.java b/src/main/javassist/bytecode/stackmap/TypeData.java index f6c6c4e..9bc837d 100644 --- a/src/main/javassist/bytecode/stackmap/TypeData.java +++ b/src/main/javassist/bytecode/stackmap/TypeData.java @@ -1,11 +1,12 @@ /* * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. + * Copyright (C) 1999- 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. + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License @@ -15,22 +16,34 @@ package javassist.bytecode.stackmap; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; +import javassist.bytecode.BadBytecode; import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; 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 static TypeData[] make(int size) { + TypeData[] array = new TypeData[size]; + for (int i = 0; i < size; i++) + array[i] = TypeTag.TOP; + + return array; + } - public abstract void merge(TypeData neighbor); + protected TypeData() {} /** * Sets the type name of this object type. If the given type name is @@ -39,36 +52,71 @@ public abstract class TypeData { * * @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); + @SuppressWarnings("unused") + private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { + 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; } + public TypeData join() { return new TypeVar(this); } - /* An operand value is copied when it is stored in a - * local variable. + /** + * If the type is a basic type, this method normalizes the type + * and returns a BasicType object. Otherwise, it returns null. */ - public abstract TypeData copy(); + public abstract BasicType isBasicType(); + + public abstract boolean is2WordType(); - public abstract boolean isObjectType(); - public boolean is2WordType() { return false; } + /** + * Returns false if getName() returns a valid type name. + */ 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; + public boolean isUninit() { return false; } + + public abstract boolean eq(TypeData d); + + public abstract String getName(); + public abstract void setType(String s, ClassPool cp) throws BadBytecode; + + /** + * @param dim array dimension. It may be negative. + */ + public abstract TypeData getArrayType(int dim) throws NotFoundException; + + /** + * Depth-first search by Tarjan's algorithm + * + * @param order a node stack in the order in which nodes are visited. + * @param index the index used by the algorithm. + */ + public int dfs(List<TypeData> order, int index, ClassPool cp) + throws NotFoundException + { + return index; + } + + /** + * Returns this if it is a TypeVar or a TypeVar that this + * type depends on. Otherwise, this method returns null. + * It is used by dfs(). + * + * @param dim dimension + */ + protected TypeVar toTypeVar(int dim) { return null; } + + // see UninitTypeVar and UninitData + public void constructorCalled(int offset) {} + + @Override + public String toString() { + return super.toString() + "(" + toString2(new HashSet<TypeData>()) + ")"; + } + + abstract String toString2(Set<TypeData> set); /** * Primitive types. @@ -76,420 +124,860 @@ public abstract class TypeData { protected static class BasicType extends TypeData { private String name; private int typeTag; + private char decodedName; - public BasicType(String type, int tag) { + public BasicType(String type, int tag, char decoded) { name = type; typeTag = tag; + decodedName = decoded; } - public void merge(TypeData neighbor) {} - - public boolean equals(Object obj) { - return this == obj; - } - + @Override public int getTypeTag() { return typeTag; } + @Override public int getTypeData(ConstPool cp) { return 0; } - public boolean isObjectType() { return false; } + @Override + public TypeData join() { + if (this == TypeTag.TOP) + return this; + return super.join(); + } + @Override + public BasicType isBasicType() { return this; } + + @Override 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; - } + @Override + public boolean eq(TypeData d) { return this == d; } + @Override public String getName() { return name; } - protected void setType(String s, ClassPool cp) throws BadBytecode { + public char getDecodedName() { return decodedName; } + + @Override + public void setType(String s, ClassPool cp) throws BadBytecode { throw new BadBytecode("conflict: " + name + " and " + s); } - public String toString() { return name; } - } + /** + * @param dim array dimension. It may be negative. + */ + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + if (this == TypeTag.TOP) + return this; + else if (dim < 0) + throw new NotFoundException("no element type: " + name); + else if (dim == 0) + return this; + else { + char[] name = new char[dim + 1]; + for (int i = 0; i < dim; i++) + name[i] = '['; - protected static abstract class TypeName extends TypeData { - protected ArrayList equivalences; + name[dim] = decodedName; + return new ClassName(new String(name)); + } + } - protected String expectedName; - private CtClass cache; - private boolean evalDone; + @Override + String toString2(Set<TypeData> set) { return name; } + } + + // a type variable + public static abstract class AbsTypeVar extends TypeData { + public AbsTypeVar() {} + public abstract void merge(TypeData t); + @Override + public int getTypeTag() { return StackMapTable.OBJECT; } - protected TypeName() { - equivalences = new ArrayList(); - equivalences.add(this); - expectedName = null; - cache = null; - evalDone = false; + @Override + public int getTypeData(ConstPool cp) { + return cp.addClassInfo(getName()); } - public void merge(TypeData neighbor) { - if (this == neighbor) - return; + @Override + public boolean eq(TypeData d) { return getName().equals(d.getName()); } + } - if (!(neighbor instanceof TypeName)) - return; // neighbor might be UninitData + /* a type variable representing a class type or a basic type. + */ + public static class TypeVar extends AbsTypeVar { + protected List<TypeData> lowers;// lower bounds of this type. ArrayList<TypeData> + protected List<TypeData> usedBy;// reverse relations of lowers + protected List<String> uppers; // upper bounds of this type. + protected String fixedType; + private boolean is2WordType; // cache + + public TypeVar(TypeData t) { + uppers = null; + lowers = new ArrayList<TypeData>(2); + usedBy = new ArrayList<TypeData>(2); + merge(t); + fixedType = null; + is2WordType = t.is2WordType(); + } + + @Override + public String getName() { + if (fixedType == null) + return lowers.get(0).getName(); + return fixedType; + } - TypeName neighbor2 = (TypeName)neighbor; - ArrayList list = equivalences; - ArrayList list2 = neighbor2.equivalences; - if (list == list2) - return; + @Override + public BasicType isBasicType() { + if (fixedType == null) + return lowers.get(0).isBasicType(); + return null; + } - int n = list2.size(); - for (int i = 0; i < n; i++) { - TypeName tn = (TypeName)list2.get(i); - add(list, tn); - tn.equivalences = list; + @Override + public boolean is2WordType() { + if (fixedType == null) { + return is2WordType; + // return ((TypeData)lowers.get(0)).is2WordType(); } + return false; } - 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; + @Override + public boolean isNullType() { + if (fixedType == null) + return lowers.get(0).isNullType(); + return false; + } - list.add(td); + @Override + public boolean isUninit() { + if (fixedType == null) + return lowers.get(0).isUninit(); + return false; } - /* NullType overrides this method. - */ - public int getTypeTag() { return StackMapTable.OBJECT; } + @Override + public void merge(TypeData t) { + lowers.add(t); + if (t instanceof TypeVar) + ((TypeVar)t).usedBy.add(this); + } + @Override + public int getTypeTag() { + /* If fixedType is null after calling dfs(), then this + type is NULL, Uninit, or a basic type. So call + getTypeTag() on the first element of lowers. */ + if (fixedType == null) + return lowers.get(0).getTypeTag(); + return super.getTypeTag(); + } + + @Override public int getTypeData(ConstPool cp) { - String type; - try { - type = getExpected(); - } catch (BadBytecode e) { - throw new RuntimeException("fatal error: ", e); - } + if (fixedType == null) + return lowers.get(0).getTypeData(cp); + return super.getTypeData(cp); + } + + @Override + public void setType(String typeName, ClassPool cp) throws BadBytecode { + if (uppers == null) + uppers = new ArrayList<String>(); + + uppers.add(typeName); + } + + private int visited = 0; + private int smallest = 0; + private boolean inList = false; + private int dimension = 0; - return getTypeData2(cp, type); + @Override + protected TypeVar toTypeVar(int dim) { + dimension = dim; + return this; } - /* NullType overrides this method. + /* When fixTypes() is called, getName() will return the correct + * (i.e. fixed) type name. */ - protected int getTypeData2(ConstPool cp, String type) { - return cp.addClassInfo(type); + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + if (dim == 0) + return this; + BasicType bt = isBasicType(); + if (bt == null) + if (isNullType()) + return new NullType(); + else + return new ClassName(getName()).getArrayType(dim); + return bt.getArrayType(dim); } - public boolean equals(Object obj) { - if (obj instanceof TypeName) { - try { - TypeName tn = (TypeName)obj; - return getExpected().equals(tn.getExpected()); + // depth-first serach + @Override + public int dfs(List<TypeData> preOrder, int index, ClassPool cp) throws NotFoundException { + if (visited > 0) + return index; // MapMaker.make() may call an already visited node. + + visited = smallest = ++index; + preOrder.add(this); + inList = true; + int n = lowers.size(); + for (int i = 0; i < n; i++) { + TypeVar child = lowers.get(i).toTypeVar(dimension); + if (child != null) + if (child.visited == 0) { + index = child.dfs(preOrder, index, cp); + if (child.smallest < smallest) + smallest = child.smallest; + } + else if (child.inList) + if (child.visited < smallest) + smallest = child.visited; + } + + if (visited == smallest) { + List<TypeData> scc = new ArrayList<TypeData>(); // strongly connected component + TypeVar cv; + do { + cv = (TypeVar)preOrder.remove(preOrder.size() - 1); + cv.inList = false; + scc.add(cv); + } while (cv != this); + fixTypes(scc, cp); + } + + return index; + } + + private void fixTypes(List<TypeData> scc, ClassPool cp) throws NotFoundException { + Set<String> lowersSet = new HashSet<String>(); + boolean isBasicType = false; + TypeData kind = null; + int size = scc.size(); + for (int i = 0; i < size; i++) { + TypeVar tvar = (TypeVar)scc.get(i); + List<TypeData> tds = tvar.lowers; + int size2 = tds.size(); + for (int j = 0; j < size2; j++) { + TypeData td = tds.get(j); + TypeData d = td.getArrayType(tvar.dimension); + BasicType bt = d.isBasicType(); + if (kind == null) { + if (bt == null) { + isBasicType = false; + kind = d; + /* If scc has only an UninitData, fixedType is kept null. + So lowerSet must be empty. If scc has not only an UninitData + but also another TypeData, an error must be thrown but this + error detection has not been implemented. */ + if (d.isUninit()) + break; + } + else { + isBasicType = true; + kind = bt; + } + } + else { + if ((bt == null && isBasicType) || (bt != null && kind != bt)) { + isBasicType = true; + kind = TypeTag.TOP; + break; + } + } + + if (bt == null && !d.isNullType()) + lowersSet.add(d.getName()); } - catch (BadBytecode e) {} } - return false; + if (isBasicType) { + is2WordType = kind.is2WordType(); // necessary? + fixTypes1(scc, kind); + } + else { + String typeName = fixTypes2(scc, lowersSet, cp); + fixTypes1(scc, new ClassName(typeName)); + } } - public boolean isObjectType() { return true; } + private void fixTypes1(List<TypeData> scc, TypeData kind) throws NotFoundException { + int size = scc.size(); + for (int i = 0; i < size; i++) { + TypeVar cv = (TypeVar)scc.get(i); + TypeData kind2 = kind.getArrayType(-cv.dimension); + if (kind2.isBasicType() == null) + cv.fixedType = kind2.getName(); + else { + cv.lowers.clear(); + cv.lowers.add(kind2); + cv.is2WordType = kind2.is2WordType(); + } + } + } - protected void setType(String typeName, ClassPool cp) throws BadBytecode { - if (update(cp, expectedName, typeName)) - expectedName = typeName; + private String fixTypes2(List<TypeData> scc, Set<String> lowersSet, ClassPool cp) throws NotFoundException { + Iterator<String> it = lowersSet.iterator(); + if (lowersSet.size() == 0) + return null; // only NullType + else if (lowersSet.size() == 1) + return it.next(); + else { + CtClass cc = cp.get(it.next()); + while (it.hasNext()) + cc = commonSuperClassEx(cc, cp.get(it.next())); + + if (cc.getSuperclass() == null || isObjectArray(cc)) + cc = fixByUppers(scc, cp, new HashSet<TypeData>(), cc); + + if (cc.isArray()) + return Descriptor.toJvmName(cc); + + return cc.getName(); + } } - public void evalExpectedType(ClassPool cp) throws BadBytecode { - if (this.evalDone) - return; + private static boolean isObjectArray(CtClass cc) throws NotFoundException { + return cc.isArray() && cc.getComponentType().getSuperclass() == null; + } + + private CtClass fixByUppers(List<TypeData> users, ClassPool cp, Set<TypeData> visited, CtClass type) + throws NotFoundException + { + if (users == null) + return type; + + int size = users.size(); + for (int i = 0; i < size; i++) { + TypeVar t = (TypeVar)users.get(i); + if (!visited.add(t)) + return type; - 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; + if (t.uppers != null) { + int s = t.uppers.size(); + for (int k = 0; k < s; k++) { + CtClass cc = cp.get(t.uppers.get(k)); + if (cc.subtypeOf(type)) + type = cc; } } + + type = fixByUppers(t.usedBy, cp, visited, type); } - 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; - } + return type; + } + + @Override + String toString2(Set<TypeData> hash) { + hash.add(this); + if (lowers.size() > 0) { + TypeData e = lowers.get(0); + if (e != null && !hash.contains(e)) + return e.toString2(hash); } + + return "?"; } + } - 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; + /** + * Finds the most specific common super class of the given classes + * by considering array types. + */ + public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException { + if (one == two) + return one; + else if (one.isArray() && two.isArray()) { + CtClass ele1 = one.getComponentType(); + CtClass ele2 = two.getComponentType(); + CtClass element = commonSuperClassEx(ele1, ele2); + if (element == ele1) + return one; + else if (element == ele2) + return two; + else + return one.getClassPool().get(element == null ? "java.lang.Object" + : element.getName() + "[]"); + } + else if (one.isPrimitive() || two.isPrimitive()) + return null; // TOP + else if (one.isArray() || two.isArray()) // but !(one.isArray() && two.isArray()) + return one.getClassPool().get("java.lang.Object"); + else + return commonSuperClass(one, two); + } + + /** + * Finds the most specific common super class of the given classes. + * This method is a copy from javassist.bytecode.analysis.Type. + */ + public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException { + CtClass deep = one; + CtClass shallow = two; + CtClass backupShallow = shallow; + CtClass backupDeep = deep; + + // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly + for (;;) { + // In case we get lucky, and find a match early + if (eq(deep, shallow) && deep.getSuperclass() != null) + return deep; + + CtClass deepSuper = deep.getSuperclass(); + CtClass shallowSuper = shallow.getSuperclass(); + + if (shallowSuper == null) { + // right, now reset shallow + shallow = backupShallow; + break; } - 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; + if (deepSuper == null) { + // wrong, swap them, since deep is now useless, its our tmp before we swap it + deep = backupDeep; + backupDeep = backupShallow; + backupShallow = deep; + + deep = shallow; + shallow = backupShallow; + break; } - try { - if (cache == null) - cache = cp.get(oldName); - - CtClass cache2 = cp.get(typeName); - if (cache2.subtypeOf(cache)) { - cache = cache2; - return true; + deep = deepSuper; + shallow = shallowSuper; + } + + // Phase 2 - Move deepBackup up by (deep end - deep) + for (;;) { + deep = deep.getSuperclass(); + if (deep == null) + break; + + backupDeep = backupDeep.getSuperclass(); + } + + deep = backupDeep; + + // Phase 3 - The hierarchy positions are now aligned + // The common super class is easy to find now + while (!eq(deep, shallow)) { + deep = deep.getSuperclass(); + shallow = shallow.getSuperclass(); + } + + return deep; + } + + static boolean eq(CtClass one, CtClass two) { + return one == two || (one != null && two != null && one.getName().equals(two.getName())); + } + + public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode { + if (array instanceof AbsTypeVar) + if (!value.isNullType()) + ((AbsTypeVar)array).merge(ArrayType.make(value)); + + if (value instanceof AbsTypeVar) + if (array instanceof AbsTypeVar) + ArrayElement.make(array); // should call value.setType() later. + else if (array instanceof ClassName) { + if (!array.isNullType()) { + String type = ArrayElement.typeName(array.getName()); + value.setType(type, cp); } - else - return false; - } - catch (NotFoundException e) { - throw new BadBytecode("cannot find " + e.getMessage()); } + else + throw new BadBytecode("bad AASTORE: " + array); + } + + /* A type variable representing an array type. + * It is a decorator of another type variable. + */ + public static class ArrayType extends AbsTypeVar { + private AbsTypeVar element; + + private ArrayType(AbsTypeVar elementType) { + element = elementType; } - /* 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; - } + static TypeData make(TypeData element) throws BadBytecode { + if (element instanceof ArrayElement) + return ((ArrayElement)element).arrayType(); + else if (element instanceof AbsTypeVar) + return new ArrayType((AbsTypeVar)element); + else if (element instanceof ClassName) + if (!element.isNullType()) + return new ClassName(typeName(element.getName())); + + throw new BadBytecode("bad AASTORE: " + element); } - public String toString() { + @Override + public void merge(TypeData t) { try { - String en = expectedName; - if (en != null) - return en; - - String name = getName(); - if (equivalences.size() == 1) - return name; - else - return name + "?"; + if (!t.isNullType()) + element.merge(ArrayElement.make(t)); } catch (BadBytecode e) { - return "<" + e.getMessage() + ">"; + // never happens + throw new RuntimeException("fatal: " + e); } } - } - /** - * 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; + @Override + public String getName() { + return typeName(element.getName()); } - public TypeData copy() { - return new ClassName(name); + public AbsTypeVar elementType() { return element; } + + @Override + public BasicType isBasicType() { return null; } + @Override + public boolean is2WordType() { return false; } + + /* elementType must be a class name. Basic type names + * are not allowed. + */ + public static String typeName(String elementType) { + if (elementType.charAt(0) == '[') + return "[" + elementType; + return "[L" + elementType.replace('.', '/') + ";"; } - public String getName() { // never returns null. - return name; + @Override + public void setType(String s, ClassPool cp) throws BadBytecode { + element.setType(ArrayElement.typeName(s), cp); } - } - /** - * 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 + @Override + protected TypeVar toTypeVar(int dim) { return element.toTypeVar(dim + 1); } + + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + return element.getArrayType(dim + 1); } - public TypeData copy() { - return new NullType(); + @Override + public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException { + return element.dfs(order, index, cp); } - public boolean isNullType() { return true; } + @Override + String toString2(Set<TypeData> set) { + return "[" + element.toString2(set); + } + } - public int getTypeTag() { + /* A type variable representing an array-element type. + * It is a decorator of another type variable. + */ + public static class ArrayElement extends AbsTypeVar { + private AbsTypeVar array; + + private ArrayElement(AbsTypeVar a) { // a is never null + array = a; + } + + public static TypeData make(TypeData array) throws BadBytecode { + if (array instanceof ArrayType) + return ((ArrayType)array).elementType(); + else if (array instanceof AbsTypeVar) + return new ArrayElement((AbsTypeVar)array); + else if (array instanceof ClassName) + if (!array.isNullType()) + return new ClassName(typeName(array.getName())); + + throw new BadBytecode("bad AASTORE: " + array); + } + + @Override + public void merge(TypeData t) { try { - if ("null".equals(getExpected())) - return StackMapTable.NULL; - else - return super.getTypeTag(); + if (!t.isNullType()) + array.merge(ArrayType.make(t)); } catch (BadBytecode e) { - throw new RuntimeException("fatal error: ", e); + // never happens + throw new RuntimeException("fatal: " + e); } } - protected int getTypeData2(ConstPool cp, String type) { - if ("null".equals(type)) - return 0; - else - return super.getTypeData2(cp, type); + @Override + public String getName() { + return typeName(array.getName()); } - 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"; + public AbsTypeVar arrayType() { return array; } + + /* arrayType must be a class name. Basic type names are + * not allowed. + */ + + @Override + public BasicType isBasicType() { return null; } + + @Override + public boolean is2WordType() { return false; } + + private static String typeName(String arrayType) { + if (arrayType.length() > 1 && arrayType.charAt(0) == '[') { + 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 en; + + return "java.lang.Object"; // the array type may be NullType + } + + @Override + public void setType(String s, ClassPool cp) throws BadBytecode { + array.setType(ArrayType.typeName(s), cp); + } + + @Override + protected TypeVar toTypeVar(int dim) { return array.toTypeVar(dim - 1); } + + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + return array.getArrayType(dim - 1); + } + + @Override + public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException { + return array.dfs(order, index, cp); + } + + @Override + String toString2(Set<TypeData> set) { + return "*" + array.toString2(set); + } + } + + public static class UninitTypeVar extends AbsTypeVar { + protected TypeData type; // UninitData or TOP + + public UninitTypeVar(UninitData t) { type = t; } + @Override + public int getTypeTag() { return type.getTypeTag(); } + @Override + public int getTypeData(ConstPool cp) { return type.getTypeData(cp); } + @Override + public BasicType isBasicType() { return type.isBasicType(); } + @Override + public boolean is2WordType() { return type.is2WordType(); } + @Override + public boolean isUninit() { return type.isUninit(); } + @Override + public boolean eq(TypeData d) { return type.eq(d); } + @Override + public String getName() { return type.getName(); } + + @Override + protected TypeVar toTypeVar(int dim) { return null; } + @Override + public TypeData join() { return type.join(); } + + @Override + public void setType(String s, ClassPool cp) throws BadBytecode { + type.setType(s, cp); + } + + @Override + public void merge(TypeData t) { + if (!t.eq(type)) + type = TypeTag.TOP; + } + + @Override + public void constructorCalled(int offset) { + type.constructorCalled(offset); + } + + public int offset() { + if (type instanceof UninitData) + return ((UninitData)type).offset; + throw new RuntimeException("not available"); + } + + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + return type.getArrayType(dim); } + + @Override + String toString2(Set<TypeData> set) { return ""; } } /** - * Type data for OBJECT if the type is an object type and is - * derived as an element type from an array type by AALOAD. + * Type data for OBJECT. */ - public static class ArrayElement extends TypeName { - TypeData array; - - public ArrayElement(TypeData a) { // a is never null - array = a; + public static class ClassName extends TypeData { + private String name; // dot separated. + + public ClassName(String n) { + name = n; } - public TypeData copy() { - return new ArrayElement(array); + @Override + public String getName() { + return name; } - protected void setType(String typeName, ClassPool cp) throws BadBytecode { - super.setType(typeName, cp); - array.setType(getArrayType(typeName), cp); + @Override + public BasicType isBasicType() { return null; } + + @Override + public boolean is2WordType() { return false; } + + @Override + public int getTypeTag() { return StackMapTable.OBJECT; } + + @Override + public int getTypeData(ConstPool cp) { + return cp.addClassInfo(getName()); } - 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); + @Override + public boolean eq(TypeData d) { return name.equals(d.getName()); } + + @Override + public void setType(String typeName, ClassPool cp) throws BadBytecode {} + + @Override + public TypeData getArrayType(int dim) throws NotFoundException { + if (dim == 0) + return this; + else if (dim > 0) { + char[] dimType = new char[dim]; + for (int i = 0; i < dim; i++) + dimType[i] = '['; + + String elementType = getName(); + if (elementType.charAt(0) != '[') + elementType = "L" + elementType.replace('.', '/') + ";"; + + return new ClassName(new String(dimType) + elementType); + } + else { + for (int i = 0; i < -dim; i++) + if (name.charAt(i) != '[') + throw new NotFoundException("no " + dim + " dimensional array type: " + getName()); + + char type = name.charAt(-dim); + if (type == '[') + return new ClassName(name.substring(-dim)); + else if (type == 'L') + return new ClassName(name.substring(-dim + 1, name.length() - 1).replace('/', '.')); + else if (type == TypeTag.DOUBLE.decodedName) + return TypeTag.DOUBLE; + else if (type == TypeTag.FLOAT.decodedName) + return TypeTag.FLOAT; + else if (type == TypeTag.LONG.decodedName) + return TypeTag.LONG; + else + return TypeTag.INTEGER; } - - 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('.', '/') + ";"; + @Override + String toString2(Set<TypeData> set) { + return name; } + } - 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 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"); // type name } + + @Override + public int getTypeTag() { + return StackMapTable.NULL; + } + + @Override + public boolean isNullType() { return true; } + @Override + public int getTypeData(ConstPool cp) { return 0; } + + @Override + public TypeData getArrayType(int dim) { return this; } } /** * Type data for UNINIT. */ - public static class UninitData extends TypeData { - String className; + public static class UninitData extends ClassName { int offset; boolean initialized; UninitData(int offset, String className) { - this.className = className; + super(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 UninitData copy() { return new UninitData(offset, getName()); } - public boolean equals(Object obj) { - if (obj instanceof UninitData) { - UninitData ud = (UninitData)obj; - return offset == ud.offset && className.equals(ud.className); - } - else - return false; + @Override + public int getTypeTag() { + return StackMapTable.UNINIT; } - public TypeData getSelf() { - if (initialized) - return copy(); - else - return this; + @Override + public int getTypeData(ConstPool cp) { + return offset; } - public TypeData copy() { - return new ClassName(className); + @Override + public TypeData join() { + if (initialized) + return new TypeVar(new ClassName(getName())); + return new UninitTypeVar(copy()); } - public boolean isObjectType() { return true; } + @Override + public boolean isUninit() { return true; } - protected void setType(String typeName, ClassPool cp) throws BadBytecode { - initialized = true; + @Override + public boolean eq(TypeData d) { + if (d instanceof UninitData) { + UninitData ud = (UninitData)d; + return offset == ud.offset && getName().equals(ud.getName()); + } + return false; } - public void evalExpectedType(ClassPool cp) throws BadBytecode {} + public int offset() { return offset; } - public String getName() { - return className; + @Override + public void constructorCalled(int offset) { + if (offset == this.offset) + initialized = true; } - public String getExpected() { return className; } - - public String toString() { return "uninit:" + className + "@" + offset; } + @Override + String toString2(Set<TypeData> set) { return getName() + "," + offset; } } public static class UninitThis extends UninitData { @@ -497,13 +985,20 @@ public abstract class TypeData { super(-1, className); } - public int getTypeTag() { return StackMapTable.THIS; } - public int getTypeData(ConstPool cp) { return 0; } + @Override + public UninitData copy() { return new UninitThis(getName()); } - public boolean equals(Object obj) { - return obj instanceof UninitThis; + @Override + public int getTypeTag() { + return StackMapTable.THIS; + } + + @Override + public int getTypeData(ConstPool cp) { + return 0; } - public String toString() { return "uninit:this"; } + @Override + String toString2(Set<TypeData> set) { return "uninit:this"; } } } |