blob: 9ce007b4f35a96aeee3d12bb6abd34cddf99d4d9 [file] [log] [blame]
package org.aspectj.apache.bcel.generic;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache BCEL" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache BCEL", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.ConstantsInitializer;
import org.aspectj.apache.bcel.classfile.ClassFormatException;
import org.aspectj.apache.bcel.classfile.Utility;
/**
* Abstract super class for all possible java types, namely basic types such as int, object types like String and array types, e.g.
* int[]
*
* @version $Id: Type.java,v 1.14 2011/09/28 01:14:54 aclement Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*
* modified: AndyClement 2-mar-05: Removed unnecessary static and optimized
*/
public abstract class Type {
protected byte type;
protected String signature;
/* Predefined constants */
public static final BasicType VOID = new BasicType(Constants.T_VOID);
public static final BasicType BOOLEAN = new BasicType(Constants.T_BOOLEAN);
public static final BasicType INT = new BasicType(Constants.T_INT);
public static final BasicType SHORT = new BasicType(Constants.T_SHORT);
public static final BasicType BYTE = new BasicType(Constants.T_BYTE);
public static final BasicType LONG = new BasicType(Constants.T_LONG);
public static final BasicType DOUBLE = new BasicType(Constants.T_DOUBLE);
public static final BasicType FLOAT = new BasicType(Constants.T_FLOAT);
public static final BasicType CHAR = new BasicType(Constants.T_CHAR);
public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
public static final ObjectType STRING = new ObjectType("java.lang.String");
public static final ArrayType OBJECT_ARRAY = new ArrayType("java.lang.Object",1);
public static final ArrayType STRING_ARRAY = new ArrayType("java.lang.String",1);
public static final ArrayType CLASS_ARRAY = new ArrayType("java.lang.Class",1);
public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
public static final ObjectType STRINGBUILDER = new ObjectType("java.lang.StringBuilder");
public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");
public static final ObjectType CLASS = new ObjectType("java.lang.Class");
public static final ObjectType INTEGER = new ObjectType("java.lang.Integer");
public static final ObjectType EXCEPTION = new ObjectType("java.lang.Exception");
public static final ObjectType LIST = new ObjectType("java.util.List");
public static final ObjectType ITERATOR = new ObjectType("java.util.Iterator");
public static final Type[] NO_ARGS = new Type[0];
public static final ReferenceType NULL = new ReferenceType() {
};
public static final Type UNKNOWN = new Type(Constants.T_UNKNOWN, "<unknown object>") {
};
public static final Type[] STRINGARRAY1 = new Type[] { STRING };
public static final Type[] STRINGARRAY2 = new Type[] { STRING, STRING };
public static final Type[] STRINGARRAY3 = new Type[] { STRING, STRING, STRING };
public static final Type[] STRINGARRAY4 = new Type[] { STRING, STRING, STRING, STRING };
public static final Type[] STRINGARRAY5 = new Type[] { STRING, STRING, STRING, STRING, STRING };
public static final Type[] STRINGARRAY6 = new Type[] { STRING, STRING, STRING, STRING, STRING, STRING };
public static final Type[] STRINGARRAY7 = new Type[] { STRING, STRING, STRING, STRING, STRING, STRING, STRING };
private static Map<String, Type> commonTypes = new HashMap<String, Type>();
static {
commonTypes.put(STRING.getSignature(), STRING);
commonTypes.put(THROWABLE.getSignature(), THROWABLE);
commonTypes.put(VOID.getSignature(), VOID);
commonTypes.put(BOOLEAN.getSignature(), BOOLEAN);
commonTypes.put(BYTE.getSignature(), BYTE);
commonTypes.put(SHORT.getSignature(), SHORT);
commonTypes.put(CHAR.getSignature(), CHAR);
commonTypes.put(INT.getSignature(), INT);
commonTypes.put(LONG.getSignature(), LONG);
commonTypes.put(DOUBLE.getSignature(), DOUBLE);
commonTypes.put(FLOAT.getSignature(), FLOAT);
commonTypes.put(CLASS.getSignature(), CLASS);
commonTypes.put(OBJECT.getSignature(), OBJECT);
commonTypes.put(STRING_ARRAY.getSignature(), STRING_ARRAY);
commonTypes.put(CLASS_ARRAY.getSignature(), CLASS_ARRAY);
commonTypes.put(OBJECT_ARRAY.getSignature(), OBJECT_ARRAY);
commonTypes.put(INTEGER.getSignature(), INTEGER);
commonTypes.put(EXCEPTION.getSignature(), EXCEPTION);
commonTypes.put(STRINGBUFFER.getSignature(), STRINGBUFFER);
commonTypes.put(STRINGBUILDER.getSignature(), STRINGBUILDER);
commonTypes.put(LIST.getSignature(), LIST);
commonTypes.put(ITERATOR.getSignature(), ITERATOR);
ConstantsInitializer.initialize(); // needs calling because it will not have run properly the first time
}
protected Type(byte t, String s) {
type = t;
signature = s;
}
public String getSignature() {
return signature;
}
public byte getType() {
return type;
}
/**
* @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
*/
public int getSize() {
switch (type) {
case Constants.T_DOUBLE:
case Constants.T_LONG:
return 2;
case Constants.T_VOID:
return 0;
default:
return 1;
}
}
/**
* @return Type string, e.g. 'int[]'
*/
@Override
public String toString() {
return ((this.equals(Type.NULL) || (type >= Constants.T_UNKNOWN))) ? signature : Utility
.signatureToString(signature, false);
}
public static final Type getType(String signature) {
Type t = commonTypes.get(signature);
if (t != null) {
return t;
}
byte type = Utility.typeOfSignature(signature);
if (type <= Constants.T_VOID) {
return BasicType.getType(type);
} else if (type == Constants.T_ARRAY) {
int dim = 0;
do {
dim++;
} while (signature.charAt(dim) == '[');
// Recurse, but just once, if the signature is ok
Type componentType = getType(signature.substring(dim));
return new ArrayType(componentType, dim);
} else { // type == T_REFERENCE
// generics awareness
int nextAngly = signature.indexOf('<');
// Format is 'Lblahblah;'
int index = signature.indexOf(';'); // Look for closing ';'
String typeString = null;
if (nextAngly == -1 || nextAngly > index) {
typeString = signature.substring(1, index).replace('/', '.');
} else {
boolean endOfSigReached = false;
int posn = nextAngly;
int genericDepth = 0;
while (!endOfSigReached) {
switch (signature.charAt(posn++)) {
case '<':
genericDepth++;
break;
case '>':
genericDepth--;
break;
case ';':
if (genericDepth == 0) {
endOfSigReached = true;
}
break;
default:
}
}
index = posn - 1;
typeString = signature.substring(1, nextAngly).replace('/', '.');
}
// ObjectType doesn't currently store parameterized info
return new ObjectType(typeString);
}
}
/**
* Convert signature to a Type object.
*
* @param signature signature string such as Ljava/lang/String;
* @return type object
*/
public static final TypeHolder getTypeInternal(String signature) throws StringIndexOutOfBoundsException {
byte type = Utility.typeOfSignature(signature);
if (type <= Constants.T_VOID) {
return new TypeHolder(BasicType.getType(type), 1);
} else if (type == Constants.T_ARRAY) {
int dim = 0;
do {
dim++;
} while (signature.charAt(dim) == '[');
// Recurse, but just once, if the signature is ok
TypeHolder th = getTypeInternal(signature.substring(dim));
return new TypeHolder(new ArrayType(th.getType(), dim), dim + th.getConsumed());
} else { // type == T_REFERENCE
// Format is 'Lblahblah;'
int index = signature.indexOf(';'); // Look for closing ';'
if (index < 0) {
throw new ClassFormatException("Invalid signature: " + signature);
}
// generics awareness
int nextAngly = signature.indexOf('<');
String typeString = null;
if (nextAngly == -1 || nextAngly > index) {
typeString = signature.substring(1, index).replace('/', '.');
} else {
boolean endOfSigReached = false;
int posn = nextAngly;
int genericDepth = 0;
while (!endOfSigReached) {
switch (signature.charAt(posn++)) {
case '<':
genericDepth++;
break;
case '>':
genericDepth--;
break;
case ';':
if (genericDepth == 0) {
endOfSigReached = true;
}
break;
default:
}
}
index = posn - 1;
typeString = signature.substring(1, nextAngly).replace('/', '.');
}
// ObjectType doesn't currently store parameterized info
return new TypeHolder(new ObjectType(typeString), index + 1);
}
}
/**
* Convert return value of a method (signature) to a Type object.
*
* @param signature signature string such as (Ljava/lang/String;)V
* @return return type
*/
public static Type getReturnType(String signature) {
try {
// Read return type after ')'
int index = signature.lastIndexOf(')') + 1;
return getType(signature.substring(index));
} catch (StringIndexOutOfBoundsException e) { // Should never occur
throw new ClassFormatException("Invalid method signature: " + signature);
}
}
/**
* Convert arguments of a method (signature) to an array of Type objects.
*
* @param signature signature string such as (Ljava/lang/String;)V
* @return array of argument types
*/
// OPTIMIZE crap impl
public static Type[] getArgumentTypes(String signature) {
List<Type> argumentTypes = new ArrayList<Type>();
int index;
Type[] types;
try { // Read all declarations between for `(' and `)'
if (signature.charAt(0) != '(') {
throw new ClassFormatException("Invalid method signature: " + signature);
}
index = 1; // current string position
while (signature.charAt(index) != ')') {
TypeHolder th = getTypeInternal(signature.substring(index));
argumentTypes.add(th.getType());
index += th.getConsumed(); // update position
}
} catch (StringIndexOutOfBoundsException e) { // Should never occur
throw new ClassFormatException("Invalid method signature: " + signature);
}
types = new Type[argumentTypes.size()];
argumentTypes.toArray(types);
return types;
}
/**
* Work out the type of each argument in the signature and return the cumulative sizes of all the types (size means number of
* stack slots it consumes, eg double=2, int=1). Unlike the call above, this does minimal unpacking
*/
public static int getArgumentSizes(String signature) {
int size = 0;
if (signature.charAt(0) != '(') {
throw new ClassFormatException("Invalid method signature: " + signature);
}
int index = 1; // current string position
try {
while (signature.charAt(index) != ')') {
byte type = Utility.typeOfSignature(signature.charAt(index));
if (type <= Constants.T_VOID) {
size += BasicType.getType(type).getSize();
index++;
} else if (type == Constants.T_ARRAY) {
int dim = 0;
do {
dim++;
} while (signature.charAt(dim + index) == '[');
TypeHolder th = getTypeInternal(signature.substring(dim + index));
size += 1;
index += dim + th.getConsumed();
} else { // type == T_REFERENCE
// Format is 'Lblahblah;'
int index2 = signature.indexOf(';', index); // Look for closing ';'
// generics awareness
int nextAngly = signature.indexOf('<', index);
if (nextAngly == -1 || nextAngly > index2) {
} else {
boolean endOfSigReached = false;
int posn = nextAngly;
int genericDepth = 0;
while (!endOfSigReached) {
switch (signature.charAt(posn++)) {
case '<':
genericDepth++;
break;
case '>':
genericDepth--;
break;
case ';':
if (genericDepth == 0) {
endOfSigReached = true;
}
break;
default:
}
}
index2 = posn - 1;
}
size++;
index = index2 + 1;
}
}
} catch (StringIndexOutOfBoundsException e) { // Should never occur
throw new ClassFormatException("Invalid method signature: " + signature);
}
return size;
}
/**
* Return the size of the type expressed in the signature. The signature should contain only one type.
*/
public static int getTypeSize(String signature) {
byte type = Utility.typeOfSignature(signature.charAt(0));
if (type <= Constants.T_VOID) {
return BasicType.getType(type).getSize();
} else if (type == Constants.T_ARRAY) {
return 1;
} else { // type == T_REFERENCE
return 1;
}
}
/**
* Convert runtime java.lang.Class to BCEL Type object.
*
* @param cl Java class
* @return corresponding Type object
*/
public static Type getType(java.lang.Class cl) {
if (cl == null) {
throw new IllegalArgumentException("Class must not be null");
}
/*
* That's an amazingly easy case, because getName() returns the signature. That's what we would have liked anyway.
*/
if (cl.isArray()) {
return getType(cl.getName());
} else if (cl.isPrimitive()) {
if (cl == Integer.TYPE) {
return INT;
} else if (cl == Void.TYPE) {
return VOID;
} else if (cl == Double.TYPE) {
return DOUBLE;
} else if (cl == Float.TYPE) {
return FLOAT;
} else if (cl == Boolean.TYPE) {
return BOOLEAN;
} else if (cl == Byte.TYPE) {
return BYTE;
} else if (cl == Short.TYPE) {
return SHORT;
} else if (cl == Byte.TYPE) {
return BYTE;
} else if (cl == Long.TYPE) {
return LONG;
} else if (cl == Character.TYPE) {
return CHAR;
} else {
throw new IllegalStateException("Ooops, what primitive type is " + cl);
}
} else { // "Real" class
return new ObjectType(cl.getName());
}
}
public static String getSignature(java.lang.reflect.Method meth) {
StringBuffer sb = new StringBuffer("(");
Class[] params = meth.getParameterTypes(); // avoid clone
for (int j = 0; j < params.length; j++) {
sb.append(getType(params[j]).getSignature());
}
sb.append(")");
sb.append(getType(meth.getReturnType()).getSignature());
return sb.toString();
}
public static String getSignature(java.lang.reflect.Constructor<?> cons) {
StringBuffer sb = new StringBuffer("(");
Class<?>[] params = cons.getParameterTypes(); // avoid clone
for (int j = 0; j < params.length; j++) {
sb.append(getType(params[j]).getSignature());
}
sb.append(")V");
return sb.toString();
}
public static class TypeHolder {
private Type t;
private int consumed;
public Type getType() {
return t;
}
public int getConsumed() {
return consumed;
}
public TypeHolder(Type t, int i) {
this.t = t;
this.consumed = i;
}
}
}