diff options
Diffstat (limited to 'src/org/jetbrains/java/decompiler/struct')
34 files changed, 4697 insertions, 0 deletions
diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java new file mode 100644 index 0000000..d859b29 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -0,0 +1,210 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Manifest; + +import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link; + +public class ContextUnit { + + public static final int TYPE_FOLDER = 0; + public static final int TYPE_JAR = 1; + public static final int TYPE_ZIP = 2; + + private static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF"; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private int type; + + // relative path to jar/zip + private String archivepath; + + // folder: relative path + // archive: file name + private String filename; + + private List<StructClass> classes = new ArrayList<StructClass>(); + + // class file or jar/zip entry. Should, but doesn't have to be the same as qualifiedName of the class + private List<String> classentries = new ArrayList<String>(); + + private List<String> direntries = new ArrayList<String>(); + + private List<String[]> otherentries = new ArrayList<String[]>(); + + private Manifest manifest; + + private IDecompilatSaver decompilatSaver; + + private IDecompiledData decompiledData; + + private boolean own = true; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public ContextUnit(int type, String archivepath, String filename, boolean own, + IDecompilatSaver decompilatSaver, IDecompiledData decompiledData) { + this.type = type; + this.own = own; + this.archivepath = archivepath; + this.filename = filename; + this.decompilatSaver = decompilatSaver; + this.decompiledData = decompiledData; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void addClass(StructClass cl, String entryname) { + classes.add(cl); + classentries.add(entryname); + } + + public void addDirEntry(String entry) { + direntries.add(entry); + } + + public void addOtherEntry(String fullpath, String entry) { + otherentries.add(new String[]{fullpath, entry}); + } + + public void reload(LazyLoader loader) throws IOException { + + List<StructClass> lstClasses = new ArrayList<StructClass>(); + for(StructClass cl : classes) { + String oldname = cl.qualifiedName; + StructClass newcl = new StructClass(loader.getClassStream(oldname), cl.isOwn(), loader); + + lstClasses.add(newcl); + + Link lnk = loader.getClassLink(oldname); + loader.removeClassLink(oldname); + loader.addClassLink(newcl.qualifiedName, lnk); + } + + classes = lstClasses; + } + + public void save() { + + switch(type) { + case TYPE_FOLDER: + + // create folder + decompilatSaver.saveFolder(filename); + + // non-class files + for(String[] arr: otherentries) { + decompilatSaver.copyFile(arr[0], filename, arr[0]); + } + + // classes + for(int i=0;i<classes.size();i++) { + + StructClass cl = classes.get(i); + String entryname = classentries.get(i); + + entryname = decompiledData.getClassEntryName(cl, entryname); + if(entryname != null) { + String content = decompiledData.getClassContent(cl); + if(content != null) { + decompilatSaver.saveClassFile(filename, cl.qualifiedName, entryname, content); + } + } + } + + break; + case TYPE_JAR: + case TYPE_ZIP: + + // create archive file + decompilatSaver.saveFolder(archivepath); + decompilatSaver.createArchive(archivepath, filename, manifest); + + // directory entries + for(String direntry: direntries) { + decompilatSaver.saveEntry(archivepath, filename, direntry, null); + } + + // non-class entries + for(String[] arr: otherentries) { + // manifest was defined by constructor invocation + if(type != TYPE_JAR || !MANIFEST_ENTRY.equalsIgnoreCase(arr[1])) { + decompilatSaver.copyEntry(arr[0], archivepath, filename, arr[1]); + } + } + + // classes + for(int i=0;i<classes.size();i++) { + + StructClass cl = classes.get(i); + String entryname = classentries.get(i); + + entryname = decompiledData.getClassEntryName(cl, entryname); + if(entryname != null) { + String content = decompiledData.getClassContent(cl); + decompilatSaver.saveClassEntry(archivepath, filename, cl.qualifiedName, entryname, content); + } + } + + decompilatSaver.closeArchive(archivepath, filename); + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public void setManifest(Manifest manifest) { + this.manifest = manifest; + } + + public boolean isOwn() { + return own; + } + + public List<StructClass> getClasses() { + return classes; + } + + public int getType() { + return type; + } + + public void setDecompilatSaver(IDecompilatSaver decompilatSaver) { + this.decompilatSaver = decompilatSaver; + } + + public void setDecompiledData(IDecompiledData decompiledData) { + this.decompiledData = decompiledData; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java new file mode 100644 index 0000000..9c8bc14 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java @@ -0,0 +1,23 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +public interface IDecompiledData { + + public String getClassEntryName(StructClass cl, String entryname); + + public String getClassContent(StructClass cl); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/ISaveClass.java b/src/org/jetbrains/java/decompiler/struct/ISaveClass.java new file mode 100644 index 0000000..6fbfb9c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/ISaveClass.java @@ -0,0 +1,30 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +public interface ISaveClass { + + public String getClassEntryName(StructClass cl, String entryname); + + public void saveClassToFile(StructClass cl, File file) throws FileNotFoundException, IOException; + + public void saveClassToStream(StructClass cl, DataOutputStream out); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java new file mode 100644 index 0000000..456cc70 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -0,0 +1,352 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import org.jetbrains.java.decompiler.util.DataInputFullStream; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +/* + ClassFile { + u4 magic; + u2 minor_version; + u2 major_version; + u2 constant_pool_count; + cp_info constant_pool[constant_pool_count-1]; + u2 access_flags; + u2 this_class; + u2 super_class; + u2 interfaces_count; + u2 interfaces[interfaces_count]; + u2 fields_count; + field_info fields[fields_count]; + u2 methods_count; + method_info methods[methods_count]; + u2 attributes_count; + attribute_info attributes[attributes_count]; + } +*/ + +public class StructClass { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int minor_version; + + public int major_version; + + public int access_flags; + + public int this_class; + + public int super_class; + + public PrimitiveConstant thisClass; + + public PrimitiveConstant superClass; + + public String qualifiedName; + + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private ConstantPool pool; + + private int[] interfaces; + + private String[] interfaceNames; + + private VBStyleCollection<StructField, String> fields = new VBStyleCollection<StructField, String>(); + + private VBStyleCollection<StructMethod, String> methods = new VBStyleCollection<StructMethod, String>(); + + private VBStyleCollection<StructGeneralAttribute, String> attributes = new VBStyleCollection<StructGeneralAttribute, String>(); + + private boolean own = true; + + private LazyLoader loader; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructClass(String filename, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this(new FileInputStream(filename), own, loader); + } + + public StructClass(InputStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this(new DataInputFullStream(inStream), own, loader); + } + + public StructClass(DataInputFullStream inStream, boolean own, LazyLoader loader) throws FileNotFoundException, IOException { + this.own = own; + this.loader = loader; + + initStruct(inStream); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public boolean hasField(String name, String descriptor) { + return getField(name, descriptor) != null; + } + + public StructField getField(String name, String descriptor) { + return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); + } + + public StructMethod getMethod(String key) { + return methods.getWithKey(key); + } + + public StructMethod getMethod(String name, String descriptor) { + return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor)); + } + + public void writeToFile(File file) throws IOException { + DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); + writeToOutputStream(out); + out.close(); + } + + public void writeToOutputStream(DataOutputStream out) throws IOException { + + out.writeInt(0xCAFEBABE); + out.writeShort(minor_version); + out.writeShort(major_version); + + getPool().writeToOutputStream(out); + + out.writeShort(access_flags); + out.writeShort(this_class); + out.writeShort(super_class); + + out.writeShort(interfaces.length); + for(int i=0;i<interfaces.length;i++) { + out.writeShort(interfaces[i]); + } + + out.writeShort(fields.size()); + for(int i=0;i<fields.size();i++) { + fields.get(i).writeToStream(out); + } + + out.writeShort(methods.size()); + for(int i=0;i<methods.size();i++) { + methods.get(i).writeToStream(out); + } + + out.writeShort(attributes.size()); + for(StructGeneralAttribute attr: attributes) { + attr.writeToStream(out); + } + + } + + public String getInterface(int i) { + return interfaceNames[i]; + } + + public void releaseResources() { + if(loader != null) { + pool = null; + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void initStruct(DataInputFullStream in) throws IOException { + + in.skip(4); + + this.minor_version = in.readUnsignedShort(); + this.major_version = in.readUnsignedShort(); + + pool = new ConstantPool(in); + + this.access_flags = in.readUnsignedShort(); + + this_class = in.readUnsignedShort(); + thisClass = pool.getPrimitiveConstant(this_class); + qualifiedName = thisClass.getString(); + + super_class = in.readUnsignedShort(); + superClass = pool.getPrimitiveConstant(super_class); + + // interfaces + int length = in.readUnsignedShort(); + int[] arrInterfaces = new int[length]; + String[] arrInterfaceNames = new String[length]; + + for (int i = 0; i < length; i++) { + arrInterfaces[i] = in.readUnsignedShort(); + arrInterfaceNames[i] = pool.getPrimitiveConstant(arrInterfaces[i]).getString(); + } + this.interfaces = arrInterfaces; + this.interfaceNames = arrInterfaceNames; + + // fields + VBStyleCollection<StructField, String> lstFields = new VBStyleCollection<StructField, String>(); + length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + StructField field = new StructField(); + field.access_flags = in.readUnsignedShort(); + field.name_index = in.readUnsignedShort(); + field.descriptor_index = in.readUnsignedShort(); + + field.initStrings(pool, this_class); + + field.setAttributes(readAttributes(in)); + + lstFields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); + } + this.fields = lstFields; + + // methods + length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + StructMethod meth = new StructMethod(in, own, this); + + //if(qualifiedName.endsWith("JUnitStatusLine") && !meth.getName().equals("onProcessStarted") && !meth.getName().startsWith("access")) { + //if(!meth.getName().equals("run")) { + // continue; + //} + + methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(meth.getName(), meth.getDescriptor())); + } + + // attributes + this.attributes = readAttributes(in); + + + // release memory + if(loader != null) { + pool = null; + } + } + + private VBStyleCollection<StructGeneralAttribute, String> readAttributes(DataInputFullStream in) throws IOException { + + VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); + + int length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + StructGeneralAttribute attr = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); + + if(attr != null) { + byte[] arr = new byte[in.readInt()]; + in.readFull(arr); + attr.setInfo(arr); + + attr.initContent(pool); + lstAttribute.addWithKey(attr, attr.getName()); + } else { + in.skip(in.readInt()); + } + } + + return lstAttribute; + } + + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public ConstantPool getPool() { + + if(pool == null && loader != null) { + pool = loader.loadPool(qualifiedName); + } + + return pool; + } + + public int[] getInterfaces() { + return interfaces; + } + + public String[] getInterfaceNames() { + return interfaceNames; + } + + public VBStyleCollection<StructMethod, String> getMethods() { + return methods; + } + + public VBStyleCollection<StructField, String> getFields() { + return fields; + } + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public boolean isOwn() { + return own; + } + + public LazyLoader getLoader() { + return loader; + } + + public boolean isVersionGE_1_5() { + return (major_version > 48 || (major_version == 48 && minor_version > 0)); // FIXME: check second condition + } + + public boolean isVersionGE_1_7() { + return (major_version >= 51); + } + + public int getBytecodeVersion() { + switch(major_version) { + case 52: + return CodeConstants.BYTECODE_JAVA_8; + case 51: + return CodeConstants.BYTECODE_JAVA_7; + case 50: + return CodeConstants.BYTECODE_JAVA_6; + case 49: + return CodeConstants.BYTECODE_JAVA_5; + } + + return CodeConstants.BYTECODE_JAVA_LE_4; + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java new file mode 100644 index 0000000..c253a10 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -0,0 +1,211 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; + + +public class StructContext { + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private LazyLoader loader; + + private HashMap<String, StructClass> classes = new HashMap<String, StructClass>(); + + private HashMap<String, ContextUnit> units = new HashMap<String, ContextUnit>(); + + private ContextUnit defaultUnit; + + private IDecompilatSaver saver; + + private IDecompiledData decdata; + + public StructContext(IDecompilatSaver saver, IDecompiledData decdata, LazyLoader loader) { + + this.saver = saver; + this.decdata = decdata; + this.loader = loader; + + defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, "", true, saver, decdata); + units.put("", defaultUnit); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public StructClass getClass(String name) { + return classes.get(name); + } + + public void reloadContext() throws IOException { + + for(ContextUnit unit: units.values()) { + + for(StructClass cl : unit.getClasses()) { + classes.remove(cl.qualifiedName); + } + + unit.reload(loader); + + // adjust lobal class collection + for(StructClass cl : unit.getClasses()) { + classes.put(cl.qualifiedName, cl); + } + } + } + + public void saveContext() { + + for(ContextUnit unit: units.values()) { + if(unit.isOwn()) { + unit.save(); + } + } + } + + public void addSpace(File file, boolean isOwn) throws IOException { + addSpace("", file, isOwn); + } + + private void addSpace(String path, File file, boolean isOwn) throws IOException { + + if(file.isDirectory()) { + + File[] files = file.listFiles(); + path += "/" + (path.length()==0?"":file.getName()); + + for(int i=files.length-1;i>=0;i--) { + addSpace(path, files[i], isOwn); + } + + } else { + + String filename = file.getName(); + + boolean isArchive = false; + + try { + if(filename.endsWith(".jar")) { + addArchive(path, file, ContextUnit.TYPE_JAR, isOwn); + isArchive = true; + } else if(filename.endsWith(".zip")) { + addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn); + isArchive = true; + } + } catch(IOException ex) { + DecompilerContext.getLogger() + .writeMessage("Invalid archive file: "+(path.length()>0?path+"/":"")+filename, IFernflowerLogger.ERROR); + } + + if(!isArchive) { + ContextUnit unit = units.get(path); + if(unit == null) { + unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decdata); + units.put(path, unit); + } + + boolean isClass = false; + + if(filename.endsWith(".class")) { + try { + StructClass cl = new StructClass(loader.getClassStream(file.getAbsolutePath(), null), isOwn, loader); + + classes.put(cl.qualifiedName, cl); + unit.addClass(cl, filename); + loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.CLASS, file.getAbsolutePath(), null)); + + isClass = true; + } catch(IOException ex) { + DecompilerContext.getLogger() + .writeMessage("Invalid class file: "+(path.length()>0?path+"/":"")+filename, IFernflowerLogger.ERROR); + } + } + + if(!isClass) { + unit.addOtherEntry(file.getAbsolutePath(), filename); + } + } + } + } + + + private void addArchive(String path, File file, int type, boolean isOwn) throws IOException { + + ZipFile archive; + + if(type == ContextUnit.TYPE_JAR) { // jar + archive = new JarFile(file); + } else { // zip + archive = new ZipFile(file); + } + + Enumeration<? extends ZipEntry> en = archive.entries(); + while(en.hasMoreElements()) { + ZipEntry entr = en.nextElement(); + + ContextUnit unit = units.get(path+"/"+file.getName()); + if(unit == null) { + unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decdata); + if(type == ContextUnit.TYPE_JAR) { + unit.setManifest(((JarFile)archive).getManifest()); + } + units.put(path+"/"+file.getName(), unit); + } + + String name = entr.getName(); + if(!entr.isDirectory()) { + if(name.endsWith(".class")) { + StructClass cl = new StructClass(archive.getInputStream(entr), isOwn, loader); + classes.put(cl.qualifiedName, cl); + + unit.addClass(cl, name); + + if(loader != null) { + loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.ENTRY, file.getAbsolutePath(), name)); + } + + } else { + unit.addOtherEntry(file.getAbsolutePath(), name); + } + } else if(entr.isDirectory()) { + unit.addDirEntry(name); + } + } + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public HashMap<String, StructClass> getClasses() { + return classes; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java new file mode 100644 index 0000000..2537e7b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -0,0 +1,109 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +/* + field_info { + u2 access_flags; + u2 name_index; + u2 descriptor_index; + u2 attributes_count; + attribute_info attributes[attributes_count]; + } +*/ + +public class StructField { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int access_flags; + public int name_index; + public int descriptor_index; + + private String name; + private String descriptor; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private VBStyleCollection<StructGeneralAttribute, String> attributes; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructField() {} + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(access_flags); + out.writeShort(name_index); + out.writeShort(descriptor_index); + + out.writeShort(attributes.size()); + for(StructGeneralAttribute attr: attributes) { + attr.writeToStream(out); + } + } + + public void initStrings(ConstantPool pool, int class_index) { + String[] values = pool.getClassElement(ConstantPool.FIELD, class_index, name_index, descriptor_index); + name = values[0]; + descriptor = values[1]; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public void setAttributes(VBStyleCollection<StructGeneralAttribute, String> attributes) { + this.attributes = attributes; + } + + public String getDescriptor() { + return descriptor; + } + + public void setDescriptor(String descriptor) { + this.descriptor = descriptor; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java new file mode 100644 index 0000000..20fdae9 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -0,0 +1,560 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.ConstantsUtil; +import org.jetbrains.java.decompiler.code.ExceptionHandler; +import org.jetbrains.java.decompiler.code.ExceptionTable; +import org.jetbrains.java.decompiler.code.FullInstructionSequence; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +/* + method_info { + u2 access_flags; + u2 name_index; + u2 descriptor_index; + u2 attributes_count; + attribute_info attributes[attributes_count]; + } +*/ + +public class StructMethod implements CodeConstants { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int name_index; + + public int descriptor_index; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private static final int[] opr_iconst = new int[] {-1,0,1,2,3,4,5}; + + private static final int[] opr_loadstore = new int[] {0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3}; + + private static final int[] opcs_load = new int[] {opc_iload,opc_lload,opc_fload,opc_dload,opc_aload}; + + private static final int[] opcs_store = new int[] {opc_istore,opc_lstore,opc_fstore,opc_dstore,opc_astore}; + + + private int accessFlags; + + private VBStyleCollection<StructGeneralAttribute, String> attributes; + + private int localVariables; + + private int maxStack; + + private String name; + + private String descriptor; + + private InstructionSequence seq; + + private boolean containsCode = false; + + private boolean own; + + private StructClass classStruct; + + + // lazy properties + private boolean lazy; + + private boolean expanded; + + private byte[] code_content; + + private int code_length = 0; + + private int code_fulllength = 0; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StructMethod(DataInputFullStream in, boolean own, StructClass clstruct) throws IOException { + this(in, true, own, clstruct); + } + + public StructMethod(DataInputFullStream in, boolean lazy, boolean own, StructClass clstruct) throws IOException { + + this.own = own; + this.lazy = lazy; + this.expanded = !lazy; + this.classStruct = clstruct; + + accessFlags = in.readUnsignedShort(); + name_index = in.readUnsignedShort(); + descriptor_index = in.readUnsignedShort(); + + ConstantPool pool = clstruct.getPool(); + + initStrings(pool, clstruct.this_class); + + VBStyleCollection<StructGeneralAttribute, String> lstAttribute = new VBStyleCollection<StructGeneralAttribute, String>(); + int len = in.readUnsignedShort(); + for(int i=0;i<len;i++) { + + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { + if(!this.own) { + // skip code in foreign classes + in.skip(8); + in.skip(in.readInt()); + in.skip(8*in.readUnsignedShort()); + } else { + containsCode = true; + + in.skip(4); + + maxStack = in.readUnsignedShort(); + localVariables = in.readUnsignedShort(); + + if(lazy) { + code_length = in.readInt(); + + in.skip(code_length); + + int exc_length = in.readUnsignedShort(); + code_fulllength = code_length + exc_length*8+2; + + in.skip(exc_length*8); + + } else { + seq = parseBytecode(in, in.readInt(), pool); + } + } + + // code attributes + int length = in.readUnsignedShort(); + for (int j = 0; j < length; j++) { + int codeattr_nameindex = in.readUnsignedShort(); + String codeattrname = pool.getPrimitiveConstant(codeattr_nameindex).getString(); + + readAttribute(in, pool, lstAttribute, codeattr_nameindex, codeattrname); + } + } else { + readAttribute(in, pool, lstAttribute, attr_nameindex, attrname); + } + } + + attributes = lstAttribute; + } + + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(accessFlags); + out.writeShort(name_index); + out.writeShort(descriptor_index); + + out.writeShort(attributes.size()); + + for(StructGeneralAttribute attr: attributes) { + if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attr.getName())){ + out.writeShort(attr.getAttribute_name_index()); + + if(lazy && !expanded) { + out.writeInt(10+code_content.length); + out.writeShort(maxStack); + out.writeShort(localVariables); + out.writeInt(code_length); + out.write(code_content); + } else { + ByteArrayOutputStream codeout = new ByteArrayOutputStream(); + seq.writeCodeToStream(new DataOutputStream(codeout)); + + ByteArrayOutputStream excout = new ByteArrayOutputStream(); + seq.writeExceptionsToStream(new DataOutputStream(excout)); + + out.writeInt(10+codeout.size()+excout.size()); + + out.writeShort(maxStack); + out.writeShort(localVariables); + out.writeInt(codeout.size()); + codeout.writeTo(out); + excout.writeTo(out); + } + // no attributes + out.writeShort(0); + } else { + attr.writeToStream(out); + } + } + + } + + private void readAttribute(DataInputFullStream in, ConstantPool pool, VBStyleCollection<StructGeneralAttribute, String> lstAttribute, + int attr_nameindex, String attrname) throws IOException { + + StructGeneralAttribute attribute = StructGeneralAttribute.getMatchingAttributeInstance(attr_nameindex, attrname); + + if(attribute != null) { + attrname = attribute.getName(); + + byte[] arr = new byte[in.readInt()]; + in.readFull(arr); + attribute.setInfo(arr); + + attribute.initContent(pool); + + if(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname) && + lstAttribute.containsKey(attrname)) { + // merge all variable tables + StructLocalVariableTableAttribute oldattr = (StructLocalVariableTableAttribute)lstAttribute.getWithKey(attrname); + oldattr.addLocalVariableTable((StructLocalVariableTableAttribute)attribute); + } else { + lstAttribute.addWithKey(attribute, attribute.getName()); + } + } else { + in.skip(in.readInt()); + } + } + + private void initStrings(ConstantPool pool, int class_index) { + String[] values = pool.getClassElement(ConstantPool.METHOD, class_index, name_index, descriptor_index); + name = values[0]; + descriptor = values[1]; + } + + public void expandData() throws IOException { + if(containsCode && lazy && !expanded) { + + byte[] codearr = classStruct.getLoader().loadBytecode(this, code_fulllength); + + seq = parseBytecode(new DataInputFullStream(new ByteArrayInputStream(codearr)), code_length, classStruct.getPool()); + expanded = true; + } + } + + public void releaseResources() throws IOException { + if(containsCode && lazy && expanded) { + seq = null; + expanded = false; + } + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { + + VBStyleCollection<Instruction, Integer> collinstr = new VBStyleCollection<Instruction, Integer>(); + + int bytecode_version = classStruct.getBytecodeVersion(); + + for(int i=0;i<length;) { + + int offset = i; + + int opcode = in.readUnsignedByte(); + int group = GROUP_GENERAL; + + boolean wide = (opcode == opc_wide); + + if(wide) { + i++; + opcode = in.readUnsignedByte(); + } + + List<Integer> operands = new ArrayList<Integer>(); + + if(opcode>=opc_iconst_m1 && opcode<=opc_iconst_5) { + operands.add(new Integer(opr_iconst[opcode-opc_iconst_m1])); + opcode = opc_bipush; + }else if(opcode>=opc_iload_0 && opcode<=opc_aload_3) { + operands.add(new Integer(opr_loadstore[opcode-opc_iload_0])); + opcode = opcs_load[(opcode-opc_iload_0)/4]; + }else if(opcode>=opc_istore_0 && opcode<=opc_astore_3) { + operands.add(new Integer(opr_loadstore[opcode-opc_istore_0])); + opcode = opcs_store[(opcode-opc_istore_0)/4]; + } else { + switch (opcode) { + case opc_bipush: + operands.add(new Integer(in.readByte())); + i++; + break; + case opc_ldc: + case opc_newarray: + operands.add(new Integer(in.readUnsignedByte())); + i++; + break; + case opc_sipush: + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmplt: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_goto: + case opc_jsr: + case opc_ifnull: + case opc_ifnonnull: + if(opcode!=opc_sipush) { + group = GROUP_JUMP; + } + operands.add(new Integer(in.readShort())); + i+=2; + break; + case opc_ldc_w: + case opc_ldc2_w: + case opc_getstatic: + case opc_putstatic: + case opc_getfield: + case opc_putfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_new: + case opc_anewarray: + case opc_checkcast: + case opc_instanceof: + operands.add(new Integer(in.readUnsignedShort())); + i+=2; + if(opcode>=opc_getstatic && opcode<=opc_putfield) { + group = GROUP_FIELDACCESS; + } else if(opcode>=opc_invokevirtual && opcode<=opc_invokestatic) { + group = GROUP_INVOCATION; + } + break; + case opc_invokedynamic: + if(classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before + operands.add(new Integer(in.readUnsignedShort())); + in.skip(2); + group = GROUP_INVOCATION; + i+=4; + } + break; + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + case opc_ret: + if(wide) { + operands.add(new Integer(in.readUnsignedShort())); + i+=2; + } else { + operands.add(new Integer(in.readUnsignedByte())); + i++; + } + if(opcode == opc_ret) { + group = GROUP_RETURN; + } + break; + case opc_iinc: + if (wide) { + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readShort())); + i+=4; + } else { + operands.add(new Integer(in.readUnsignedByte())); + operands.add(new Integer(in.readByte())); + i+=2; + } + break; + case opc_goto_w: + case opc_jsr_w: + opcode = opcode == opc_jsr_w?opc_jsr:opc_goto; + operands.add(new Integer(in.readInt())); + group = GROUP_JUMP; + i+=4; + break; + case opc_invokeinterface: + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readUnsignedByte())); + in.skip(1); + group = GROUP_INVOCATION; + i+=4; + break; + case opc_multianewarray: + operands.add(new Integer(in.readUnsignedShort())); + operands.add(new Integer(in.readUnsignedByte())); + i+=3; + break; + case opc_tableswitch: + in.skip((4-(i+1)%4)%4); + i+=((4-(i+1)%4)%4); // padding + operands.add(new Integer(in.readInt())); + i+=4; + int low = in.readInt(); + operands.add(new Integer(low)); + i+=4; + int high = in.readInt(); + operands.add(new Integer(high)); + i+=4; + + for(int j=0;j<high-low+1;j++) { + operands.add(new Integer(in.readInt())); + i+=4; + } + group = GROUP_SWITCH; + + break; + case opc_lookupswitch: + in.skip((4-(i+1)%4)%4); + i+=((4-(i+1)%4)%4); // padding + operands.add(new Integer(in.readInt())); + i+=4; + int npairs = in.readInt(); + operands.add(new Integer(npairs)); + i+=4; + + for(int j=0;j<npairs;j++) { + operands.add(new Integer(in.readInt())); + i+=4; + operands.add(new Integer(in.readInt())); + i+=4; + } + group = GROUP_SWITCH; + break; + case opc_ireturn: + case opc_lreturn: + case opc_freturn: + case opc_dreturn: + case opc_areturn: + case opc_return: + case opc_athrow: + group = GROUP_RETURN; + } + } + + int[] ops = new int[operands.size()]; + for(int j=0;j<operands.size();j++) { + ops[j] = ((Integer)operands.get(j)).intValue(); + } + + Instruction instr = ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, ops); + + collinstr.addWithKey(instr, new Integer(offset)); + + i++; + } + + // initialize exception table + List<ExceptionHandler> lstHandlers = new ArrayList<ExceptionHandler>(); + + int exception_count = in.readUnsignedShort(); + for(int i=0;i<exception_count;i++) { + ExceptionHandler handler = new ExceptionHandler(); + handler.from = in.readUnsignedShort(); + handler.to = in.readUnsignedShort(); + handler.handler = in.readUnsignedShort(); + + int excclass = in.readUnsignedShort(); + handler.class_index = excclass; + if(excclass!=0) { + handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString(); + } + + lstHandlers.add(handler); + } + + InstructionSequence seq = new FullInstructionSequence(collinstr, new ExceptionTable(lstHandlers)); + + // initialize instructions + int i = seq.length()-1; + seq.setPointer(i); + + while(i>=0) { + Instruction instr = seq.getInstr(i--); + if(instr.group!=GROUP_GENERAL) { + instr.initInstruction(seq); + } + seq.addToPointer(-1); + } + + return seq; + + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public InstructionSequence getInstructionSequence() { + return seq; + } + + public String getDescriptor() { + return descriptor; + } + + public String getName() { + return name; + } + + public int getAccessFlags() { + return accessFlags; + } + + public int getLocalVariables() { + return localVariables; + } + + public VBStyleCollection<StructGeneralAttribute, String> getAttributes() { + return attributes; + } + + public StructClass getClassStruct() { + return classStruct; + } + + public boolean containsCode() { + return containsCode; + } +} + + diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java new file mode 100644 index 0000000..d171349 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java @@ -0,0 +1,40 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + + +public class StructAnnDefaultAttribute extends StructGeneralAttribute { + + private Exprent defaultValue; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_ANNOTATION_DEFAULT; + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool); + } + + public Exprent getDefaultValue() { + return defaultValue; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java new file mode 100644 index 0000000..8958299 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java @@ -0,0 +1,187 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class StructAnnotationAttribute extends StructGeneralAttribute { + + private List<AnnotationExprent> annotations; + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + annotations = new ArrayList<AnnotationExprent>(); + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 2, info.length)); + + int len = (((info[0] & 0xFF)<<8) | (info[1] & 0xFF)); + for(int i=0;i<len;i++) { + annotations.add(parseAnnotation(data, pool)); + } + + } + + public static AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) { + + try { + + String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + VarType cltype = new VarType(classname); + + int len = data.readUnsignedShort(); + + List<String> parnames = new ArrayList<String>(); + List<Exprent> parvalues = new ArrayList<Exprent>(); + + for(int i=0;i<len;i++) { + parnames.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); + parvalues.add(parseAnnotationElement(data, pool)); + } + + return new AnnotationExprent(cltype.value, parnames, parvalues); + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + + public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) { + + try { + int tag = data.readUnsignedByte(); + + switch(tag) { + case 'e': // enum constant + String classname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + String constname = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + + FieldDescriptor descr = FieldDescriptor.parseDescriptor(classname); + return new FieldExprent(constname, descr.type.value, true, null, descr); + case 'c': // class + String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); + VarType type = FieldDescriptor.parseDescriptor(descriptor).type; + + String value; + switch(type.type) { + case CodeConstants.TYPE_OBJECT: + value = type.value; + break; + case CodeConstants.TYPE_BYTE: + value = byte.class.getName(); + break; + case CodeConstants.TYPE_CHAR: + value = char.class.getName(); + break; + case CodeConstants.TYPE_DOUBLE: + value = double.class.getName(); + break; + case CodeConstants.TYPE_FLOAT: + value = float.class.getName(); + break; + case CodeConstants.TYPE_INT: + value = int.class.getName(); + break; + case CodeConstants.TYPE_LONG: + value = long.class.getName(); + break; + case CodeConstants.TYPE_SHORT: + value = short.class.getName(); + break; + case CodeConstants.TYPE_BOOLEAN: + value = boolean.class.getName(); + break; + case CodeConstants.TYPE_VOID: + value = void.class.getName(); + break; + default: + throw new RuntimeException("invalid class type: " + type.type); + } + return new ConstExprent(VarType.VARTYPE_CLASS, value); + case '[': // array + int len = data.readUnsignedShort(); + List<Exprent> lst = new ArrayList<Exprent>(); + + for(int i=0;i<len;i++) { + lst.add(parseAnnotationElement(data, pool)); + } + + VarType newtype; + if(lst.isEmpty()) { + newtype = new VarType(CodeConstants.TYPE_OBJECT, 1, "java/lang/Object"); + } else { + VarType eltype = lst.get(0).getExprType(); + newtype = new VarType(eltype.type, 1, eltype.value); + } + + NewExprent newexpr = new NewExprent(newtype, new ArrayList<Exprent>()); + newexpr.setDirectArrayInit(true); + newexpr.setLstArrayElements(lst); + return newexpr; + case '@': // annotation + return parseAnnotation(data, pool); + default: + PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); + switch(tag) { + case 'B': + return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); + case 'C': + return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); + case 'D': + return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); + case 'F': + return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); + case 'I': + return new ConstExprent(VarType.VARTYPE_INT, cn.value); + case 'J': + return new ConstExprent(VarType.VARTYPE_LONG, cn.value); + case 'S': + return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); + case 'Z': + return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); + case 's': + return new ConstExprent(VarType.VARTYPE_STRING, cn.value); + default: + throw new RuntimeException("invalid element type!"); + } + } + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + + + public List<AnnotationExprent> getAnnotations() { + return annotations; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java new file mode 100644 index 0000000..dd28aa7 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java @@ -0,0 +1,57 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructAnnotationParameterAttribute extends StructGeneralAttribute { + + private List<List<AnnotationExprent>> paramAnnotations; + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + paramAnnotations = new ArrayList<List<AnnotationExprent>>(); + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + + try { + int len = data.readUnsignedByte(); + for(int i=0;i<len;i++) { + List<AnnotationExprent> lst = new ArrayList<AnnotationExprent>(); + int annsize = data.readUnsignedShort(); + + for(int j=0;j<annsize;j++) { + lst.add(StructAnnotationAttribute.parseAnnotation(data, pool)); + } + paramAnnotations.add(lst); + } + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + + public List<List<AnnotationExprent>> getParamAnnotations() { + return paramAnnotations; + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java new file mode 100644 index 0000000..9677ddd --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java @@ -0,0 +1,188 @@ +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructAnnotationTypeAttribute extends StructGeneralAttribute { + + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; + public static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; + public static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; + public static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; + public static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; + public static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; + public static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; + public static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; + public static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; + public static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; + public static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; + public static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW = 0x45; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID = 0x46; + public static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR = 0x48; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_METHOD = 0x49; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW = 0x4A; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID = 0x4B; + + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; + public static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; + public static final int ANNOTATION_TARGET_UNION_EMPTY = 4; + public static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; + public static final int ANNOTATION_TARGET_UNION_THROWS = 6; + public static final int ANNOTATION_TARGET_UNION_LOCALVAR = 7; + public static final int ANNOTATION_TARGET_UNION_CATCH = 8; + public static final int ANNOTATION_TARGET_UNION_OFFSET = 9; + public static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; + + + List<AnnotationLocation> locations = new ArrayList<AnnotationLocation>(); + List<AnnotationExprent> annotations = new ArrayList<AnnotationExprent>(); + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + + try { + + int ann_number = data.readUnsignedByte(); + for(int i = 0; i < ann_number; i++) { + locations.add(parseAnnotationLocation(data)); + annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); + } + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public AnnotationLocation parseAnnotationLocation(DataInputStream data) throws IOException { + + AnnotationLocation ann_location = new AnnotationLocation(); + + // target type + + ann_location.target_type = data.readUnsignedByte(); + + // target union + + switch(ann_location.target_type) { + case ANNOTATION_TARGET_TYPE_GENERIC_CLASS: + case ANNOTATION_TARGET_TYPE_GENERIC_METHOD: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER; + break; + case ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS: + ann_location.target_union = ANNOTATION_TARGET_UNION_SUPERTYPE; + break; + case ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND: + case ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND; + break; + case ANNOTATION_TARGET_TYPE_FIELD: + case ANNOTATION_TARGET_TYPE_RETURN: + case ANNOTATION_TARGET_TYPE_RECEIVER: + ann_location.target_union = ANNOTATION_TARGET_UNION_EMPTY; + break; + case ANNOTATION_TARGET_TYPE_FORMAL: + ann_location.target_union = ANNOTATION_TARGET_UNION_FORMAL_PARAMETER; + break; + case ANNOTATION_TARGET_TYPE_THROWS: + ann_location.target_union = ANNOTATION_TARGET_UNION_THROWS; + break; + case ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE: + case ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE: + ann_location.target_union = ANNOTATION_TARGET_UNION_LOCALVAR; + break; + case ANNOTATION_TARGET_TYPE_EXCEPTION: + ann_location.target_union = ANNOTATION_TARGET_UNION_CATCH; + break; + case ANNOTATION_TARGET_TYPE_INSTANCEOF: + case ANNOTATION_TARGET_TYPE_NEW: + case ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID: + ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; + break; + case ANNOTATION_TARGET_TYPE_CAST: + case ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR: + case ANNOTATION_TARGET_TYPE_INVOKATION_METHOD: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID: + ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_ARGUMENT; + break; + default: + throw new RuntimeException("Unknown target type in a type annotation!"); + } + + // target union data + + switch(ann_location.target_union) { + case ANNOTATION_TARGET_UNION_TYPE_PARAMETER: + case ANNOTATION_TARGET_UNION_FORMAL_PARAMETER: + ann_location.data = new int[] {data.readUnsignedByte()}; + break; + case ANNOTATION_TARGET_UNION_SUPERTYPE: + case ANNOTATION_TARGET_UNION_THROWS: + case ANNOTATION_TARGET_UNION_CATCH: + case ANNOTATION_TARGET_UNION_OFFSET: + ann_location.data = new int[] {data.readUnsignedShort()}; + break; + case ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND: + ann_location.data = new int[] {data.readUnsignedByte(), data.readUnsignedByte()}; + break; + case ANNOTATION_TARGET_UNION_EMPTY: + break; + case ANNOTATION_TARGET_UNION_LOCALVAR: + int table_length = data.readUnsignedShort(); + + ann_location.data = new int[table_length * 3 + 1]; + ann_location.data[0] = table_length; + + for(int i = 0; i < table_length; ++i) { + ann_location.data[3 * i + 1] = data.readUnsignedShort(); + ann_location.data[3 * i + 2] = data.readUnsignedShort(); + ann_location.data[3 * i + 3] = data.readUnsignedShort(); + } + break; + case ANNOTATION_TARGET_UNION_TYPE_ARGUMENT: + ann_location.data = new int[] {data.readUnsignedShort(), data.readUnsignedByte()}; + } + + // target path + + int path_length = data.readUnsignedByte(); + + ann_location.target_path_kind = new int[path_length]; + ann_location.target_argument_index = new int[path_length]; + + for(int i = 0; i < path_length; ++i) { + ann_location.target_path_kind[i] = data.readUnsignedByte(); + ann_location.target_argument_index[i] = data.readUnsignedByte(); + } + + return ann_location; + } + + private static class AnnotationLocation { + + public int target_type; + public int target_union; + + public int[] data; + + public int[] target_path_kind; + public int[] target_argument_index; + } +} + diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java new file mode 100644 index 0000000..ac51027 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java @@ -0,0 +1,62 @@ +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.consts.PooledConstant; + +public class StructBootstrapMethodsAttribute extends StructGeneralAttribute { + + private List<LinkConstant> method_refs = new ArrayList<LinkConstant>(); + private List<List<PooledConstant>> method_arguments = new ArrayList<List<PooledConstant>>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_BOOTSTRAP_METHODS; + + try { + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 0, info.length)); + + int method_number = data.readUnsignedShort(); + + for(int i = 0; i < method_number; ++i) { + int bootstrap_method_ref = data.readUnsignedShort(); + int num_bootstrap_arguments = data.readUnsignedShort(); + + List<PooledConstant> list_arguments = new ArrayList<PooledConstant>(); + + for(int j = 0; j < num_bootstrap_arguments; ++j) { + int bootstrap_argument_ref = data.readUnsignedShort(); + + list_arguments.add(pool.getConstant(bootstrap_argument_ref)); + } + + method_refs.add(pool.getLinkConstant(bootstrap_method_ref)); + method_arguments.add(list_arguments); + } + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + + public int getMethodsNumber() { + return method_refs.size(); + } + + public LinkConstant getMethodReference(int index) { + return method_refs.get(index); + } + + public List<PooledConstant> getMethodArguments(int index) { + return method_arguments.get(index); + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java new file mode 100644 index 0000000..f186767 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java @@ -0,0 +1,34 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructConstantValueAttribute extends StructGeneralAttribute { + + private int index; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_CONSTANT_VALUE; + index = ((info[0] & 0xFF)<<8) | (info[1] & 0xFF); + } + + public int getIndex() { + return index; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java new file mode 100644 index 0000000..61bb886 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java @@ -0,0 +1,57 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; + +public class StructEnclosingMethodAttribute extends StructGeneralAttribute { + + private String classname; + + private String mtname; + + private String methodDescriptor; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_ENCLOSING_METHOD; + + int clindex = (((info[0] & 0xFF)<<8) | (info[1] & 0xFF)); + int mtindex = (((info[2] & 0xFF)<<8) | (info[3] & 0xFF)); + + classname = pool.getPrimitiveConstant(clindex).getString(); + if(mtindex != 0) { + LinkConstant lk = pool.getLinkConstant(mtindex); + + mtname = lk.elementname; + methodDescriptor = lk.descriptor; + } + } + + public String getClassname() { + return classname; + } + + public String getMethodDescriptor() { + return methodDescriptor; + } + + public String getMethodName() { + return mtname; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java new file mode 100644 index 0000000..4d6bd4c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java @@ -0,0 +1,78 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructExceptionsAttribute extends StructGeneralAttribute { + + private List<Integer> throwsExceptions = new ArrayList<Integer>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_EXCEPTIONS; + + int length = 2+(((info[0] & 0xFF)<<8) | (info[1] & 0xFF))*2; + for(int i=2;i<length;i+=2) { + int index = ((info[i] & 0xFF)<<8) | (info[i+1] & 0xFF); + throwsExceptions.add(index); + } + + } + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeShort(attribute_name_index); + + ByteArrayOutputStream codeout = new ByteArrayOutputStream(); + DataOutputStream dataout = new DataOutputStream(codeout); + + int len = throwsExceptions.size(); + dataout.writeShort(len); + + if(len>0) { + info = new byte[len*2]; + for(int i=0,j=0;i<len;i++,j+=2) { + int index = ((Integer)throwsExceptions.get(i)).intValue(); + info[j] = (byte)(index >> 8); + info[j+1] = (byte)(index & 0xFF); + } + dataout.write(info); + } + + out.writeInt(codeout.size()); + out.write(codeout.toByteArray()); + } + + public String getExcClassname(int index, ConstantPool pool) { + return pool.getPrimitiveConstant(((Integer)throwsExceptions.get(index)).intValue()).getString(); + } + + public List<Integer> getThrowsExceptions() { + return throwsExceptions; + } + + public void setThrowsExceptions(List<Integer> throwsExceptions) { + this.throwsExceptions = throwsExceptions; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java new file mode 100644 index 0000000..2648b3c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -0,0 +1,145 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +/* + attribute_info { + u2 attribute_name_index; + u4 attribute_length; + u1 info[attribute_length]; + } +*/ + +public class StructGeneralAttribute { + + public static final String ATTRIBUTE_CODE = "Code"; + public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; + public static final String ATTRIBUTE_SIGNATURE = "Signature"; + public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; + public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; + public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; + public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; + public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; + public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; + public static final String ATTRIBUTE_SYNTHETIC = "Synthetic"; + public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; + + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + protected int attribute_name_index; + + protected byte[] info; + + protected String name; + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToStream(DataOutputStream out) throws IOException { + out.writeShort(attribute_name_index); + out.writeInt(info.length); + if(info.length>0) { + out.write(info); + } + } + + public void initContent(ConstantPool pool) { + name = pool.getPrimitiveConstant(attribute_name_index).getString(); + } + + public static StructGeneralAttribute getMatchingAttributeInstance(int nameindex, String attrname) { + + StructGeneralAttribute attr; + + if(ATTRIBUTE_INNER_CLASSES.equals(attrname)) { + attr = new StructInnerClassesAttribute(); + } else if(ATTRIBUTE_CONSTANT_VALUE.equals(attrname)) { + attr = new StructConstantValueAttribute(); + } else if(ATTRIBUTE_SIGNATURE.equals(attrname)) { + attr = new StructGenericSignatureAttribute(); + } else if(ATTRIBUTE_ANNOTATION_DEFAULT.equals(attrname)) { + attr = new StructAnnDefaultAttribute(); + } else if(ATTRIBUTE_EXCEPTIONS.equals(attrname)) { + attr = new StructExceptionsAttribute(); + } else if(ATTRIBUTE_ENCLOSING_METHOD.equals(attrname)) { + attr = new StructEnclosingMethodAttribute(); + } else if(ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationAttribute(); + } else if(ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationParameterAttribute(); + } else if(ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationTypeAttribute(); + } else if(ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname)) { + attr = new StructLocalVariableTableAttribute(); + } else if(ATTRIBUTE_BOOTSTRAP_METHODS.equals(attrname)) { + attr = new StructBootstrapMethodsAttribute(); + } else if(ATTRIBUTE_SYNTHETIC.equals(attrname) || ATTRIBUTE_DEPRECATED.equals(attrname)) { + attr = new StructGeneralAttribute(); + } else { + // unsupported attribute + return null; + } + + attr.setAttribute_name_index(nameindex); + return attr; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public byte[] getInfo() { + return info; + } + + public void setInfo(byte[] info) { + this.info = info; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAttribute_name_index() { + return attribute_name_index; + } + + public void setAttribute_name_index(int attribute_name_index) { + this.attribute_name_index = attribute_name_index; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java new file mode 100644 index 0000000..11206e0 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java @@ -0,0 +1,34 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructGenericSignatureAttribute extends StructGeneralAttribute { + + private String signature; + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_SIGNATURE; + signature = pool.getPrimitiveConstant(((info[0] & 0xFF)<<8) | (info[1] & 0xFF)).getString(); + } + + public String getSignature() { + return signature; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java new file mode 100644 index 0000000..794da15 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java @@ -0,0 +1,73 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructInnerClassesAttribute extends StructGeneralAttribute { + + private List<int[]> classentries = new ArrayList<int[]>(); + + private List<String[]> stringentries = new ArrayList<String[]>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_INNER_CLASSES; + + int length = 2+(((info[0] & 0xFF)<<8) | (info[1] & 0xFF))*8; + int i=2; + + while(i<length) { + + int[] arr = new int[4]; + for(int j=0;j<4;j++) { + arr[j] = ((info[i] & 0xFF)<<8) | (info[i+1] & 0xFF); + i+=2; + } + + classentries.add(arr); + } + + for(int[] entry: classentries) { + + String[] arr = new String[3]; + // inner name + arr[0] = pool.getPrimitiveConstant(entry[0]).getString(); + //enclosing class + if(entry[1] != 0) { + arr[1] = pool.getPrimitiveConstant(entry[1]).getString(); + } + // original simple name + if(entry[2]!=0) { + arr[2] = pool.getPrimitiveConstant(entry[2]).getString(); + } + + stringentries.add(arr); + } + + } + + public List<int[]> getClassentries() { + return classentries; + } + + public List<String[]> getStringentries() { + return stringentries; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java new file mode 100644 index 0000000..119d1ff --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -0,0 +1,47 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.attr; + +import java.util.HashMap; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +public class StructLocalVariableTableAttribute extends StructGeneralAttribute { + + private HashMap<Integer, String> mapVarNames = new HashMap<Integer, String>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_LOCAL_VARIABLE_TABLE; + + int len = ((info[0] & 0xFF)<<8) | (info[1] & 0xFF); + + int ind = 6; + for(int i=0;i<len;i++, ind+=10) { + int nindex = ((info[ind] & 0xFF)<<8) | (info[ind+1] & 0xFF); + int vindex = ((info[ind+4] & 0xFF)<<8) | (info[ind+5] & 0xFF); + + mapVarNames.put(vindex, pool.getPrimitiveConstant(nindex).getString()); + } + } + + public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { + mapVarNames.putAll(attr.getMapVarNames()); + } + + public HashMap<Integer, String> getMapVarNames() { + return mapVarNames; + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java new file mode 100644 index 0000000..8951cd1 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -0,0 +1,316 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.consts; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; +import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class ConstantPool { + + public static final int FIELD = 1; + + public static final int METHOD = 2; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private List<PooledConstant> pool = new ArrayList<PooledConstant>(); + + private PoolInterceptor interceptor; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public ConstantPool(DataInputStream in) throws IOException { + + int size = in.readUnsignedShort(); + + int[] pass = new int[size]; + + // first dummy constant + pool.add(null); + + // first pass: read the elements + for (int i = 1; i < size; i++) { + + byte tag = (byte)in.readUnsignedByte(); + + switch (tag) { + case CodeConstants.CONSTANT_Utf8: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF())); + break; + case CodeConstants.CONSTANT_Integer: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, new Integer(in.readInt()))); + break; + case CodeConstants.CONSTANT_Float: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, new Float(in.readFloat()))); + break; + case CodeConstants.CONSTANT_Long: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, new Long(in.readLong()))); + pool.add(null); + i++; + break; + case CodeConstants.CONSTANT_Double: + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, new Double(in.readDouble()))); + pool.add(null); + i++; + break; + case CodeConstants.CONSTANT_Class: + case CodeConstants.CONSTANT_String: + case CodeConstants.CONSTANT_MethodType: + pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); + pass[i] = 1; + break; + case CodeConstants.CONSTANT_Fieldref: + case CodeConstants.CONSTANT_Methodref: + case CodeConstants.CONSTANT_InterfaceMethodref: + case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: + pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); + if(tag == CodeConstants.CONSTANT_NameAndType) { + pass[i] = 1; + } else { + pass[i] = 2; + } + break; + case CodeConstants.CONSTANT_MethodHandle: + pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); + pass[i] = 3; + break; + } + } + + + // resolving complex pool elements + for(int pass_index = 1; pass_index <= 3; pass_index++) { + for(int i = 1; i < size; i++) { + if(pass[i] == pass_index) { + pool.get(i).resolveConstant(this); + } + } + } + + // get global constant pool interceptor instance, if any available + interceptor = DecompilerContext.getPoolInterceptor(); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void writeToOutputStream(DataOutputStream out) throws FileNotFoundException, IOException { + + out.writeShort(pool.size()); + for(int i=1;i<pool.size();i++) { + PooledConstant cnst = (PooledConstant)pool.get(i); + if(cnst!=null) { + cnst.writeToStream(out); + } + } + } + + public static void skipPool(DataInputStream in) throws IOException { + + int size = in.readUnsignedShort(); + + for (int i = 1; i < size; i++) { + switch (in.readUnsignedByte()) { + case CodeConstants.CONSTANT_Utf8: + in.readUTF(); + break; + case CodeConstants.CONSTANT_Integer: + case CodeConstants.CONSTANT_Float: + case CodeConstants.CONSTANT_Fieldref: + case CodeConstants.CONSTANT_Methodref: + case CodeConstants.CONSTANT_InterfaceMethodref: + case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: + in.skip(4); + break; + case CodeConstants.CONSTANT_Long: + case CodeConstants.CONSTANT_Double: + in.skip(8); + i++; + break; + case CodeConstants.CONSTANT_Class: + case CodeConstants.CONSTANT_String: + case CodeConstants.CONSTANT_MethodType: + in.skip(2); + break; + case CodeConstants.CONSTANT_MethodHandle: + in.skip(3); + } + } + } + + public int size() { + return pool.size(); + } + + public String[] getClassElement(int element_type, int class_index, int name_index, int descriptor_index) { + + String classname = ((PrimitiveConstant)getConstant(class_index)).getString(); + String elementname = ((PrimitiveConstant)getConstant(name_index)).getString(); + String descriptor = ((PrimitiveConstant)getConstant(descriptor_index)).getString(); + + if(interceptor != null) { + String new_element = interceptor.getName(classname+" "+elementname+" "+descriptor); + + if(new_element != null) { + elementname = new_element.split(" ")[1]; + } + + String new_descriptor = buildNewDescriptor(element_type == FIELD?CodeConstants.CONSTANT_Fieldref:CodeConstants.CONSTANT_Methodref, + descriptor); + if(new_descriptor != null) { + descriptor = new_descriptor; + } + } + + return new String[] {elementname, descriptor}; + } + + public PooledConstant getConstant(int index) { + return pool.get(index); + } + + public PrimitiveConstant getPrimitiveConstant(int index) { + PrimitiveConstant cn = (PrimitiveConstant)getConstant(index); + + if(cn != null && interceptor != null) { + if(cn.type == CodeConstants.CONSTANT_Class) { + String newname = buildNewClassname(cn.getString()); + if(newname != null) { + cn = new PrimitiveConstant(CodeConstants.CONSTANT_Class, newname); + } + } + } + + return cn; + } + + public LinkConstant getLinkConstant(int index) { + LinkConstant ln = (LinkConstant)getConstant(index); + + if(ln != null && interceptor != null) { + if(ln.type == CodeConstants.CONSTANT_Fieldref || + ln.type == CodeConstants.CONSTANT_Methodref || + ln.type == CodeConstants.CONSTANT_InterfaceMethodref) { + + String new_classname = buildNewClassname(ln.classname); + String new_element = interceptor.getName(ln.classname+" "+ln.elementname+" "+ln.descriptor); + String new_descriptor = buildNewDescriptor(ln.type, ln.descriptor); + + if(new_classname != null || new_element != null || new_descriptor != null) { + + ln = new LinkConstant(ln.type, new_classname==null?ln.classname:new_classname, + new_element==null?ln.elementname:new_element.split(" ")[1], + new_descriptor==null?ln.descriptor:new_descriptor); + } + } + } + + return ln; + } + + private String buildNewClassname(String classname) { + + VarType vt = new VarType(classname, true); + + String newname = interceptor.getName(vt.value); + if(newname != null) { + StringBuilder buffer = new StringBuilder(); + + if(vt.arraydim > 0) { + for(int i=0;i<vt.arraydim;i++) { + buffer.append("["); + } + + buffer.append("L"+newname+";"); + } else { + buffer.append(newname); + } + + return buffer.toString(); + } + + return null; + } + + private String buildNewDescriptor(int type, String descriptor) { + + boolean updated = false; + + if(type == CodeConstants.CONSTANT_Fieldref) { + FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); + + VarType ftype = fd.type; + if(ftype.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(ftype.value); + if(newclname != null) { + ftype.value = newclname; + updated = true; + } + } + + if(updated) { + return fd.getDescriptor(); + } + + } else { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + // params + for(VarType partype : md.params) { + if(partype.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(partype.value); + if(newclname != null) { + partype.value = newclname; + updated = true; + } + } + } + + // return value + if(md.ret.type == CodeConstants.TYPE_OBJECT) { + String newclname = buildNewClassname(md.ret.value); + if(newclname!=null) { + md.ret.value = newclname; + updated = true; + } + } + + if(updated) { + return md.getDescriptor(); + } + } + + return null; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java new file mode 100644 index 0000000..770a5e3 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -0,0 +1,159 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.consts; + +import java.io.DataOutputStream; +import java.io.IOException; + +/* + * NameAndType, FieldRef, MethodRef, InterfaceMethodref + * InvokeDynamic, MethodHandle + */ + +public class LinkConstant extends PooledConstant { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int index1, index2; + + public String classname; + + public String elementname; + + public String descriptor; + + public int paramCount = 0; + + public boolean isVoid = false;; + + public boolean returnCategory2 = false; + + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public LinkConstant(int type, String classname, String elementname, String descriptor) { + this.type = type; + this.classname = classname; + this.elementname = elementname; + this.descriptor = descriptor; + + initConstant(); + } + + public LinkConstant(int type, int index1, int index2) { + this.type = type; + this.index1 = index1; + this.index2 = index2; + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void resolveConstant(ConstantPool pool) { + + if(type == CONSTANT_NameAndType) { + elementname = pool.getPrimitiveConstant(index1).getString(); + descriptor = pool.getPrimitiveConstant(index2).getString(); + } else if(type == CONSTANT_MethodHandle) { + LinkConstant ref_info = pool.getLinkConstant(index2); + + classname = ref_info.classname; + elementname = ref_info.elementname; + descriptor = ref_info.descriptor; + + } else { + if(type != CONSTANT_InvokeDynamic) { + classname = pool.getPrimitiveConstant(index1).getString(); + } + + LinkConstant nametype = pool.getLinkConstant(index2); + elementname = nametype.elementname; + descriptor = nametype.descriptor; + } + + initConstant(); + } + + public void writeToStream(DataOutputStream out) throws IOException { + out.writeByte(type); + if(type == CONSTANT_MethodHandle) { + out.writeByte(index1); + } else { + out.writeShort(index1); + } + out.writeShort(index2); + } + + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof LinkConstant)) return false; + + LinkConstant cn = (LinkConstant)o; + return this.type == cn.type && + this.elementname.equals(cn.elementname) && + this.descriptor.equals(cn.descriptor) && + (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname)); + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void initConstant() { + + if(type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref || type == CONSTANT_InvokeDynamic || type == CONSTANT_MethodHandle) { + resolveDescriptor(descriptor); + } else if(type == CONSTANT_Fieldref) { + returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor)); + } + + } + + private void resolveDescriptor(String descr){ + + String[] arr = descr.split("[()]"); + String par = arr[1]; + + int index = 0, counter = 0; + int len = par.length(); + + while(index<len) { + + char c = par.charAt(index); + if(c == 'L') { + index = par.indexOf(";", index); + } else if (c == '[') { + index++; + continue; + } + + counter++; + index++; + } + + paramCount = counter; + isVoid = "V".equals(arr[2]); + returnCategory2 = ("D".equals(arr[2]) || "J".equals(arr[2])); + } + +} + diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java new file mode 100644 index 0000000..ae46604 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java @@ -0,0 +1,116 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.consts; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.jetbrains.java.decompiler.code.CodeConstants; + +/* + cp_info { + u1 tag; + u1 info[]; + } + +*/ + +public class PooledConstant implements CodeConstants, VariableTypeEnum { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int type; + + public boolean own = false; + + public int returnType; + + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Object[] values; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public PooledConstant() {} + + public PooledConstant(int type, Object[] values) { + this.type = type; + this.values = values; + this.returnType = poolTypeToIntern(type); + } + + public PooledConstant(int type, boolean own, Object[] values) { + this.type = type; + this.own = own; + this.values = values; + this.returnType = poolTypeToIntern(type); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void resolveConstant(ConstantPool pool) { + // to be overwritten + } + + public void writeToStream(DataOutputStream out) throws IOException { + // to be overwritten + } + + public int poolTypeToIntern(int type) { + + switch(type){ + case CONSTANT_Integer: + return INT; + case CONSTANT_Float: + return FLOAT; + case CONSTANT_Long: + return LONG; + case CONSTANT_Double: + return DOUBLE; + case CONSTANT_String: + case CONSTANT_Class: // 1.5 -> ldc class + return REFERENCE; + default: + throw new RuntimeException("Huh?? What are you trying to load?"); + } + } + + public Object getValue(int index){ + return values[index]; + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Object[] getValues() { + return values; + } + + public void setValues(Object[] values) { + this.values = values; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java new file mode 100644 index 0000000..f01a695 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java @@ -0,0 +1,126 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.consts; + +import java.io.DataOutputStream; +import java.io.IOException; + +/* + * Integer, Long, Float, Double, String, Class, UTF8 + */ + +public class PrimitiveConstant extends PooledConstant { + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int index; + + public Object value; + + public boolean isArray; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public PrimitiveConstant(int type, Object value) { + this.type = type; + this.value = value; + + initConstant(); + } + + public PrimitiveConstant(int type, int index) { + this.type = type; + this.index = index; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public int getInt() { + return ((Integer)value).intValue(); + } + + public long getLong() { + return ((Long)value).longValue(); + } + + public float getFloat() { + return ((Float)value).floatValue(); + } + + public double getDouble() { + return ((Double)value).doubleValue(); + } + + public String getString() { + return (String)value; + } + + public void resolveConstant(ConstantPool pool) { + + if(type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { + value = pool.getPrimitiveConstant(index).getString(); + initConstant(); + } + } + + public void writeToStream(DataOutputStream out) throws IOException { + + out.writeByte(type); + switch(type) { + case CONSTANT_Integer: + out.writeInt(getInt()); + break; + case CONSTANT_Float: + out.writeFloat(getFloat()); + break; + case CONSTANT_Long: + out.writeLong(getLong()); + break; + case CONSTANT_Double: + out.writeDouble(getDouble()); + break; + case CONSTANT_Utf8: + out.writeUTF(getString()); + break; + default: // CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType + out.writeShort(index); + } + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof PrimitiveConstant)) return false; + + PrimitiveConstant cn = (PrimitiveConstant)o; + return this.type == cn.type && + this.isArray == cn.isArray && + this.value.equals(cn.value); + + } + + private void initConstant() { + if(type == CONSTANT_Class) { + String className = getString(); + isArray = (className.length() > 0 && className.charAt(0)=='['); // empty string for a class name seems to be possible in some android files + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java b/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java new file mode 100644 index 0000000..fc58c7e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java @@ -0,0 +1,47 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.consts; + +public interface VariableTypeEnum { + + public final static int BOOLEAN = 1; + public final static int BYTE = 2; + public final static int CHAR = 3; + public final static int SHORT = 4; + public final static int INT = 5; + public final static int FLOAT = 6; + public final static int LONG = 7; + public final static int DOUBLE = 8; + public final static int RETURN_ADDRESS = 9; + public final static int REFERENCE = 10; + public final static int INSTANCE_UNINITIALIZED = 11; + public final static int VALUE_UNKNOWN = 12; + public final static int VOID = 13; + + public final static Integer BOOLEAN_OBJ = new Integer(BOOLEAN); + public final static Integer BYTE_OBJ = new Integer(BYTE); + public final static Integer CHAR_OBJ = new Integer(CHAR); + public final static Integer SHORT_OBJ = new Integer(SHORT); + public final static Integer INT_OBJ = new Integer(INT); + public final static Integer FLOAT_OBJ = new Integer(FLOAT); + public final static Integer LONG_OBJ = new Integer(LONG); + public final static Integer DOUBLE_OBJ = new Integer(DOUBLE); + public final static Integer RETURN_ADDRESS_OBJ = new Integer(RETURN_ADDRESS); + public final static Integer REFERENCE_OBJ = new Integer(REFERENCE); + public final static Integer INSTANCE_UNINITIALIZED_OBJ = new Integer(INSTANCE_UNINITIALIZED); + public final static Integer VALUE_UNKNOWN_OBJ = new Integer(VALUE_UNKNOWN); + public final static Integer VOID_OBJ = new Integer(VOID); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java new file mode 100644 index 0000000..db6ecd1 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java @@ -0,0 +1,98 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.util.ListStack; + +public class DataPoint { + + private List<VarType> localVariables = new ArrayList<VarType>(); + + private ListStack<VarType> stack = new ListStack<VarType>(); + + + public void setVariable(int index, VarType value) { + if(index>=localVariables.size()) { + for(int i=localVariables.size();i<=index;i++) { + localVariables.add(new VarType(CodeConstants.TYPE_NOTINITIALIZED)); + } + } + + localVariables.set(index, value); + } + + public VarType getVariable(int index) { + if(index<localVariables.size()) { + return localVariables.get(index); + } else if(index<0) { + throw new IndexOutOfBoundsException(); + } else { + return new VarType(CodeConstants.TYPE_NOTINITIALIZED); + } + } + + public DataPoint copy() { + DataPoint point = new DataPoint(); + point.setLocalVariables(new ArrayList<VarType>(localVariables)); + point.setStack(stack.clone()); + return point; + } + + public static DataPoint getInitialDataPoint(StructMethod mt) { + + DataPoint point = new DataPoint(); + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int k = 0; + if((mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0) { + point.setVariable(k++, new VarType(CodeConstants.TYPE_OBJECT, 0, null)); + } + + for(int i=0;i<md.params.length;i++) { + VarType var = md.params[i]; + + point.setVariable(k++, var); + if(var.stack_size == 2) { + point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY)); + } + } + + return point; + } + + + public List<VarType> getLocalVariables() { + return localVariables; + } + + public void setLocalVariables(List<VarType> localVariables) { + this.localVariables = localVariables; + } + + public ListStack<VarType> getStack() { + return stack; + } + + public void setStack(ListStack<VarType> stack) { + this.stack = stack; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java new file mode 100644 index 0000000..a475937 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java @@ -0,0 +1,58 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen; + +public class FieldDescriptor { + + public static final FieldDescriptor INTEGER_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Integer;"); + public static final FieldDescriptor LONG_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Long;"); + public static final FieldDescriptor FLOAT_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Float;"); + public static final FieldDescriptor DOUBLE_DESCRIPTOR = FieldDescriptor.parseDescriptor("Ljava/lang/Double;"); + + public VarType type; + + public String descriptorString; + + private FieldDescriptor() {} + + public static FieldDescriptor parseDescriptor(String descr) { + + FieldDescriptor fd = new FieldDescriptor(); + + fd.type = new VarType(descr); + fd.descriptorString = descr; + + return fd; + } + + public String getDescriptor() { + return type.toString(); + } + + @Override + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof FieldDescriptor)) return false; + + FieldDescriptor fd = (FieldDescriptor)o; + return type.equals(fd.type); + } + + @Override + public int hashCode() { + return type.hashCode(); + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java new file mode 100644 index 0000000..77a8394 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -0,0 +1,102 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MethodDescriptor { + + public VarType[] params; + + public VarType ret; + + + public static MethodDescriptor parseDescriptor(String mdescr) { + + MethodDescriptor md = new MethodDescriptor(); + + List<String> lst = new ArrayList<String>(); + String[] pars = mdescr.split("[()]"); + + String par = pars[1]; + + int indexFrom = -1, ind,index = 0; + int len = par.length(); + + for(;index<len;index++) { + + switch(par.charAt(index)){ + case '[': + if(indexFrom<0){ + indexFrom = index; + } + break; + case 'L': + ind = par.indexOf(";", index); + lst.add(par.substring(indexFrom<0?index:indexFrom, ind+1)); + index = ind; + indexFrom = -1; + break; + default: + lst.add(par.substring(indexFrom<0?index:indexFrom, index+1)); + indexFrom = -1; + } + } + + lst.add(pars[2]); + + + md.params = new VarType[lst.size()-1]; + + int i = 0; + for(;i<lst.size()-1;i++) { + md.params[i] = new VarType(lst.get(i)); + } + md.ret = new VarType(lst.get(i)); + + return md; + } + + public String getDescriptor() { + String res = "("; + + for(int j = 0;j<params.length;j++) { + res+=params[j].toString(); + } + + res+=")"+ret.toString(); + + return res; + } + + @Override + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof MethodDescriptor)) return false; + + MethodDescriptor md = (MethodDescriptor)o; + return ret.equals(md.ret) && Arrays.equals(params, md.params); + } + + @Override + public int hashCode() { + int result = ret.hashCode(); + result = 31 * result + params.length; + return result; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java new file mode 100644 index 0000000..5ee0c4b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -0,0 +1,410 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class VarType { // TODO: optimize switch + + public static final int FALSEBOOLEAN = 1; + + public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN); + public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT); + public static final VarType VARTYPE_FLOAT = new VarType(CodeConstants.TYPE_FLOAT); + public static final VarType VARTYPE_LONG = new VarType(CodeConstants.TYPE_LONG); + public static final VarType VARTYPE_DOUBLE = new VarType(CodeConstants.TYPE_DOUBLE); + public static final VarType VARTYPE_BYTE = new VarType(CodeConstants.TYPE_BYTE); + public static final VarType VARTYPE_CHAR = new VarType(CodeConstants.TYPE_CHAR); + public static final VarType VARTYPE_SHORT = new VarType(CodeConstants.TYPE_SHORT); + public static final VarType VARTYPE_BOOLEAN = new VarType(CodeConstants.TYPE_BOOLEAN); + public static final VarType VARTYPE_BYTECHAR = new VarType(CodeConstants.TYPE_BYTECHAR); + public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR); + + public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL,0,null); + public static final VarType VARTYPE_GROUP2EMPTY = new VarType(CodeConstants.TYPE_GROUP2EMPTY); + public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String"); + public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"); + public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Object"); + public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID); + + public int type; + + public int type_family; + + public int arraydim; + + public String value; + + public int stack_size; + + public int convinfo; + + public VarType(int type) { + this.type = type; + this.arraydim = 0; + + value = getChar(type); + setStackSize(type); + setFamily(); + } + + public VarType(int type, int arraydim) { + this(type); + this.arraydim = arraydim; + setFamily(); + } + + public VarType(int type, int arraydim, String value) { + this(type); + this.arraydim = arraydim; + this.value = value; + setFamily(); + } + + public VarType(String strtype) { + this(strtype, false); + } + + public VarType(String strtype, boolean cltype) { + parseTypeString(strtype, cltype); + setStackSize(type); + setFamily(); + } + + public void decArrayDim() { + if(arraydim > 0) { + arraydim--; + setFamily(); + } else { + // throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case + } + } + + public String toString() { + String res = ""; + + for(int i=0;i<arraydim;i++) { + res+="["; + } + + if(type == CodeConstants.TYPE_OBJECT) { + res+="L"+value+";"; + } else { + res+=value; + } + + return res; + } + + public VarType copy() { + VarType v = new VarType(type, arraydim, value); + v.convinfo = convinfo; + return v; + } + + public boolean isFalseBoolean() { + return (convinfo & VarType.FALSEBOOLEAN) != 0; + } + + public boolean isSuperset(VarType val) { + + return this.equals(val) || this.isStrictSuperset(val); + } + + public boolean isStrictSuperset(VarType val) { + + int valtype = val.type; + + if(valtype == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) { + return true; + } + + if(val.arraydim > 0) { + return this.equals(VARTYPE_OBJECT); + } else if(arraydim > 0) { + return (valtype == CodeConstants.TYPE_NULL); + } + + boolean res = false; + + switch(type) { + case CodeConstants.TYPE_INT: + res |= (valtype == CodeConstants.TYPE_SHORT || + valtype == CodeConstants.TYPE_CHAR); + case CodeConstants.TYPE_SHORT: + res |= (valtype == CodeConstants.TYPE_BYTE); + case CodeConstants.TYPE_CHAR: + res |= (valtype == CodeConstants.TYPE_SHORTCHAR); + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_SHORTCHAR: + res |= (valtype == CodeConstants.TYPE_BYTECHAR); + case CodeConstants.TYPE_BYTECHAR: + res |= (valtype == CodeConstants.TYPE_BOOLEAN); + break; + case CodeConstants.TYPE_OBJECT: + if(valtype == CodeConstants.TYPE_NULL) { + return true; + } else if(this.equals(VARTYPE_OBJECT)) { + return valtype == CodeConstants.TYPE_OBJECT && + !val.equals(VARTYPE_OBJECT); + } + } + + return res; + } + + // type1 and type2 must not be null + public static VarType getCommonMinType(VarType type1, VarType type2) { + + if(type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type2 : type1; + } + + if(type1.isSuperset(type2)) { + return type2; + } else if(type2.isSuperset(type1)) { + return type1; + } else if(type1.type_family == type2.type_family) { + switch(type1.type_family) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT) + || (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) { + return VarType.VARTYPE_SHORTCHAR; + } else { + return VarType.VARTYPE_BYTECHAR; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_NULL; + } + } + + return null; + } + + // type1 and type2 must not be null + public static VarType getCommonSupertype(VarType type1, VarType type2) { + + if(type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type1 : type2; + } + + if(type1.isSuperset(type2)) { + return type1; + } else if(type2.isSuperset(type1)) { + return type2; + } else if(type1.type_family == type2.type_family) { + switch(type1.type_family) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE) + || (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) { + return VarType.VARTYPE_SHORT; + } else { + return VarType.VARTYPE_INT; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_OBJECT; + } + } + + return null; + } + + public static VarType getMinTypeInFamily(int family) { + switch(family) { + case CodeConstants.TYPE_FAMILY_BOOLEAN: + return VarType.VARTYPE_BOOLEAN; + case CodeConstants.TYPE_FAMILY_INTEGER: + return VarType.VARTYPE_BYTECHAR; + case CodeConstants.TYPE_FAMILY_OBJECT: + return VarType.VARTYPE_NULL; + case CodeConstants.TYPE_FAMILY_FLOAT: + return VarType.VARTYPE_FLOAT; + case CodeConstants.TYPE_FAMILY_LONG: + return VarType.VARTYPE_LONG; + case CodeConstants.TYPE_FAMILY_DOUBLE: + return VarType.VARTYPE_DOUBLE; + case CodeConstants.TYPE_FAMILY_UNKNOWN: + return VarType.VARTYPE_UNKNOWN; + default: + throw new RuntimeException("invalid type family!"); + } + } + + public boolean equals(Object o) { + + if(o == this) { + return true; + } + + if(o == null || !(o instanceof VarType)) { + return false; + } + + VarType vt = (VarType) o; + return type == vt.type && arraydim == vt.arraydim && InterpreterUtil.equalObjects(value, vt.value); + } + + private void parseTypeString(String strtype, boolean cltype) { + + for(int i=0;i<strtype.length();i++) { + switch(strtype.charAt(i)){ + case '[': + arraydim++; + break; + case 'L': + if(strtype.charAt(strtype.length()-1) == ';') { + type = CodeConstants.TYPE_OBJECT; + value = strtype.substring(i+1, strtype.length()-1); + return; + } + default: + value = strtype.substring(i, strtype.length()); + if((cltype && i == 0) || value.length()>1) { + type = CodeConstants.TYPE_OBJECT; + } else { + type = getType(value.charAt(0)); + } + return; + } + } + } + + private void setStackSize(int type) { + if(arraydim > 0) { + stack_size = 1; + } else { + stack_size = (type == CodeConstants.TYPE_DOUBLE || + type == CodeConstants.TYPE_LONG)?2: + ((type == CodeConstants.TYPE_VOID || + type == CodeConstants.TYPE_GROUP2EMPTY)?0:1); + } + } + + private int getType(char c) { + switch(c) { + case 'B': + return CodeConstants.TYPE_BYTE; + case 'C': + return CodeConstants.TYPE_CHAR; + case 'D': + return CodeConstants.TYPE_DOUBLE; + case 'F': + return CodeConstants.TYPE_FLOAT; + case 'I': + return CodeConstants.TYPE_INT; + case 'J': + return CodeConstants.TYPE_LONG; + case 'S': + return CodeConstants.TYPE_SHORT; + case 'Z': + return CodeConstants.TYPE_BOOLEAN; + case 'V': + return CodeConstants.TYPE_VOID; + case 'G': + return CodeConstants.TYPE_GROUP2EMPTY; + case 'N': + return CodeConstants.TYPE_NOTINITIALIZED; + case 'A': + return CodeConstants.TYPE_ADDRESS; + case 'X': + return CodeConstants.TYPE_BYTECHAR; + case 'Y': + return CodeConstants.TYPE_SHORTCHAR; + case 'U': + return CodeConstants.TYPE_UNKNOWN; + default: + throw new RuntimeException("Invalid type"); + } + } + + private String getChar(int type) { + switch(type) { + case CodeConstants.TYPE_BYTE: + return "B"; + case CodeConstants.TYPE_CHAR: + return "C"; + case CodeConstants.TYPE_DOUBLE: + return "D"; + case CodeConstants.TYPE_FLOAT: + return "F"; + case CodeConstants.TYPE_INT: + return "I"; + case CodeConstants.TYPE_LONG: + return "J"; + case CodeConstants.TYPE_SHORT: + return "S"; + case CodeConstants.TYPE_BOOLEAN: + return "Z"; + case CodeConstants.TYPE_VOID: + return "V"; + case CodeConstants.TYPE_GROUP2EMPTY: + return "G"; + case CodeConstants.TYPE_NOTINITIALIZED: + return "N"; + case CodeConstants.TYPE_ADDRESS: + return "A"; + case CodeConstants.TYPE_BYTECHAR: + return "X"; + case CodeConstants.TYPE_SHORTCHAR: + return "Y"; + case CodeConstants.TYPE_UNKNOWN: + return "U"; + case CodeConstants.TYPE_NULL: + case CodeConstants.TYPE_OBJECT: + return null; + default: + throw new RuntimeException("Invalid type"); + } + } + + public void setFamily() { + + if(arraydim > 0) { + this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; + return; + } + + switch(type) { + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_INT: + this.type_family = CodeConstants.TYPE_FAMILY_INTEGER; + break; + case CodeConstants.TYPE_DOUBLE: + this.type_family = CodeConstants.TYPE_FAMILY_DOUBLE; + break; + case CodeConstants.TYPE_FLOAT: + this.type_family = CodeConstants.TYPE_FAMILY_FLOAT; + break; + case CodeConstants.TYPE_LONG: + this.type_family = CodeConstants.TYPE_FAMILY_LONG; + break; + case CodeConstants.TYPE_BOOLEAN: + this.type_family = CodeConstants.TYPE_FAMILY_BOOLEAN; + break; + case CodeConstants.TYPE_NULL: + case CodeConstants.TYPE_OBJECT: + this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; + break; + default: + this.type_family = CodeConstants.TYPE_FAMILY_UNKNOWN; + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java new file mode 100644 index 0000000..d4551ac --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java @@ -0,0 +1,30 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen.generics; + +import java.util.ArrayList; +import java.util.List; + +public class GenericClassDescriptor { + + public GenericType superclass; + + public List<GenericType> superinterfaces = new ArrayList<GenericType>(); + + public List<String> fparameters = new ArrayList<String>(); + + public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java new file mode 100644 index 0000000..4b4114d --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java @@ -0,0 +1,21 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen.generics; + +public class GenericFieldDescriptor { + + public GenericType type; + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java new file mode 100644 index 0000000..50433a9 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java @@ -0,0 +1,229 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen.generics; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.struct.StructClass; + +public class GenericMain { + + private static final String[] typeNames = new String[] { + "byte", + "char", + "double", + "float", + "int", + "long", + "short", + "boolean", + }; + + public static GenericClassDescriptor parseClassSignature(String signature) { + + GenericClassDescriptor descriptor = new GenericClassDescriptor(); + + signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); + + String supercl = GenericType.getNextType(signature); + descriptor.superclass = new GenericType(supercl); + + signature = signature.substring(supercl.length()); + while(signature.length() > 0) { + String superintr = GenericType.getNextType(signature); + descriptor.superinterfaces.add(new GenericType(superintr)); + signature = signature.substring(superintr.length()); + } + + return descriptor; + } + + public static GenericFieldDescriptor parseFieldSignature(String signature) { + GenericFieldDescriptor descriptor = new GenericFieldDescriptor(); + descriptor.type = new GenericType(signature); + return descriptor; + } + + public static GenericMethodDescriptor parseMethodSignature(String signature) { + + GenericMethodDescriptor descriptor = new GenericMethodDescriptor(); + + signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); + + int to = signature.indexOf(")"); + String pars = signature.substring(1, to); + signature = signature.substring(to+1); + + while(pars.length() > 0) { + String par = GenericType.getNextType(pars); + descriptor.params.add(new GenericType(par)); + pars = pars.substring(par.length()); + } + + String par = GenericType.getNextType(signature); + descriptor.ret = new GenericType(par); + signature = signature.substring(par.length()); + + if(signature.length() > 0) { + String[] excs = signature.split("\\^"); + + for(int i=1;i<excs.length;i++) { + descriptor.exceptions.add(new GenericType(excs[i])); + } + } + + return descriptor; + } + + private static String parseFormalParameters(String signature, List<String> fparameters, List<List<GenericType>> fbounds) { + + if(signature.charAt(0) != '<') { + return signature; + } + + int counter = 1; + int index = 1; + + loop: + while(index < signature.length()) { + switch(signature.charAt(index)) { + case '<': + counter++; + break; + case '>': + counter--; + if(counter == 0) { + break loop; + } + } + + index++; + } + + String value = signature.substring(1, index); + signature = signature.substring(index+1); + + while(value.length() > 0) { + int parto = value.indexOf(":"); + + String param = value.substring(0, parto); + value = value.substring(parto+1); + + List<GenericType> lstBounds = new ArrayList<GenericType>(); + + for(;;) { + if(value.charAt(0) == ':') { + // empty superclass, skip + value = value.substring(1); + } + + String bound = GenericType.getNextType(value); + lstBounds.add(new GenericType(bound)); + value = value.substring(bound.length()); + + + if(value.length() == 0 || value.charAt(0) != ':') { + break; + } else { + value = value.substring(1); + } + } + + fparameters.add(param); + fbounds.add(lstBounds); + } + + return signature; + } + + public static String getGenericCastTypeName(GenericType type) { + String s = getTypeName(type); + int dim = type.arraydim; + while(dim-->0) { + s+="[]"; + } + return s; + } + + public static String getTypeName(GenericType type) { + + int tp = type.type; + if(tp <= CodeConstants.TYPE_BOOLEAN) { + return typeNames[tp]; + } else if(tp == CodeConstants.TYPE_VOID) { + return "void"; + } else if(tp == CodeConstants.TYPE_GENVAR) { + return type.value; + } else if(tp == CodeConstants.TYPE_OBJECT) { + StringBuilder buffer = new StringBuilder(); + buffer.append(DecompilerContext.getImpcollector().getShortName(buildJavaClassName(type))); + + if(!type.getArguments().isEmpty()) { + buffer.append("<"); + for(int i=0;i<type.getArguments().size();i++) { + if(i>0) { + buffer.append(", "); + } + int wildcard = type.getWildcards().get(i); + if(wildcard != GenericType.WILDCARD_NO) { + buffer.append("?"); + + switch(wildcard){ + case GenericType.WILDCARD_EXTENDS: + buffer.append(" extends "); + break; + case GenericType.WILDCARD_SUPER: + buffer.append(" super "); + } + } + + GenericType genpar = type.getArguments().get(i); + if(genpar != null) { + buffer.append(GenericMain.getGenericCastTypeName(genpar)); + } + } + buffer.append(">"); + } + + return buffer.toString(); + } + + throw new RuntimeException("invalid type"); + } + + public static String buildJavaClassName(GenericType type) { + + String name = ""; + for(GenericType tp : type.getEnclosingClasses()) { + name += tp.value+"$"; + } + name+=type.value; + + String res = name.replace('/', '.'); + + if(res.indexOf("$") >=0) { + StructClass cl = DecompilerContext.getStructcontext().getClass(name); + if(cl == null || !cl.isOwn()) { + res = res.replace('$', '.'); + } + } + + return res; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java new file mode 100644 index 0000000..7a0a4d1 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java @@ -0,0 +1,32 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen.generics; + +import java.util.ArrayList; +import java.util.List; + +public class GenericMethodDescriptor { + + public List<String> fparameters = new ArrayList<String>(); + + public List<List<GenericType>> fbounds = new ArrayList<List<GenericType>>(); + + public List<GenericType> params = new ArrayList<GenericType>(); + + public GenericType ret; + + public List<GenericType> exceptions = new ArrayList<GenericType>(); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java new file mode 100644 index 0000000..1f720e6 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -0,0 +1,270 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.gen.generics; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; + +public class GenericType { + + public static final int WILDCARD_EXTENDS = 1; + public static final int WILDCARD_SUPER = 2; + public static final int WILDCARD_UNBOUND = 3; + public static final int WILDCARD_NO = 4; + + public int type; + + public int arraydim; + + public String value; + + + private List<GenericType> enclosingClasses = new ArrayList<GenericType>(); + + private List<GenericType> arguments = new ArrayList<GenericType>(); + + private List<Integer> wildcards = new ArrayList<Integer>(); + + + public GenericType(int type, int arraydim, String value) { + this.type = type; + this.arraydim = arraydim; + this.value = value; + } + + + public GenericType(String strtype) { + + parseSignature(strtype); + + } + + private void parseSignature(String sig) { + + int index = 0; + while(index < sig.length()) { + + switch(sig.charAt(index)){ + case '[': + arraydim++; + break; + case 'T': + type = CodeConstants.TYPE_GENVAR; + value = sig.substring(index+1, sig.length()-1); + return; + case 'L': + type = CodeConstants.TYPE_OBJECT; + sig = sig.substring(index+1, sig.length()-1); + + for(;;) { + String cl = getNextClassSignature(sig); + + String name = cl; + String args = null; + + int argfrom = cl.indexOf("<"); + if(argfrom >= 0) { + name = cl.substring(0, argfrom); + args = cl.substring(argfrom+1, cl.length()-1); + } + + if(cl.length() < sig.length()) { + sig = sig.substring(cl.length()+1); // skip '.' + GenericType type = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); + parseArgumentsList(args, type); + enclosingClasses.add(type); + } else { + value = name; + parseArgumentsList(args, this); + break; + } + } + + return; + default: + value = sig.substring(index, index+1); + type = getType(value.charAt(0)); + } + + index++; + } + + } + + private String getNextClassSignature(String value) { + + int counter = 0; + int index = 0; + + loop: + while(index < value.length()) { + switch(value.charAt(index)) { + case '<': + counter++; + break; + case '>': + counter--; + break; + case '.': + if(counter == 0) { + break loop; + } + } + + index++; + } + + return value.substring(0, index); + } + + private void parseArgumentsList(String value, GenericType type) { + + if(value == null) { + return; + } + + while(value.length() > 0) { + + String tstr = getNextType(value); + int len = tstr.length(); + int wildcard = WILDCARD_NO; + + switch(tstr.charAt(0)) { + case '*': + wildcard = WILDCARD_UNBOUND; + break; + case '+': + wildcard = WILDCARD_EXTENDS; + break; + case '-': + wildcard = WILDCARD_SUPER; + break; + } + + type.getWildcards().add(wildcard); + + if(wildcard != WILDCARD_NO) { + tstr = tstr.substring(1); + } + + type.getArguments().add(tstr.length() == 0?null:new GenericType(tstr)); + + value = value.substring(len); + } + + } + + public static String getNextType(String value) { + + int counter = 0; + int index = 0; + + boolean contmode = false; + + loop: + while(index < value.length()) { + switch(value.charAt(index)) { + case '*': + if(!contmode) { + break loop; + } + break; + case 'L': + case 'T': + if(!contmode) { + contmode = true; + } + case '[': + case '+': + case '-': + break; + default: + if(!contmode) { + break loop; + } + break; + case '<': + counter++; + break; + case '>': + counter--; + break; + case ';': + if(counter == 0) { + break loop; + } + } + + index++; + } + + return value.substring(0, index+1); + } + + private int getType(char c) { + switch(c) { + case 'B': + return CodeConstants.TYPE_BYTE; + case 'C': + return CodeConstants.TYPE_CHAR; + case 'D': + return CodeConstants.TYPE_DOUBLE; + case 'F': + return CodeConstants.TYPE_FLOAT; + case 'I': + return CodeConstants.TYPE_INT; + case 'J': + return CodeConstants.TYPE_LONG; + case 'S': + return CodeConstants.TYPE_SHORT; + case 'Z': + return CodeConstants.TYPE_BOOLEAN; + case 'V': + return CodeConstants.TYPE_VOID; + case 'G': + return CodeConstants.TYPE_GROUP2EMPTY; + case 'N': + return CodeConstants.TYPE_NOTINITIALIZED; + case 'A': + return CodeConstants.TYPE_ADDRESS; + case 'X': + return CodeConstants.TYPE_BYTECHAR; + case 'Y': + return CodeConstants.TYPE_SHORTCHAR; + case 'U': + return CodeConstants.TYPE_UNKNOWN; + default: + throw new RuntimeException("Invalid type"); + } + } + + + public List<GenericType> getArguments() { + return arguments; + } + + + public List<GenericType> getEnclosingClasses() { + return enclosingClasses; + } + + + public List<Integer> getWildcards() { + return wildcards; + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java new file mode 100644 index 0000000..94cef9a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -0,0 +1,186 @@ +/* + * Fernflower - The Analytical Java Decompiler + * http://www.reversed-java.com + * + * (C) 2008 - 2010, Stiver + * + * This software is NEITHER public domain NOR free software + * as per GNU License. See license.txt for more details. + * + * This software is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +package org.jetbrains.java.decompiler.struct.lazy; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +public class LazyLoader { + + private HashMap<String, Link> mapClassLinks = new HashMap<String, Link>(); + + private IBytecodeProvider provider; + + public LazyLoader(IBytecodeProvider provider) { + this.provider = provider; + } + + public void addClassLink(String classname, Link link) { + mapClassLinks.put(classname, link); + } + + public void removeClassLink(String classname) { + mapClassLinks.remove(classname); + } + + public Link getClassLink(String classname) { + return mapClassLinks.get(classname); + } + + + public ConstantPool loadPool(String classname) { + + try { + + DataInputFullStream in = getClassStream(classname); + if(in == null) { + return null; + } + + in.skip(8); + + return new ConstantPool(in); + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public byte[] loadBytecode(StructMethod mt, int code_fulllength) { + + try { + + DataInputFullStream in = getClassStream(mt.getClassStruct().qualifiedName); + if(in == null) { + return null; + } + + byte[] res = null; + + in.skip(8); + + ConstantPool pool = mt.getClassStruct().getPool(); + if(pool == null) { + pool = new ConstantPool(in); + } else { + ConstantPool.skipPool(in); + } + + in.skip(2); + int this_class = in.readUnsignedShort(); + in.skip(2); + + // interfaces + in.skip(in.readUnsignedShort() * 2); + + // fields + int size = in.readUnsignedShort(); + for (int i = 0; i < size; i++) { + in.skip(6); + skipAttributes(in); + } + + // methods + size = in.readUnsignedShort(); + for (int i = 0; i < size; i++) { + in.skip(2); + + int name_index = in.readUnsignedShort(); + int descriptor_index = in.readUnsignedShort(); + + String elem_arr[] = pool.getClassElement(ConstantPool.METHOD, this_class, name_index, descriptor_index); + String name = elem_arr[0]; + + if(mt.getName().equals(name)) { + String descriptor = elem_arr[1]; + if(mt.getDescriptor().equals(descriptor)) { + + int len = in.readUnsignedShort(); + for(int j=0;j<len;j++) { + + int attr_nameindex = in.readUnsignedShort(); + String attrname = pool.getPrimitiveConstant(attr_nameindex).getString(); + + if(StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrname)) { + in.skip(12); + + res = new byte[code_fulllength]; + in.readFull(res); + return res; + } else { + in.skip(in.readInt()); + } + } + + return null; + } + } + + skipAttributes(in); + } + + return null; + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + + public DataInputFullStream getClassStream(String externPath, String internPath) throws IOException { + InputStream instream = provider.getBytecodeStream(externPath, internPath); + return instream == null?null:new DataInputFullStream(instream); + } + + public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException { + Link link = mapClassLinks.get(qualifiedClassName); + return link == null?null:getClassStream(link.externPath, link.internPath); + } + + private void skipAttributes(DataInputFullStream in) throws IOException { + + int length = in.readUnsignedShort(); + for (int i = 0; i < length; i++) { + in.skip(2); + in.skip(in.readInt()); + } + + } + + + public static class Link { + + public static final int CLASS = 1; + public static final int ENTRY = 2; + + public int type; + public String externPath; + public String internPath; + + public Link(int type, String externPath, String internPath) { + this.type = type; + this.externPath = externPath; + this.internPath = internPath; + } + } + +} |