日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Proguard源码分析(五) ConfigurationParser.keep参数

發布時間:2024/10/12 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Proguard源码分析(五) ConfigurationParser.keep参数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本章節我們繞回來講Keep參數,也就是ConfigurationParser 這個類。

ConfigurationParser這個類是非常重要的類,如果你已經開始看源碼,你會發現所有的類和功能都圍著它來轉,本章節我們來揭開它的地一層面紗。

else if (ConfigurationConstants.KEEP_OPTION.startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, true, false, false);
??? ??? ??? else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION
??? ??? ??? ??? ??? .startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, false, false, false);
??? ??? ??? else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION
??? ??? ??? ??? ??? .startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, false, true, false);
??? ??? ??? else if (ConfigurationConstants.KEEP_NAMES_OPTION
??? ??? ??? ??? ??? .startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, true, false, true);
??? ??? ??? else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION
??? ??? ??? ??? ??? .startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, false, false, true);
??? ??? ??? else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION
??? ??? ??? ??? ??? .startsWith(nextWord))
??? ??? ??? ??? configuration.keep = parseKeepClassSpecificationArguments(
??? ??? ??? ??? ??? ??? configuration.keep, false, true, true);

可見,所有以keep打頭的參數都是調用的parseKeepClassSpecificationArguments

跟一下這個函數的邏輯

while (true) {
??? ??? ??? readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD
??? ??? ??? ??? ??? + "', '" + ClassConstants.EXTERNAL_ACC_INTERFACE
??? ??? ??? ??? ??? + "', or '" + ClassConstants.EXTERNAL_ACC_ENUM + "'",
??? ??? ??? ??? ??? false, true);

??? ??? ??? if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD
??? ??? ??? ??? ??? .equals(nextWord)) {
??? ??? ??? ??? // Not a comma. Stop parsing the keep modifiers.
??? ??? ??? ??? break;
??? ??? ??? }

??? ??? ??? readNextWord("keyword '"
??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + "', '"
??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION
??? ??? ??? ??? ??? + "', or '"
??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + "'");

??? ??? ??? if (ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION
??? ??? ??? ??? ??? .startsWith(nextWord)) {
??? ??? ??? ??? allowShrinking = true;
??? ??? ??? } else if (ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION
??? ??? ??? ??? ??? .startsWith(nextWord)) {
??? ??? ??? ??? allowOptimization = true;
??? ??? ??? } else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION
??? ??? ??? ??? ??? .startsWith(nextWord)) {
??? ??? ??? ??? allowObfuscation = true;
??? ??? ??? } else {
??? ??? ??? ??? throw new ParseException("Expecting keyword '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION
??? ??? ??? ??? ??? ??? + "', '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION
??? ??? ??? ??? ??? ??? + "', or '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION
??? ??? ??? ??? ??? ??? + "' before " + reader.locationDescription());
??? ??? ??? }
??? ??? }

ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD
??? ??? ??? ??? ??? .equals(nextWord)代表以非ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD 作為終結符號,也就是說在keep之后可以跟一些參數,這些參數我們來看一下~

??? public static final String ALLOW_SHRINKING_SUBOPTION???????????? = "allowshrinking";
??? public static final String ALLOW_OPTIMIZATION_SUBOPTION????????? = "allowoptimization";
??? public static final String ALLOW_OBFUSCATION_SUBOPTION?????????? = "allowobfuscation";

也就是說你可以采用下面這種寫法:

-keep,allowshrinking,allowoptimization public class *;

我們來看一下這三個參數的影響:

if ((shrinking?? && !keepClassSpecification.allowShrinking)??? ||
??????????????????? (optimizing? && !keepClassSpecification.allowOptimization) ||
??????????????????? (obfuscating && !keepClassSpecification.allowObfuscation))
{
??????????????? ??? ClassPoolVisitor classPoolVisitor = createClassPoolVisitor(keepClassSpecification,
??????????????????????????? classVisitor,
??????????????????????????? memberVisitor);
??????????????????? multiClassPoolVisitor.addClassPoolVisitor(classPoolVisitor);
}

結果似乎并不是我想的那樣,要對這個類不做任何處理,必須保證這三個參數都為true.

在這之后會調用parseClassSpecificationArguments() 來生成一個ClassSpecification 的原始數據

