日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java 反编译器源码分析

發布時間:2023/12/8 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 反编译器源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

由于工作需要反編譯分析 java 源碼,于是需要反編譯器做些改動,所以就有了這篇文章。 這次要分析的反編譯器是 Femflower,是著名 IDE Idea 的反編譯器。源碼也是從 Idea 開源部分摳出來的。 [Github](https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine)

數據結構

CodeConstants

package org.jetbrains.java.decompiler.code; 代碼段,即字節碼中的關鍵字,是所有關鍵字的父類型,例如 ALOAD,NEW,RET 等。 其保存了所有字節碼中關鍵字對應的 Constant,便于詞法解析對應。

Instruction

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;} }

InstructionSequence

指令片段,其實就是一段字節碼的 Instruction 集合。

IMatchable

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 接口內。

Statement

package org.jetbrains.java.decompiler.modules.decompiler.stats;
分支結構,類似于 if,Switch,Synchronize,Try Catch等含有分支的結構。

Exprent

package org.jetbrains.java.decompiler.modules.decompiler.exps;
表達式,類似方法調用,變量引用,常量,賦值,New,return 等等都是表達式。

Struct

Struct 是比較重要的,描述了 Java 里面幾個比較重要的結構,類型,方法體,Field。除此之外還有一個 Context,上下文,和文件的路徑相關。

CosntantPool

常量池,常量池中除了保存了常量之外,還有 Field,Method 的一些關鍵信息,類成員的名稱,Modifers 信息都需要到常量池中讀取。

ControlFlowGraph

流程控制圖,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 onlymt.expandData();//獲取方法體的指令片段InstructionSequence seq = mt.getInstructionSequence();//控制流程圖ControlFlowGraph graph = new ControlFlowGraph(seq);//移除死循環的流程塊DeadCodeHelper.removeDeadBlocks(graph);//內聯方法程序跳轉處理graph.inlineJsr(mt);// TODO: move to the start, before jsr inliningDeadCodeHelper.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 rangeDeadCodeHelper.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 cycleDomHelper.removeSynchronizedHandler(root);// LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());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 permittedif (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 neededLabelHelper.replaceContinueWithBreak(root);mt.releaseResources();return root; }

BytecodeMappingTracer

BytecodeMappingTracer 是一個用于跟蹤所寫 java code 行數的追蹤器,每當 TextBuffer寫過一行時,tracer 內部的行數都被手動 +1,本來行數是不包括 Class 體上面的 package 語句和 import 語句的。由于需要自行添加 headlines 變量。

TextBuffer

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;}

過程

遍歷 .class 文件

  • Femflower.decmpileContext
    //開始反編譯
  • 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(); }
  • StructContext.saveContext
  • public void saveContext() {for (ContextUnit unit : units.values()) {if (unit.isOwn()) {unit.save();}} }
  • ContextUnit.save
  • /*** 輸入分發*/ public void save() {switch (type) {//文件夾case TYPE_FOLDER:// create folderresultSaver.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 StringString 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 fileresultSaver.saveFolder(archivePath);resultSaver.createArchive(archivePath, filename, manifest);// directory entries 生成文件夾結構for (String dirEntry : dirEntries) {resultSaver.saveDirEntry(archivePath, filename, dirEntry);}// non-class entries 無內容文件 copyfor (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;} }
  • ClassesProcessor.writeClass
    writeClass 函數邏輯比較清晰,先寫 package xxxxx, 然后是 import xxxxxx, 最后是類結構。
  • /*** 寫類文件* @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 importaddClassnameToImport(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();} }
  • initWrappers
    ClassWrapper 對比 ClassNode 這個已知的 Class 數據結構多了 Class 內部,Method 外部的 Expert 表達式集合,以及 MethodWrapper 集合,而 MethodWrapper 與 MethodNode 的區別可以類推。
  • private final VBStyleCollection<Exprent, String> staticFieldInitializers private final VBStyleCollection<Exprent, String> dynamicFieldInitializers private final VBStyleCollection<MethodWrapper, String> 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);} }
  • ClassWrapper.init
  • /*** 從字節碼中解析類結構* @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 namesSet<String> 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 fieldvarProc.refreshVarNames(new VarNamesCollector(setFieldNames));// if debug information present and should be usedif (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();if (attr != null) {// only param names herevarProc.setDebugVarNames(attr.getMapParamNames());// the rest is heremethodWrapper.getOrBuildGraph().iterateExprents(exprent -> {List<Exprent> 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

    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 processinginvokeProcessors(node);ClassWrapper wrapper = node.getWrapper();StructClass cl = wrapper.getClassStruct();DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);// write class definition// 寫方法描述 這里插入 TypeDefineNodeint 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));// 寫 Fieldsfor (StructField fd : cl.getFields()) {// 是否是隱藏 field 如 this$0boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));if (hide) continue;// enum fieldboolean 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;}// 寫 FieldsfieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracerhasContent = true;}if (enumFields) {buffer.append(';').appendLineSeparator();dummy_tracer.incrementCurrentSourceLine();}// FIXME: fields don't matter at the momentstartLine += buffer.countLines(start_class_def);// methodsfor (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);//寫廢棄 Annotationif (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<String> 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 strictfpmethod.setStatic(true);}if (CodeConstants.CLINIT_NAME.equals(mt.getName())) {flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializermethod.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<VarVersionPair> 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<VarVersionPair> 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 errorsfirstParameter = 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<String> 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 nowbuffer.append('{').appendLineSeparator();tracer.incrementCurrentSourceLine();// 寫方法內部眾多的表達式RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;if (root != null && !methodWrapper.decompiledWithErrors) { // check for existencetry {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 流程控制語句或者說是分支語句則差不多。

    總結

    以上是生活随笔為你收集整理的java 反编译器源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。