All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.java.decompiler.main.ClassReference14Processor Maven / Gradle / Ivy

The newest version!
/*
 * 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 mapClassMeths = new HashMap<>();
    mapClassMethods(node, mapClassMeths);
    if (mapClassMeths.isEmpty()) {
      return;
    }
    HashSet 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 mapClassMeths,
                                      final HashSet 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 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 initializers =
        j == 0 ? wrapper.getStaticFieldInitializers() : wrapper.getDynamicFieldInitializers();

      for (int i = 0; i < initializers.size(); i++) {
        for (Entry 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 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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy