/**
   * Constructs an instance.
   *
   * @param encoded encoded debug info
   * @param codesize size of code block in code units
   * @param regSize register size, in register units, of the register space used by this method
   * @param isStatic true if method is static
   * @param ref method descriptor of method this debug info is for
   * @param file dex file this debug info will be stored in
   */
  DebugInfoDecoder(
      byte[] encoded, int codesize, int regSize, boolean isStatic, CstMethodRef ref, DexFile file) {
    if (encoded == null) {
      throw new NullPointerException("encoded == null");
    }

    this.encoded = encoded;
    this.isStatic = isStatic;
    this.desc = ref.getPrototype();
    this.file = file;
    this.regSize = regSize;

    positions = new ArrayList<PositionEntry>();
    locals = new ArrayList<LocalEntry>();
    this.codesize = codesize;
    lastEntryForReg = new LocalEntry[regSize];

    int idx = -1;

    try {
      idx = file.getStringIds().indexOf(new CstString("this"));
    } catch (IllegalArgumentException ex) {
      /*
       * Silently tolerate not finding "this". It just means that
       * no method has local variable info that looks like
       * a standard instance method.
       */
    }

    thisStringIdx = idx;
  }
  /** {@inheritDoc} */
  @Override
  protected void place0(Section addedTo, int offset) {
    // Encode the data and note the size.

    try {
      encoded = encode(addedTo.getFile(), null, null, null, false);
      setWriteSize(encoded.length);
    } catch (RuntimeException ex) {
      throw ExceptionWithContext.withContext(
          ex, "...while placing debug info for " + ref.toHuman());
    }
  }
  /**
   * 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());
    }
  }
Example #4
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);
      }
    }
  }