/* * 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.main; import java.io.BufferedWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; 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.NewExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructAnnDefaultAttribute; import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute; import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; import org.jetbrains.java.decompiler.struct.attr.StructConstantValueAttribute; import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; public class ClassWriter { private static final int[] modval_class = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_STRICT}; private static final String[] modstr_class = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "strictfp "}; private static final int[] modval_field = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_TRANSIENT, CodeConstants.ACC_VOLATILE}; private static final String[] modstr_field = new String[] {"public ", "protected ", "private ", "static ", "final ", "transient ", "volatile "}; private static final int[] modval_meth = new int[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_PROTECTED, CodeConstants.ACC_PRIVATE, CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL, CodeConstants.ACC_SYNCHRONIZED, CodeConstants.ACC_NATIVE, CodeConstants.ACC_STRICT}; private static final String[] modstr_meth = new String[] {"public ", "protected ", "private ", "abstract ", "static ", "final ", "synchronized ", "native ", "strictfp "}; private static final HashSet mod_notinterface = new HashSet(Arrays.asList(new Integer[] {CodeConstants.ACC_ABSTRACT, CodeConstants.ACC_STATIC})); private static final HashSet mod_notinterface_fields = new HashSet(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_STATIC, CodeConstants.ACC_FINAL})); private static final HashSet mod_notinterface_meth = new HashSet(Arrays.asList(new Integer[] {CodeConstants.ACC_PUBLIC, CodeConstants.ACC_ABSTRACT})); private ClassReference14Processor ref14processor; private PoolInterceptor interceptor; public ClassWriter() { ref14processor = new ClassReference14Processor(); interceptor = DecompilerContext.getPoolInterceptor(); } private void invokeProcessors(ClassNode node) { ClassWrapper wrapper = node.wrapper; StructClass cl = wrapper.getClassStruct(); InitializerProcessor.extractInitializers(wrapper); if(node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { ref14processor.processClassReferences(node); } if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (cl.access_flags & CodeConstants.ACC_ENUM) != 0) { EnumProcessor.clearEnum(wrapper); } if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { AssertProcessor.buildAssertions(node); } } public void classLambdaToJava(ClassNode node, BufferedWriter writer, Exprent method_object, int indent) throws IOException { // get the class node with the content method ClassNode node_content = node; while(node_content != null && node_content.type == ClassNode.CLASS_LAMBDA) { node_content = node_content.parent; } if(node_content == null) { return; } boolean lambda_to_anonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); ClassWrapper wrapper = node_content.wrapper; StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(node.simpleName); if(node.lambda_information.is_method_reference) { if(!node.lambda_information.is_content_method_static && method_object != null) { // reference to a virtual method writer.write(method_object.toJava(indent)); } else { // reference to a static method writer.write(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); } writer.write("::"); writer.write(node.lambda_information.content_method_name); writer.flush(); } else { // lambda method StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); if(!lambda_to_anonymous) { // lambda parameters '() ->' StringBuilder buff = new StringBuilder("("); boolean firstpar = true; int index = node.lambda_information.is_content_method_static ? 0 : 1;; int start_index = md_content.params.length - md_lambda.params.length; for(int i=0;i= start_index) { if(!firstpar) { buff.append(", "); } String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); buff.append(parname==null ? "param"+index : parname); // null iff decompiled with errors firstpar = false; } index+=md_content.params[i].stack_size; } buff.append(") ->"); writer.write(buff.toString()); } StringWriter strwriter = new StringWriter(); BufferedWriter bufstrwriter = new BufferedWriter(strwriter); if(lambda_to_anonymous) { methodLambdaToJava(node, node_content, mt, bufstrwriter, indent+1, false); } else { methodLambdaToJava(node, node_content, mt, bufstrwriter, indent, true); } bufstrwriter.flush(); // closing up class definition writer.write(" {"); writer.write(DecompilerContext.getNewLineSeparator()); writer.write(strwriter.toString()); writer.write(InterpreterUtil.getIndentString(indent)); writer.write("}"); writer.flush(); } DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); DecompilerContext.getLogger().endWriteClass(); } public void classToJava(ClassNode node, BufferedWriter writer, int indent) throws IOException { ClassWrapper wrapper = node.wrapper; StructClass cl = wrapper.getClassStruct(); ClassNode nodeold = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, node); // last minute processing invokeProcessors(node); DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); writeClassDefinition(node, writer, indent); // methods StringWriter strwriter = new StringWriter(); BufferedWriter bufstrwriter = new BufferedWriter(strwriter); boolean firstmt = true; boolean mthidden = false; for(StructMethod mt : cl.getMethods()) { int flags = mt.getAccessFlags(); boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && (!isBridge || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE)) && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()))) { if(!mthidden && (!firstmt || node.type != ClassNode.CLASS_ANONYMOUS)) { bufstrwriter.write(DecompilerContext.getNewLineSeparator()); firstmt = false; } mthidden = !methodToJava(node, mt, bufstrwriter, indent+1); } } bufstrwriter.flush(); StringWriter strwriter1 = new StringWriter(); BufferedWriter bufstrwriter1 = new BufferedWriter(strwriter1); int fields_count = 0; boolean enumfields = false; // fields for(StructField fd: cl.getFields()) { int flags = fd.access_flags; boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && !wrapper.getHideMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; if(isEnum) { if(enumfields) { bufstrwriter1.write(","); bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); } else { enumfields = true; } } else { if(enumfields) { bufstrwriter1.write(";"); bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); enumfields = false; } } fieldToJava(wrapper, cl, fd, bufstrwriter1, indent+1); fields_count++; } } if(enumfields) { bufstrwriter1.write(";"); bufstrwriter1.write(DecompilerContext.getNewLineSeparator()); } bufstrwriter1.flush(); if(fields_count > 0) { writer.write(DecompilerContext.getNewLineSeparator()); writer.write(strwriter1.toString()); writer.write(DecompilerContext.getNewLineSeparator()); } // methods writer.write(strwriter.toString()); // member classes for(ClassNode inner : node.nested) { if(inner.type == ClassNode.CLASS_MEMBER) { StructClass innercl = inner.classStruct; boolean isSynthetic = ((inner.access | innercl.access_flags) & CodeConstants.ACC_SYNTHETIC) != 0 || innercl.getAttributes().containsKey("Synthetic"); if((!isSynthetic || !DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC)) && !wrapper.getHideMembers().contains(innercl.qualifiedName)) { writer.write(DecompilerContext.getNewLineSeparator()); classToJava(inner, writer, indent+1); } } } writer.write(InterpreterUtil.getIndentString(indent)); writer.write("}"); if(node.type != ClassNode.CLASS_ANONYMOUS) { writer.write(DecompilerContext.getNewLineSeparator()); } writer.flush(); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASSNODE, nodeold); DecompilerContext.getLogger().endWriteClass(); } private void writeClassDefinition(ClassNode node, BufferedWriter writer, int indent) throws IOException { if(node.type == ClassNode.CLASS_ANONYMOUS) { writer.write(" {"); writer.write(DecompilerContext.getNewLineSeparator()); } else { String indstr = InterpreterUtil.getIndentString(indent); ClassWrapper wrapper = node.wrapper; StructClass cl = wrapper.getClassStruct(); int flags = node.type == ClassNode.CLASS_ROOT?cl.access_flags:node.access; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); if(interceptor != null) { String oldname = interceptor.getOldName(cl.qualifiedName); if(oldname != null) { writer.write(indstr); writer.write("// $FF: renamed from: "+getDescriptorPrintOut(oldname, 0)); writer.write(DecompilerContext.getNewLineSeparator()); } } if (isDeprecated) { writer.write(indstr); writer.write("/** @deprecated */"); writer.write(DecompilerContext.getNewLineSeparator()); } // class annotations List lstAnn = getAllAnnotations(cl.getAttributes()); for(AnnotationExprent annexpr : lstAnn) { writer.write(annexpr.toJava(indent)); writer.write(DecompilerContext.getNewLineSeparator()); } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); if(isSynthetic) { writer.write(indstr); writer.write("// $FF: synthetic class"); writer.write(DecompilerContext.getNewLineSeparator()); } writer.write(indstr); if(isEnum) { // remove abstract and final flags (JLS 8.9 Enums) flags &=~CodeConstants.ACC_ABSTRACT; flags &=~CodeConstants.ACC_FINAL; } for(int i=0;i0) { writer.write(", "); } writer.write(descriptor.fparameters.get(i)); List lstBounds = descriptor.fbounds.get(i); if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { writer.write(" extends "); writer.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); for(int j=1;j"); } writer.write(" "); if(!isEnum && !isInterface && cl.superClass != null) { VarType supertype = new VarType(cl.superClass.getString(), true); if(!VarType.VARTYPE_OBJECT.equals(supertype)) { writer.write("extends "); if(descriptor != null) { writer.write(GenericMain.getGenericCastTypeName(descriptor.superclass)); } else { writer.write(ExprProcessor.getCastTypeName(supertype)); } writer.write(" "); } } if(!isAnnotation) { int[] interfaces = cl.getInterfaces(); if(interfaces.length > 0) { writer.write(isInterface?"extends ":"implements "); for(int i=0;i0) { writer.write(", "); } if(descriptor != null) { writer.write(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); } else { writer.write(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); } } writer.write(" "); } } writer.write("{"); writer.write(DecompilerContext.getNewLineSeparator()); } } private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, BufferedWriter writer, int indent) throws IOException { String indstr = InterpreterUtil.getIndentString(indent); boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; int flags = fd.access_flags; if(interceptor != null) { String oldname = interceptor.getOldName(cl.qualifiedName+" "+fd.getName()+" "+fd.getDescriptor()); if(oldname != null) { String[] element = oldname.split(" "); writer.write(indstr); writer.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 1)); writer.write(DecompilerContext.getNewLineSeparator()); } } boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); if (isDeprecated) { writer.write(indstr); writer.write("/** @deprecated */"); writer.write(DecompilerContext.getNewLineSeparator()); } // field annotations List lstAnn = getAllAnnotations(fd.getAttributes()); for(AnnotationExprent annexpr : lstAnn) { writer.write(annexpr.toJava(indent)); writer.write(DecompilerContext.getNewLineSeparator()); } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || fd.getAttributes().containsKey("Synthetic"); boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; if(isSynthetic) { writer.write(indstr); writer.write("// $FF: synthetic field"); writer.write(DecompilerContext.getNewLineSeparator()); } writer.write(indstr); if(!isEnum) { for(int i=0;i= start_index) { if(!firstpar) { bufstrwriter.write(", "); } VarType partype = md_content.params[i].copy(); String strpartype = ExprProcessor.getCastTypeName(partype); if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } bufstrwriter.write(strpartype); bufstrwriter.write(" "); String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors firstpar = false; } index+=md_content.params[i].stack_size; } bufstrwriter.write(")"); bufstrwriter.write(" "); bufstrwriter.write("{"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if(root != null && !meth.decompiledWithErrors) { // check for existence try { String code = root.toJava(indent+1); bufstrwriter.write(code); } catch(Throwable ex) { DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); meth.decompiledWithErrors = true; } } if(meth.decompiledWithErrors) { bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); bufstrwriter.write("// $FF: Couldn't be decompiled"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } if(!code_only) { bufstrwriter.write(indstr+"}"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } bufstrwriter.flush(); writer.write(strwriter.toString()); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); return true; } public boolean methodToJava(ClassNode node, StructMethod mt, BufferedWriter writer, int indent) throws IOException { ClassWrapper wrapper = node.wrapper; StructClass cl = wrapper.getClassStruct(); MethodWrapper meth = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodWrapper methold = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, meth); boolean isInterface = (cl.access_flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (cl.access_flags & CodeConstants.ACC_ANNOTATION) != 0; boolean isEnum = (cl.access_flags & CodeConstants.ACC_ENUM) != 0 && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); String indstr = InterpreterUtil.getIndentString(indent); boolean clinit = false, init = false, dinit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); StringWriter strwriter = new StringWriter(); BufferedWriter bufstrwriter = new BufferedWriter(strwriter); int flags = mt.getAccessFlags(); if((flags & CodeConstants.ACC_NATIVE) != 0) { flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp } if("".equals(mt.getName())) { flags &= CodeConstants.ACC_STATIC; // ingnore all modifiers except 'static' in a static initializer } if(interceptor != null) { String oldname = interceptor.getOldName(cl.qualifiedName+" "+mt.getName()+" "+mt.getDescriptor()); if(oldname != null) { String[] element = oldname.split(" "); bufstrwriter.write(indstr); bufstrwriter.write("// $FF: renamed from: "+element[1]+" "+getDescriptorPrintOut(element[2], 2)); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } } if (isDeprecated) { writer.write(indstr); writer.write("/** @deprecated */"); writer.write(DecompilerContext.getNewLineSeparator()); } // method annotations List lstAnn = getAllAnnotations(mt.getAttributes()); for(AnnotationExprent annexpr : lstAnn) { bufstrwriter.write(annexpr.toJava(indent)); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; if(isSynthetic) { bufstrwriter.write(indstr); bufstrwriter.write("// $FF: synthetic method"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } if(isBridge) { bufstrwriter.write(indstr); bufstrwriter.write("// $FF: bridge method"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } bufstrwriter.write(indstr); for(int i=0;i".equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; dinit = true; } else { name = node.simpleName; init = true; } } else if ("".equals(name)) { name = ""; clinit = true; } GenericMethodDescriptor descriptor = null; if(DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); if(attr != null) { descriptor = GenericMain.parseMethodSignature(attr.getSignature()); int actualParams = md.params.length; if(isEnum && init) actualParams -= 2; if(actualParams != descriptor.params.size()) { DecompilerContext.getLogger().writeMessage("Inconsistent generic signature in method "+mt.getName()+" "+mt.getDescriptor(), IFernflowerLogger.WARNING); descriptor = null; } } } boolean throwsExceptions = false; int param_count_explicit = 0; if(!clinit && !dinit) { boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; // formal type parameters if(descriptor != null && !descriptor.fparameters.isEmpty()) { bufstrwriter.write("<"); for(int i=0;i0) { bufstrwriter.write(", "); } bufstrwriter.write(descriptor.fparameters.get(i)); List lstBounds = descriptor.fbounds.get(i); if (lstBounds.size() > 1 || !"java/lang/Object".equals(lstBounds.get(0).value)) { bufstrwriter.write(" extends "); bufstrwriter.write(GenericMain.getGenericCastTypeName(lstBounds.get(0))); for(int j = 1; j < lstBounds.size(); j++) { bufstrwriter.write(" & " + GenericMain.getGenericCastTypeName(lstBounds.get(j))); } } } bufstrwriter.write("> "); } if(!init) { if(descriptor != null) { bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.ret)); } else { bufstrwriter.write(ExprProcessor.getCastTypeName(md.ret)); } bufstrwriter.write(" "); } bufstrwriter.write(name); bufstrwriter.write("("); // parameter annotations List> lstParAnn = getAllParameterAnnotations(mt.getAttributes()); List signFields = meth.signatureFields; // compute last visible parameter int lastparam_index = -1; for(int i=0;i param_count_explicit) { List annotations = lstParAnn.get(param_count_explicit); for(int j=0;j 0); if(isVarArgs) { partype.arraydim--; } String strpartype = GenericMain.getGenericCastTypeName(partype); if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } bufstrwriter.write(strpartype); if(isVarArgs) { bufstrwriter.write(" ..."); } } else { VarType partype = md.params[i].copy(); boolean isVarArgs = (i == lastparam_index && (mt.getAccessFlags() & CodeConstants.ACC_VARARGS) != 0 && partype.arraydim > 0); if(isVarArgs) { partype.decArrayDim(); } String strpartype = ExprProcessor.getCastTypeName(partype); if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strpartype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { strpartype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } bufstrwriter.write(strpartype); if(isVarArgs) { bufstrwriter.write(" ..."); } } bufstrwriter.write(" "); String parname = meth.varproc.getVarName(new VarVersionPaar(index, 0)); bufstrwriter.write(parname==null?"param"+index:parname); // null iff decompiled with errors firstpar = false; param_count_explicit++; } index+=md.params[i].stack_size; } bufstrwriter.write(")"); StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); if((descriptor!=null && !descriptor.exceptions.isEmpty()) || attr != null) { throwsExceptions = true; bufstrwriter.write(" throws "); for(int i=0;i0) { bufstrwriter.write(", "); } if(descriptor!=null && !descriptor.exceptions.isEmpty()) { bufstrwriter.write(GenericMain.getGenericCastTypeName(descriptor.exceptions.get(i))); } else { VarType exctype = new VarType(attr.getExcClassname(i, cl.getPool()), true); bufstrwriter.write(ExprProcessor.getCastTypeName(exctype)); } } } } boolean hidemethod = false; if((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) if(isAnnotation) { StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); if(attr != null) { bufstrwriter.write(" default "); bufstrwriter.write(attr.getDefaultValue().toJava(indent+1)); } } bufstrwriter.write(";"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } else { if(!clinit && !dinit) { bufstrwriter.write(" "); } bufstrwriter.write("{"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if(root != null && !meth.decompiledWithErrors) { // check for existence try { String code = root.toJava(indent+1); boolean singleinit = false; if(init && param_count_explicit == 0 && !throwsExceptions && DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { int init_counter = 0; for(MethodWrapper mth : wrapper.getMethods()) { if("".equals(mth.methodStruct.getName())) { init_counter++; } } singleinit = (init_counter == 1); } hidemethod = (clinit || dinit || singleinit) && code.length() == 0; bufstrwriter.write(code); } catch(Throwable ex) { DecompilerContext.getLogger().writeMessage("Method "+mt.getName()+" "+mt.getDescriptor()+" couldn't be written.", ex); meth.decompiledWithErrors = true; } } if(meth.decompiledWithErrors) { bufstrwriter.write(InterpreterUtil.getIndentString(indent+1)); bufstrwriter.write("// $FF: Couldn't be decompiled"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } bufstrwriter.write(indstr+"}"); bufstrwriter.write(DecompilerContext.getNewLineSeparator()); } bufstrwriter.flush(); if(!hidemethod) { writer.write(strwriter.toString()); } DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methold); return !hidemethod; } private List getAllAnnotations(VBStyleCollection attributes) { String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; List lst = new ArrayList(); for(String attrname : annattrnames) { StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(attrname); if(attr != null) { lst.addAll(attr.getAnnotations()); } } return lst; } private List> getAllParameterAnnotations(VBStyleCollection attributes) { String[] annattrnames = new String[] {StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; List> ret = new ArrayList>(); for(String attrname : annattrnames) { StructAnnotationParameterAttribute attr = (StructAnnotationParameterAttribute)attributes.getWithKey(attrname); if(attr != null) { for(int i=0;i lst = new ArrayList(); boolean isnew = (ret.size()<=i); if(!isnew) { lst = ret.get(i); } lst.addAll(attr.getParamAnnotations().get(i)); if(isnew) { ret.add(lst); } else { ret.set(i, lst); } } } } return ret; } private String getDescriptorPrintOut(String descriptor, int element) { switch(element) { case 0: // class return ExprProcessor.buildJavaClassName(descriptor); case 1: // field return getTypePrintOut(FieldDescriptor.parseDescriptor(descriptor).type); case 2: // method default: MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); StringBuilder buffer = new StringBuilder("("); boolean first = true; for(VarType partype : md.params) { if(first) { first = false; } else { buffer.append(", "); } buffer.append(getTypePrintOut(partype)); } buffer.append(") "); buffer.append(getTypePrintOut(md.ret)); return buffer.toString(); } } private String getTypePrintOut(VarType type) { String strtype = ExprProcessor.getCastTypeName(type, false); if(ExprProcessor.UNDEFINED_TYPE_STRING.equals(strtype) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { strtype = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); } return strtype; } }