???????????? classSpecification.requiredSetAccessFlags,
???????????? classSpecification.requiredUnsetAccessFlags,
???????????? classSpecification.annotationType,
???????????? classSpecification.className,
???????????? classSpecification.extendsAnnotationType,
???????????? classSpecification.extendsClassName,
???????????? classSpecification.fieldSpecifications,
???????????? classSpecification.methodSpecifications

requiredSetAccessFlags 和requiredUnsetAccessFlags 兩個是必須設置的
它是檢測是否加載該類的入口之一。他們的值是:

public static final int INTERNAL_ACC_PUBLIC?????? = 0x0001;
??? public static final int INTERNAL_ACC_PRIVATE????? = 0x0002;
??? public static final int INTERNAL_ACC_PROTECTED??? = 0x0004;
??? public static final int INTERNAL_ACC_STATIC?????? = 0x0008;
??? public static final int INTERNAL_ACC_FINAL??????? = 0x0010;
??? public static final int INTERNAL_ACC_SUPER??????? = 0x0020;
??? public static final int INTERNAL_ACC_SYNCHRONIZED = 0x0020;
??? public static final int INTERNAL_ACC_VOLATILE???? = 0x0040;
??? public static final int INTERNAL_ACC_TRANSIENT??? = 0x0080;
??? public static final int INTERNAL_ACC_BRIDGE?????? = 0x0040;
??? public static final int INTERNAL_ACC_VARARGS????? = 0x0080;
??? public static final int INTERNAL_ACC_NATIVE?????? = 0x0100;
??? public static final int INTERNAL_ACC_INTERFACE??? = 0x0200;
??? public static final int INTERNAL_ACC_ABSTRACT???? = 0x0400;
??? public static final int INTERNAL_ACC_STRICT?????? = 0x0800;
??? public static final int INTERNAL_ACC_SYNTHETIC??? = 0x1000;
??? public static final int INTERNAL_ACC_ANNOTATTION? = 0x2000;
??? public static final int INTERNAL_ACC_ENUM???????? = 0x4000;

parseClassSpecificationArguments() 方法中定義了class的寫法

當你的:

if (accessFlag == ClassConstants.INTERNAL_ACC_ANNOTATTION) {
??? ??? ??? ??? // Already read the next word.
??? ??? ??? ??? readNextWord("annotation type or keyword '"
??? ??? ??? ??? ??? ??? + ClassConstants.EXTERNAL_ACC_INTERFACE + "'", false,
??? ??? ??? ??? ??? ??? false);

??? ??? ??? ??? // Is the next word actually an annotation type?
??? ??? ??? ??? if (!nextWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE)
??? ??? ??? ??? ??? ??? && !nextWord.equals(ClassConstants.EXTERNAL_ACC_ENUM)
??? ??? ??? ??? ??? ??? && !nextWord.equals(ConfigurationConstants.CLASS_KEYWORD)) {
??? ??? ??? ??? ??? // Parse the annotation type.
??? ??? ??? ??? ??? annotationType = ListUtil.commaSeparatedString(
??? ??? ??? ??? ??? ??? ??? parseCommaSeparatedList("annotation type", false,
??? ??? ??? ??? ??? ??? ??? ??? ??? false, false, false, true, false, false,
??? ??? ??? ??? ??? ??? ??? ??? ??? true, null), false);

??? ??? ??? ??? ??? // Continue parsing the access modifier that we just read
??? ??? ??? ??? ??? // in the next cycle.
??? ??? ??? ??? ??? continue;
??? ??? ??? ??? }

??? ??? ??? ??? // Otherwise just handle the annotation modifier.
??? ??? ??? }

accessFlag 為注解符號的時候,大致寫法是這樣的:

-keep @com.test.TestAnno
public class * {
*;
}
-keepclassmembers class * {
??? @com.test.TestAnno <methods>;
}

也就是說完全按照java的語法標準來實現。

解析完注解之后直到解析class interface enum 這些關鍵字

if (strippedWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE)
??? ??? ??? ??? ??? || strippedWord.equals(ClassConstants.EXTERNAL_ACC_ENUM)
??? ??? ??? ??? ??? || strippedWord.equals(ConfigurationConstants.CLASS_KEYWORD)) {
??? ??? ??? ??? // The interface or enum keyword. Stop parsing the class flags.
??? ??? ??? ??? break;
??? ??? ??? }

得到externalClassName

之后調用

