// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * 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. // * Neither the name of the <organization> 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 DAVID J. PEARCE 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 jasm.lang; import jasm.util.Pair; import java.util.*; /** * Represents types used in Java programs (e.g. int, String, Object[], etc). * <p> * JKit provides classes and methods for representing and manipulating Java * types (such <code>int</code>, <code>String[]</code> etc). The majority * of these can be found here. For example, the <code>Type.Int</code> class is * used to represent <code>int</code> types, whilst * <code>Type.Reference</code> represents general reference types, such as * <code>java.lang.String</code>. */ public interface JvmType extends Comparable<JvmType> { /** * <p> * This method returns the list of generic variables used in this type. So, * for example, suppose we have: * </p> * * <pre> * T = java.lang.ArrayList<? extends S> * </pre> * * <p> * Then, <code>T.usedVariables()=[S]</code>. * </p> * * @return */ public List<JvmType.Variable> usedVariables(); /** * The Primitive type abstracts all the primitive types. */ public interface Primitive extends JvmType {} /** * The Reference type abstracts all the reference types, including class * types, array types, variable and wildcard types. */ public interface Reference extends JvmType {} /** * The Null type is a special type given to the null value. We require that * Null is a subtype of any Reference. */ public static class Null implements Reference { public String toString() { return "null"; } public boolean equals(Object o) { return o instanceof JvmType.Null; } public int hashCode() { return 0; } public int compareTo(JvmType t) { if(t instanceof JvmType.Null) { return 0; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * The Void type is used to represent "void" types, found in method * declarations. * * @author David J. Pearce * */ public static class Void implements Primitive { public String toString() { return "void"; } public boolean equals(Object o) { return o instanceof JvmType.Void; } public int hashCode() { return 1; } public int compareTo(JvmType t) { if (t instanceof JvmType.Null) { return 1; } else if (t instanceof JvmType.Void) { return 0; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "boolean" * @author David J. Pearce * */ public static class Bool implements Primitive { public String toString() { return "boolean"; } public boolean equals(Object o) { return o instanceof JvmType.Bool; } public int hashCode() { return 2; } public int compareTo(JvmType t) { if (t instanceof JvmType.Null || t instanceof JvmType.Void) { return 1; } else if (t instanceof JvmType.Bool) { return 0; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "byte" * @author David J. Pearce * */ public static class Byte implements Primitive { public String toString() { return "byte"; } public boolean equals(Object o) { return o instanceof JvmType.Byte; } public int hashCode() { return 3; } public int compareTo(JvmType t) { if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool) { return 1; } else if (t instanceof JvmType.Byte) { return 0; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "char" * @author David J. Pearce * */ public static class Char implements Primitive { public String toString() { return "char"; } public boolean equals(Object o) { return o instanceof JvmType.Char; } public int hashCode() { return 4; } public int compareTo(JvmType t) { if (t instanceof JvmType.Char) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "short" * @author David J. Pearce * */ public static class Short implements Primitive { public String toString() { return "short"; } public boolean equals(Object o) { return o instanceof JvmType.Short; } public int hashCode() { return 5; } public int compareTo(JvmType t) { if (t instanceof JvmType.Short) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte || t instanceof JvmType.Char) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "int" * @author David J. Pearce * */ public static class Int implements Primitive { public String toString() { return "int"; } public boolean equals(Object o) { return o instanceof JvmType.Int; } public int hashCode() { return 6; } public int compareTo(JvmType t) { if (t instanceof JvmType.Int) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte || t instanceof JvmType.Char || t instanceof JvmType.Short) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "long" * @author David J. Pearce * */ public static class Long implements Primitive { public String toString() { return "long"; } public boolean equals(Object o) { return o instanceof JvmType.Long; } public int hashCode() { return 7; } public int compareTo(JvmType t) { if (t instanceof JvmType.Long) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte || t instanceof JvmType.Char || t instanceof JvmType.Int) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "float" * @author David J. Pearce * */ public static class Float implements Primitive { public String toString() { return "float"; } public boolean equals(Object o) { return o instanceof JvmType.Float; } public int hashCode() { return 8; } public int compareTo(JvmType t) { if (t instanceof JvmType.Float) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte || t instanceof JvmType.Char || t instanceof JvmType.Int || t instanceof JvmType.Long) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * Represents the Java type "double" * @author David J. Pearce * */ public static class Double implements Primitive { public String toString() { return "double"; } public boolean equals(Object o) { return o instanceof JvmType.Double; } public int hashCode() { return 9; } public int compareTo(JvmType t) { if (t instanceof JvmType.Double) { return 0; } else if (t instanceof JvmType.Null || t instanceof JvmType.Void || t instanceof JvmType.Bool || t instanceof JvmType.Byte || t instanceof JvmType.Char || t instanceof JvmType.Int || t instanceof JvmType.Long || t instanceof JvmType.Float) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return new ArrayList<JvmType.Variable>(); } } /** * The Array type captures array types! The elementType gives the types of * the elements held in the array. For example, in "int[]", the element type * is int. * * @author David J. Pearce */ public static class Array implements Reference { private final JvmType element; public Array(JvmType element) { if (element == null) { throw new IllegalArgumentException( "Supplied element type cannot be null."); } this.element = element; } public JvmType element() { return element; } public String toString() { return element + "[]"; } public boolean equals(Object o) { if(o instanceof JvmType.Array) { JvmType.Array a = (JvmType.Array) o; return element.equals(a.element); } return false; } public int hashCode() { return 1 + element.hashCode(); } public int compareTo(JvmType t) { if (t instanceof JvmType.Array) { return element.compareTo(((JvmType.Array) t).element()); } else if (t instanceof JvmType.Primitive) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { return element.usedVariables(); } } /** * This represents a reference to a class. E.g. java.lang.String * * @author David J. Pearce * */ public static class Clazz implements Reference { private final String pkg; private final List<Pair<String, List<JvmType.Reference>>> components; public Clazz(String pkg, List<Pair<String, List<JvmType.Reference>>> components) { if (components == null) { throw new IllegalArgumentException( "Supplied class components type cannot be null."); } this.pkg = pkg; this.components = components; } public Clazz(String pkg, String... components) { this.pkg = pkg; this.components = new ArrayList<Pair<String, List<JvmType.Reference>>>(); for (String c : components) { this.components.add(new Pair<String, List<JvmType.Reference>>( c, new ArrayList<JvmType.Reference>())); } } public List<Pair<String, List<JvmType.Reference>>> components() { return components; } public Pair<String, List<JvmType.Reference>> lastComponent() { return components.get(components.size()-1); } /** * Return the package. If no package, then the value is simply "", * rather than null. * * @return */ public String pkg() { return pkg; } public String toString() { String r = pkg; boolean firstTime = pkg.length() == 0; for (Pair<String, List<JvmType.Reference>> n : components) { if (!firstTime) { r += "."; } firstTime = false; r += n.first(); List<JvmType.Reference> typeArgs = n.second(); if (typeArgs != null && typeArgs.size() > 0) { r += "<"; boolean innerFirstTime = true; for (JvmType t : typeArgs) { if (!innerFirstTime) { r += ", "; } innerFirstTime = false; r += t; } r += ">"; } } return r; } public boolean equals(Object o) { if(o instanceof JvmType.Clazz) { JvmType.Clazz c = (JvmType.Clazz) o; return pkg.equals(c.pkg) && components.equals(c.components); } return false; } public int hashCode() { int hc = 0; for (Pair<String, List<JvmType.Reference>> n : components) { hc ^= n.first().hashCode(); } return hc; } public int compareTo(JvmType t) { if (t instanceof JvmType.Clazz) { JvmType.Clazz tc = (JvmType.Clazz) t; int pct = pkg.compareTo(tc.pkg); if(pct != 0) { return pct; } if(components.size() < tc.components.size()) { return -1; } else if(components.size() == tc.components.size()) { return 1; } for(int i=0;i!=components.size();++i) { Pair<String,List<JvmType.Reference>> t1 = components.get(i); Pair<String,List<JvmType.Reference>> t2 = tc.components.get(i); int fct = t1.first().compareTo(t2.first()); if(fct != 0) { return fct; } if(t1.second().size() < t2.second().size()) { return -1; } else if(t1.second().size() > t2.second().size()) { return 1; } for(int j=0;j!=t1.second().size();++j) { JvmType.Reference r1 = t1.second().get(j); JvmType.Reference r2 = t2.second().get(j); int rct = r1.compareTo(r2); if(rct != 0) { return rct; } } } return 0; } else if (t instanceof JvmType.Primitive || t instanceof JvmType.Array) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { ArrayList<JvmType.Variable> ls = new ArrayList(); for(Pair<String,List<JvmType.Reference>> p : components) { for(JvmType.Reference r : p.second()) { ls.addAll(r.usedVariables()); } } return ls; } } /** * This represents the special "?" type. As used, for example, in the * following method declaration: * * void printAll(Collection<? extends MyClass> { ... } * * @author David J. Pearce * */ public static class Wildcard implements Reference { private final JvmType.Reference lowerBound; private final JvmType.Reference upperBound; public Wildcard(JvmType.Reference lowerBound, JvmType.Reference upperBound) { this.lowerBound = lowerBound; this.upperBound = upperBound; } /** * Return the upper bound of this wildcard. This will be null if there * is none. * * @return */ public JvmType.Reference upperBound() { return upperBound; } /** * Return the lower bound of this wildcard. This will be null if there * is none. * * @return */ public JvmType.Reference lowerBound() { return lowerBound; } public boolean equals(Object o) { if (o instanceof Wildcard) { Wildcard w = (Wildcard) o; boolean lb; if(lowerBound == null) { lb = w.lowerBound == null; } else { lb = lowerBound.equals(w.lowerBound); } boolean ub; if(upperBound == null) { ub = w.upperBound == null; } else { ub = upperBound.equals(w.upperBound); } return lb && ub; } return false; } public String toString() { String r = "?"; if(lowerBound != null) { r += " extends " + lowerBound; } if(upperBound != null) { r += " super " + upperBound; } return r; } public int hashCode() { int hc = 0; if(lowerBound != null) { hc ^= lowerBound.hashCode(); } if(upperBound != null) { hc ^= upperBound.hashCode(); } return hc; } public int compareTo(JvmType t) { if (t instanceof JvmType.Wildcard) { JvmType.Wildcard tw = (JvmType.Wildcard) t; if(lowerBound == null && tw.lowerBound != null) { return -1; } else if(lowerBound != null && tw.lowerBound == null) { return 1; } if(upperBound == null && tw.upperBound != null) { return -1; } else if(upperBound != null && tw.upperBound == null) { return 1; } if(lowerBound != null) { int lbct = lowerBound.compareTo(tw.lowerBound); if(lbct != 0) { return lbct; } } if(upperBound != null) { int lbct = upperBound.compareTo(tw.upperBound); if(lbct != 0) { return lbct; } } return 0; } else if (t instanceof JvmType.Primitive || t instanceof JvmType.Array || t instanceof JvmType.Clazz) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { ArrayList<JvmType.Variable> ls = new ArrayList(); if(lowerBound != null) { ls.addAll(lowerBound.usedVariables()); } if(upperBound != null) { ls.addAll(upperBound.usedVariables()); } return ls; } } /** * Represents a Generic type variable. For example, the <code>T</code> in * class <code>ArrayList<T> { ... }</code> * * @author David J. Pearce * */ public static class Variable implements Reference { private final String variable; private final JvmType.Reference lowerBound; public Variable(String variable, JvmType.Reference lowerBound) { if(lowerBound == null) { throw new IllegalArgumentException("Type.Variable lowerBound cannot be null"); } this.variable = variable; this.lowerBound = lowerBound; } public String variable() { return variable; } public JvmType.Reference lowerBound() { return lowerBound; } public boolean equals(Object o) { if (o instanceof Variable) { Variable v = (Variable) o; return variable.equals(v.variable) && lowerBound.equals(v.lowerBound); } return false; } public String toString() { if(lowerBound == null) { return variable; } else { return variable + " extends " + lowerBound; } } public int hashCode() { return variable.hashCode(); } public int compareTo(JvmType t) { if (t instanceof JvmType.Variable) { JvmType.Variable tv = (JvmType.Variable) t; int vct = variable.compareTo(tv.variable); if(vct != 0) { return vct; } if(lowerBound == null && tv.lowerBound != null) { return -1; } else if(lowerBound != null && tv.lowerBound == null) { return 1; } else if (lowerBound != null) { return lowerBound.compareTo(tv.lowerBound); } return 0; } else if (t instanceof JvmType.Primitive || t instanceof JvmType.Array || t instanceof JvmType.Clazz || t instanceof JvmType.Wildcard) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { ArrayList<JvmType.Variable> ls = new ArrayList(); ls.add(this); if(lowerBound != null) { ls.addAll(lowerBound.usedVariables()); } return ls; } } /** * An intersection type represents a (unknown) type which known to be a * subtype of several types. For example, given types <code>T1</code> and * <code>T2</code>, then their intersection type is <code>T1 & T2</code> * . The intersection type represents an object which is *both* an instance * of T1 and an instance of T2. Thus, we always have that * <code>T1 :> T1 & T2</code> and <code>T2 :> T1 & T2</code>. * * @author David J. Pearce */ public static class Intersection implements Reference { private final ArrayList<JvmType.Reference> bounds; public Intersection(List<JvmType.Reference> bounds) { if (bounds == null) { throw new IllegalArgumentException( "Supplied bounds cannot be null."); } else if(bounds.size() <= 1) { throw new IllegalArgumentException( "Require more than one bound."); } this.bounds = new ArrayList<JvmType.Reference>(bounds); } public List<JvmType.Reference> bounds() { return bounds; } public String toString() { String r = ""; if(bounds.size() > 1) { r += "("; } boolean firstTime = true; for(JvmType.Reference b : bounds) { if(!firstTime) { r += " & "; } firstTime = false; r += b.toString(); } if(bounds.size() > 1) { r += ")"; } return r; } public boolean equals(Object o) { if(o instanceof Intersection) { Intersection t = (Intersection) o; if(t.bounds.size() == bounds.size()) { for(int i=0;i!=bounds.size();++i) { if(!t.bounds.get(i).equals(bounds.get(i))) { return false; } } return true; } } return false; } public int hashCode() { int hc = 0; for(JvmType.Reference r : bounds) { hc ^= r.hashCode(); } return hc; } public int compareTo(JvmType t) { if (t instanceof JvmType.Intersection) { JvmType.Intersection tv = (JvmType.Intersection) t; if(bounds.size() < tv.bounds.size()) { return -1; } else if(bounds.size() > tv.bounds.size()) { return 1; } for(int i=0;i!=bounds.size();++i) { JvmType.Reference r1 = bounds.get(i); JvmType.Reference r2 = tv.bounds.get(i); int rct = r1.compareTo(r2); if(rct != 0) { return rct; } } return 0; } else if (t instanceof JvmType.Reference) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { ArrayList<JvmType.Variable> ls = new ArrayList(); for(JvmType.Reference r : bounds) { ls.addAll(r.usedVariables()); } return ls; } } /** * Represents the type of a method. For example, the following method * * void m(int x) { ... } has type "void(int)" * @author David J. Pearce * */ public static class Function implements JvmType { private final List<JvmType> parameters; private final JvmType returnType; private final List<JvmType.Variable> typeArgs; public Function(JvmType returnType, JvmType... parameters) { if (returnType == null) { throw new IllegalArgumentException( "Supplied return type cannot be null."); } this.returnType = returnType; this.parameters = new ArrayList<JvmType>(); this.typeArgs = new ArrayList(); for(JvmType t : parameters) { this.parameters.add(t); } } public Function(JvmType returnType, List<JvmType> parameters) { if (returnType == null) { throw new IllegalArgumentException( "Supplied return type cannot be null."); } this.returnType = returnType; this.parameters = parameters; this.typeArgs = new ArrayList(); } public Function(JvmType returnType, List<JvmType> parameters, List<JvmType.Variable> typeArgs) { if (returnType == null) { throw new IllegalArgumentException( "Supplied return type cannot be null."); } this.returnType = returnType; this.parameters = parameters; this.typeArgs = typeArgs; } public JvmType returnType() { return returnType; } public List<JvmType> parameterTypes() { return parameters; } public List<JvmType.Variable> typeArguments() { return typeArgs; } public boolean equals(Object o) { if (o instanceof Function) { Function f = (Function) o; return returnType.equals(f.returnType) && parameters.equals(f.parameters) && typeArgs.equals(f.typeArgs); } return false; } public String toString() { String r=""; boolean firstTime; if(typeArgs.size() > 0) { r += "<"; firstTime=true; for(JvmType.Variable v : typeArgs) { if(!firstTime) { r += ", "; } firstTime=false; r += v; } r += "> "; } r += returnType; r += " ("; firstTime=true; for(JvmType t : parameters) { if(!firstTime) { r += ", "; } firstTime=false; r += t; } r+= ")"; return r; } public int hashCode() { int hc = 0; for(JvmType t : parameters) { hc ^= t.hashCode(); } return hc; } public int compareTo(JvmType t) { if (t instanceof JvmType.Function) { JvmType.Function tf = (JvmType.Function) t; if(parameters.size() < tf.parameters.size()) { return -1; } else if(parameters.size() > tf.parameters.size()) { return 1; } for(int i=0;i!=parameters.size();++i) { JvmType p1 = parameters.get(i); JvmType p2 = tf.parameters.get(i); int pct = p1.compareTo(p2); if(pct != 0) { return pct; } } return returnType.compareTo(tf.returnType); } else if (t instanceof JvmType.Reference) { return 1; } else { return -1; } } public List<JvmType.Variable> usedVariables() { ArrayList<JvmType.Variable> ls = new ArrayList(); ls.addAll(returnType.usedVariables()); for(JvmType r : parameters) { ls.addAll(r.usedVariables()); } ls.addAll(typeArgs); return ls; } } }