混淆的艺术-(苍井空变凤姐)Proguard源码分析(二)Proguard参数解析

Proguard作为一个java的程序,它的入口在ProGuard.main()中

这章节我们讲Proguard的参数解析。实际上Proguard的处理无非分两步,解析参数和处理逻辑。解析参数这一步的主要类是:

ConfigurationParser.java

它的解析结果将放置在:

Configuration.java类中

我们打开Configuration 来看,发现Configuration类如其名,就是记录配置的一个Pojo类,虽然我这么说可能不合理,但是我们确实可以把它当成一个非常简单的类来看。既然我们这一章节的标题叫做参数解析,我们要重点分析的对象自然是:

ConfigurationParser

参数数据源->ConfigurationParser->Configuration对象,在分析ConfigurationParser之前我们先打开

ConfigurationConstants.java这个类,这个类记录了Proguard里面定义的关键字。有些常量用来模式匹配,有些常量是用来提供混淆范围,还有一些是用来配置Proguard的环境参数。这里我们只讲模式匹配。也就是下列这些参数:

public static final char OPEN_SYSTEM_PROPERTY = '<';
public static final char CLOSE_SYSTEM_PROPERTY = '>';

public static final String ANNOTATION_KEYWORD = "@";
public static final String NEGATOR_KEYWORD = "!";
public static final String CLASS_KEYWORD = "class";
public static final String ANY_CLASS_KEYWORD = "*";
public static final String ANY_TYPE_KEYWORD = "***";
public static final String IMPLEMENTS_KEYWORD = "implements";
public static final String EXTENDS_KEYWORD = "extends";
public static final String OPEN_KEYWORD = "{";
public static final String ANY_CLASS_MEMBER_KEYWORD = "*";
public static final String ANY_FIELD_KEYWORD = "<fields>";
public static final String ANY_METHOD_KEYWORD = "<methods>";
public static final String OPEN_ARGUMENTS_KEYWORD = "(";
public static final String ARGUMENT_SEPARATOR_KEYWORD = ",";
public static final String ANY_ARGUMENTS_KEYWORD = "...";
public static final String CLOSE_ARGUMENTS_KEYWORD = ")";
public static final String SEPARATOR_KEYWORD = ";";
public static final String CLOSE_KEYWORD = "}";


Proguard中的取词操作放在WordReader这个类中,为何我要顺带说一下这个类呢?是因为这个类被复用的功能极强如果你想要写一个词法解析器的话,不妨直接考虑这个东西。WordReader本身是一个抽象类他的实现主要有

LineWordReader 和 ArgumentWordReader ,其中Proguard默认使用的是ArgumentWordReader.但是你如果使用的文件方式来配置proguard的话,那么它使用的是LineWordReader 这个类。我们就使用LineWordReader 这个类。当然我在我的工程下面建了一个目录resources和outs用来保存我的资源配置和输出文件。

我们看到WordReader这个类抽象的非常好,它抽象出了一个模板方法nextLine,用于抽象取行的方式(图1)。我们来看一下它取词的过程,在nextWord(boolean isFileName) 方法中:


[图1]

我们将nextWord函数的源码分成几个部分:

第一:预先编译(过滤掉空白符号和注释)



while (currentLine == null || currentIndex == currentLineLength)
{
currentLine = nextLine();
lineNumber++;
if (currentLine == null)
{
return null;
}

currentLineLength = currentLine.length();

// Skip any leading whitespace.
currentIndex = 0;
while (currentIndex < currentLineLength &&
Character.isWhitespace(currentLine.charAt(currentIndex)))
{
currentIndex++;
}

// Remember any leading comments.
if (currentIndex < currentLineLength &&
isComment(currentLine.charAt(currentIndex)))
{
// Remember the comments.
String comment = currentLine.substring(currentIndex + 1);
currentComments = currentComments == null ?
comment :
currentComments + '\n' + comment;

// Skip the comments.
currentIndex = currentLineLength;
}
}

我们可以看到代码的红色那一部分,实际上对于过滤空格后的第一个字符,如果是"#"的话,那么它直接会将索引currentIndex设置为字符串长度currentLineLength那么也就过滤掉了注释的这一行,而如果到达结尾了~它将会再取得的下一行。然后重新解析,而你所得到的这些注释会拼成一个comment的长字符,用\n分割然后放在currentComments变量中。接下来就是第二步:

取词:


int startIndex = currentIndex;
int endIndex;

char startChar = currentLine.charAt(startIndex);

if (isQuote(startChar))
{
// The next word is starting with a quote character.
// Skip the opening quote.
startIndex++;

// The next word is a quoted character string.
// Find the closing quote.
do
{
currentIndex++;

if (currentIndex == currentLineLength)
{
currentWord = currentLine.substring(startIndex-1, currentIndex);
throw new IOException("Missing closing quote for "+locationDescription());
}
}
while (currentLine.charAt(currentIndex) != startChar);

endIndex = currentIndex++;
}
elseif (isFileName &&
!isOption(startChar))
{
// The next word is a (possibly optional) file name.
// Find the end of the line, the first path separator, the first
// option, or the first comment.
while (currentIndex < currentLineLength)
{
char currentCharacter = currentLine.charAt(currentIndex);
if (isFileDelimiter(currentCharacter) ||
((isOption(currentCharacter) ||
isComment(currentCharacter)) &&
Character.isWhitespace(currentLine.charAt(currentIndex-1)))) {
break;
}

currentIndex++;
}

endIndex = currentIndex;

// Trim any trailing whitespace.
while (endIndex > startIndex &&
Character.isWhitespace(currentLine.charAt(endIndex-1)))
{
endIndex--;
}
}
else if (isDelimiter(startChar))
{
// The next word is a single delimiting character.
endIndex = ++currentIndex;
}
else
{
// The next word is a simple character string.
// Find the end of the line, the first delimiter, or the first
// white space.
while (currentIndex < currentLineLength)
{
char currentCharacter = currentLine.charAt(currentIndex);
if (isDelimiter(currentCharacter) ||
Character.isWhitespace(currentCharacter) ||
isComment(currentCharacter)) {
break;
}

currentIndex++;
}

endIndex = currentIndex;
}

// Remember and return the parsed word.
currentWord = currentLine.substring(startIndex, endIndex);

return currentWord;


红色区域的代码代表引号这种成对出现的符号内的全部字母都作为nextword的一部分,当然包括空格。蓝色部分的代码是为了解决在文件读入的时候的文字匹配。我们以后再提。金黄色部分可以解决对于类似@xxx或者(XXX)这类,不知道大家是否也发现了~其实写成(xxx也是可以的.而对于以文件方式读入的参数将会携带@标识符作为函数的返回值。

--非子墨


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值