if (!configurationEnd()) {
??? ??? ??? // Parse 'implements ...' or 'extends ...' part, if any.
??? ??? ??? if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord)
??? ??? ??? ??? ??? || ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord)) {
??? ??? ??? ??? readNextWord("class name or interface name", false, true);
??? ??? ??? ??? // Parse the annotation type, if any.
??? ??? ??? ??? LOG.log("start ");
??? ??? ??? ??? if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) {
??? ??? ??? ??? ??? extendsAnnotationType = ListUtil.commaSeparatedString(
??? ??? ??? ??? ??? ??? ??? parseCommaSeparatedList("annotation type", true,
??? ??? ??? ??? ??? ??? ??? ??? ??? false, false, false, true, false, false,
??? ??? ??? ??? ??? ??? ??? ??? ??? true, null), false);
??? ??? ??? ??? }

??? ??? ??? ??? String externalExtendsClassName = ListUtil
??? ??? ??? ??? ??? ??? .commaSeparatedString(
??? ??? ??? ??? ??? ??? ??? ??? parseCommaSeparatedList(
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? "class name or interface name", false,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? false, false, false, true, false,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? false, false, null), false);
??? ??? ??? ??? extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD
??? ??? ??? ??? ??? ??? .equals(externalExtendsClassName) ? null : ClassUtil
??? ??? ??? ??? ??? ??? .internalClassName(externalExtendsClassName);
??? ??? ??? }
??? ??? }

configurationEnd() 的結束條件是-和@,那么括號里面的又是干什么用的呢?

這是一種語法結構大致結構是這個樣子的:

-keep public class * extends @com.test.TestAnno * #here

{
*;
}

解析到here這個位置,代表保持這個這個標注注解類的子類

最后將定義個類的元數據:

ClassSpecification classSpecification = new ClassSpecification(
??? ??? ??? ??? lastComments, requiredSetClassAccessFlags,
??? ??? ??? ??? requiredUnsetClassAccessFlags, annotationType, className,
??? ??? ??? ??? extendsAnnotationType, extendsClassName);

進行下一次的匹配,

if (!configurationEnd()) {
??? ??? ??? // Check the class member opening part.
??? ??? ??? if (!ConfigurationConstants.OPEN_KEYWORD.equals(nextWord)) {
??? ??? ??? ??? throw new ParseException("Expecting opening '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.OPEN_KEYWORD + "' at "
??? ??? ??? ??? ??? ??? + reader.locationDescription());
??? ??? ??? }

??? ??? ??? // Parse all class members.
??? ??? ??? while (true) {
??? ??? ??? ??? readNextWord("class member description" + " or closing '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.CLOSE_KEYWORD + "'", false,
??? ??? ??? ??? ??? ??? true);

??? ??? ??? ??? if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) {
??? ??? ??? ??? ??? // The closing brace. Stop parsing the class members.
??? ??? ??? ??? ??? readNextWord();

??? ??? ??? ??? ??? break;
??? ??? ??? ??? }

??? ??? ??? ??? parseMemberSpecificationArguments(externalClassName,
??? ??? ??? ??? ??? ??? classSpecification);
??? ??? ??? }
??? ??? }

這個匹配必須是非結束符號也就是不是 - 或者@

這就說明proguard的語法支持

-keep public class ...或者

-keep public class ...{...}

我們來看下第二種它是怎么做的:

if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) {
??? ??? ??? ??? ??? // The closing brace. Stop parsing the class members.
??? ??? ??? ??? ??? readNextWord();

??? ??? ??? ??? ??? break;
?}

當讀入的字符不為}的時候將繼續讀入

解析成員通過方法parseMemberSpecificationArguments 來生成

這個方法跟類分析程序非常相似多了一些參數的條件,比如static native transient volatile final 這類用來形容方法或者變量的屬性當然在這之前有過注解驗證,也就是說支持:

{

@anno

static test

}

這種寫法

接下來會通過

if (??? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord)
??? ??? ??? ??? || ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord)
??? ??? ??? ??? || ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord))

三個方法來對三種通配符號做處理,這三種通配符號分別是:*,<fields>,<methods>

匹配完成之后會生成叫做MemberSpecification 的對象來倒入到class的配置中

*可以看作是后面兩個東西的集合,所以在proguard處理的時候會同時調用

