|
|
@ -17,6 +17,7 @@ package org.jetbrains.java.decompiler.main; |
|
|
|
|
|
|
|
|
|
|
|
import org.jetbrains.java.decompiler.code.CodeConstants; |
|
|
|
import org.jetbrains.java.decompiler.code.CodeConstants; |
|
|
|
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; |
|
|
|
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; |
|
|
|
|
|
|
|
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; |
|
|
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
|
|
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
|
|
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; |
|
|
|
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; |
|
|
|
import org.jetbrains.java.decompiler.main.rels.ClassWrapper; |
|
|
|
import org.jetbrains.java.decompiler.main.rels.ClassWrapper; |
|
|
@ -90,6 +91,8 @@ public class ClassWriter { |
|
|
|
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); |
|
|
|
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); |
|
|
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); |
|
|
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BytecodeMappingTracer tracer = new BytecodeMappingTracer(); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
ClassWrapper wrapper = classNode.wrapper; |
|
|
|
ClassWrapper wrapper = classNode.wrapper; |
|
|
|
StructClass cl = wrapper.getClassStruct(); |
|
|
|
StructClass cl = wrapper.getClassStruct(); |
|
|
@ -99,7 +102,7 @@ public class ClassWriter { |
|
|
|
if (node.lambda_information.is_method_reference) { |
|
|
|
if (node.lambda_information.is_method_reference) { |
|
|
|
if (!node.lambda_information.is_content_method_static && method_object != null) { |
|
|
|
if (!node.lambda_information.is_content_method_static && method_object != null) { |
|
|
|
// reference to a virtual method
|
|
|
|
// reference to a virtual method
|
|
|
|
buffer.append(method_object.toJava(indent)); |
|
|
|
buffer.append(method_object.toJava(indent, tracer)); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
// reference to a static method
|
|
|
|
// reference to a static method
|
|
|
@ -144,7 +147,7 @@ public class ClassWriter { |
|
|
|
buffer.append(" {"); |
|
|
|
buffer.append(" {"); |
|
|
|
buffer.append(DecompilerContext.getNewLineSeparator()); |
|
|
|
buffer.append(DecompilerContext.getNewLineSeparator()); |
|
|
|
|
|
|
|
|
|
|
|
methodLambdaToJava(node, classNode, mt, buffer, indent + 1, !lambdaToAnonymous); |
|
|
|
methodLambdaToJava(node, classNode, mt, buffer, indent + 1, !lambdaToAnonymous, tracer); |
|
|
|
|
|
|
|
|
|
|
|
InterpreterUtil.appendIndent(buffer, indent); |
|
|
|
InterpreterUtil.appendIndent(buffer, indent); |
|
|
|
buffer.append("}"); |
|
|
|
buffer.append("}"); |
|
|
@ -161,6 +164,8 @@ public class ClassWriter { |
|
|
|
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); |
|
|
|
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); |
|
|
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); |
|
|
|
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BytecodeMappingTracer tracer = new BytecodeMappingTracer(); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
// last minute processing
|
|
|
|
// last minute processing
|
|
|
|
invokeProcessors(node); |
|
|
|
invokeProcessors(node); |
|
|
@ -199,7 +204,7 @@ public class ClassWriter { |
|
|
|
enumFields = false; |
|
|
|
enumFields = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fieldToJava(wrapper, cl, fd, buffer, indent + 1); |
|
|
|
fieldToJava(wrapper, cl, fd, buffer, indent + 1, tracer); |
|
|
|
|
|
|
|
|
|
|
|
hasContent = true; |
|
|
|
hasContent = true; |
|
|
|
} |
|
|
|
} |
|
|
@ -220,7 +225,7 @@ public class ClassWriter { |
|
|
|
if (hasContent) { |
|
|
|
if (hasContent) { |
|
|
|
buffer.append(lineSeparator); |
|
|
|
buffer.append(lineSeparator); |
|
|
|
} |
|
|
|
} |
|
|
|
boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1); |
|
|
|
boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, tracer); |
|
|
|
if (!methodSkipped) { |
|
|
|
if (!methodSkipped) { |
|
|
|
hasContent = true; |
|
|
|
hasContent = true; |
|
|
|
} |
|
|
|
} |
|
|
@ -372,7 +377,7 @@ public class ClassWriter { |
|
|
|
buffer.append(lineSeparator); |
|
|
|
buffer.append(lineSeparator); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, StringBuilder buffer, int indent) { |
|
|
|
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, StringBuilder buffer, int indent, BytecodeMappingTracer tracer) { |
|
|
|
String indentString = InterpreterUtil.getIndentString(indent); |
|
|
|
String indentString = InterpreterUtil.getIndentString(indent); |
|
|
|
String lineSeparator = DecompilerContext.getNewLineSeparator(); |
|
|
|
String lineSeparator = DecompilerContext.getNewLineSeparator(); |
|
|
|
|
|
|
|
|
|
|
@ -434,11 +439,12 @@ public class ClassWriter { |
|
|
|
if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { |
|
|
|
if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { |
|
|
|
NewExprent nexpr = (NewExprent)initializer; |
|
|
|
NewExprent nexpr = (NewExprent)initializer; |
|
|
|
nexpr.setEnumconst(true); |
|
|
|
nexpr.setEnumconst(true); |
|
|
|
buffer.append(nexpr.toJava(indent)); |
|
|
|
buffer.append(nexpr.toJava(indent, tracer)); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
buffer.append(" = "); |
|
|
|
buffer.append(" = "); |
|
|
|
buffer.append(initializer.toJava(indent)); |
|
|
|
// FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction.
|
|
|
|
|
|
|
|
buffer.append(initializer.toJava(indent, tracer)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) { |
|
|
|
else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) { |
|
|
@ -447,7 +453,7 @@ public class ClassWriter { |
|
|
|
if (attr != null) { |
|
|
|
if (attr != null) { |
|
|
|
PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex()); |
|
|
|
PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex()); |
|
|
|
buffer.append(" = "); |
|
|
|
buffer.append(" = "); |
|
|
|
buffer.append(new ConstExprent(fieldType, constant.value).toJava(indent)); |
|
|
|
buffer.append(new ConstExprent(fieldType, constant.value).toJava(indent, tracer)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -462,7 +468,7 @@ public class ClassWriter { |
|
|
|
StructMethod mt, |
|
|
|
StructMethod mt, |
|
|
|
StringBuilder buffer, |
|
|
|
StringBuilder buffer, |
|
|
|
int indent, |
|
|
|
int indent, |
|
|
|
boolean codeOnly) { |
|
|
|
boolean codeOnly, BytecodeMappingTracer tracer) { |
|
|
|
ClassWrapper classWrapper = classNode.wrapper; |
|
|
|
ClassWrapper classWrapper = classNode.wrapper; |
|
|
|
MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); |
|
|
|
MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); |
|
|
|
|
|
|
|
|
|
|
@ -518,7 +524,7 @@ public class ClassWriter { |
|
|
|
RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; |
|
|
|
RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; |
|
|
|
if (root != null) { // check for existence
|
|
|
|
if (root != null) { // check for existence
|
|
|
|
try { |
|
|
|
try { |
|
|
|
buffer.append(root.toJava(indent)); |
|
|
|
buffer.append(root.toJava(indent, tracer)); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (Throwable ex) { |
|
|
|
catch (Throwable ex) { |
|
|
|
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); |
|
|
|
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); |
|
|
@ -546,7 +552,7 @@ public class ClassWriter { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean methodToJava(ClassNode node, StructMethod mt, StringBuilder buffer, int indent) { |
|
|
|
private boolean methodToJava(ClassNode node, StructMethod mt, StringBuilder buffer, int indent, BytecodeMappingTracer tracer) { |
|
|
|
ClassWrapper wrapper = node.wrapper; |
|
|
|
ClassWrapper wrapper = node.wrapper; |
|
|
|
StructClass cl = wrapper.getClassStruct(); |
|
|
|
StructClass cl = wrapper.getClassStruct(); |
|
|
|
MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); |
|
|
|
MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); |
|
|
@ -768,7 +774,7 @@ public class ClassWriter { |
|
|
|
StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); |
|
|
|
StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); |
|
|
|
if (attr != null) { |
|
|
|
if (attr != null) { |
|
|
|
buffer.append(" default "); |
|
|
|
buffer.append(" default "); |
|
|
|
buffer.append(attr.getDefaultValue().toJava(indent + 1)); |
|
|
|
buffer.append(attr.getDefaultValue().toJava(indent + 1, new BytecodeMappingTracer())); // dummy tracer
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -787,7 +793,7 @@ public class ClassWriter { |
|
|
|
|
|
|
|
|
|
|
|
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
|
|
|
|
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
|
|
|
|
try { |
|
|
|
try { |
|
|
|
String code = root.toJava(indent + 1); |
|
|
|
String code = root.toJava(indent + 1, tracer); |
|
|
|
|
|
|
|
|
|
|
|
hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; |
|
|
|
hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; |
|
|
|
|
|
|
|
|
|
|
@ -896,11 +902,14 @@ public class ClassWriter { |
|
|
|
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; |
|
|
|
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; |
|
|
|
|
|
|
|
|
|
|
|
private static void appendAnnotations(StringBuilder buffer, StructMember mb, int indent, String lineSeparator) { |
|
|
|
private static void appendAnnotations(StringBuilder buffer, StructMember mb, int indent, String lineSeparator) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
|
|
|
|
|
|
|
|
|
|
|
|
for (String name : ANNOTATION_ATTRIBUTES) { |
|
|
|
for (String name : ANNOTATION_ATTRIBUTES) { |
|
|
|
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name); |
|
|
|
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name); |
|
|
|
if (attribute != null) { |
|
|
|
if (attribute != null) { |
|
|
|
for (AnnotationExprent annotation : attribute.getAnnotations()) { |
|
|
|
for (AnnotationExprent annotation : attribute.getAnnotations()) { |
|
|
|
buffer.append(annotation.toJava(indent)).append(lineSeparator); |
|
|
|
buffer.append(annotation.toJava(indent, tracer_dummy)).append(lineSeparator); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -910,13 +919,16 @@ public class ClassWriter { |
|
|
|
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; |
|
|
|
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; |
|
|
|
|
|
|
|
|
|
|
|
private static void appendParameterAnnotations(StringBuilder buffer, StructMethod mt, int param) { |
|
|
|
private static void appendParameterAnnotations(StringBuilder buffer, StructMethod mt, int param) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
|
|
|
|
|
|
|
|
|
|
|
|
for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) { |
|
|
|
for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) { |
|
|
|
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name); |
|
|
|
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name); |
|
|
|
if (attribute != null) { |
|
|
|
if (attribute != null) { |
|
|
|
List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations(); |
|
|
|
List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations(); |
|
|
|
if (param < annotations.size()) { |
|
|
|
if (param < annotations.size()) { |
|
|
|
for (AnnotationExprent annotation : annotations.get(param)) { |
|
|
|
for (AnnotationExprent annotation : annotations.get(param)) { |
|
|
|
buffer.append(annotation.toJava(0)).append(' '); |
|
|
|
buffer.append(annotation.toJava(0, tracer_dummy)).append(' '); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|