2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.jetbrains.java.decompiler.main;
18 import org.jetbrains.java.decompiler.code.CodeConstants;
19 import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
20 import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
21 import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
22 import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
23 import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
24 import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
25 import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
26 import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;
27 import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
28 import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
29 import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
30 import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
31 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
32 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
33 import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
34 import org.jetbrains.java.decompiler.struct.StructClass;
35 import org.jetbrains.java.decompiler.struct.StructField;
36 import org.jetbrains.java.decompiler.struct.StructMember;
37 import org.jetbrains.java.decompiler.struct.StructMethod;
38 import org.jetbrains.java.decompiler.struct.attr.*;
39 import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
40 import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
41 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
42 import org.jetbrains.java.decompiler.struct.gen.VarType;
43 import org.jetbrains.java.decompiler.struct.gen.generics.*;
44 import org.jetbrains.java.decompiler.util.InterpreterUtil;
48 public class ClassWriter {
49 private final ClassReference14Processor ref14processor;
50 private final PoolInterceptor interceptor;
52 public ClassWriter() {
53 ref14processor = new ClassReference14Processor();
54 interceptor = DecompilerContext.getPoolInterceptor();
57 private void invokeProcessors(ClassNode node) {
58 ClassWrapper wrapper = node.getWrapper();
59 StructClass cl = wrapper.getClassStruct();
61 InitializerProcessor.extractInitializers(wrapper);
63 if (node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {
64 ref14processor.processClassReferences(node);
67 if (cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) {
68 EnumProcessor.clearEnum(wrapper);
71 if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) {
72 AssertProcessor.buildAssertions(node);
76 public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent, BytecodeMappingTracer origTracer) {
77 ClassWrapper wrapper = node.getWrapper();
78 if (wrapper == null) {
82 boolean lambdaToAnonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS);
84 ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
85 DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node);
87 BytecodeMappingTracer tracer = new BytecodeMappingTracer(origTracer.getCurrentSourceLine());
90 StructClass cl = wrapper.getClassStruct();
92 DecompilerContext.getLogger().startWriteClass(node.simpleName);
94 if (node.lambdaInformation.is_method_reference) {
95 if (!node.lambdaInformation.is_content_method_static && method_object != null) {
96 // reference to a virtual method
97 buffer.append(method_object.toJava(indent, tracer));
100 // reference to a static method
101 buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambdaInformation.content_class_name, true)));
105 buffer.append(node.lambdaInformation.content_method_name);
109 StructMethod mt = cl.getMethod(node.lambdaInformation.content_method_key);
110 MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
111 MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambdaInformation.content_method_descriptor);
112 MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambdaInformation.method_descriptor);
114 if (!lambdaToAnonymous) {
117 boolean firstParameter = true;
118 int index = node.lambdaInformation.is_content_method_static ? 0 : 1;
119 int start_index = md_content.params.length - md_lambda.params.length;
121 for (int i = 0; i < md_content.params.length; i++) {
122 if (i >= start_index) {
123 if (!firstParameter) {
127 String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
128 buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
130 firstParameter = false;
133 index += md_content.params[i].stackSize;
136 buffer.append(") ->");
139 buffer.append(" {").appendLineSeparator();
140 tracer.incrementCurrentSourceLine();
142 methodLambdaToJava(node, wrapper, mt, buffer, indent + 1, !lambdaToAnonymous, tracer);
144 buffer.appendIndent(indent).append("}");
146 addTracer(cl, mt, tracer);
150 DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode);
153 DecompilerContext.getLogger().endWriteClass();
156 public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
157 ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
158 DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node);
160 int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0;
161 BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(startLine);
164 // last minute processing
165 invokeProcessors(node);
167 ClassWrapper wrapper = node.getWrapper();
168 StructClass cl = wrapper.getClassStruct();
170 DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
172 // write class definition
173 int start_class_def = buffer.length();
174 writeClassDefinition(node, buffer, indent);
176 boolean hasContent = false;
177 boolean enumFields = false;
179 dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def));
181 for (StructField fd : cl.getFields()) {
182 boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
183 wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
186 boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
189 buffer.append(',').appendLineSeparator();
190 dummy_tracer.incrementCurrentSourceLine();
194 else if (enumFields) {
196 buffer.appendLineSeparator();
197 buffer.appendLineSeparator();
198 dummy_tracer.incrementCurrentSourceLine(2);
202 fieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracer
208 buffer.append(';').appendLineSeparator();
209 dummy_tracer.incrementCurrentSourceLine();
212 // FIXME: fields don't matter at the moment
213 startLine += buffer.countLines(start_class_def);
216 for (StructMethod mt : cl.getMethods()) {
217 boolean hide = mt.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
218 mt.hasModifier(CodeConstants.ACC_BRIDGE) && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE) ||
219 wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
222 int position = buffer.length();
223 int storedLine = startLine;
225 buffer.appendLineSeparator();
228 BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(startLine);
229 boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer);
230 if (!methodSkipped) {
232 addTracer(cl, mt, method_tracer);
233 startLine = method_tracer.getCurrentSourceLine();
236 buffer.setLength(position);
237 startLine = storedLine;
242 for (ClassNode inner : node.nested) {
243 if (inner.type == ClassNode.CLASS_MEMBER) {
244 StructClass innerCl = inner.classStruct;
245 boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic() || inner.namelessConstructorStub;
246 boolean hide = isSynthetic && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
247 wrapper.getHiddenMembers().contains(innerCl.qualifiedName);
251 buffer.appendLineSeparator();
254 BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine);
255 classToJava(inner, buffer, indent + 1, class_tracer);
256 startLine = buffer.countLines();
262 buffer.appendIndent(indent).append('}');
264 if (node.type != ClassNode.CLASS_ANONYMOUS) {
265 buffer.appendLineSeparator();
269 DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode);
272 DecompilerContext.getLogger().endWriteClass();
275 private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
276 StructLineNumberTableAttribute table = (StructLineNumberTableAttribute)method.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
277 tracer.setLineNumberTable(table);
278 String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor());
279 DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer);
282 private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) {
283 if (node.type == ClassNode.CLASS_ANONYMOUS) {
284 buffer.append(" {").appendLineSeparator();
288 ClassWrapper wrapper = node.getWrapper();
289 StructClass cl = wrapper.getClassStruct();
291 int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
292 boolean isDeprecated = cl.getAttributes().containsKey("Deprecated");
293 boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic");
294 boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0;
295 boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0;
296 boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0;
299 appendDeprecation(buffer, indent);
302 if (interceptor != null) {
303 String oldName = interceptor.getOldName(cl.qualifiedName);
304 appendRenameComment(buffer, oldName, MType.CLASS, indent);
308 appendComment(buffer, "synthetic class", indent);
311 appendAnnotations(buffer, cl, indent);
313 buffer.appendIndent(indent);
316 // remove abstract and final flags (JLS 8.9 Enums)
317 flags &= ~CodeConstants.ACC_ABSTRACT;
318 flags &= ~CodeConstants.ACC_FINAL;
321 appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED);
324 buffer.append("enum ");
326 else if (isInterface) {
330 buffer.append("interface ");
333 buffer.append("class ");
336 GenericClassDescriptor descriptor = null;
337 if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
338 StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature");
340 descriptor = GenericMain.parseClassSignature(attr.getSignature());
344 buffer.append(node.simpleName);
346 if (descriptor != null && !descriptor.fparameters.isEmpty()) {
347 appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
352 if (!isEnum && !isInterface && cl.superClass != null) {
353 VarType supertype = new VarType(cl.superClass.getString(), true);
354 if (!VarType.VARTYPE_OBJECT.equals(supertype)) {
355 buffer.append("extends ");
356 if (descriptor != null) {
357 buffer.append(GenericMain.getGenericCastTypeName(descriptor.superclass));
360 buffer.append(ExprProcessor.getCastTypeName(supertype));
367 int[] interfaces = cl.getInterfaces();
368 if (interfaces.length > 0) {
369 buffer.append(isInterface ? "extends " : "implements ");
370 for (int i = 0; i < interfaces.length; i++) {
374 if (descriptor != null) {
375 buffer.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i)));
378 buffer.append(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true)));
385 buffer.append('{').appendLineSeparator();
388 private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
389 int start = buffer.length();
390 boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
391 boolean isDeprecated = fd.getAttributes().containsKey("Deprecated");
392 boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
395 appendDeprecation(buffer, indent);
398 if (interceptor != null) {
399 String oldName = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor());
400 appendRenameComment(buffer, oldName, MType.FIELD, indent);
403 if (fd.isSynthetic()) {
404 appendComment(buffer, "synthetic field", indent);
407 appendAnnotations(buffer, fd, indent);
409 buffer.appendIndent(indent);
412 appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED);
415 VarType fieldType = new VarType(fd.getDescriptor(), false);
417 GenericFieldDescriptor descriptor = null;
418 if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
419 StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature");
421 descriptor = GenericMain.parseFieldSignature(attr.getSignature());
426 if (descriptor != null) {
427 buffer.append(GenericMain.getGenericCastTypeName(descriptor.type));
430 buffer.append(ExprProcessor.getCastTypeName(fieldType));
435 buffer.append(fd.getName());
437 tracer.incrementCurrentSourceLine(buffer.countLines(start));
440 if (fd.hasModifier(CodeConstants.ACC_STATIC)) {
441 initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
444 initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
446 if (initializer != null) {
447 if (isEnum && initializer.type == Exprent.EXPRENT_NEW) {
448 NewExprent nexpr = (NewExprent)initializer;
449 nexpr.setEnumConst(true);
450 buffer.append(nexpr.toJava(indent, tracer));
453 buffer.append(" = ");
454 // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction.
455 buffer.append(initializer.toJava(indent, tracer));
458 else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) {
459 StructConstantValueAttribute attr =
460 (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE);
462 PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex());
463 buffer.append(" = ");
464 buffer.append(new ConstExprent(fieldType, constant.value, null).toJava(indent, tracer));
469 buffer.append(";").appendLineSeparator();
470 tracer.incrementCurrentSourceLine();
474 private static void methodLambdaToJava(ClassNode lambdaNode,
475 ClassWrapper classWrapper,
479 boolean codeOnly, BytecodeMappingTracer tracer) {
480 MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
482 MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
483 DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);
486 String method_name = lambdaNode.lambdaInformation.method_name;
487 MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor);
488 MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor);
491 buffer.appendIndent(indent);
492 buffer.append("public ");
493 buffer.append(method_name);
496 boolean firstParameter = true;
497 int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1;
498 int start_index = md_content.params.length - md_lambda.params.length;
500 for (int i = 0; i < md_content.params.length; i++) {
501 if (i >= start_index) {
502 if (!firstParameter) {
506 String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy());
507 if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
508 DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
509 typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
512 buffer.append(typeName);
515 String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
516 buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
518 firstParameter = false;
521 index += md_content.params[i].stackSize;
524 buffer.append(") {").appendLineSeparator();
529 RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
530 if (!methodWrapper.decompiledWithErrors) {
531 if (root != null) { // check for existence
533 buffer.append(root.toJava(indent, tracer));
535 catch (Throwable ex) {
536 DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex);
537 methodWrapper.decompiledWithErrors = true;
542 if (methodWrapper.decompiledWithErrors) {
543 buffer.appendIndent(indent);
544 buffer.append("// $FF: Couldn't be decompiled");
545 buffer.appendLineSeparator();
549 tracer.addMapping(root.getDummyExit().bytecode);
554 buffer.appendIndent(indent).append('}').appendLineSeparator();
558 DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
562 private static String toValidJavaIdentifier(String name) {
563 if (name == null || name.isEmpty()) return name;
565 boolean changed = false;
566 StringBuilder res = new StringBuilder(name.length());
567 for (int i = 0; i < name.length(); i++) {
568 char c = name.charAt(i);
569 if ((i == 0 && !Character.isJavaIdentifierStart(c))
570 || (i > 0 && !Character.isJavaIdentifierPart(c))) {
579 return res.append("/* $FF was: ").append(name).append("*/").toString();
582 private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
583 ClassWrapper wrapper = node.getWrapper();
584 StructClass cl = wrapper.getClassStruct();
585 MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
587 boolean hideMethod = false;
588 int start_index_method = buffer.length();
590 MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
591 DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);
594 boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
595 boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION);
596 boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
597 boolean isDeprecated = mt.getAttributes().containsKey("Deprecated");
598 boolean clinit = false, init = false, dinit = false;
600 MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
602 int flags = mt.getAccessFlags();
603 if ((flags & CodeConstants.ACC_NATIVE) != 0) {
604 flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp
606 if (CodeConstants.CLINIT_NAME.equals(mt.getName())) {
607 flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer
611 appendDeprecation(buffer, indent);
614 if (interceptor != null) {
615 String oldName = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor());
616 appendRenameComment(buffer, oldName, MType.METHOD, indent);
619 boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic");
620 boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0;
622 appendComment(buffer, "synthetic method", indent);
625 appendComment(buffer, "bridge method", indent);
628 appendAnnotations(buffer, mt, indent);
630 buffer.appendIndent(indent);
632 appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED);
634 if (isInterface && mt.containsCode()) {
635 // 'default' modifier (Java 8)
636 buffer.append("default ");
639 String name = mt.getName();
640 if (CodeConstants.INIT_NAME.equals(name)) {
641 if (node.type == ClassNode.CLASS_ANONYMOUS) {
646 name = node.simpleName;
650 else if (CodeConstants.CLINIT_NAME.equals(name)) {
655 GenericMethodDescriptor descriptor = null;
656 if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
657 StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature");
659 descriptor = GenericMain.parseMethodSignature(attr.getSignature());
660 if (descriptor != null) {
661 int actualParams = md.params.length;
662 List<VarVersionPair> sigFields = methodWrapper.signatureFields;
663 if (sigFields != null) {
665 for (VarVersionPair field : methodWrapper.signatureFields) {
671 else if (isEnum && init) actualParams -= 2;
672 if (actualParams != descriptor.params.size()) {
673 String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName;
674 DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
681 boolean throwsExceptions = false;
684 if (!clinit && !dinit) {
685 boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
687 if (descriptor != null && !descriptor.fparameters.isEmpty()) {
688 appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
693 if (descriptor != null) {
694 buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret));
697 buffer.append(ExprProcessor.getCastTypeName(md.ret));
702 buffer.append(toValidJavaIdentifier(name));
706 List<VarVersionPair> signFields = methodWrapper.signatureFields;
708 int lastVisibleParameterIndex = -1;
709 for (int i = 0; i < md.params.length; i++) {
710 if (signFields == null || signFields.get(i) == null) {
711 lastVisibleParameterIndex = i;
715 boolean firstParameter = true;
716 int index = isEnum && init ? 3 : thisVar ? 1 : 0;
717 boolean hasDescriptor = descriptor != null;
718 int start = isEnum && init && !hasDescriptor ? 2 : 0;
719 int params = hasDescriptor ? descriptor.params.size() : md.params.length;
720 for (int i = start; i < params; i++) {
721 if (hasDescriptor || (signFields == null || signFields.get(i) == null)) {
722 if (!firstParameter) {
726 appendParameterAnnotations(buffer, mt, paramCount);
728 if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) {
729 buffer.append("final ");
732 if (descriptor != null) {
733 GenericType parameterType = descriptor.params.get(i);
735 boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0);
737 parameterType = parameterType.decreaseArrayDim();
740 String typeName = GenericMain.getGenericCastTypeName(parameterType);
741 if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
742 DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
743 typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
746 buffer.append(typeName);
749 buffer.append("...");
753 VarType parameterType = md.params[i];
755 boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0);
757 parameterType = parameterType.decreaseArrayDim();
760 String typeName = ExprProcessor.getCastTypeName(parameterType);
761 if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
762 DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
763 typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
766 buffer.append(typeName);
769 buffer.append("...");
774 String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
775 buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
777 firstParameter = false;
781 index += md.params[i].stackSize;
786 StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions");
787 if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) {
788 throwsExceptions = true;
789 buffer.append(" throws ");
791 for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
795 if (descriptor != null && !descriptor.exceptions.isEmpty()) {
796 GenericType type = descriptor.exceptions.get(i);
797 buffer.append(GenericMain.getGenericCastTypeName(type));
800 VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true);
801 buffer.append(ExprProcessor.getCastTypeName(type));
807 tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method));
809 if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface)
811 StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault");
813 buffer.append(" default ");
814 buffer.append(attr.getDefaultValue().toJava(indent + 1, new BytecodeMappingTracer())); // dummy tracer
819 buffer.appendLineSeparator();
820 tracer.incrementCurrentSourceLine();
823 if (!clinit && !dinit) {
827 // We do not have line information for method start, lets have it here for now
828 buffer.append('{').appendLineSeparator();
829 tracer.incrementCurrentSourceLine();
831 RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
833 if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
835 TextBuffer code = root.toJava(indent + 1, tracer);
837 hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0;
841 catch (Throwable ex) {
842 DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex);
843 methodWrapper.decompiledWithErrors = true;
847 if (methodWrapper.decompiledWithErrors) {
848 buffer.appendIndent(indent + 1);
849 buffer.append("// $FF: Couldn't be decompiled");
850 buffer.appendLineSeparator();
851 tracer.incrementCurrentSourceLine();
855 tracer.addMapping(root.getDummyExit().bytecode);
857 buffer.appendIndent(indent).append('}').appendLineSeparator();
858 tracer.incrementCurrentSourceLine();
862 DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
867 //tracer.setCurrentSourceLine(buffer.countLines(start_index_method));
872 private static boolean hideConstructor(ClassWrapper wrapper, boolean init, boolean throwsExceptions, int paramCount) {
873 if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) {
878 for (StructMethod mt : wrapper.getClassStruct().getMethods()) {
879 if (CodeConstants.INIT_NAME.equals(mt.getName())) {
889 private static void appendDeprecation(TextBuffer buffer, int indent) {
890 buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator();
893 private enum MType {CLASS, FIELD, METHOD}
895 private static void appendRenameComment(TextBuffer buffer, String oldName, MType type, int indent) {
896 if (oldName == null) return;
898 buffer.appendIndent(indent);
899 buffer.append("// $FF: renamed from: ");
903 buffer.append(ExprProcessor.buildJavaClassName(oldName));
907 String[] fParts = oldName.split(" ");
908 FieldDescriptor fd = FieldDescriptor.parseDescriptor(fParts[2]);
909 buffer.append(fParts[1]);
911 buffer.append(getTypePrintOut(fd.type));
915 String[] mParts = oldName.split(" ");
916 MethodDescriptor md = MethodDescriptor.parseDescriptor(mParts[2]);
917 buffer.append(mParts[1]);
919 boolean first = true;
920 for (VarType paramType : md.params) {
925 buffer.append(getTypePrintOut(paramType));
928 buffer.append(getTypePrintOut(md.ret));
931 buffer.appendLineSeparator();
934 private static String getTypePrintOut(VarType type) {
935 String typeText = ExprProcessor.getCastTypeName(type, false);
936 if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeText) &&
937 DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
938 typeText = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false);
943 private static void appendComment(TextBuffer buffer, String comment, int indent) {
944 buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator();
947 private static final String[] ANNOTATION_ATTRIBUTES = {
948 StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS};
950 private static void appendAnnotations(TextBuffer buffer, StructMember mb, int indent) {
951 BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
953 for (String name : ANNOTATION_ATTRIBUTES) {
954 StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name);
955 if (attribute != null) {
956 for (AnnotationExprent annotation : attribute.getAnnotations()) {
957 buffer.append(annotation.toJava(indent, tracer_dummy)).appendLineSeparator();
963 private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = {
964 StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS};
966 private static void appendParameterAnnotations(TextBuffer buffer, StructMethod mt, int param) {
968 BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
970 for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) {
971 StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name);
972 if (attribute != null) {
973 List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations();
974 if (param < annotations.size()) {
975 for (AnnotationExprent annotation : annotations.get(param)) {
976 buffer.append(annotation.toJava(0, tracer_dummy)).append(' ');
983 private static final Map<Integer, String> MODIFIERS = new LinkedHashMap<Integer, String>() {{
984 put(CodeConstants.ACC_PUBLIC, "public");
985 put(CodeConstants.ACC_PROTECTED, "protected");
986 put(CodeConstants.ACC_PRIVATE, "private");
987 put(CodeConstants.ACC_ABSTRACT, "abstract");
988 put(CodeConstants.ACC_STATIC, "static");
989 put(CodeConstants.ACC_FINAL, "final");
990 put(CodeConstants.ACC_STRICT, "strictfp");
991 put(CodeConstants.ACC_TRANSIENT, "transient");
992 put(CodeConstants.ACC_VOLATILE, "volatile");
993 put(CodeConstants.ACC_SYNCHRONIZED, "synchronized");
994 put(CodeConstants.ACC_NATIVE, "native");
997 private static final int CLASS_ALLOWED =
998 CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |
999 CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_STRICT;
1000 private static final int FIELD_ALLOWED =
1001 CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_STATIC |
1002 CodeConstants.ACC_FINAL | CodeConstants.ACC_TRANSIENT | CodeConstants.ACC_VOLATILE;
1003 private static final int METHOD_ALLOWED =
1004 CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |
1005 CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE | CodeConstants.ACC_STRICT;
1007 private static final int CLASS_EXCLUDED = CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_STATIC;
1008 private static final int FIELD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL;
1009 private static final int METHOD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_ABSTRACT;
1011 private static void appendModifiers(TextBuffer buffer, int flags, int allowed, boolean isInterface, int excluded) {
1013 if (!isInterface) excluded = 0;
1014 for (int modifier : MODIFIERS.keySet()) {
1015 if ((flags & modifier) == modifier && (modifier & excluded) == 0) {
1016 buffer.append(MODIFIERS.get(modifier)).append(' ');
1021 private static void appendTypeParameters(TextBuffer buffer, List<String> parameters, List<List<GenericType>> bounds) {
1024 for (int i = 0; i < parameters.size(); i++) {
1026 buffer.append(", ");
1029 buffer.append(parameters.get(i));
1031 List<GenericType> parameterBounds = bounds.get(i);
1032 if (parameterBounds.size() > 1 || !"java/lang/Object".equals(parameterBounds.get(0).value)) {
1033 buffer.append(" extends ");
1034 buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(0)));
1035 for (int j = 1; j < parameterBounds.size(); j++) {
1036 buffer.append(" & ");
1037 buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(j)));