/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; import java.util.Map.Entry; public class ClassReference14Processor { private static final ExitExprent BODY_EXPR; private static final ExitExprent HANDLER_EXPR; static { InvocationExprent invFor = new InvocationExprent(); invFor.setName("forName"); invFor.setClassname("java/lang/Class"); invFor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); invFor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); invFor.setStatic(true); invFor.setLstParameters(Collections.singletonList(new VarExprent(0, VarType.VARTYPE_STRING, null))); BODY_EXPR = new ExitExprent(ExitExprent.EXIT_RETURN, invFor, VarType.VARTYPE_CLASS, null); InvocationExprent ctor = new InvocationExprent(); ctor.setName(CodeConstants.INIT_NAME); ctor.setClassname("java/lang/NoClassDefFoundError"); ctor.setStringDescriptor("()V"); ctor.setFunctype(InvocationExprent.TYP_INIT); ctor.setDescriptor(MethodDescriptor.parseDescriptor("()V")); NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<>(), null); newExpr.setConstructor(ctor); InvocationExprent invCause = new InvocationExprent(); invCause.setName("initCause"); invCause.setClassname("java/lang/NoClassDefFoundError"); invCause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); invCause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); invCause.setInstance(newExpr); invCause.setLstParameters( Collections.singletonList(new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null))); HANDLER_EXPR = new ExitExprent(ExitExprent.EXIT_THROW, invCause, null, null); } public static void processClassReferences(ClassNode node) { // find the synthetic method Class class$(String) if present HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<>(); mapClassMethods(node, mapClassMeths); if (mapClassMeths.isEmpty()) { return; } HashSet<ClassWrapper> setFound = new HashSet<>(); processClassRec(node, mapClassMeths, setFound); if (!setFound.isEmpty()) { for (ClassWrapper wrp : setFound) { StructMethod mt = mapClassMeths.get(wrp).methodStruct; wrp.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); } } } private static void processClassRec(ClassNode node, final HashMap<ClassWrapper, MethodWrapper> mapClassMeths, final HashSet<ClassWrapper> setFound) { final ClassWrapper wrapper = node.getWrapper(); // search code for (MethodWrapper meth : wrapper.getMethods()) { RootStatement root = meth.root; if (root != null) { DirectGraph graph = meth.getOrBuildGraph(); graph.iterateExprents(new DirectGraph.ExprentIterator() { public int processExprent(Exprent exprent) { for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { setFound.add(ent.getKey()); } } return 0; } }); } } // search initializers for (int j = 0; j < 2; j++) { VBStyleCollection<Exprent, String> initializers = j == 0 ? wrapper.getStaticFieldInitializers() : wrapper.getDynamicFieldInitializers(); for (int i = 0; i < initializers.size(); i++) { for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) { Exprent exprent = initializers.get(i); if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { setFound.add(ent.getKey()); } String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue()); if (cl != null) { initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), exprent.bytecode)); setFound.add(ent.getKey()); } } } } // iterate nested classes for (ClassNode nd : node.nested) { processClassRec(nd, mapClassMeths, setFound); } } private static void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) { boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); ClassWrapper wrapper = node.getWrapper(); for (MethodWrapper method : wrapper.getMethods()) { StructMethod mt = method.methodStruct; if ((noSynthFlag || mt.isSynthetic()) && mt.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") && mt.hasModifier(CodeConstants.ACC_STATIC)) { RootStatement root = method.root; if (root != null && root.getFirst().type == Statement.TYPE_TRYCATCH) { CatchStatement cst = (CatchStatement)root.getFirst(); if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK && cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK && cst.getVars().get(0).getVarType().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) { BasicBlockStatement body = (BasicBlockStatement)cst.getFirst(); BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1); if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) { if (BODY_EXPR.equals(body.getExprents().get(0)) && HANDLER_EXPR.equals(handler.getExprents().get(0))) { map.put(wrapper, method); break; } } } } } } // iterate nested classes for (ClassNode nd : node.nested) { mapClassMethods(nd, map); } } private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { boolean res = false; while (true) { boolean found = false; for (Exprent expr : exprent.getAllExprents()) { String cl = isClass14Invocation(expr, wrapper, meth); if (cl != null) { exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), expr.bytecode)); found = true; res = true; break; } res |= replaceInvocations(expr, wrapper, meth); } if (!found) { break; } } return res; } private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { if (exprent.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fexpr = (FunctionExprent)exprent; if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) { if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0); if (headexpr.getFuncType() == FunctionExprent.FUNCTION_EQ) { if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD && headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST && ((ConstExprent)headexpr.getLstOperands().get(1)).getConstType().equals(VarType.VARTYPE_NULL)) { FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0); ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname()); if (fieldnode != null && fieldnode.classStruct.qualifiedName.equals(wrapper.getClassStruct().qualifiedName)) { // source class StructField fd = wrapper.getClassStruct().getField(field.getName(), field.getDescriptor().descriptorString); // FIXME: can be null! why?? if (fd != null && fd.hasModifier(CodeConstants.ACC_STATIC) && (fd.isSynthetic() || DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET))) { if (fexpr.getLstOperands().get(1).type == Exprent.EXPRENT_ASSIGNMENT && fexpr.getLstOperands().get(2).equals(field)) { AssignmentExprent asexpr = (AssignmentExprent)fexpr.getLstOperands().get(1); if (asexpr.getLeft().equals(field) && asexpr.getRight().type == Exprent.EXPRENT_INVOCATION) { InvocationExprent invexpr = (InvocationExprent)asexpr.getRight(); if (invexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName) && invexpr.getName().equals(meth.methodStruct.getName()) && invexpr.getStringDescriptor().equals(meth.methodStruct.getDescriptor())) { if (invexpr.getLstParameters().get(0).type == Exprent.EXPRENT_CONST) { wrapper.getHiddenMembers() .add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); // hide synthetic field return ((ConstExprent)invexpr.getLstParameters().get(0)).getValue().toString(); } } } } } } } } } } } return null; } }