/** Decodes the debug info sequence. */
 public void decode() {
   try {
     decode0();
   } catch (Exception ex) {
     throw ExceptionWithContext.withContext(ex, "...while decoding debug info");
   }
 }
예제 #2
0
  /**
   * Processes the fields of the given class.
   *
   * @param cf {@code non-null;} class being translated
   * @param out {@code non-null;} output class
   */
  private static void processFields(DirectClassFile cf, ClassDefItem out) {
    CstType thisClass = cf.getThisClass();
    FieldList fields = cf.getFields();
    int sz = fields.size();

    for (int i = 0; i < sz; i++) {
      Field one = fields.get(i);
      try {
        CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
        int accessFlags = one.getAccessFlags();

        if (AccessFlags.isStatic(accessFlags)) {
          TypedConstant constVal = one.getConstantValue();
          EncodedField fi = new EncodedField(field, accessFlags);
          if (constVal != null) {
            constVal = coerceConstant(constVal, field.getType());
          }
          out.addStaticField(fi, constVal);
        } else {
          EncodedField fi = new EncodedField(field, accessFlags);
          out.addInstanceField(fi);
        }

        Annotations annotations = AttributeTranslator.getAnnotations(one.getAttributes());
        if (annotations.size() != 0) {
          out.addFieldAnnotations(field, annotations);
        }
      } catch (RuntimeException ex) {
        String msg =
            "...while processing " + one.getName().toHuman() + " " + one.getDescriptor().toHuman();
        throw ExceptionWithContext.withContext(ex, msg);
      }
    }
  }
예제 #3
0
 /**
  * Takes a {@code byte[]}, interprets it as a Java classfile, and translates it into a {@link
  * ClassDefItem}.
  *
  * @param filePath {@code non-null;} the file path for the class, excluding any base directory
  *     specification
  * @param bytes {@code non-null;} contents of the file
  * @param cfOptions options for class translation
  * @param dexOptions options for dex output
  * @return {@code non-null;} the translated class
  */
 public static ClassDefItem translate(
     String filePath, byte[] bytes, CfOptions cfOptions, DexOptions dexOptions) {
   try {
     return translate0(filePath, bytes, cfOptions, dexOptions);
   } catch (RuntimeException ex) {
     String msg = "...while processing " + filePath;
     throw ExceptionWithContext.withContext(ex, msg);
   }
 }
  /**
   * Validates an encoded debug info stream against data used to encode it, throwing an exception if
   * they do not match. Used to validate the encoder.
   *
   * @param info encoded debug info
   * @param file {@code non-null;} file to refer to during decoding
   * @param ref {@code non-null;} method whose info is being decoded
   * @param code {@code non-null;} original code object that was encoded
   * @param isStatic whether the method is static
   */
  public static void validateEncode(
      byte[] info, DexFile file, CstMethodRef ref, DalvCode code, boolean isStatic) {
    PositionList pl = code.getPositions();
    LocalList ll = code.getLocals();
    DalvInsnList insns = code.getInsns();
    int codeSize = insns.codeSize();
    int countRegisters = insns.getRegistersSize();

    try {
      validateEncode0(info, codeSize, countRegisters, isStatic, ref, file, pl, ll);
    } catch (RuntimeException ex) {
      System.err.println("instructions:");
      insns.debugPrint(System.err, "  ", true);
      System.err.println("local list:");
      ll.debugPrint(System.err, "  ");
      throw ExceptionWithContext.withContext(ex, "while processing " + ref.toHuman());
    }
  }
