diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java | 987 |
1 files changed, 987 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java new file mode 100644 index 000000000000..856ad225d0e1 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -0,0 +1,987 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.main; + +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.StructMember; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.*; +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.*; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ClassWriter { + + 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 (cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) { + EnumProcessor.clearEnum(wrapper); + } + + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) { + AssertProcessor.buildAssertions(node); + } + } + + public void classLambdaToJava(ClassNode node, StringBuilder buffer, Exprent method_object, int indent) { + // get the class node with the content method + ClassNode classNode = node; + while (classNode != null && classNode.type == ClassNode.CLASS_LAMBDA) { + classNode = classNode.parent; + } + if (classNode == null) { + return; + } + + boolean lambdaToAnonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); + + ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); + + try { + ClassWrapper wrapper = classNode.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 + buffer.append(method_object.toJava(indent)); + } + else { + // reference to a static method + buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); + } + + buffer.append("::"); + buffer.append(node.lambda_information.content_method_name); + } + else { + // lambda method + StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); + MethodWrapper methodWrapper = 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 (!lambdaToAnonymous) { + buffer.append('('); + + boolean firstParameter = 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 < md_content.params.length; i++) { + if (i >= start_index) { + if (!firstParameter) { + buffer.append(", "); + } + + String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0)); + buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors + + firstParameter = false; + } + + index += md_content.params[i].stack_size; + } + + buffer.append(") ->"); + } + + buffer.append(" {"); + buffer.append(DecompilerContext.getNewLineSeparator()); + + methodLambdaToJava(node, classNode, mt, buffer, indent + 1, !lambdaToAnonymous); + + InterpreterUtil.appendIndent(buffer, indent); + buffer.append("}"); + } + } + finally { + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode); + } + + DecompilerContext.getLogger().endWriteClass(); + } + + public void classToJava(ClassNode node, StringBuilder buffer, int indent) { + ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); + + try { + // last minute processing + invokeProcessors(node); + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); + + String lineSeparator = DecompilerContext.getNewLineSeparator(); + + writeClassDefinition(node, buffer, indent); + + boolean hasContent = false; + + // fields + boolean enumFields = false; + + for (StructField fd : cl.getFields()) { + boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || + wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + if (hide) continue; + + boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + if (isEnum) { + if (enumFields) { + buffer.append(','); + buffer.append(lineSeparator); + } + enumFields = true; + } + else if (enumFields) { + buffer.append(';'); + buffer.append(lineSeparator); + buffer.append(lineSeparator); + enumFields = false; + } + + fieldToJava(wrapper, cl, fd, buffer, indent + 1); + + hasContent = true; + } + + if (enumFields) { + buffer.append(';'); + buffer.append(lineSeparator); + } + + // methods + for (StructMethod mt : cl.getMethods()) { + boolean hide = mt.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || + mt.hasModifier(CodeConstants.ACC_BRIDGE) && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE) || + wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); + if (hide) continue; + + int position = buffer.length(); + if (hasContent) { + buffer.append(lineSeparator); + } + boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1); + if (!methodSkipped) { + hasContent = true; + } + else { + buffer.setLength(position); + } + } + + // member classes + for (ClassNode inner : node.nested) { + if (inner.type == ClassNode.CLASS_MEMBER) { + StructClass innerCl = inner.classStruct; + boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic(); + boolean hide = isSynthetic && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || + wrapper.getHiddenMembers().contains(innerCl.qualifiedName); + if (hide) continue; + + if (hasContent) { + buffer.append(lineSeparator); + } + classToJava(inner, buffer, indent + 1); + + hasContent = true; + } + } + + InterpreterUtil.appendIndent(buffer, indent); + buffer.append('}'); + + if (node.type != ClassNode.CLASS_ANONYMOUS) { + buffer.append(lineSeparator); + } + } + finally { + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode); + } + + DecompilerContext.getLogger().endWriteClass(); + } + + private void writeClassDefinition(ClassNode node, StringBuilder buffer, int indent) { + String lineSeparator = DecompilerContext.getNewLineSeparator(); + String indentString = InterpreterUtil.getIndentString(indent); + + if (node.type == ClassNode.CLASS_ANONYMOUS) { + buffer.append(" {"); + buffer.append(lineSeparator); + return; + } + + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + + int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; + boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); + boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; + boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; + boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; + + if (isDeprecated) { + appendDeprecation(buffer, indentString, lineSeparator); + } + + if (interceptor != null) { + String oldName = interceptor.getOldName(cl.qualifiedName); + appendRenameComment(buffer, oldName, MType.CLASS, indent, lineSeparator); + } + + if (isSynthetic) { + appendComment(buffer, "synthetic class", indentString, lineSeparator); + } + + appendAnnotations(buffer, cl, indent, lineSeparator); + + buffer.append(indentString); + + if (isEnum) { + // remove abstract and final flags (JLS 8.9 Enums) + flags &= ~CodeConstants.ACC_ABSTRACT; + flags &= ~CodeConstants.ACC_FINAL; + } + + appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED); + + if (isEnum) { + buffer.append("enum "); + } + else if (isInterface) { + if (isAnnotation) { + buffer.append('@'); + } + buffer.append("interface "); + } + else { + buffer.append("class "); + } + + GenericClassDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseClassSignature(attr.getSignature()); + } + } + + buffer.append(node.simpleName); + + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); + } + + buffer.append(' '); + + if (!isEnum && !isInterface && cl.superClass != null) { + VarType supertype = new VarType(cl.superClass.getString(), true); + if (!VarType.VARTYPE_OBJECT.equals(supertype)) { + buffer.append("extends "); + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.superclass)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(supertype)); + } + buffer.append(' '); + } + } + + if (!isAnnotation) { + int[] interfaces = cl.getInterfaces(); + if (interfaces.length > 0) { + buffer.append(isInterface ? "extends " : "implements "); + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) { + buffer.append(", "); + } + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); + } + else { + buffer.append(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); + } + } + buffer.append(' '); + } + } + + buffer.append('{'); + buffer.append(lineSeparator); + } + + private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, StringBuilder buffer, int indent) { + String indentString = InterpreterUtil.getIndentString(indent); + String lineSeparator = DecompilerContext.getNewLineSeparator(); + + boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); + boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); + boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + + if (isDeprecated) { + appendDeprecation(buffer, indentString, lineSeparator); + } + + if (interceptor != null) { + String oldName = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor()); + appendRenameComment(buffer, oldName, MType.FIELD, indent, lineSeparator); + } + + if (fd.isSynthetic()) { + appendComment(buffer, "synthetic field", indentString, lineSeparator); + } + + appendAnnotations(buffer, fd, indent, lineSeparator); + + buffer.append(indentString); + + if (!isEnum) { + appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED); + } + + VarType fieldType = new VarType(fd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + if (!isEnum) { + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.type)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(fieldType)); + } + buffer.append(' '); + } + + buffer.append(fd.getName()); + + Exprent initializer; + if (fd.hasModifier(CodeConstants.ACC_STATIC)) { + initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + else { + initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + if (initializer != null) { + if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)initializer; + nexpr.setEnumconst(true); + buffer.append(nexpr.toJava(indent)); + } + else { + buffer.append(" = "); + buffer.append(initializer.toJava(indent)); + } + } + else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) { + StructConstantValueAttribute attr = + (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); + if (attr != null) { + PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex()); + buffer.append(" = "); + buffer.append(new ConstExprent(fieldType, constant.value).toJava(indent)); + } + } + + if (!isEnum) { + buffer.append(";"); + buffer.append(lineSeparator); + } + } + + private static void methodLambdaToJava(ClassNode lambdaNode, + ClassNode classNode, + StructMethod mt, + StringBuilder buffer, + int indent, + boolean codeOnly) { + ClassWrapper classWrapper = classNode.wrapper; + MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); + + try { + String method_name = lambdaNode.lambda_information.method_name; + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambda_information.method_descriptor); + + if (!codeOnly) { + InterpreterUtil.appendIndent(buffer, indent); + buffer.append("public "); + buffer.append(method_name); + buffer.append("("); + + boolean firstParameter = true; + int index = lambdaNode.lambda_information.is_content_method_static ? 0 : 1; + int start_index = md_content.params.length - md_lambda.params.length; + + for (int i = 0; i < md_content.params.length; i++) { + if (i >= start_index) { + if (!firstParameter) { + buffer.append(", "); + } + + String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy()); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + buffer.append(typeName); + buffer.append(" "); + + String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0)); + buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors + + firstParameter = false; + } + + index += md_content.params[i].stack_size; + } + + buffer.append(") {"); + buffer.append(DecompilerContext.getNewLineSeparator()); + + indent += 1; + } + + if (!methodWrapper.decompiledWithErrors) { + RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + if (root != null) { // check for existence + try { + buffer.append(root.toJava(indent)); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + methodWrapper.decompiledWithErrors = true; + } + } + } + + if (methodWrapper.decompiledWithErrors) { + InterpreterUtil.appendIndent(buffer, indent); + buffer.append("// $FF: Couldn't be decompiled"); + buffer.append(DecompilerContext.getNewLineSeparator()); + } + + if (!codeOnly) { + indent -= 1; + + InterpreterUtil.appendIndent(buffer, indent); + buffer.append('}'); + buffer.append(DecompilerContext.getNewLineSeparator()); + } + } + finally { + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); + } + } + + private boolean methodToJava(ClassNode node, StructMethod mt, StringBuilder buffer, int indent) { + ClassWrapper wrapper = node.wrapper; + StructClass cl = wrapper.getClassStruct(); + MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); + + boolean hideMethod = false; + + MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); + + try { + boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); + boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); + boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); + boolean clinit = false, init = false, dinit = false; + + String indentString = InterpreterUtil.getIndentString(indent); + String lineSeparator = DecompilerContext.getNewLineSeparator(); + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + 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 ("<clinit>".equals(mt.getName())) { + flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer + } + + if (isDeprecated) { + appendDeprecation(buffer, indentString, lineSeparator); + } + + if (interceptor != null) { + String oldName = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); + appendRenameComment(buffer, oldName, MType.METHOD, indent, lineSeparator); + } + + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; + if (isSynthetic) { + appendComment(buffer, "synthetic method", indentString, lineSeparator); + } + if (isBridge) { + appendComment(buffer, "bridge method", indentString, lineSeparator); + } + + appendAnnotations(buffer, mt, indent, lineSeparator); + + buffer.append(indentString); + + appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED); + + if (isInterface && mt.containsCode()) { + // 'default' modifier (Java 8) + buffer.append("default "); + } + + String name = mt.getName(); + if ("<init>".equals(name)) { + if (node.type == ClassNode.CLASS_ANONYMOUS) { + name = ""; + dinit = true; + } + else { + name = node.simpleName; + init = true; + } + } + else if ("<clinit>".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()); + if (descriptor != null) { + int actualParams = md.params.length; + if (isEnum && init) actualParams -= 2; + if (actualParams != descriptor.params.size()) { + String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor(); + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + descriptor = null; + } + } + } + } + + boolean throwsExceptions = false; + int paramCount = 0; + + if (!clinit && !dinit) { + boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); + + if (descriptor != null && !descriptor.fparameters.isEmpty()) { + appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); + buffer.append(' '); + } + + if (!init) { + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(md.ret)); + } + buffer.append(' '); + } + + buffer.append(name); + buffer.append('('); + + // parameters + List<VarVersionPaar> signFields = methodWrapper.signatureFields; + + int lastVisibleParameterIndex = -1; + for (int i = 0; i < md.params.length; i++) { + if (signFields == null || signFields.get(i) == null) { + lastVisibleParameterIndex = i; + } + } + + boolean firstParameter = true; + int index = isEnum && init ? 3 : thisVar ? 1 : 0; + int start = isEnum && init && descriptor == null ? 2 : 0; + int params = descriptor == null ? md.params.length : descriptor.params.size(); + for (int i = start; i < params; i++) { + if (signFields == null || signFields.get(i) == null) { + if (!firstParameter) { + buffer.append(", "); + } + + appendParameterAnnotations(buffer, mt, paramCount); + + if (methodWrapper.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + buffer.append("final "); + } + + if (descriptor != null) { + GenericType parameterType = descriptor.params.get(i); + + boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arraydim > 0); + if (isVarArg) { + parameterType.arraydim--; + } + + String typeName = GenericMain.getGenericCastTypeName(parameterType); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + buffer.append(typeName); + + if (isVarArg) { + buffer.append("..."); + } + } + else { + VarType parameterType = md.params[i].copy(); + + boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arraydim > 0); + if (isVarArg) { + parameterType.decArrayDim(); + } + + String typeName = ExprProcessor.getCastTypeName(parameterType); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + + buffer.append(typeName); + + if (isVarArg) { + buffer.append("..."); + } + } + + buffer.append(' '); + String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0)); + buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors + + firstParameter = false; + paramCount++; + } + + index += md.params[i].stack_size; + } + + buffer.append(')'); + + StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); + if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { + throwsExceptions = true; + buffer.append(" throws "); + + for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { + if (i > 0) { + buffer.append(", "); + } + if (descriptor != null && !descriptor.exceptions.isEmpty()) { + GenericType type = descriptor.exceptions.get(i); + buffer.append(GenericMain.getGenericCastTypeName(type)); + } + else { + VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true); + buffer.append(ExprProcessor.getCastTypeName(type)); + } + } + } + } + + 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) { + buffer.append(" default "); + buffer.append(attr.getDefaultValue().toJava(indent + 1)); + } + } + + buffer.append(';'); + buffer.append(lineSeparator); + } + else { + if (!clinit && !dinit) { + buffer.append(' '); + } + + buffer.append('{'); + buffer.append(lineSeparator); + + RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; + + if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence + try { + String code = root.toJava(indent + 1); + + hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; + + buffer.append(code); + } + catch (Throwable ex) { + DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + methodWrapper.decompiledWithErrors = true; + } + } + + if (methodWrapper.decompiledWithErrors) { + buffer.append(InterpreterUtil.getIndentString(indent + 1)); + buffer.append("// $FF: Couldn't be decompiled"); + buffer.append(lineSeparator); + } + + buffer.append(indentString); + buffer.append('}'); + buffer.append(lineSeparator); + } + } + finally { + DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); + } + + return !hideMethod; + } + + private static boolean hideConstructor(ClassWrapper wrapper, boolean init, boolean throwsExceptions, int paramCount) { + if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { + return false; + } + + int count = 0; + for (StructMethod mt : wrapper.getClassStruct().getMethods()) { + if ("<init>".equals(mt.getName())) { + if (++count > 1) { + return false; + } + } + } + + return true; + } + + private static void appendDeprecation(StringBuilder buffer, String indentString, String lineSeparator) { + buffer.append(indentString).append("/** @deprecated */").append(lineSeparator); + } + + private enum MType {CLASS, FIELD, METHOD} + + private static void appendRenameComment(StringBuilder buffer, String oldName, MType type, int indent, String lineSeparator) { + if (oldName == null) return; + + InterpreterUtil.appendIndent(buffer, indent); + buffer.append("// $FF: renamed from: "); + + switch (type) { + case CLASS: + buffer.append(ExprProcessor.buildJavaClassName(oldName)); + break; + + case FIELD: + String[] fParts = oldName.split(" "); + FieldDescriptor fd = FieldDescriptor.parseDescriptor(fParts[2]); + buffer.append(fParts[1]); + buffer.append(' '); + buffer.append(getTypePrintOut(fd.type)); + break; + + default: + String[] mParts = oldName.split(" "); + MethodDescriptor md = MethodDescriptor.parseDescriptor(mParts[2]); + buffer.append(mParts[1]); + buffer.append(" ("); + boolean first = true; + for (VarType paramType : md.params) { + if (!first) { + buffer.append(", "); + } + first = false; + buffer.append(getTypePrintOut(paramType)); + } + buffer.append(") "); + buffer.append(getTypePrintOut(md.ret)); + } + + buffer.append(lineSeparator); + } + + private static String getTypePrintOut(VarType type) { + String typeText = ExprProcessor.getCastTypeName(type, false); + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeText) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeText = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false); + } + return typeText; + } + + private static void appendComment(StringBuilder buffer, String comment, String indentString, String lineSeparator) { + buffer.append(indentString).append("// $FF: ").append(comment).append(lineSeparator); + } + + private static final String[] ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; + + private static void appendAnnotations(StringBuilder buffer, StructMember mb, int indent, String lineSeparator) { + for (String name : ANNOTATION_ATTRIBUTES) { + StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name); + if (attribute != null) { + for (AnnotationExprent annotation : attribute.getAnnotations()) { + buffer.append(annotation.toJava(indent)).append(lineSeparator); + } + } + } + } + + private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + + private static void appendParameterAnnotations(StringBuilder buffer, StructMethod mt, int param) { + for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) { + StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name); + if (attribute != null) { + List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations(); + if (param < annotations.size()) { + for (AnnotationExprent annotation : annotations.get(param)) { + buffer.append(annotation.toJava(0)).append(' '); + } + } + } + } + } + + private static final Map<Integer, String> MODIFIERS = new LinkedHashMap<Integer, String>() {{ + put(CodeConstants.ACC_PUBLIC, "public"); + put(CodeConstants.ACC_PROTECTED, "protected"); + put(CodeConstants.ACC_PRIVATE, "private"); + put(CodeConstants.ACC_ABSTRACT, "abstract"); + put(CodeConstants.ACC_STATIC, "static"); + put(CodeConstants.ACC_FINAL, "final"); + put(CodeConstants.ACC_STRICT, "strictfp"); + put(CodeConstants.ACC_TRANSIENT, "transient"); + put(CodeConstants.ACC_VOLATILE, "volatile"); + put(CodeConstants.ACC_SYNCHRONIZED, "synchronized"); + put(CodeConstants.ACC_NATIVE, "native"); + }}; + + private static final int CLASS_ALLOWED = + CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT | + CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_STRICT; + private static final int FIELD_ALLOWED = + CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_STATIC | + CodeConstants.ACC_FINAL | CodeConstants.ACC_TRANSIENT | CodeConstants.ACC_VOLATILE; + private static final int METHOD_ALLOWED = + 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 int CLASS_EXCLUDED = CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_STATIC; + private static final int FIELD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL; + private static final int METHOD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_ABSTRACT; + + private static void appendModifiers(StringBuilder buffer, int flags, int allowed, boolean isInterface, int excluded) { + flags &= allowed; + if (!isInterface) excluded = 0; + for (int modifier : MODIFIERS.keySet()) { + if ((flags & modifier) == modifier && (modifier & excluded) == 0) { + buffer.append(MODIFIERS.get(modifier)).append(' '); + } + } + } + + private static void appendTypeParameters(StringBuilder buffer, List<String> parameters, List<List<GenericType>> bounds) { + buffer.append('<'); + + for (int i = 0; i < parameters.size(); i++) { + if (i > 0) { + buffer.append(", "); + } + + buffer.append(parameters.get(i)); + + List<GenericType> parameterBounds = bounds.get(i); + if (parameterBounds.size() > 1 || !"java/lang/Object".equals(parameterBounds.get(0).value)) { + buffer.append(" extends "); + buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(0))); + for (int j = 1; j < parameterBounds.size(); j++) { + buffer.append(" & "); + buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(j))); + } + } + } + + buffer.append('>'); + } +} |