由于工作需要反编译分析 java 源码,于是需要反编译器做些改动,所以就有了这篇文章。
这次要分析的反编译器是 Femflower,是著名 IDE Idea 的反编译器。源码也是从 Idea 开源部分抠出来的。
[Github](https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine)
package org.jetbrains.java.decompiler.code;
代码段,即字节码中的关键字,是所有关键字的父类型,例如 ALOAD,NEW,RET 等。
其保存了所有字节码中关键字对应的 Constant,便于词法解析对应。
package org.jetbrains.java.decompiler.code;
指令,CodeConstants 的子类型,对应字节码中的指令,同上例如 ALOAD,NEW,RET。众多指令的父类:
这些 Instructions 将被保存在一个 Class[] 中。方法 ConstantsUtil. getInstructionInstance 从字节码数据中获取指令对应的 Instruction 类型。
private static Instruction getInstructionInstance(int opcode, int bytecode_version) {
try {
Instruction instr;
if ((opcode >= CodeConstants.opc_ifeq &&
opcode <= CodeConstants.opc_if_acmpne) ||
opcode == CodeConstants.opc_ifnull ||
opcode == CodeConstants.opc_ifnonnull) {
instr = new IfInstruction();
}
else {
Class cl = opcodeClasses[opcode];
if (opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) {
cl = null; // instruction unused in Java 6 and before
}
if (cl == null) {
instr = new Instruction();
}
else {
instr = (Instruction)cl.newInstance();
}
}
instr.opcode = opcode;
return instr;
}
catch (Exception ex) {
return null;
}
}
指令片段,其实就是一段字节码的 Instruction 集合。
package org.jetbrains.java.decompiler.struct.match;
可匹配接口,在字节码中,除了上面的所说的指令一类的关键字,那些类似定义的类名,变量名,方法名被称为 Matchable 字段,可结构化的字段。
Matchable 主要有两个实现,一是 Statement 分支结构,对应方法,Field,Var 的等声明,二是 Exprent 表达式,类似等于,Field 引用,New Object,if,等等。简单的说 Matchable 类似于指令后面的操作数,即 ALOAD {Mathable}。
从字节码中匹配对应的 IMatchable 类型如下:
public IMatchable findObject(MatchNode matchNode, int index)
位于 Imatchbale 接口内。
package org.jetbrains.java.decompiler.modules.decompiler.stats;
分支结构,类似于 if,Switch,Synchronize,Try Catch等含有分支的结构。
package org.jetbrains.java.decompiler.modules.decompiler.exps;
表达式,类似方法调用,变量引用,常量,赋值,New,return 等等都是表达式。
Struct 是比较重要的,描述了 Java 里面几个比较重要的结构,类型,方法体,Field。除此之外还有一个 Context,上下文,和文件的路径相关。
常量池,常量池中除了保存了常量之外,还有 Field,Method 的一些关键信息,类成员的名称,Modifers 信息都需要到常量池中读取。
流程控制图,CFG,程序中有关流程控制例如,循环,if 判断等等,在类似于汇编的字节码中,程序顺序执行,流程控制也是以顺序执行 + 跳转的方式实现。也就是说在字节码中流程控制是一种扁平结构的代码段,而在 java 源码中是类似于图的立体分支结构。
那么将字节码中的流程控制代码段转化为 java 代码的大致过程就是:
InstructionSequence 代码段 —(构建图)—> ControlFlowGraph —> Statement 分支语句。。。
/**
* 将方法结构体 中的流程控制代码段 转化为分支语句
* @param mt
* @param md
* @param varProc
* @return
* @throws IOException
*/
public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
StructClass cl = mt.getClassStruct();
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
mt.expandData();
//获取方法体的指令片段
InstructionSequence seq = mt.getInstructionSequence();
//控制流程图
ControlFlowGraph graph = new ControlFlowGraph(seq);
//移除死循环的流程块
DeadCodeHelper.removeDeadBlocks(graph);
//内联方法程序跳转处理
graph.inlineJsr(mt);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
DeadCodeHelper.removeGotos(graph);
ExceptionDeobfuscator.removeCircularRanges(graph);
ExceptionDeobfuscator.restorePopRanges(graph);
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
ExceptionDeobfuscator.removeEmptyRanges(graph);
}
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
// special case: single return instruction outside of a protected range
DeadCodeHelper.incorporateValueReturns(graph);
}
// ExceptionDeobfuscator.restorePopRanges(graph);
ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);
DeadCodeHelper.mergeBasicBlocks(graph);
DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN);
}
//流程控制快 -> 代码块
RootStatement root = DomHelper.parseGraph(graph);
//处理 finally 块
FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(mt, root, graph)) {
root = DomHelper.parseGraph(graph);
}
// remove synchronized exception handler
// not until now because of comparison between synchronized statements in the finally cycle
DomHelper.removeSynchronizedHandler(root);
// LabelHelper.lowContinueLabels(root, new HashSet());
SequenceHelper.condenseSequences(root);
ClearStructHelper.clearStatements(root);
//表达式处理
ExprProcessor proc = new ExprProcessor(md, varProc);
proc.processStatement(root, cl);
SequenceHelper.condenseSequences(root);
//参数栈 v1 v2 v3 什么的
while (true) {
StackVarsProcessor stackProc = new StackVarsProcessor();
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
if (!new PPandMMHelper().findPPandMM(root)) {
break;
}
}
while (true) {
LabelHelper.cleanUpEdges(root);
while (true) {
MergeHelper.enhanceLoops(root);
if (LoopExtractHelper.extractLoops(root)) {
continue;
}
if (!IfHelper.mergeAllIfs(root)) {
break;
}
}
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
SequenceHelper.condenseSequences(root);
StackVarsProcessor stackProc = new StackVarsProcessor();
stackProc.simplifyStackVars(root, mt, cl);
varProc.setVarVersions(root);
}
}
LabelHelper.identifyLabels(root);
if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {
continue;
}
// initializer may have at most one return point, so no transformation of method exits permitted
if (isInitializer || !ExitHelper.condenseExits(root)) {
break;
}
// FIXME: !!
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
// break;
// }
}
ExitHelper.removeRedundantReturns(root);
SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);
varProc.setVarDefinitions(root);
// must be the last invocation, because it makes the statement structure inconsistent
// FIXME: new edge type needed
LabelHelper.replaceContinueWithBreak(root);
mt.releaseResources();
return root;
}
BytecodeMappingTracer 是一个用于跟踪所写 java code 行数的追踪器,每当 TextBuffer写过一行时,tracer 内部的行数都被手动 +1,本来行数是不包括 Class 体上面的 package 语句和 import 语句的。由于需要自行添加 headlines 变量。
TextBuffer 其实没什么好讲的,java code 的字符串最后都被塞到了这里,类似于 StringBuffer,不过加上了一些函数。
需要注意的是对于表达式来说,表达式是一个树形的集合,将表达式树写成 java code 就是对树进行遍历,而每个表达式即树节点都是一个单独的 TextBuffer,最后由 append 拼接。所以想知道某个表达式在一行中具体的位置是比较困难的。
拿 ExitExprent 即 return 表达式来说:
@Override
public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
tracer.addMapping(bytecode);
if (exitType == EXIT_RETURN) {
TextBuffer buffer = new TextBuffer("return");
if (retType.type != CodeConstants.TYPE_VOID) {
buffer.append(' ');
ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer);
}
…………………………..
return buffer;
}
public void decompileContext() {
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
new IdentifierConverter().rename(structContext);
}
//从 Class 节点开始反编译
classesProcessor = new ClassesProcessor(structContext);
classesProcessor.visitor = visitor;
DecompilerContext.setClassProcessor(classesProcessor);
DecompilerContext.setStructContext(structContext);
structContext.saveContext();
}
public void saveContext() {
for (ContextUnit unit : units.values()) {
if (unit.isOwn()) {
unit.save();
}
}
}
/**
* 输入分发
*/
public void save() {
switch (type) {
//文件夹
case TYPE_FOLDER:
// create folder
resultSaver.saveFolder(filename);
// non-class files 无内容的文件,直接拷贝
for (String[] pair : otherEntries) {
resultSaver.copyFile(pair[0], filename, pair[1]);
}
// classes 类文件,需要解析
for (int i = 0; i < classes.size(); i++) {
StructClass cl = classes.get(i);
String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i));
if (entryName != null) {
//反编译这个类 得到 java String
String content = decompiledData.getClassContent(cl);
if (content != null) {
int[] mapping = null;
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
mapping = DecompilerContext.getBytecodeSourceMapper().getOriginalLinesMapping();
}
//保存到本地
resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content, mapping);
}
}
}
break;
//jar 文件同下 zip 文件
case TYPE_JAR:
case TYPE_ZIP:
// create archive file
resultSaver.saveFolder(archivePath);
resultSaver.createArchive(archivePath, filename, manifest);
// directory entries 生成文件夹结构
for (String dirEntry : dirEntries) {
resultSaver.saveDirEntry(archivePath, filename, dirEntry);
}
// non-class entries 无内容文件 copy
for (String[] pair : otherEntries) {
if (type != TYPE_JAR || !JarFile.MANIFEST_NAME.equalsIgnoreCase(pair[1])) {
resultSaver.copyEntry(pair[0], archivePath, filename, pair[1]);
}
}
// classes 类文件
for (int i = 0; i < classes.size(); i++) {
StructClass cl = classes.get(i);
String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i));
if (entryName != null) {
//反编译
String content = decompiledData.getClassContent(cl);
//保存
resultSaver.saveClassEntry(archivePath, filename, cl.qualifiedName, entryName, content);
}
}
//关闭 zip 文件
resultSaver.closeArchive(archivePath, filename);
}
}
对类基础结构的解析
/**
* 反编译类文件
* @param cl
* @return
*/
@Override
public String getClassContent(StructClass cl) {
try {
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());
classesProcessor.writeClass(cl, buffer);
return buffer.toString();
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex);
return null;
}
}
/**
* 写类文件
* @param cl
* @param buffer
* @throws IOException
*/
public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {
//class 树,包含父类,内部类外部类等等
ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.CLASS_ROOT) {
return;
}
DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try {
//import 语句的集合
ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.setImportCollector(importCollector);
DecompilerContext.setCounterContainer(new CounterContainer());
DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper());
new LambdaProcessor().processClass(root);
// add simple class names to implicit import
addClassnameToImport(root, importCollector);
// build wrappers for all nested classes (that's where actual processing takes place) 实际拼装类代码的地方
initWrappers(root, null);
// build 内部类
new NestedClassProcessor().processClass(root, root);
// build 内部类 meber 引用
new NestedMemberAccess().propagateMemberAccess(root);
// 写 package 语句
int index = cl.qualifiedName.lastIndexOf("/");
if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ");
buffer.append(packageName);
buffer.append(";");
buffer.appendLineSeparator();
buffer.appendLineSeparator();
}
// 写 import 语句
int import_lines_written = importCollector.writeImports(buffer);
if (import_lines_written > 0) {
buffer.appendLineSeparator();
}
int offsetLines = buffer.countLines();
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
// 重点,解析 class 结构
new ClassWriter(visitor).classToJava(root, classBuffer, 0, null, offsetLines);
buffer.append(classBuffer);
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
mapper.addTotalOffset(offsetLines);
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {
buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());
}
if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) {
buffer.appendLineSeparator();
mapper.dumpMapping(buffer, true);
}
}
}
finally {
destroyWrappers(root);
DecompilerContext.getLogger().endReadingClass();
}
}
private final VBStyleCollection staticFieldInitializers
private final VBStyleCollection dynamicFieldInitializers
private final VBStyleCollection methods
可以看到主要是 Field 定义右边的初始化表达式。
/**
*
* @param node 进入是 root class
* @throws IOException
*/
private static void initWrappers(ClassNode node, ClassNode root) throws IOException {
if (node.type == ClassNode.CLASS_LAMBDA) {
return;
}
ClassWrapper wrapper = new ClassWrapper(node.classStruct);
// 手动添加根类型
if (root == null) {
wrapper.rootClass = node.classStruct;
root = node;
} else {
wrapper.rootClass = root.classStruct;
}
//结构解析入口
wrapper.init();
node.wrapper = wrapper;
// 递归解析内部类
for (ClassNode nd : node.nested) {
initWrappers(nd, root);
}
}
/**
* 从字节码中解析类结构
* @throws IOException
*/
public void init() throws IOException {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
// collect field names
Set setFieldNames = new HashSet<>();
for (StructField fd : classStruct.getFields()) {
setFieldNames.add(fd.getName());
}
int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);
// 拼装方法
for (StructMethod mt : classStruct.getMethods()) {
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
// 参数名列表
VarNamesCollector vc = new VarNamesCollector();
DecompilerContext.setVarNamesCollector(vc);
// 引用计数器
CounterContainer counter = new CounterContainer();
DecompilerContext.setCounterContainer(counter);
// 方法描述, 根据方法全限定名
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
VarProcessor varProc = new VarProcessor(mt, md);
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varProc);
RootStatement root = null;
boolean isError = false;
try {
if (mt.containsCode()) {
if (maxSec == 0 || testMode) {
root = MethodProcessorRunnable.codeToJava(mt, md, varProc);
}
else {
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtThread = new Thread(mtProc, "Java decompiler");
//看门狗,当处理方法超过指定时间时,则认为处理失败超时,需要强行杀死线程.
long stopAt = System.currentTimeMillis() + maxSec * 1000;
mtThread.start();
while (!mtProc.isFinished()) {
try {
synchronized (mtProc.lock) {
// 看门狗每 0.2s 检查一次时间
mtProc.lock.wait(200);
}
}
catch (InterruptedException e) {
killThread(mtThread);
throw e;
}
//同上
if (System.currentTimeMillis() >= stopAt) {
String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR);
killThread(mtThread);
isError = true;
break;
}
}
if (!isError) {
root = mtProc.getResult();
}
}
}
else {
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
int paramCount = 0;
if (thisVar) {
varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName);
paramCount = 1;
}
paramCount += md.params.length;
int varIndex = 0;
for (int i = 0; i < paramCount; i++) {
varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getFreeName(varIndex));
if (thisVar) {
if (i == 0) {
varIndex++;
}
else {
varIndex += md.params[i - 1].stackSize;
}
}
else {
varIndex += md.params[i].stackSize;
}
}
}
}
catch (Throwable ex) {
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex);
isError = true;
}
MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter);
methodWrapper.decompiledWithErrors = isError;
methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
// rename vars so that no one has the same name as a field
varProc.refreshVarNames(new VarNamesCollector(setFieldNames));
// if debug information present and should be used
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
if (attr != null) {
// only param names here
varProc.setDebugVarNames(attr.getMapParamNames());
// the rest is here
methodWrapper.getOrBuildGraph().iterateExprents(exprent -> {
List lst = exprent.getAllExprents(true);
lst.add(exprent);
lst.stream()
.filter(e -> e.type == Exprent.EXPRENT_VAR)
.forEach(e -> {
VarExprent varExprent = (VarExprent)e;
String name = varExprent.getDebugName(mt);
if (name != null) {
varProc.setVarName(varExprent.getVarVersionPair(), name);
}
});
return 0;
});
}
}
DecompilerContext.getLogger().endMethod();
}
DecompilerContext.getLogger().endClass();
}
ClassWritter 主要解析了上面所说的类的最主要的直接成员,嵌套的内部类,Field,和 Method。
有三个主要方法:1.classTojava:解析类结构本身,2.fieldTojava:解析 Field 3.methodTojava:解析 Method。
classTojava
classToJava 是 ClassWrite 类中其他函数的入口。其最后一个参数 headLines 是自行添加的。
/**
*
* @param node
* @param buffer
* @param indent 缩进 在 Class 块中就是 {}
* @param tracer
* @param headLines 头部行数
*/
public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer, int headLines) {
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, headLines);
dummy_tracer.visitor = visitor;
dummy_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java";
dummy_tracer.classKey = node.classStruct.qualifiedName;
dummy_tracer.methodKey = "";
try {
Type type = new Type();
type.setName(node.simpleName);
type.setKey(node.classStruct.qualifiedName);
type.setFullName(node.classStruct.qualifiedName.replaceAll("/", "."));
type.setPosition(new Position());
// last minute processing
invokeProcessors(node);
ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct();
DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
// write class definition
// 写方法描述 这里插入 TypeDefineNode
int start_class_def = buffer.length();
writeClassDefinition(node, buffer, indent, type);
type.getPosition().line = startLine + headLines;
boolean hasContent = false;
boolean enumFields = false;
dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def));
// 写 Fields
for (StructField fd : cl.getFields()) {
// 是否是隐藏 field 如 this$0
boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if (hide) continue;
// enum field
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;
}
// 写 Fields
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, headLines);
method_tracer.visitor = visitor;
method_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java";
method_tracer.classKey = node.classStruct.qualifiedName;
method_tracer.methodKey = "." + mt.getName() + mt.getDescriptor();
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() || inner.namelessConstructorStub;
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, headLines);
class_tracer.visitor = visitor;
class_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java";
class_tracer.classKey = inner.classStruct.qualifiedName;
class_tracer.methodKey = "";
// 递归调用
classToJava(inner, buffer, indent + 1, class_tracer, headLines);
startLine = buffer.countLines();
hasContent = true;
}
}
buffer.appendIndent(indent).append('}');
if (node.type != ClassNode.CLASS_ANONYMOUS) {
buffer.appendLineSeparator();
}
if (visitor != null) {
visitor.typeDefine(type);
}
}
finally {
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode);
}
DecompilerContext.getLogger().endWriteClass();
}
classToField
写 Field ,需要注意的是 Field 的初始化即 = 右边的写则代理给了 Exprent.toJava 方法。
//写 Field 重点 ReferenceNode 切入点
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
Field field = new Field();
int start = buffer.length();
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);
field.setName(fd.getName());
field.setInterface(isInterface);
field.setEnum(isEnum);
field.setKey(cl.qualifiedName + "." + fd.getName());
field.setFullName(cl.qualifiedName.replaceAll("/", ".") + "." + fd.getName());
field.setMemberClass(tracer.classKey);
//写废弃 Annotation
if (isDeprecated) {
appendDeprecation(buffer, indent);
}
if (interceptor != null) {
String oldName = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor());
appendRenameComment(buffer, oldName, MType.FIELD, indent);
}
//匿名字段 多为编译器生成字段
if (fd.isSynthetic()) {
appendComment(buffer, "synthetic field", indent);
}
//写注解 插入点
appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD);
buffer.appendIndent(indent);
if (!isEnum) {
appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED);
}
VarType fieldType = new VarType(fd.getDescriptor(), false);
field.setTypeKey(fieldType.value);
// field 全限定名
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) {
Position typePosition = new Position();
typePosition.line = tracer.getLinesInJavaSource();
field.setTypePosition(typePosition);
if (descriptor != null) {
//由 field 全限定名获取 field 类型的名称
typePosition.start = buffer.length();
String typeKey = GenericMain.getGenericCastTypeName(descriptor.type);
buffer.append(typeKey);
typePosition.end = buffer.length();
}
else {
typePosition.start = buffer.length();
String typeKey = ExprProcessor.getCastTypeName(fieldType);
buffer.append(typeKey);
typePosition.end = buffer.length();
}
buffer.append(' ');
}
int charStart = buffer.length();
//重点 写 field 的名字
buffer.append(fd.getName());
int charEnd = buffer.length();
int line = tracer.getLinesInJavaSource();
//行号增加
tracer.incrementCurrentSourceLine(buffer.countLines(start));
Position position = new Position();
position.start = charStart;
position.end = charEnd;
position.line = line;
position.src = wrapper.rootClass.qualifiedName + ".java";
field.setPosition(position);
//写初始化表达式 int a = {初始化表达式};
Exprent initializer;
if (fd.hasModifier(CodeConstants.ACC_STATIC)) {
field.setStatic(true);
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, tracer));
}
else {
buffer.append(" = ");
// 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)) {
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, null).toJava(indent, tracer));
}
}
//结束
if (!isEnum) {
buffer.append(";").appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
if (visitor != null) {
visitor.fieldDefine(field);
}
}
methodToJava
同样的,Method 内部也有很多表达式,同样被代理给了 Exprent.toJava
/**
* 写 method
* @param node
* @param mt
* @param buffer
* @param indent
* @param tracer
* @return
*/
private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
Method method = new Method();
method.setName(mt.getName());
method.setPosition(new Position());
method.getPosition().src = node.getWrapper().rootClass.qualifiedName + ".java";
ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct();
MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
method.setDefineClass(cl.qualifiedName);
method.setKey(cl.qualifiedName + "." + mt.getName() + mt.getDescriptor());
method.setFullName(cl.qualifiedName.replaceAll("/", ".") + "." + mt.getName());
boolean hideMethod = false;
int start_index_method = buffer.length();
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;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
method.setRetType(md.ret.value);
if (md.params != null && md.params.length > 0) {
List parsKey = new ArrayList<>();
for (VarType parType:md.params) {
parsKey.add(parType.value);
}
}
int flags = mt.getAccessFlags();
method.setStatic(mt.hasModifier(CodeConstants.ACC_STATIC));
if ((flags & CodeConstants.ACC_NATIVE) != 0) {
flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp
method.setStatic(true);
}
if (CodeConstants.CLINIT_NAME.equals(mt.getName())) {
flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer
method.setStatic(true);
}
if (isDeprecated) {
appendDeprecation(buffer, indent);
}
if (interceptor != null) {
String oldName = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor());
appendRenameComment(buffer, oldName, MType.METHOD, indent);
}
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic");
boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0;
if (isSynthetic) {
appendComment(buffer, "synthetic method", indent);
}
if (isBridge) {
appendComment(buffer, "bridge method", indent);
}
//写注解
appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE);
buffer.appendIndent(indent);
appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED);
method.setAbstract(mt.hasModifier(CodeConstants.ACC_ABSTRACT));
method.setPublic(mt.hasModifier(CodeConstants.ACC_PUBLIC));
method.setModifiers(flags);
if (isInterface) {
method.setAbstract(true);
}
if (isInterface && mt.containsCode()) {
// 'default' modifier (Java 8)
buffer.append("default ");
method.setAbstract(false);
}
String name = mt.getName();
if (CodeConstants.INIT_NAME.equals(name)) {
if (node.type == ClassNode.CLASS_ANONYMOUS) {
name = "";
dinit = true;
}
else {
name = node.simpleName;
init = true;
}
}
else if (CodeConstants.CLINIT_NAME.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) {
long actualParams = md.params.length;
List sigFields = methodWrapper.signatureFields;
if (sigFields != null) {
actualParams = sigFields.stream().filter(Objects::isNull).count();
}
else if (isEnum && init) actualParams -= 2;
if (actualParams != descriptor.params.size()) {
String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName;
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(' ');
}
//append 方法名
method.getPosition().start = buffer.length();
buffer.append(toValidJavaIdentifier(name));
method.getPosition().end = buffer.length();
method.getPosition().line = tracer.getLinesInJavaSource();
buffer.append('(');
// parameters 方法参数
List 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;
boolean hasDescriptor = descriptor != null;
int start = isEnum && init && !hasDescriptor ? 2 : 0;
int params = hasDescriptor ? descriptor.params.size() : md.params.length;
for (int i = start; i < params; i++) {
if (hasDescriptor || (signFields == null || signFields.get(i) == null)) {
if (!firstParameter) {
buffer.append(", ");
}
// 参数上的注解
appendParameterAnnotations(buffer, mt, paramCount);
if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) {
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 = parameterType.decreaseArrayDim();
}
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];
boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0);
if (isVarArg) {
parameterType = parameterType.decreaseArrayDim();
}
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 VarVersionPair(index, 0));
// 写参数名字
buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
firstParameter = false;
paramCount++;
}
index += md.params[i].stackSize;
}
buffer.append(')');
// 异常列表
StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions");
if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) {
throwsExceptions = true;
buffer.append(" throws ");
List exceptions = new ArrayList<>();
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));
exceptions.add(descriptor.exceptions.get(i).value);
}
else {
VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true);
buffer.append(ExprProcessor.getCastTypeName(type));
}
}
method.setExceptions(exceptions);
}
}
tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method));
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(0, BytecodeMappingTracer.DUMMY));
}
}
buffer.append(';');
buffer.appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
else {
if (!clinit && !dinit) {
buffer.append(' ');
}
// We do not have line information for method start, lets have it here for now
buffer.append('{').appendLineSeparator();
tracer.incrementCurrentSourceLine();
// 写方法内部众多的表达式
RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
try {
TextBuffer code = root.toJava(indent + 1, tracer);
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.appendIndent(indent + 1);
buffer.append("// $FF: Couldn't be decompiled");
buffer.appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
if (root != null) {
tracer.addMapping(root.getDummyExit().bytecode);
}
buffer.appendIndent(indent).append('}').appendLineSeparator();
tracer.incrementCurrentSourceLine();
}
}
finally {
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
}
if (visitor != null) {
visitor.methodDefine(method);
}
// save total lines
// TODO: optimize
//tracer.setCurrentSourceLine(buffer.countLines(start_index_method));
return !hideMethod;
}
前面相当于 java code 的骨架,表达式相当于 java code 的内容了,表达式一般存在于 变量定义的 = 右边初始化字段,以及 Method 内部大量的表达式。
一组有关的表达式(一般是一行)是树形结构,上面说过将表达式树转换为 java code 即是树的遍历。遍历的同时调用 toJava 组装 TextBuffer
public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
throw new RuntimeException("not implemented");
}
可见是交给具体的表达式实现。
至于 Statement 流程控制语句或者说是分支语句则差不多。