Package jadx.core.dex.nodes.parser

Source Code of jadx.core.dex.nodes.parser.SignatureParser

package jadx.core.dex.nodes.parser;

import jadx.core.Consts;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SignatureParser {

  private static final Logger LOG = LoggerFactory.getLogger(SignatureParser.class);
  private static final char STOP_CHAR = 0;

  private final String sign;
  private final int end;
  private int pos;
  private int mark;

  public SignatureParser(String signature) {
    sign = signature;
    end = sign.length();
    pos = -1;
    mark = 0;
  }

  @SuppressWarnings("unchecked")
  public static SignatureParser fromNode(IAttributeNode node) {
    Annotation a = node.getAnnotation(Consts.DALVIK_SIGNATURE);
    if (a == null) {
      return null;
    }
    return new SignatureParser(mergeSignature((List<String>) a.getDefaultValue()));
  }

  private char next() {
    pos++;
    if (pos >= end) {
      return STOP_CHAR;
    }
    return sign.charAt(pos);
  }

  private boolean lookAhead(char ch) {
    int next = pos + 1;
    return next < end && sign.charAt(next) == ch;
  }

  private void mark() {
    mark = pos;
  }

  /**
   * Exclusive slice.
   *
   * @return string from 'mark' to current position (not including current character)
   */
  private String slice() {
    if (mark >= pos) {
      return "";
    }
    return sign.substring(mark, pos);
  }

  /**
   * Inclusive slice (includes current character)
   */
  private String inclusiveSlice() {
    if (mark >= pos) {
      return "";
    }
    return sign.substring(mark, pos + 1);
  }

  private boolean forwardTo(char lastChar) {
    int startPos = pos;
    char ch;
    while ((ch = next()) != STOP_CHAR) {
      if (ch == lastChar) {
        return true;
      }
    }
    pos = startPos;
    return false;
  }

  private void consume(char exp) {
    char c = next();
    if (exp != c) {
      throw new JadxRuntimeException("Consume wrong char: '" + c + "' != '" + exp
          + "', sign: " + debugString());
    }
  }

  private boolean tryConsume(char exp) {
    if (lookAhead(exp)) {
      next();
      return true;
    }
    return false;
  }

  private String consumeUntil(char lastChar) {
    mark();
    return forwardTo(lastChar) ? slice() : null;
  }

  public ArgType consumeType() {
    char ch = next();
    mark();
    switch (ch) {
      case 'L':
        ArgType obj = consumeObjectType(false);
        if (obj != null) {
          return obj;
        }
        break;
      case 'T':
        next();
        mark();
        if (forwardTo(';')) {
          return ArgType.genericType(slice());
        }
        break;
      case '[':
        return ArgType.array(consumeType());

      case STOP_CHAR:
        return null;

      default:
        // primitive type (one char)
        ArgType type = ArgType.parse(ch);
        if (type != null) {
          return type;
        }
        break;
    }
    throw new JadxRuntimeException("Can't parse type: " + debugString());
  }

  private ArgType consumeObjectType(boolean incompleteType) {
    mark();
    int ch;
    do {
      ch = next();
      if (ch == STOP_CHAR) {
        return null;
      }
    } while (ch != '<' && ch != ';');

    if (ch == ';') {
      String obj;
      if (incompleteType) {
        obj = slice().replace('/', '.');
      } else {
        obj = inclusiveSlice();
      }
      return ArgType.object(obj);
    } else {
      // generic type start ('<')
      String obj = slice();
      if (!incompleteType) {
        obj += ";";
      }
      ArgType[] genArr = consumeGenericArgs();
      consume('>');

      ArgType genericType = ArgType.generic(obj, genArr);
      if (lookAhead('.')) {
        consume('.');
        next();
        // type parsing not completed, proceed to inner class
        ArgType inner = consumeObjectType(true);
        return ArgType.genericInner(genericType, inner.getObject(), inner.getGenericTypes());
      } else {
        consume(';');
        return genericType;
      }
    }
  }

  private ArgType[] consumeGenericArgs() {
    List<ArgType> list = new LinkedList<ArgType>();
    ArgType type;
    do {
      if (lookAhead('*')) {
        next();
        type = ArgType.wildcard();
      } else if (lookAhead('+')) {
        next();
        type = ArgType.wildcard(consumeType(), 1);
      } else if (lookAhead('-')) {
        next();
        type = ArgType.wildcard(consumeType(), -1);
      } else {
        type = consumeType();
      }
      if (type != null) {
        list.add(type);
      }
    } while (type != null && !lookAhead('>'));
    return list.toArray(new ArgType[list.size()]);
  }

  /**
   * Map of generic types names to extends classes.
   * <p/>
   * Example: "<T:Ljava/lang/Exception;:Ljava/lang/Object;>"
   */
  public Map<ArgType, List<ArgType>> consumeGenericMap() {
    if (!lookAhead('<')) {
      return Collections.emptyMap();
    }
    Map<ArgType, List<ArgType>> map = new LinkedHashMap<ArgType, List<ArgType>>(2);
    consume('<');
    while (true) {
      if (lookAhead('>') || next() == STOP_CHAR) {
        break;
      }
      String id = consumeUntil(':');
      if (id == null) {
        LOG.error("Can't parse generic map: {}", sign);
        return Collections.emptyMap();
      }
      tryConsume(':');
      List<ArgType> types = consumeExtendsTypesList();
      map.put(ArgType.genericType(id), types);
    }
    consume('>');
    return map;
  }

  /**
   * List of types separated by ':' last type is 'java.lang.Object'.
   * <p/>
   * Example: "Ljava/lang/Exception;:Ljava/lang/Object;"
   */
  private List<ArgType> consumeExtendsTypesList() {
    List<ArgType> types = Collections.emptyList();
    boolean next;
    do {
      ArgType argType = consumeType();
      if (!argType.equals(ArgType.OBJECT)) {
        if (types.isEmpty()) {
          types = new LinkedList<ArgType>();
        }
        types.add(argType);
      }
      next = lookAhead(':');
      if (next) {
        consume(':');
      }
    } while (next);
    return types;
  }

  public List<ArgType> consumeMethodArgs() {
    consume('(');
    if (lookAhead(')')) {
      consume(')');
      return Collections.emptyList();
    }
    List<ArgType> args = new LinkedList<ArgType>();
    do {
      args.add(consumeType());
    } while (!lookAhead(')'));
    consume(')');
    return args;
  }

  private static String mergeSignature(List<String> list) {
    if (list.size() == 1) {
      return list.get(0);
    }
    StringBuilder sb = new StringBuilder();
    for (String s : list) {
      sb.append(s);
    }
    return sb.toString();
  }

  private String debugString() {
    return sign + " at position " + pos + " ('" + sign.charAt(pos) + "')";
  }

  @Override
  public String toString() {
    if (pos == -1) {
      return sign;
    }
    return sign.substring(0, mark) + '{' + sign.substring(mark, pos) + '}' + sign.substring(pos);
  }
}
TOP

Related Classes of jadx.core.dex.nodes.parser.SignatureParser

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.