classSpecification.addField(new MemberSpecification(
??? ??? ??? ??? ??? ??? requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags, annotationType, null,
??? ??? ??? ??? ??? ??? null));
??? ??? ??? ??? classSpecification.addMethod(new MemberSpecification(
??? ??? ??? ??? ??? ??? requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags, annotationType, null,
??? ??? ??? ??? ??? ??? null));

這兩個方法。

如果你不采用通配符號的方式來寫的話,也就是說你默認會給出一個精確表達式,也有可能是一個模式匹配的表達式。我們來看一下Proguard對它的處理流程:

ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name)

proguard會先檢測是否是攜帶參數:

這里它對構造器方法和一些錯誤的可能做的屏蔽處理:

if (!(type.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)
??? ??? ??? ??? ??? ??? || type.equals(externalClassName) || type
??? ??? ??? ??? ??? ??? ??? .equals(ClassUtil
??? ??? ??? ??? ??? ??? ??? ??? ??? .externalShortClassName(externalClassName)))) {
??? ??? ??? ??? ??? throw new ParseException("Expecting type and name "
??? ??? ??? ??? ??? ??? ??? + "instead of just '" + type + "' before "
??? ??? ??? ??? ??? ??? ??? + reader.locationDescription());
??? ??? ??? ??? }

?原理很簡單,由于構造器是沒有返回值的,所以你之前期望得到的返回類型應該就是構造器的方法名<init>

if (ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) {
??? ??? ??? ??? // It's a field.
??? ??? ??? ??? checkFieldAccessFlags(requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags);

??? ??? ??? ??? // We already have a field descriptor.
??? ??? ??? ??? String descriptor = ClassUtil.internalType(type);

??? ??? ??? ??? // Add the field.
??? ??? ??? ??? classSpecification.addField(new MemberSpecification(
??? ??? ??? ??? ??? ??? requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags, annotationType, name,
??? ??? ??? ??? ??? ??? descriptor));
??? ??? ??? } else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD
??? ??? ??? ??? ??? .equals(nextWord)) {
??? ??? ??? ??? // It's a method.
??? ??? ??? ??? checkMethodAccessFlags(requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags);

??? ??? ??? ??? // Parse the method arguments.
??? ??? ??? ??? String descriptor = ClassUtil.internalMethodDescriptor(
??? ??? ??? ??? ??? ??? type,
??? ??? ??? ??? ??? ??? parseCommaSeparatedList("argument", true, true, true,
??? ??? ??? ??? ??? ??? ??? ??? false, true, false, false, false, null));

??? ??? ??? ??? if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD
??? ??? ??? ??? ??? ??? .equals(nextWord)) {
??? ??? ??? ??? ??? throw new ParseException("Expecting separating '"
??? ??? ??? ??? ??? ??? ??? + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD
??? ??? ??? ??? ??? ??? ??? + "' or closing '"
??? ??? ??? ??? ??? ??? ??? + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD
??? ??? ??? ??? ??? ??? ??? + "' before " + reader.locationDescription());
??? ??? ??? ??? }

??? ??? ??? ??? // Read the separator after the closing parenthesis.
??? ??? ??? ??? readNextWord("separator '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.SEPARATOR_KEYWORD + "'");

??? ??? ??? ??? if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) {
??? ??? ??? ??? ??? throw new ParseException("Expecting separator '"
??? ??? ??? ??? ??? ??? ??? + ConfigurationConstants.SEPARATOR_KEYWORD
??? ??? ??? ??? ??? ??? ??? + "' before " + reader.locationDescription());
??? ??? ??? ??? }

??? ??? ??? ??? // Add the method.
??? ??? ??? ??? classSpecification.addMethod(new MemberSpecification(
??? ??? ??? ??? ??? ??? requiredSetMemberAccessFlags,
??? ??? ??? ??? ??? ??? requiredUnsetMemberAccessFlags, annotationType, name,
??? ??? ??? ??? ??? ??? descriptor));
??? ??? ??? } else {
??? ??? ??? ??? // It doesn't look like a field or a method.
??? ??? ??? ??? throw new ParseException("Expecting opening '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD
??? ??? ??? ??? ??? ??? + "' or separator '"
??? ??? ??? ??? ??? ??? + ConfigurationConstants.SEPARATOR_KEYWORD
??? ??? ??? ??? ??? ??? + "' before " + reader.locationDescription());
??? ??? ??? }

