Package jadx.tests.api

Source Code of jadx.tests.api.IntegrationTest

package jadx.tests.api;

import jadx.api.DefaultJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess;
import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.FileUtils;
import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.compiler.StaticCompiler;
import jadx.tests.api.utils.TestUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarOutputStream;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public abstract class IntegrationTest extends TestUtils {

  private static final String TEST_DIRECTORY = "src/test/java";
  private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;

  protected boolean outputCFG = false;
  protected boolean isFallback = false;
  protected boolean deleteTmpFiles = true;

  protected boolean withDebugInfo = true;

  protected String outDir = "test-out-tmp";

  protected boolean compile = true;
  private DynamicCompiler dynamicCompiler;

  public ClassNode getClassNode(Class<?> clazz) {
    try {
      File jar = getJarForClass(clazz);
      return getClassNodeFromFile(jar, clazz.getName());
    } catch (Exception e) {
      fail(e.getMessage());
    }
    return null;
  }

  public ClassNode getClassNodeFromFile(File file, String clsName) {
    JadxDecompiler d = new JadxDecompiler();
    try {
      d.loadFile(file);
    } catch (JadxException e) {
      fail(e.getMessage());
    }
    ClassNode cls = JadxInternalAccess.getRoot(d).searchClassByName(clsName);
    assertNotNull("Class not found: " + clsName, cls);
    assertEquals(cls.getFullName(), clsName);

    cls.load();
    for (IDexTreeVisitor visitor : getPasses()) {
      DepthTraversal.visit(visitor, cls);
    }
    // don't unload class

    System.out.println("-----------------------------------------------------------");
    System.out.println(cls.getCode());
    System.out.println("-----------------------------------------------------------");

    checkCode(cls);
    compile(cls);
    runAutoCheck(clsName);
    return cls;
  }

  private static void checkCode(ClassNode cls) {
    assertTrue("Inconsistent cls: " + cls,
        !cls.contains(AFlag.INCONSISTENT_CODE) && !cls.contains(AType.JADX_ERROR));
    for (MethodNode mthNode : cls.getMethods()) {
      assertTrue("Inconsistent method: " + mthNode,
          !mthNode.contains(AFlag.INCONSISTENT_CODE) && !mthNode.contains(AType.JADX_ERROR));
    }
    assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
  }

  protected List<IDexTreeVisitor> getPasses() {
    return Jadx.getPassesList(new DefaultJadxArgs() {
      @Override
      public boolean isCFGOutput() {
        return outputCFG;
      }

      @Override
      public boolean isRawCFGOutput() {
        return outputCFG;
      }

      @Override
      public boolean isFallbackMode() {
        return isFallback;
      }

      @Override
      public boolean isShowInconsistentCode() {
        return true;
      }

      @Override
      public int getThreadsCount() {
        return 1;
      }
    }, new File(outDir));
  }

  private void runAutoCheck(String clsName) {
    try {
      // run 'check' method from original class
      Class<?> origCls;
      try {
        origCls = Class.forName(clsName);
      } catch (ClassNotFoundException e) {
        // ignore
        return;
      }
      Method checkMth;
      try {
        checkMth = origCls.getMethod("check");
      } catch (NoSuchMethodException e) {
        // ignore
        return;
      }
      if (!checkMth.getReturnType().equals(void.class)
          || !Modifier.isPublic(checkMth.getModifiers())
          || Modifier.isStatic(checkMth.getModifiers())) {
        fail("Wrong 'check' method");
        return;
      }
      try {
        checkMth.invoke(origCls.newInstance());
      } catch (InvocationTargetException ie) {
        rethrow("Java check failed", ie);
      }
      // run 'check' method from decompiled class
      try {
        invoke("check");
      } catch (InvocationTargetException ie) {
        rethrow("Decompiled check failed", ie);
      }
      System.out.println("Auto check: PASSED");
    } catch (Exception e) {
      e.printStackTrace();
      fail("Auto check exception: " + e.getMessage());
    }
  }

  private void rethrow(String msg, InvocationTargetException ie) {
    Throwable cause = ie.getCause();
    if (cause instanceof AssertionError) {
      System.err.println(msg);
      throw ((AssertionError) cause);
    } else {
      cause.printStackTrace();
      fail(msg + cause.getMessage());
    }
  }

  protected MethodNode getMethod(ClassNode cls, String method) {
    for (MethodNode mth : cls.getMethods()) {
      if (mth.getName().equals(method)) {
        return mth;
      }
    }
    fail("Method not found " + method + " in class " + cls);
    return null;
  }

  void compile(ClassNode cls) {
    if (!compile) {
      return;
    }
    try {
      dynamicCompiler = new DynamicCompiler(cls);
      boolean result = dynamicCompiler.compile();
      assertTrue("Compilation failed", result);
      System.out.println("Compilation: PASSED");
    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
  }

  public Object invoke(String method) throws Exception {
    return invoke(method, new Class[0]);
  }

  public Object invoke(String method, Class[] types, Object... args) throws Exception {
    Method mth = getReflectMethod(method, types);
    return invoke(mth, args);
  }

  public Method getReflectMethod(String method, Class... types) {
    assertNotNull("dynamicCompiler not ready", dynamicCompiler);
    try {
      return dynamicCompiler.getMethod(method, types);
    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
    return null;
  }

  public Object invoke(Method mth, Object... args) throws Exception {
    assertNotNull("dynamicCompiler not ready", dynamicCompiler);
    assertNotNull("unknown method", mth);
    return dynamicCompiler.invoke(mth, args);
  }

  public File getJarForClass(Class<?> cls) throws IOException {
    String path = cls.getPackage().getName().replace('.', '/');
    List<File> list;
    if (!withDebugInfo) {
      list = compileClass(cls);
    } else {
      list = getClassFilesWithInners(cls);
      if (list.isEmpty()) {
        list = compileClass(cls);
      }
    }
    assertNotEquals("File list is empty", 0, list.size());

    File temp = createTempFile(".jar");
    JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp));
    for (File file : list) {
      FileUtils.addFileToJar(jo, file, path + "/" + file.getName());
    }
    jo.close();
    return temp;
  }

  protected File createTempFile(String suffix) {
    File temp = null;
    try {
      temp = File.createTempFile("jadx-tmp-", System.nanoTime() + suffix);
      if (deleteTmpFiles) {
        temp.deleteOnExit();
      } else {
        System.out.println("Temporary file path: " + temp.getAbsolutePath());
      }
    } catch (IOException e) {
      fail(e.getMessage());
    }
    return temp;
  }

  private static File createTempDir(String prefix) throws IOException {
    File baseDir = new File(System.getProperty("java.io.tmpdir"));
    String baseName = prefix + "-" + System.nanoTime();
    for (int counter = 1; counter < 1000; counter++) {
      File tempDir = new File(baseDir, baseName + counter);
      if (tempDir.mkdir()) {
        return tempDir;
      }
    }
    throw new IOException("Failed to create temp directory");
  }

  private List<File> getClassFilesWithInners(Class<?> cls) {
    List<File> list = new ArrayList<File>();
    String pkgName = cls.getPackage().getName();
    URL pkgResource = ClassLoader.getSystemClassLoader().getResource(pkgName.replace('.', '/'));
    if (pkgResource != null) {
      try {
        String clsName = cls.getName();
        File directory = new File(pkgResource.toURI());
        String[] files = directory.list();
        for (String file : files) {
          String fullName = pkgName + "." + file;
          if (fullName.startsWith(clsName)) {
            list.add(new File(directory, file));
          }
        }
      } catch (URISyntaxException e) {
        fail(e.getMessage());
      }
    }
    return list;
  }

  private List<File> compileClass(Class<?> cls) throws IOException {
    String fileName = cls.getName();
    int end = fileName.indexOf('$');
    if (end != -1) {
      fileName = fileName.substring(0, end);
    }
    fileName = fileName.replace('.', '/') + ".java";
    File file = new File(TEST_DIRECTORY, fileName);
    if (!file.exists()) {
      file = new File(TEST_DIRECTORY2, fileName);
    }
    assertTrue("Test source file not found: " + fileName, file.exists());

    File outTmp = createTempDir("jadx-tmp-classes");
    outTmp.deleteOnExit();
    List<File> files = StaticCompiler.compile(Arrays.asList(file), outTmp, withDebugInfo);
    // remove classes which are parents for test class
    Iterator<File> iterator = files.iterator();
    while (iterator.hasNext()) {
      File next = iterator.next();
      if (!next.getName().contains(cls.getSimpleName())) {
        iterator.remove();
      }
    }
    for (File clsFile : files) {
      clsFile.deleteOnExit();
    }
    return files;
  }

  protected void noDebugInfo() {
    this.withDebugInfo = false;
  }

  protected void setFallback() {
    this.isFallback = true;
  }

  protected void disableCompilation() {
    this.compile = false;
  }

  // Use only for debug purpose
  @Deprecated
  protected void setOutputCFG() {
    this.outputCFG = true;
  }

  // Use only for debug purpose
  @Deprecated
  protected void notDeleteTmpJar() {
    this.deleteTmpFiles = false;
  }
}
TOP

Related Classes of jadx.tests.api.IntegrationTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.