// 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.*;
public class JvmTypes {
/**
* Given a primitive type, determine the equivalent boxed type. For example,
* the primitive type int yields the type java.lang.Integer.
*
* @param p
* @return
*/
public static JvmType.Clazz boxedType(JvmType.Primitive p) {
if(p instanceof JvmType.Bool) {
return new JvmType.Clazz("java.lang","Boolean");
} else if(p instanceof JvmType.Byte) {
return new JvmType.Clazz("java.lang","Byte");
} else if(p instanceof JvmType.Char) {
return new JvmType.Clazz("java.lang","Character");
} else if(p instanceof JvmType.Short) {
return new JvmType.Clazz("java.lang","Short");
} else if(p instanceof JvmType.Int) {
return new JvmType.Clazz("java.lang","Integer");
} else if(p instanceof JvmType.Long) {
return new JvmType.Clazz("java.lang","Long");
} else if(p instanceof JvmType.Float) {
return new JvmType.Clazz("java.lang","Float");
} else {
return new JvmType.Clazz("java.lang","Double");
}
}
/**
* Given a primitive wrapper class (i.e. a boxed type), return the unboxed
* equivalent. For example, java.lang.Integer yields Type.Int, whilst
* java.lang.Boolean yields Type.Bool.
*
* @param p
* @return
*/
public static JvmType.Primitive unboxedType(JvmType.Clazz p) {
String type = p.components().get(p.components().size()-1).first();
if(type.equals("Boolean")) {
return new JvmType.Bool();
} else if(type.equals("Byte")) {
return new JvmType.Byte();
} else if(type.equals("Character")) {
return new JvmType.Char();
} else if(type.equals("Short")) {
return new JvmType.Short();
} else if(type.equals("Integer")) {
return new JvmType.Int();
} else if(type.equals("Long")) {
return new JvmType.Long();
} else if(type.equals("Float")) {
return new JvmType.Float();
} else if(type.equals("Double")) {
return new JvmType.Double();
} else {
throw new RuntimeException("unknown boxed type \"" + p.toString()
+ "\" encountered.");
}
}
/**
* Determine whether or not the given type is a wrapper for a primitive
* type. E.g. java.lang.Integer is a wrapper for int.
*
* @param t
* @return
*/
public static boolean isBoxedType(JvmType t) {
if(!(t instanceof JvmType.Clazz)) {
return false;
}
JvmType.Clazz ref = (JvmType.Clazz) t;
if(ref.pkg().equals("java.lang") && ref.components().size() == 1) {
String s = ref.components().get(0).first();
if(s.equals("Byte") || s.equals("Character") || s.equals("Short") ||
s.equals("Integer") || s.equals("Long")
|| s.equals("Float") || s.equals("Double")
|| s.equals("Boolean")) {
return true;
}
}
return false;
}
/**
* Determine whether or not the given type is a wrapper for a primitive
* type. E.g. java.lang.Integer is a wrapper for int.
*
* @param t
* --- type to test
* @param wrapper
* --- specific wrapper class to look for (i.e. Integer, Boolean,
* Character).
* @return
*/
public static boolean isBoxedTypeOf(JvmType t, String wrapper) {
if(!(t instanceof JvmType.Clazz)) {
return false;
}
JvmType.Clazz ref = (JvmType.Clazz) t;
if(ref.pkg().equals("java.lang") && ref.components().size() == 1) {
String s = ref.components().get(0).first();
if(s.equals(wrapper)) {
return true;
}
}
return false;
}
/**
* This method accepts a binding from type variables to concrete types, and
* then substitutes each such variable occuring in the target type with its
* corresponding instantation. For example, suppose we have this binding:
*
* <pre>
* K -> String
* V -> Integer
* </pre>
*
* Then, substituting against <code>HashMap<K,V></code> yields
* <code>HashMap<String,Integer></code>.
*
* @param type
* @param binding
* @return
*/
public static JvmType.Reference substitute(JvmType.Reference type, Map<String,JvmType.Reference> binding) {
if (type instanceof JvmType.Variable) {
// Ok, we've reached a type variable, so we can now bind this with
// what we already have.
JvmType.Variable v = (JvmType.Variable) type;
JvmType.Reference r = binding.get(v.variable());
if(r == null) {
// if the variable is not part of the binding, then we simply do
// not do anything with it.
return v;
} else {
return r;
}
} else if(type instanceof JvmType.Wildcard) {
JvmType.Wildcard wc = (JvmType.Wildcard) type;
JvmType.Reference lb = wc.lowerBound();
JvmType.Reference ub = wc.upperBound();
if(lb != null) { lb = substitute(lb,binding); }
if(ub != null) { ub = substitute(ub,binding); }
return new JvmType.Wildcard(lb,ub);
} else if(type instanceof JvmType.Array) {
JvmType.Array at = (JvmType.Array) type;
if(at.element() instanceof JvmType.Reference) {
return new JvmType.Array(substitute((JvmType.Reference) at.element(),binding));
} else {
return type;
}
} else if(type instanceof JvmType.Clazz) {
JvmType.Clazz ct = (JvmType.Clazz) type;
ArrayList<Pair<String,List<JvmType.Reference>>> ncomponents = new ArrayList();
List<Pair<String,List<JvmType.Reference>>> components = ct.components();
for(Pair<String,List<JvmType.Reference>> c : components) {
ArrayList<JvmType.Reference> nc = new ArrayList<JvmType.Reference>();
for(JvmType.Reference r : c.second()) {
nc.add(substitute(r,binding));
}
ncomponents.add(new Pair(c.first(),nc));
}
return new JvmType.Clazz(ct.pkg(),ncomponents);
}
throw new RuntimeException("Cannot substitute against type " + type);
}
/**
* This method accepts a binding from type variables to concrete types, and
* then substitutes each such variable occuring in the target (function)
* type with its corresponding instantation. For example, suppose we have
* this binding:
*
* <pre>
* K -> String
* V -> Integer
* </pre>
*
* Then, substituting against <code>v f(K)</code> yields
* <code>Integer f(String)</code>.
*
* @param type
* @param binding
* @return
*/
public static JvmType.Function substitute(JvmType.Function type, Map<String,JvmType.Reference> binding) {
JvmType returnType = type.returnType();
if(returnType instanceof JvmType.Reference) {
returnType = substitute((JvmType.Reference) returnType,binding);
}
ArrayList<JvmType> paramTypes = new ArrayList<JvmType>();
for(JvmType t : type.parameterTypes()) {
if(t instanceof JvmType.Reference) {
t = substitute((JvmType.Reference)t,binding);
}
paramTypes.add(t);
}
ArrayList<JvmType.Variable> varTypes = new ArrayList<JvmType.Variable>();
for(JvmType.Variable v : type.typeArguments()) {
if(!binding.containsKey(v.variable())) {
varTypes.add(v);
}
}
return new JvmType.Function(returnType,paramTypes,varTypes);
}
/**
* Check wither a given type is a reference to java.lang.Object or not.
*
* @param t
* @return
*/
public static boolean isJavaLangObject(JvmType t) {
if(t instanceof JvmType.Clazz) {
JvmType.Clazz c = (JvmType.Clazz) t;
return c.pkg().equals("java.lang") && c.components().size() == 1
&& c.components().get(0).first().equals("Object");
}
return false;
}
/**
* Check wither a given type is a reference to java.lang.String or not.
*
* @param t
* @return
*/
public static boolean isJavaLangString(JvmType t) {
if(t instanceof JvmType.Clazz) {
JvmType.Clazz c = (JvmType.Clazz) t;
return c.pkg().equals("java.lang") && c.components().size() == 1
&& c.components().get(0).first().equals("String");
}
return false;
}
public static boolean isClass(String pkg, String clazz, JvmType t) {
if(t instanceof JvmType.Clazz) {
JvmType.Clazz c = (JvmType.Clazz) t;
return c.pkg().equals(pkg) && c.components().size() == 1
&& c.components().get(0).first().equals(clazz);
}
return false;
}
public static boolean isGeneric(JvmType t) {
if (t instanceof JvmType.Variable) {
return true;
} else if(t instanceof JvmType.Function) {
JvmType.Function ft = (JvmType.Function) t;
if(ft.typeArguments().size() > 0) {
return true;
} else {
for(JvmType p : ft.parameterTypes()) {
if(isGeneric(p)) {
return true;
}
}
}
return isGeneric(ft.returnType());
} else if (!(t instanceof JvmType.Clazz)) {
return false;
}
JvmType.Clazz ref = (JvmType.Clazz) t;
for(Pair<String, List<JvmType.Reference>> p : ref.components()) {
if(p.second().size() > 0) {
return true;
}
}
return false;
}
public static boolean isGenericArray(JvmType t) {
if(t instanceof JvmType.Array) {
JvmType et = ((JvmType.Array)t).element();
if(et instanceof JvmType.Variable) {
return true;
} else {
return isGenericArray(et);
}
}
return false;
}
/**
* Return the depth of array nesting. E.g. "int" has 0 depth, "int[]" has
* depth 1, "int[][]" has depth 2, etc.
*
* @param t
* @return
*/
public static int arrayDepth(JvmType t) {
if(t instanceof JvmType.Array) {
JvmType.Array _t = (JvmType.Array) t;
return 1 + arrayDepth(_t.element());
} else {
return 0;
}
}
/**
* Return type representing the enclosing class for the given type, or null
* if the given type is already outermost. For example, given
* <code>pkg.Test$inner</code> return <code>pkg.Test</code>.
*
* @param t
* @return
*/
public static JvmType.Clazz parentType(JvmType.Clazz t) {
List<Pair<String,List<JvmType.Reference>>> components = t.components();
if(components.size() == 0) {
return null;
}
ArrayList<Pair<String,List<JvmType.Reference>>> ncomponents = new ArrayList();
for(int i=0;i!=components.size()-1;++i) {
ncomponents.add(components.get(i));
}
return new JvmType.Clazz(t.pkg(),ncomponents);
}
/**
* The purpose of this method is to strip the generic information from a
* type.
*
* @param t
* @return
*/
public static JvmType stripGenerics(JvmType t) {
if(t instanceof JvmType.Clazz) {
return stripGenerics((JvmType.Clazz)t);
} else if(t instanceof JvmType.Function) {
return stripGenerics((JvmType.Function)t);
} else if(t instanceof JvmType.Variable) {
return stripGenerics((JvmType.Variable)t);
} else if(t instanceof JvmType.Wildcard) {
return stripGenerics((JvmType.Wildcard)t);
} else if(t instanceof JvmType.Intersection) {
return stripGenerics((JvmType.Intersection)t);
} else {
return t;
}
}
public static JvmType.Clazz stripGenerics(JvmType.Clazz ct) {
ArrayList<Pair<String,List<JvmType.Reference>>> ncomponents = new ArrayList();
for(Pair<String,List<JvmType.Reference>> p : ct.components()) {
ncomponents.add(new Pair(p.first(),new ArrayList()));
}
return new JvmType.Clazz(ct.pkg(),ncomponents);
}
public static JvmType.Function stripGenerics(JvmType.Function ft) {
ArrayList<JvmType> params = new ArrayList<JvmType>();
for(JvmType t : ft.parameterTypes()) {
params.add(stripGenerics(t));
}
return new JvmType.Function(stripGenerics(ft.returnType()),params);
}
public static JvmType stripGenerics(JvmType.Variable vt) {
if(vt.lowerBound() == null) {
return JAVA_LANG_OBJECT;
} else {
return stripGenerics(vt.lowerBound());
}
}
public static JvmType stripGenerics(JvmType.Wildcard wt) {
if(wt.lowerBound() == null) {
return JAVA_LANG_OBJECT;
} else {
return stripGenerics(wt.lowerBound());
}
}
public static JvmType.Intersection stripGenerics(JvmType.Intersection wt) {
ArrayList<JvmType.Reference> bounds = new ArrayList();
for(JvmType.Reference t : wt.bounds()) {
bounds.add((JvmType.Reference) stripGenerics(t));
}
return new JvmType.Intersection(bounds);
}
/**
* The following are provided for performance reasons, particularly to help
* reduce the memory footprint during compilation.
*/
public static final JvmType.Void VOID = new JvmType.Void();
public static final JvmType.Null NULL = new JvmType.Null();
public static final JvmType.Bool BOOL = new JvmType.Bool();
public static final JvmType.Byte BYTE = new JvmType.Byte();
public static final JvmType.Char CHAR = new JvmType.Char();
public static final JvmType.Short SHORT = new JvmType.Short();
public static final JvmType.Int INT = new JvmType.Int();
public static final JvmType.Long LONG = new JvmType.Long();
public static final JvmType.Float FLOAT = new JvmType.Float();
public static final JvmType.Double DOUBLE = new JvmType.Double();
public static final JvmType.Clazz JAVA_LANG_OBJECT = new JvmType.Clazz("java.lang","Object");
public static final JvmType.Clazz JAVA_LANG_CLASS = new JvmType.Clazz("java.lang","Class");
public static final JvmType.Clazz JAVA_LANG_CLONEABLE = new JvmType.Clazz("java.lang","Cloneable");
public static final JvmType.Clazz JAVA_LANG_STRING = new JvmType.Clazz("java.lang","String");
public static final JvmType.Clazz JAVA_LANG_ENUM = new JvmType.Clazz("java.lang","Enum");
public static final JvmType.Clazz JAVA_LANG_BOOLEAN = new JvmType.Clazz("java.lang","Boolean");
public static final JvmType.Clazz JAVA_LANG_CHARACTER = new JvmType.Clazz("java.lang","Character");
public static final JvmType.Clazz JAVA_LANG_BYTE = new JvmType.Clazz("java.lang","Byte");
public static final JvmType.Clazz JAVA_LANG_SHORT = new JvmType.Clazz("java.lang","Short");
public static final JvmType.Clazz JAVA_LANG_INTEGER = new JvmType.Clazz("java.lang","Integer");
public static final JvmType.Clazz JAVA_LANG_LONG = new JvmType.Clazz("java.lang","Long");
public static final JvmType.Clazz JAVA_LANG_FLOAT = new JvmType.Clazz("java.lang","Float");
public static final JvmType.Clazz JAVA_LANG_DOUBLE = new JvmType.Clazz("java.lang","Double");
// io
public static final JvmType.Clazz JAVA_IO_SERIALIZABLE = new JvmType.Clazz("java.io","Serializable");
// util
public static final JvmType.Clazz JAVA_UTIL_ITERATOR = new JvmType.Clazz("java.util","Iterator");
// exceptions related types
public static final JvmType.Clazz JAVA_LANG_THROWABLE = new JvmType.Clazz("java.lang","Throwable");
public static final JvmType.Clazz JAVA_LANG_RUNTIMEEXCEPTION = new JvmType.Clazz("java.lang","RuntimeException");
public static final JvmType.Clazz JAVA_LANG_VIRTUALMACHINEERROR = new JvmType.Clazz("java.lang","VirtualMachineError");
public static final JvmType.Clazz JAVA_LANG_NULLPOINTEREXCEPTION = new JvmType.Clazz("java.lang","NullPointerException");
public static final JvmType.Clazz JAVA_LANG_ARITHMETICEXCEPTION = new JvmType.Clazz("java.lang","ArithmeticException");
}