這段代碼也非常好理解,對于只有名字然后直接跟分號的話,它認為是成員變量參數,如果是(則是方法,對于方法來說最重要的就是方法的簽名,我們來關注一下方法是如何獲得簽名的.

方法簽名是通過String descriptor = ClassUtil.internalMethodDescriptor(
??? ??? ??? ??? ??? ??? type,
??? ??? ??? ??? ??? ??? parseCommaSeparatedList("argument", true, true, true,
??? ??? ??? ??? ??? ??? ??? ??? false, true, false, false, false, null));

來獲得,其中type就是你的返回值,我們不說詳細過程,只注重一些細節的結果parseCommaSeparatedList的參數列表最后得到相應的方法簽名例如:

(ILcom/test/Base;)V

第一個I代表的是INT ,如果你想關注這些,不妨看一下jvm匯編一類的知識,其實c++的方法簽名方式也大同小異。所以如果你之前從事過這方面的話,應該是不會陌生的。

轉載于:https://www.cnblogs.com/feizimo/p/3523832.html

總結

以上是生活随笔為你收集整理的Proguard源码分析(五) ConfigurationParser.keep参数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲一区二区在线免费 | 国产精品麻豆成人av电影艾秋 | 黄色电影在线视频 | 熟女一区二区三区视频 | 18视频在线观看娇喘 | 2019亚洲男人天堂 | 久久黄色 | melody在线高清免费观看 | 精品人妻码一区二区三区红楼视频 | 96国产在线 | 日本不卡视频在线播放 | 韩国裸体网站 | 50一60岁老妇女毛片 | 亚洲第一页在线 | 综合精品一区 | 欧美黑人又粗又大高潮喷水 | 中文字幕一区二区不卡 | 欧美性xxxxxxxxx| 恶虐女帝安卓汉化版最新版本 | 欧美xxxx日本和非洲 | 欧美aa在线观看 | 中文精品一区二区三区 | 亚洲黄色小说图片 | 激情狠狠 | 亚洲无码久久久久久久 | 97久久超碰| 国产高清自拍视频 | 涩涩视频在线免费看 | www.夜夜骑 | 激情小说图片视频 | 中文字幕免费观看 | 午夜电影你懂的 | 久久中文字幕人妻熟av女蜜柚m | 久久亚洲一区二区三区四区五区 | 日韩在线视频看看 | 色悠悠国产精品 | 欧美体内谢she精2性欧美 | 欧美成人久久 | 99综合色| 日本毛片在线观看 | 成人深夜视频 | 无码精品在线观看 | 玖草在线 | 亚洲破处视频 | 亚洲色婷婷久久精品av蜜桃 | 瑟瑟视频在线免费观看 | 玖玖色在线 | 亚洲资源在线播放 | aa片在线观看视频在线播放 | 成年人小视频 | 麻豆精品视频 | 国产真实自拍 | 欧美激情3p | 欧美日韩在线播放 | 久久久一区二区三区四区 | 国产精品水嫩水嫩 | 人妻在线一区二区三区 | 亚洲欧美另类日本 | 都市激情亚洲色图 | 欧美日韩中文字幕一区二区三区 | 性色在线 | 精品一区二区欧美 | 日韩av一卡 | 激情图片在线观看 | 原创少妇半推半就88av | 插插插日日日 | 精品国产aⅴ一区二区三区东京热 | aaa日韩 | 视频1区 | 国产午夜激情 | a黄色片| 亚洲美女高潮久久久 | 欧美日韩中字 | 在线观看特色大片免费网站 | 国产不卡视频在线 | 粉嫩欧美一区二区三区 | 4438激情网 | hs网站在线观看 | 亚洲人高潮女人毛茸茸 | 草草影院ccyycom | 91九色在线播放 | 亚洲免费激情视频 | 亚洲a一区 | 青青五月天 | 亚洲啪av永久无码精品放毛片 | 精品视频一区二区三区在线观看 | 日本人与黑人做爰视频 | 婷婷在线免费视频 | 中文字幕日韩欧美一区二区 | 亚洲激情欧美激情 | 黄色香蕉软件 | 色婷婷久久综合中文久久蜜桃av | 99资源站| 国产在线一级片 | 婷婷色网站 | 国产精久久一区二区三区 | 秋霞网一区二区 | 婷婷在线播放 | 在线观看视频中文字幕 |