예제 #5
0
  /**
   * Processes the methods of the given class.
   *
   * @param cf {@code non-null;} class being translated
   * @param cfOptions {@code non-null;} options for class translation
   * @param dexOptions {@code non-null;} options for dex output
   * @param out {@code non-null;} output class
   */
  private static void processMethods(
      DirectClassFile cf, CfOptions cfOptions, DexOptions dexOptions, ClassDefItem out) {
    CstType thisClass = cf.getThisClass();
    MethodList methods = cf.getMethods();
    int sz = methods.size();

    for (int i = 0; i < sz; i++) {
      Method one = methods.get(i);
      try {
        CstMethodRef meth = new CstMethodRef(thisClass, one.getNat());
        int accessFlags = one.getAccessFlags();
        boolean isStatic = AccessFlags.isStatic(accessFlags);
        boolean isPrivate = AccessFlags.isPrivate(accessFlags);
        boolean isNative = AccessFlags.isNative(accessFlags);
        boolean isAbstract = AccessFlags.isAbstract(accessFlags);
        boolean isConstructor = meth.isInstanceInit() || meth.isClassInit();
        DalvCode code;

        if (isNative || isAbstract) {
          // There's no code for native or abstract methods.
          code = null;
        } else {
          ConcreteMethod concrete =
              new ConcreteMethod(
                  one, cf, (cfOptions.positionInfo != PositionList.NONE), cfOptions.localInfo);

          TranslationAdvice advice;

          advice = DexTranslationAdvice.THE_ONE;

          RopMethod rmeth = Ropper.convert(concrete, advice);
          RopMethod nonOptRmeth = null;
          int paramSize;

          paramSize = meth.getParameterWordCount(isStatic);

          String canonicalName =
              thisClass.getClassType().getDescriptor() + "." + one.getName().getString();

          if (cfOptions.optimize && OptimizerOptions.shouldOptimize(canonicalName)) {
            if (DEBUG) {
              System.err.println("Optimizing " + canonicalName);
            }

            nonOptRmeth = rmeth;
            rmeth = Optimizer.optimize(rmeth, paramSize, isStatic, cfOptions.localInfo, advice);

            if (DEBUG) {
              OptimizerOptions.compareOptimizerStep(
                  nonOptRmeth, paramSize, isStatic, cfOptions, advice, rmeth);
            }

            if (cfOptions.statistics) {
              CodeStatistics.updateRopStatistics(nonOptRmeth, rmeth);
            }
          }

          LocalVariableInfo locals = null;

          if (cfOptions.localInfo) {
            locals = LocalVariableExtractor.extract(rmeth);
          }

          code =
              RopTranslator.translate(rmeth, cfOptions.positionInfo, locals, paramSize, dexOptions);

          if (cfOptions.statistics && nonOptRmeth != null) {
            updateDexStatistics(
                cfOptions,
                dexOptions,
                rmeth,
                nonOptRmeth,
                locals,
                paramSize,
                concrete.getCode().size());
          }
        }

        // Preserve the synchronized flag as its "declared" variant...
        if (AccessFlags.isSynchronized(accessFlags)) {
          accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;

          /*
           * ...but only native methods are actually allowed to be
           * synchronized.
           */
          if (!isNative) {
            accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
          }
        }

        if (isConstructor) {
          accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
        }

        TypeList exceptions = AttributeTranslator.getExceptions(one);
        EncodedMethod mi = new EncodedMethod(meth, accessFlags, code, exceptions);

        if (meth.isInstanceInit() || meth.isClassInit() || isStatic || isPrivate) {
          out.addDirectMethod(mi);
        } else {
          out.addVirtualMethod(mi);
        }

        Annotations annotations = AttributeTranslator.getMethodAnnotations(one);
        if (annotations.size() != 0) {
          out.addMethodAnnotations(meth, annotations);
        }

        AnnotationsList list = AttributeTranslator.getParameterAnnotations(one);
        if (list.size() != 0) {
          out.addParameterAnnotations(meth, list);
        }
      } catch (RuntimeException ex) {
        String msg =
            "...while processing " + one.getName().toHuman() + " " + one.getDescriptor().toHuman();
        throw ExceptionWithContext.withContext(ex, msg);
      }
    }
  }