void (ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0; BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(startLine); try { // last minute processing invokeProcessors(node); ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); // write class definition int start_class_def = buffer.length(); writeClassDefinition(node, buffer, indent); boolean hasContent = false; boolean enumFields = false; dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def)); List<StructRecordComponent> components = cl.getRecordComponents(); 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; if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) && components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) { // Record component field: skip it continue; } boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); if (isEnum) { if (enumFields) { buffer.append(',').appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(); } enumFields = true; } else if (enumFields) { buffer.append(';'); buffer.appendLineSeparator(); buffer.appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(2); enumFields = false; } fieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracer hasContent = true; } if (enumFields) { buffer.append(';').appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(); } // FIXME: fields don't matter at the moment startLine += buffer.countLines(start_class_def); // 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(); int storedLine = startLine; if (hasContent) { buffer.appendLineSeparator(); startLine++; } BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(startLine); boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer); if (!methodSkipped) { hasContent = true; addTracer(cl, mt, method_tracer); startLine = method_tracer.getCurrentSourceLine(); } else { buffer.setLength(position); startLine = storedLine; } } // 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.appendLineSeparator(); startLine++; } BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine); classToJava(inner, buffer, indent + 1, class_tracer); startLine = buffer.countLines(); hasContent = true; } } buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { buffer.appendLineSeparator(); } }