// 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.attributes.*;
import jasm.util.Pair;
import java.util.*;
public class ClassFile {
// =========================================================================
// State
// =========================================================================
protected int version;
protected JvmType.Clazz type;
protected JvmType.Clazz superClazz;
protected List<JvmType.Clazz> interfaces;
protected List<Modifier> modifiers;
protected ArrayList<BytecodeAttribute> attributes;
protected ArrayList<Field> fields;
protected ArrayList<Method> methods;
// =========================================================================
// Constructors
// =========================================================================
public ClassFile(int version, JvmType.Clazz type, JvmType.Clazz superClazz,
List<JvmType.Clazz> interfaces, List<Modifier> modifiers, BytecodeAttribute... attributes) {
this.version = version;
this.type = type;
this.superClazz = superClazz;
this.interfaces = interfaces;
this.modifiers = modifiers;
this.fields = new ArrayList<Field>();
this.methods = new ArrayList<Method>();
this.attributes = new ArrayList<BytecodeAttribute>();
for(BytecodeAttribute a : attributes) {
this.attributes.add(a);
}
}
public ClassFile(int version, JvmType.Clazz type, JvmType.Clazz superClazz,
List<JvmType.Clazz> interfaces, List<Modifier> modifiers, Collection<BytecodeAttribute> attributes) {
this.version = version;
this.type = type;
this.superClazz = superClazz;
this.interfaces = interfaces;
this.modifiers = modifiers;
this.fields = new ArrayList<Field>();
this.methods = new ArrayList<Method>();
this.attributes = new ArrayList<BytecodeAttribute>(attributes);
}
// =========================================================================
// Accessors
// =========================================================================
public JvmType.Clazz type() {
return type;
}
public String name() {
return type.lastComponent().first();
}
public JvmType.Clazz superClass() {
return superClazz;
}
public List<JvmType.Clazz> interfaces() {
return interfaces;
}
public List<JvmType.Clazz> inners() {
// this needs to be fixed. Essentially, by looking for an InnerClasses
// attribute and then digging stuff out of it.
return new ArrayList<JvmType.Clazz>();
}
public List<BytecodeAttribute> attributes() {
return attributes;
}
public BytecodeAttribute attribute(String name) {
for(BytecodeAttribute a : attributes) {
if(a.name().equals(name)) {
return a;
}
}
return null;
}
public List<Modifier> modifiers() {
return modifiers;
}
public List<Field> fields() {
return fields;
}
public Field field(String name) {
for(Field f : fields) {
if(f.name().equals(name)) {
return f;
}
}
return null;
}
public List<Method> methods() {
return methods;
}
public List<Method> methods(String name) {
ArrayList<Method> r = new ArrayList<Method>();
for(Method m : methods) {
if(m.name().equals(name)) {
r.add(m);
}
}
return r;
}
public int version() {
return version;
}
/**
* Check whether this method has one of the "base" modifiers (e.g. static,
* public, private, etc). These are found in java.lang.reflect.Modifier.
*
* @param modifierClass
* @return true if it does!
*/
public boolean hasModifier(Class modifierClass) {
for(Modifier m : modifiers) {
if(m.getClass().equals(modifierClass)) {
return true;
}
}
return false;
}
/**
* Check whether this class is abstract
*
* @return true if this class is abstract
*/
public boolean isInterface() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Interface) {
return true;
}
}
return false;
}
/**
* Check whether this class is abstract
*/
public boolean isAbstract() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Abstract) {
return true;
}
}
return false;
}
/**
* Check whether this class is final
*
* @return true if this class is final
*/
public boolean isFinal() {
for(Modifier m : modifiers) { if (m instanceof Modifier.Final) { return true; }}
return false;
}
/**
* Check whether this class is static
*
* @return true if this class is static
*/
public boolean isStatic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Static) {
return true;
}
}
return false;
}
/**
* Check whether this class is public
*
* @return true if this class is public
*/
public boolean isPublic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Public) {
return true;
}
}
return false;
}
/**
* Check whether this class is protected
*
* @return true if this class is protected
*/
public boolean isProtected() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Protected) {
return true;
}
}
return false;
}
/**
* Check whether this class is private
*
* @return true if this class is private
*/
public boolean isPrivate() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Private) {
return true;
}
}
return false;
}
/**
* Check whether this class is native
*
* @return true if this class is native
*/
public boolean isNative() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Native) {
return true;
}
}
return false;
}
/**
* Check whether this class is synchronized
*
* @return true if this class is syncthonized
*/
public boolean isSynchronized() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Synchronized) {
return true;
}
}
return false;
}
/**
* Check whether this class is synthetic
*
* @return true if this class is synthetic
*/
public boolean isSynthetic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Synthetic) {
return true;
}
}
return false;
}
/**
* Check whether or not this is an inner class.
* @return
*/
public boolean isInnerClass() {
return type.components().size() > 1;
}
// =========================================================================
// Mutators
// =========================================================================
public void setType(JvmType.Clazz t) {
type = t;
}
// =========================================================================
// Types
// =========================================================================
public static class Field {
protected String name;
protected JvmType type;
protected List<Modifier> modifiers;
protected ArrayList<BytecodeAttribute> attributes;
public Field(String name, JvmType type, List<Modifier> modifiers) {
this.name = name;
this.type = type;
this.modifiers = modifiers;
this.attributes = new ArrayList<BytecodeAttribute>();
}
public String name() {
return name;
}
public void setName(String n) {
name = n;
}
public JvmType type() {
return type;
}
public void setType(JvmType t) {
type = t;
}
public List<Modifier> modifiers() {
return modifiers;
}
public List<BytecodeAttribute> attributes() {
return attributes;
}
public BytecodeAttribute attribute(String name) {
for(BytecodeAttribute a : attributes) {
if(a.name().equals(name)) {
return a;
}
}
return null;
}
/**
* Check whether this field has one of the "base" modifiers (e.g. static,
* public, private, etc). These are found in Modifier.ACC_
*
* @param modifierClass
* @return true if it does!
*/
public boolean hasModifier(Class modifierClass) {
for(Modifier m : modifiers) {
if(m.getClass().equals(modifierClass)) {
return true;
}
}
return false;
}
/**
* Check whether this field is abstract
*
* @return true if this field is abstract
*/
public boolean isAbstract() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Abstract) {
return true;
}
}
return false;
}
/**
* Check whether this field is final
*
* @return true if this field is final
*/
public boolean isFinal() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Final) {
return true;
}
}
return false;
}
/**
* Check whether this field is static
*
* @return true if this field is static
*/
public boolean isStatic() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Static) {
return true;
}
}
return false;
}
/**
* Check whether this field is public
*
* @return true if this field is public
*/
public boolean isPublic() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Public) {
return true;
}
}
return false;
}
/**
* Check whether this field is protected
*
* @return true if this field is protected
*/
public boolean isProtected() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Protected) {
return true;
}
}
return false;
}
/**
* Check whether this field is private
*
* @return true if this field is private
*/
public boolean isPrivate() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Private) {
return true;
}
}
return false;
}
/**
* Check whether this method is synthetic
*
* @return true if this field is synthetic
*/
public boolean isSynthetic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Synthetic) {
return true;
}
}
return false;
}
public boolean isConstant() {
for(BytecodeAttribute a : attributes) {
if(a instanceof ConstantValue) {
return true;
}
}
return false;
}
public Object constant() {
for(BytecodeAttribute a : attributes) {
if(a instanceof ConstantValue) {
ConstantValue x = (ConstantValue) a;
return x.constant();
}
}
return null;
}
}
public static class Method {
protected String name;
protected JvmType.Function type;
protected List<Modifier> modifiers;
protected ArrayList<BytecodeAttribute> attributes;
public Method(String name, JvmType.Function type,
List<Modifier> modifiers) {
this.name = name;
this.type = type;
this.modifiers = modifiers;
attributes = new ArrayList<BytecodeAttribute>();
}
public String name() {
return name;
}
public JvmType.Function type() {
return type;
}
public void setType(JvmType.Function t) {
type = t;
}
public List<Parameter> parameters() {
ArrayList<Parameter> r = new ArrayList<Parameter>();
for(JvmType t : type.parameterTypes()) {
r.add(new Parameter());
}
return r;
}
public List<Modifier> modifiers() {
return modifiers;
}
public List<JvmType.Clazz> exceptions() {
for(BytecodeAttribute a : attributes) {
if(a instanceof Exceptions) {
return ((Exceptions)a).exceptions();
}
}
return new ArrayList();
}
public <T extends BytecodeAttribute> T attribute(Class<T> c) {
for(BytecodeAttribute a : attributes) {
if(c.isInstance(a)) {
return (T) a;
}
}
return null;
}
public List<BytecodeAttribute> attributes() {
return attributes;
}
public BytecodeAttribute attribute(String name) {
for(BytecodeAttribute a : attributes) {
if(a.name().equals(name)) {
return a;
}
}
return null;
}
/**
* Check whether this method has one of the "base" modifiers (e.g.
* static, public, private, etc). These are found in Modifier.ACC_
*
* @param modifierClass
* Class for modifier in question
* @return true if it does!
*/
public boolean hasModifier(Class modifierClass) {
for(Modifier m : modifiers) {
if(m.getClass().equals(modifierClass)) {
return true;
}
}
return false;
}
/**
* Check whether this method is abstract
*
* @return true if this field is abstract
*/
public boolean isAbstract() {
for(Modifier m : modifiers) {
if(m instanceof Modifier.Abstract) {
return true;
}
}
return false;
}
/**
* Check whether this method is final
*
* @return true if this field is final
*/
public boolean isFinal() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Final) {
return true;
}
}
return false;
}
/**
* Check whether this method is static
*
* @return true if this method is static
*/
public boolean isStatic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Static) {
return true;
}
}
return false;
}
/**
* Check whether this method is public
*
* @return true if this method is public
*/
public boolean isPublic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Public) {
return true;
}
}
return false;
}
/**
* Check whether this method is protected
*
* @return true if this method is protected
*/
public boolean isProtected() {
for(Modifier m : modifiers) { if(m instanceof Modifier.Protected) { return true; }}
return false;
}
/**
* Check whether this method is private
*
* @return true if this method is private
*/
public boolean isPrivate() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Private) {
return true;
}
}
return false;
}
/**
* Check whether this method is native
*
* @return true if this method is native
*/
public boolean isNative() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Native) {
return true;
}
}
return false;
}
/**
* Check whether this method is synchronized
*
* @return true if this method is synchronized
*/
public boolean isSynchronized() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Synchronized) {
return true;
}
}
return false;
}
/**
* Check whether this method is synthetic
*
* @return true if this method is synthetic
*/
public boolean isSynthetic() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.Synthetic) {
return true;
}
}
return false;
}
/**
* Check whether this method has varargs
*
* @return true if this method has varags
*/
public boolean isVariableArity() {
for (Modifier m : modifiers) {
if (m instanceof Modifier.VarArgs) {
return true;
}
}
return false;
}
}
public static class Parameter {
public List<Modifier> modifiers() {
return new ArrayList();
}
public boolean isSynthetic() {
return false;
}
}
/**
* This method builds a constant pool for this class file.
*
* @return
*/
public ArrayList<Constant.Info> constantPool() {
HashSet<Constant.Info> constantPool = new HashSet<Constant.Info>();
// Now, add constant pool items
Constant.addPoolItem(Constant.buildClass(type),constantPool);
Constant.addPoolItem(new Constant.Utf8("Signature"),constantPool);
Constant.addPoolItem(new Constant.Utf8("ConstantValue"),constantPool);
if (superClazz != null) {
Constant.addPoolItem(Constant.buildClass(superClazz), constantPool);
}
for (JvmType.Reference i : interfaces) {
Constant.addPoolItem(Constant.buildClass(i), constantPool);
}
// Now, add all constant pool information for fields
for (Field f : fields) {
// Now, add pool items
Constant.addPoolItem(new Constant.Utf8(f.name()), constantPool);
Constant.addPoolItem(
new Constant.Utf8(descriptor(f.type(), false)),
constantPool);
for(BytecodeAttribute a : f.attributes()) {
a.addPoolItems(constantPool);
}
}
for(Method m : methods) {
// Now, add all constant pool information for methods
Constant.addPoolItem(new Constant.Utf8(m.name()), constantPool);
Constant.addPoolItem(new Constant.Utf8(descriptor(m.type(),
false)), constantPool);
for(BytecodeAttribute a : m.attributes()) {
a.addPoolItems(constantPool);
}
}
for(BytecodeAttribute a : attributes) {
a.addPoolItems(constantPool);
}
// Finally, we need to flatten the constant pool
ArrayList<Constant.Info> pool = new ArrayList<Constant.Info>();
pool.add(null); // first entry is not used
for (Constant.Info ci : constantPool) {
pool.add(ci);
// Doubles and Longs require (for some reason) double slots.
if(ci instanceof Constant.Double || ci instanceof Constant.Long) {
pool.add(null);
}
}
return pool;
}
/**
* This method returns a JVM descriptor string for the type in question. The
* format of the string is defined in "The Java Virtual Machine
* Specification, 2nd ed", Section 4.3. Example descriptor strings include:
*
* <table summary="">
* <tr>
* <td><b>Type</b></td>
* <td><b>Descriptor</b></td>
* </tr>
*
* <tr>
* <td>int</td>
* <td>I</td>
* </tr>
*
* <tr>
* <td>boolean</td>
* <td>Z</td>
* </tr>
*
* <tr>
* <td>float[]</td>
* <td>F[</td>
* </tr>
*
* <tr>
* <td>java.lang.Integer</td>
* <td>Ljava/lang/Integer;</td>
* </tr>
*
* <tr>
* <td>int(Double,Float)</td>
* <td>(DF)I</td>
* </tr>
* </table>
* <p>
* The descriptor string is used, amongst other things, to uniquely identify
* a class in the ClassTable.
* </p>
*
* See the <a href=
* "http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#1169">
* JVM Specification</a> for more information.
*
* @param t
* The type to generate the descriptor for
* @param generic
* True indicates generic information should be included.
* @return
*/
public static String descriptor(JvmType t, boolean generic) {
if(t instanceof JvmType.Bool) {
return "Z";
} if(t instanceof JvmType.Byte) {
return "B";
} else if(t instanceof JvmType.Char) {
return "C";
} else if(t instanceof JvmType.Short) {
return "S";
} else if(t instanceof JvmType.Int) {
return "I";
} else if(t instanceof JvmType.Long) {
return "J";
} else if(t instanceof JvmType.Float) {
return "F";
} else if(t instanceof JvmType.Double) {
return "D";
} else if(t instanceof JvmType.Void) {
return "V";
} else if(t instanceof JvmType.Array) {
JvmType.Array at = (JvmType.Array) t;
return "[" + descriptor(at.element(),generic);
} else if(t instanceof JvmType.Clazz) {
JvmType.Clazz ref = (JvmType.Clazz) t;
String r = "L" + ref.pkg().replace(".","/");
List<Pair<String, List<JvmType.Reference>>> classes = ref.components();
for (int i = 0; i != classes.size(); ++i) {
if (i == 0 && r.length() > 1) {
r += "/";
} else if(i > 0) {
r += "$";
}
r += classes.get(i).first();
if(generic) {
List<JvmType.Reference> gparams = classes.get(i).second();
if(gparams != null && gparams.size() > 0) {
r += "<";
for(JvmType gt : gparams) {
r += descriptor(gt,generic);
}
r += ">";
}
}
}
return r + ";";
} else if(t instanceof JvmType.Function) {
// For simplicity, this code does not support generic function
// types. The reason for this is that, to do so, requires access to
// the ClassLoader. Instead, generic method signatures are supported
// only by the MethodSignature class.
JvmType.Function ft = (JvmType.Function) t;
String r = "(";
for (JvmType pt : ft.parameterTypes()) {
r += ClassFile.descriptor(pt,generic);
}
r = r + ")" + ClassFile.descriptor(ft.returnType(),generic);
return r;
} if(t instanceof JvmType.Variable) {
if(generic) {
JvmType.Variable tv = (JvmType.Variable) t;
return "T" + tv.variable() + ";";
} else {
JvmType.Variable tv = (JvmType.Variable) t;
JvmType.Reference lb = tv.lowerBound();
if(lb != null) {
return descriptor(lb,generic);
} else {
return "Ljava/lang/Object;";
}
}
} else if(t instanceof JvmType.Wildcard) {
if(generic) {
JvmType.Wildcard tw = (JvmType.Wildcard) t;
if(tw.lowerBound() == null) {
return "*";
} else {
return "+" + descriptor(tw.lowerBound(),generic);
}
} else {
return "Ljava/lang/Object;";
}
}
throw new RuntimeException("Invalid type passed to descriptor(): " + t);
}
/**
* Determine the slot size for the corresponding Java type.
*
* @param type
* The type to determine the slot size for.
* @return the slot size in slots.
*/
public static int slotSize(JvmType type) {
if (type instanceof JvmType.Double || type instanceof JvmType.Long) {
return 2;
} else {
return 1;
}
}
}