精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Go Generate 完全指南,你會嗎?

開發 后端
開發人員有很強的自動化重復性任務的傾向,這也適用于編寫代碼。因此,元編程(metaprogramming)的主題是一個開發和研究的熱門領域,可以追溯到 1960 年代的 Lisp。

[[433502]]

開發人員有很強的自動化重復性任務的傾向,這也適用于編寫代碼。因此,元編程(metaprogramming)的主題是一個開發和研究的熱門領域,可以追溯到 1960 年代的 Lisp。元編程中一個特別有用的領域是代碼生成(code-generation)。支持宏的語言內置了此功能;其他語言擴展了現有功能以支持這一點(例如 C++模板元編程[1])。

雖然 Go 沒有宏或其他形式的元編程,但它是一種實用語言,它包含官方工具鏈支持的代碼生成。

自從 Go 1.4[2] 引入 go generate 命令后,它一直廣泛應用于 Go 生態系統。Go 項目本身在很多地方都依賴于 go generate;我將在后面的帖子中快速概述這些用例。

01 基礎知識

讓我們從一些術語開始。go generate 工作方式主要由三個參與者之間協調進行的:

  • Generator:是由 go generate 調用的程序或腳本。在任何給定的項目中,可以調用多個生成器,可以多次調用單個生成器等。
  • Magic comments:是 .go 文件中以特殊方式格式化的注釋,用于指定調用哪個生成器以及如何調用。任何以文本 //go:generate 行開頭的注釋都是合法的。
  • go generate : 是 Go 工具,它讀取 Go 源文件、查找和解析 magic comments 并運行指定的生成器。

需要強調的是,以上是 Go 為代碼生成提供的自動化的全部范圍。對于其他任何事情,開發人員可以自由使用適合他們的任何工作流程。例如,go generate 應該始終由開發人員手動運行;它永遠不會自動調用(比如不會作為 go build 的一部分)。此外,由于我們通常使用 Go 將二進制文件發送給用戶或執行環境,因此很容易理解 go generate 僅在開發期間運行(可能就在運行 go build 之前);Go 程序的用戶不會知道哪部分代碼是生成的以及如何生成的。(實際上,很多時候會在生成的文件開頭加上注釋,這是生成的,請別手動修改。)

這也適用于生成 module;go generate 不會運行導入包的生成器。因此,當一個項目發布時,生成的代碼應該與其余代碼一起 checked 和分發。

02 一個簡單的例子

學習最好是動手做;為此,我創建了幾個簡單的 Go 項目,它們將幫助我說明這篇文章中解釋的主題。第一個是samplegentool[3],一個基本的 Go 工具,用于模擬生成器。

這是它的完整源代碼:

  1. package main 
  2.  
  3. import ( 
  4.   "fmt" 
  5.   "os" 
  6.  
  7. func main() { 
  8.   fmt.Printf("Running %s go on %s\n", os.Args[0], os.Getenv("GOFILE")) 
  9.  
  10.   cwd, err := os.Getwd() 
  11.   if err != nil { 
  12.     panic(err) 
  13.   } 
  14.   fmt.Printf("  cwd = %s\n", cwd) 
  15.   fmt.Printf("  os.Args = %#v\n", os.Args) 
  16.  
  17.   for _, ev := range []string{"GOARCH""GOOS""GOFILE""GOLINE""GOPACKAGE""DOLLAR"} { 
  18.     fmt.Println("  ", ev, "=", os.Getenv(ev)) 
  19.   } 

這個工具不讀任何代碼,也不寫任何代碼;它所做的只是報告它是如何被調用的。我們很快就會了解細節。首先我們看另一個項目 - mymod[4]。這是一個示例 Go 模塊,包含 3 個文件,分為兩個包:

  1. $ tree 
  2. ├── anotherfile.go 
  3. ├── go.mod 
  4. ├── mymod.go 
  5. └── mypack 
  6.     └── mypack.go 

這些文件的內容只是填充物;重要的是 go:generate 這個神奇的注釋。讓我們以mypack/mypack.go 中的那個為例:

  1. //go:generate samplegentool arg1 "multiword arg" 

我們看到它調用帶有一些參數的 samplegentool。為了使這個調用起作用,應該在 PATH 的某個地方能找到 samplegentool。這可以通過在 samplegentool項目運行 go build 來完成,以生成二進制,然后設置 PATH。現在,如果我們在 mymod 項目的根目錄中運行 go generate ./...,我們將看到如下內容:

  1. $ go generate ./... 
  2. Running samplegentool go on anotherfile.go 
  3.   cwd = /tmp/mymod 
  4.   os.Args = []string{"samplegentool""arg1""arg2""arg3""arg4"
  5.    GOARCH = amd64 
  6.    GOOS = linux 
  7.    GOFILE = anotherfile.go 
  8.    GOLINE = 1 
  9.    GOPACKAGE = mymod 
  10.    DOLLAR = $ 
  11. Running samplegentool go on mymod.go 
  12.   cwd = /tmp/mymod 
  13.   os.Args = []string{"samplegentool""arg1""arg2""-flag"
  14.    GOARCH = amd64 
  15.    GOOS = linux 
  16.    GOFILE = mymod.go 
  17.    GOLINE = 3 
  18.    GOPACKAGE = mymod 
  19.    DOLLAR = $ 
  20. Running samplegentool go on mypack.go 
  21.   cwd = /tmp/mymod/mypack 
  22.   os.Args = []string{"samplegentool""arg1""multiword arg"
  23.    GOARCH = amd64 
  24.    GOOS = linux 
  25.    GOFILE = mypack.go 
  26.    GOLINE = 3 
  27.    GOPACKAGE = mypack 
  28.    DOLLAR = $ 

首先,注意 samplegentool 在它出現在 magic comment 中的每個文件上被調用;這包括子目錄,因為我們 使用 ./... 模式運行 go generate。這對于在不同地方有很多生成器的大型項目來說真的很方便。

輸出中有很多有趣的東西;讓我們一行一行地剖析它:

  • cwd 報告調用 samplegentool 的工作目錄。這始終是找到帶有 magic 注釋的文件的目錄;這由 go generate 保證,并讓生成器知道它在目錄樹中的位置。
  • os.Args 報告傳遞給生成器的命令行參數。正如上面的輸出所示,這包括 flag 以及用引號括起來的多詞參數。
  • 傳遞給生成器的環境變量被打印出來;有關這些的完整解釋,請參閱 官方文檔[5]。這里最有趣的環境變量是 GOFILE ,它指向在其中找到 magic 注釋的文件名(此路徑是相對于工作目錄的),而 GOPACKAGE 告訴生成器,此文件屬于哪個包。

03 generators(生成器) 能做什么?

現在我們已經很好地了解了 go generate 是如何調用生成器的,那么它們能做什么呢?事實上他們可以做任何我們想做的事情。畢竟,生成器是計算機程序。如前所述,生成的文件通常也會放入到源代碼中,因此生成器可能只需要很少次運行。在許多項目中,開發人員不會像我在上面的示例中那樣從根運行 go generate ./...;相反,他們只會根據需要在特定目錄中運行特定的生成器。

在下一節中,我將深入介紹一個非常流行的生成器 — stringer工具。同時,以下是 Go 項目本身使用生成器執行的一些任務(這不是完整列表;所有用途都可以通過在 Go 源代碼樹中 grepping go:generate 找到):

  • gob 包使用生成器生成重復的輔助函數用于編碼/解碼數據。
  • math/bits 包使用生成器為其提供的某些位操作生成快速查找表。
  • 個別 crypto 包使用生成器為某些操作生成散列函數混洗模式和重復的匯編代碼。
  • 某些 crypto 包還使用生成器從特定的 HTTP URL 獲取證書。顯然,這些不是為了經常運行而設計的...
  • net/http 使用生成器來生成各種 HTTP 常量。
  • Go 運行時的源代碼中有幾個有趣的生成器,例如為各種任務生成匯編代碼,為數學運算生成查找表等。
  • Go 編譯器實現使用生成器為 IR 節點生成重復的類型和方法。

此外,標準庫中至少有兩個地方使用生成器來實現類似泛型的功能,其中幾乎重復的代碼是從不同類型的現有代碼中生成的,比如 sort 和 suffixarray 包。

04 深挖生成器 stringer

Go 項目中最常用的生成器之一是stringer[6] — 一種自動為類型創建 String() 方法的工具,以便它們實現 fmt.Stringer 接口。它最常用于為枚舉生成文本表示。

我們看標準庫math.big 包中的一個例子;具體來說是 RoundingMode[7] 類型,其定義如下:

  1. type RoundingMode byte 
  2.  
  3. const ( 
  4.   ToNearestEven RoundingMode = iota 
  5.   ToNearestAway 
  6.   ToZero 
  7.   AwayFromZero 
  8.   ToNegativeInf 
  9.   ToPositiveInf 

至少在 Go 1.18 之前,這是一個慣用的 Go 枚舉;為了使這些枚舉值的名稱可打印,我們需要為這種類型實現一個 String() 方法,這會使用 switch 語句,枚舉每個值及其字符串表示。這是一項非常重復的工作,stringer 工具正好派上用場。

我在一個小示例模塊中[8]復制了 RoundingMode 類型及其值, 以便我們可以更輕松地試驗生成器。讓我們在文件中添加適當的 magic 注釋:

  1. //go:generate stringer -type=RoundingMode 

我們將快速討論 stringer 接受的 flag。確保先安裝了它:

  1. $ go install golang.org/x/tools/cmd/stringer@latest 

現在我們可以運行 go generate;因為在示例項目中,帶有 magic 注釋的文件位于一個子包中,所以我將從模塊根目錄運行它:

  1. $ go generate ./... 

如果一切設置正確,此命令成功完成后不會有任何輸出。查看項目內容,會發現生成了一個名為roundingmode_string.go 的文件,內容如下:

  1. // Code generated by "stringer -type=RoundingMode"; DO NOT EDIT.package floatimport "strconv"func _() {  // An "invalid array index" compiler error signifies that the constant values have changed.  // Re-run the stringer command to generate them again.  var x [1]struct{}  _ = x[ToNearestEven-0]  _ = x[ToNearestAway-1]  _ = x[ToZero-2]  _ = x[AwayFromZero-3]  _ = x[ToNegativeInf-4]  _ = x[ToPositiveInf-5]}const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf"var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70}func (i RoundingMode) String() string {  if i >= RoundingMode(len(_RoundingMode_index)-1) {    return "RoundingMode(" + strconv.FormatInt(int64(i), 10) + ")"  }  return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]} 

工具 stringer 擁有多個代碼生成策略,取決于調用它的枚舉值的性質。我們的案例是最簡單的案例,其中包含“單次連續運行(single consecutive run)”的值。如果這些值形成多個連續運行,stringer 將生成稍微不同的代碼,如果這些值根本不形成運行,則生成另一個版本。為了娛樂和講解,詳細研究 stringer 的來源;在這里,讓我們關注當前使用的策略。

首先,_RoundingMode_name 常量用于有效地將所有字符串表示形式保存在單個連續字符串中。_RoundingMode_index 用作此字符串的查找表;例如 ToZero 值為 2。_RoundingMode_index[2] 是 26,所以該代碼將索引_RoundingMode_name在索引 26 中,這使我們的ToZero部(端是下一個索引,32 在這種情況下) . 因此,代碼將索引到索引 26 處的 _RoundingMode_name,這將引導我們找到 ToZero 部分。

String() 中的代碼有一個回調函數,以防添加更多枚舉值但未重新運行 stringer 工具。在這種情況下,產生的值將是 RoundingMode(N),其中 N 是數值。

這個回調很有用,因為 Go 工具鏈中沒有任何內容可以保證生成的代碼與源代碼保持同步;如前所述,運行生成器完全是開發人員的責任。

但是 func _() 中的奇怪代碼呢?首先,請注意它實際上什么也沒有編譯:該函數不返回任何內容,沒有副作用并且不會被調用。這個函數的目的是作為 編譯守衛;如果原始 enum 以與生成的代碼根本不兼容的方式發生變化,并且開發人員忘記重新運行 go generate,則這是一種額外的安全性。具體來說,它將防止現有的枚舉值被修改。在這種情況下,除非重新運行 go generate,否則 String() 方法可能會成功,但會產生完全錯誤的值。編譯守衛試圖通過使代碼無法編譯越界數組查找來捕獲這種情況。

現在讓我們談談 stringer 的工作原理;首先,閱讀它的 -help 是有指導意義的:

  1. $ stringer -helpUsage of stringer:  stringer [flags] -type T [directory]  stringer [flags] -type T files... # Must be a single packageFor more information, see:  https://pkg.go.dev/golang.org/x/tools/cmd/stringerFlags:  -linecomment      use line comment text as printed text when present  -output string      output file namedefault srcdir/<type>_string.go  -tags string      comma-separated list of build tags to apply  -trimprefix prefix      trim the prefix from the generated constant names  -type string      comma-separated list of type names; must be set 

我們已經使用 -type 參數告訴 stringer 為哪種類型生成 String() 方法。在現實的代碼庫中,人們可能希望在其中定義了多種類型的包上調用該工具;在這種情況下,我們可能希望stringer 只為特定類型生成 String() 方法。

我們沒有指定 -output flag,所以使用默認值;在這種情況下,生成的文件名為 roundingmode_string.go。

眼尖的讀者會注意到,當我們調用 stringer 時,我們沒有指定它應該用作輸入的文件。快速瀏覽該工具的源代碼會發現它也不使用 GOFILE 環境變量。那么它如何知道要分析哪些文件呢?事實證明,stringer 使用 golang.org/x/tools/go/packages 從其當前工作目錄(你還記得,這是包含 magic 注釋的文件所在的目錄)加載整個包。這意味著無論魔術(magic)注釋在哪個文件中,stringer 默認情況下會分析整個包。如果你仔細考慮一下,這是有道理的,誰說常量必須與類型聲明在同一個文件中?在 Go 中,文件只是一個方便的代碼容器;包是工具關心的真正輸入單位。

05 源碼生成器和構建 tags

到目前為止,我們假設生成器在 go generate 運行時位于 PATH 中的某個位置,但情況并非總是如此。

考慮一個非常常見的場景,你的模塊有自己的生成器,它只對這個特定的模塊有用。當有人對模塊進行黑客攻擊時,他們能夠克隆代碼,運行 go generate 和 go build 等。但是,如果魔術注釋假定生成器始終位于 PATH 中,則除非在運行 go generate 之前構建并正確指向生成器,否則這將無法工作。

Go 中的解決方案很簡單,因為 go run 是運行生成器的完美搭配,這些生成器只是模塊樹中某處的 .go 文件。這里有[9]一個簡單的例子。這是一個帶有神奇注釋的包文件:

  1. package mypack//go:generate go run gen.go arg1 arg2func PackFunc() string {  return "insourcegenerator/mypack.PackFunc"

請注意此處如何調用生成器:使用 go run gen.go。這意味著 go generate 將期望在與包含魔術注釋的文件相同的目錄中找到 gen.go。gen.go 的內容是:

  1. //go:build ignorepackage mainimport (  "fmt"  "os")func main() {  // ... same main() as the simple example at the top of the post} 

它只是一個小的 Go 程序(在包 main 中)。唯一需要注意的是 //go:build 約束,它告訴 Go 工具鏈在構建項目時忽略這個文件。事實上,gen.go 不是包的一部分;它位于 main 包中,旨在與 go generate 一起運行,而不是編譯到包中。

標準庫中有許多小程序的示例,這些小程序旨在通過作為生成器的 go run 調用。

典型的模式是代碼生成涉及 3 個文件,它們都共存于同一個目錄/包中:

  • 源文件包含一些包的代碼,以及一條神奇的注釋,用于調用帶有 go run 的生成器。
  • generator,它是一個單一的包含 package main 的 .go 文件; 該生成器由源文件中的魔術注釋中的 go run 調用以生成生成的文件。生成器 .go 文件通常會有一個 //go:build ignore 約束,以將其從包本身的構建中排除。
  • generated file 由 generator 生成; 在某些約定中,它與源文件具有相同的名稱,但后跟_gen(如 pack.go --> pack_gen.go);或者它可能是某種前綴(如 gen)。生成文件中的代碼與源文件中的代碼在同一個包中。在許多情況下,生成的文件包含一些未導出符號的實現細節;源文件可以在其代碼中引用這一點,因為這兩個文件位于同一個包中。

當然,這些都不是工具所要求的——它只是描述了一個通用的約定;特定的項目可以以不同的方式設置(例如,一個生成器為多個包生成代碼)。

06 高級功能

本節討論 go generate 的一些高級或較少使用的功能。

-command 標志

這個 flag 讓我們為 go:generate 行定義別名;如果某些生成器是一個多字命令,我們想為多次調用縮短它,這可能會很有用。

最初的動機可能是將 go tool yacc 縮短為 yacc :

  1. //go:generate -command yacc go tool yacc 

之后 yacc 可以只用這個 4 個字母的名字而不是三個詞來調用多次。

有趣的是,go tool yacc在 1.8 中[10]從核心 Go 工具鏈中刪除了,而且我在主 Go 存儲庫(除了測試go generate本身)或x/tools模塊中都沒有發現 -command 的任何用法 。

-run 標志

該標志用于 go generate 命令本身,用于選擇要運行的生成器。回想一下我們在同一個項目中調用了 3 次 samplegentool 的簡單示例 。我們只能選擇其中之一來使用 -run 標志運行:

  1. $ go generate -run multi ./...Running samplegentool go on mypack.go  cwd = /tmp/mymod/mypack  os.Args = []string{"samplegentool""arg1""multiword arg"}   GOARCH = amd64   GOOS = linux   GOFILE = mypack.go   GOLINE = 3   GOPACKAGE = mypack   DOLLAR = $ 

這對于調試應該是顯而易見的:在具有多個生成器的大型項目中,我們通常只想運行一個子集以進行調試/快速編輯這樣的循環目的。

DOLLAR

在自動神奇地傳遞給生成器的環境變量( env var )中,有一個脫穎而出 —— DOLLAR。它是做什么用的?為什么要將 env var 專用于一個字符?在 Go 源代碼樹中沒有使用這個 env var。

DOLLAR的起源可以追溯到Rob Pike 的這個提交[11]。正如更改描述所說,這里的動機是將 $ 字符傳遞到生成器中,而無需復雜的shell escaping[12]。如果 go generate 調用 shell 腳本或將正則表達式作為參數的東西,這很有用。

可以使用我們的 samplegentool 生成器觀察 DOLLAR 的效果。如果我們將其中一個神奇的注釋更改為:

  1. //go:generate samplegentool arg1 $somevar 

生成器報告其參數為

  1. os.Args = []string{"samplegentool""arg1"""

這是因為 $somevar 被 shell 解釋為引用 somevar 變量,該變量不存在,因此其默認值為空。相反,我們可以如下使用 DOLLAR:

  1. //go:generate samplegentool arg1 ${DOLLAR}somevar 

然后生成器報告:

  1. os.Args = []string{"samplegentool""arg1""$somevar"

原文鏈接:https://eli.thegreenplace.net/2021/a-comprehensive-guide-to-go-generate/

參考資料

[1]C++模板元編程: https://en.wikipedia.org/wiki/Template_metaprogramming

[2]Go 1.4: https://go.dev/blog/generate

[3]samplegentool: https://github.com/eliben/code-for-blog/tree/master/2021/go-generate-guide/samplegentool

[4]mymod: https://github.com/eliben/code-for-blog/tree/master/2021/go-generate-guide/mymod

[5]官方文檔: https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source

[6]stringer: https://pkg.go.dev/golang.org/x/tools/cmd/stringer

[7]RoundingMode: https://pkg.go.dev/math/big#RoundingMode

[8]小示例模塊中: https://github.com/eliben/code-for-blog/tree/master/2021/go-generate-guide/stringerusage

[9]這里有: https://github.com/eliben/code-for-blog/tree/master/2021/go-generate-guide/insourcegenerator

[10]在 1.8 中: https://tip.golang.org/doc/go1.8#tool_yacc

[11]Rob Pike 的這個提交: https://go-review.googlesource.com/c/go/+/8091/

[12]shell escaping: http://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Quoting

 

責任編輯:武曉燕 來源: 幽鬼
相關推薦

2022-04-12 11:46:08

服務gRPC反向代理

2021-09-13 07:23:52

Go Set 設計

2021-10-27 10:55:18

Go入門Demo

2022-05-06 09:00:56

CSS元素Flex

2021-02-01 13:59:47

比特幣區塊鏈安全

2021-08-19 15:36:09

數據備份存儲備份策略

2021-04-16 15:02:11

CAP理論分布式

2021-04-14 06:53:52

C# 修飾符 Public

2024-02-22 08:31:26

數據恢復工具MySQL回滾SQL

2012-06-20 10:47:25

Team Leader

2019-05-07 15:49:27

AI人工智能藝術

2010-09-16 12:40:04

PPPOE SERVE

2018-11-26 06:22:32

WiFi無線網絡路由器

2024-08-22 08:50:51

2025-02-11 09:01:57

2010-07-13 10:40:30

唐駿

2023-10-08 18:07:42

Kubernetes開源容器

2022-10-24 09:57:02

runeGo語言

2024-05-14 08:19:54

2021-03-10 18:07:58

協議調試 Modbus
點贊
收藏

51CTO技術棧公眾號

欧美激情亚洲激情| 日韩精品一区二区三区四区视频 | 男女爽爽爽视频| av中文字幕在线| 韩国欧美国产1区| 91精品国产高清久久久久久久久| 欧美老熟妇乱大交xxxxx| 亚洲精品成a人ⅴ香蕉片| 亚洲成人av一区二区三区| 日韩一区国产在线观看| 成人激情四射网| 久久久久久亚洲精品杨幂换脸| 久色乳综合思思在线视频| 国产人妻人伦精品1国产丝袜| 日韩高清不卡| 天天影视色香欲综合网老头| 一道精品一区二区三区| 天堂资源中文在线| 国产麻豆9l精品三级站| 欧美做爰性生交视频| 国产精品成人免费观看| blacked蜜桃精品一区| 欧美成人精品高清在线播放| 手机在线看福利| 不卡视频观看| 亚洲一区二区影院| 中文字幕一区二区三区精彩视频| 日韩大片b站免费观看直播| 国产精品18久久久久久久网站| 国产成人精品综合| 亚洲精品77777| 欧美黄色aaaa| 久久精品小视频| 奇米网一区二区| 在线看成人短视频| 亚洲国产黄色片| 又色又爽又黄18网站| 成人日韩视频| 欧美日本在线看| 久久久久国产一区| 国产精品av一区二区三区| 五月婷婷综合在线| 免费视频爱爱太爽了| 三级福利片在线观看| 综合电影一区二区三区 | 大片免费在线看视频| 中国色在线观看另类| 欧美一区观看| 你懂的在线看| 久久婷婷国产综合精品青草| 国产一区二区免费在线观看| 人人妻人人澡人人爽精品日本| 国产精品影视网| 999在线观看免费大全电视剧| 国产免费黄色网址| 国产精品一级片| 懂色av一区二区三区在线播放| aaa一区二区| 国产精品白丝jk黑袜喷水| 99久久99久久精品国产片| 亚洲av永久无码国产精品久久| 国产美女在线精品| 91久久国产自产拍夜夜嗨| 精品久久久久中文慕人妻 | 99电影在线观看| 亚洲av无码乱码国产精品久久| 国产激情精品久久久第一区二区 | 国产精品永久免费视频| 97超碰人人草| 国产精品99久久久| 国产欧美一区二区在线播放| 天天干免费视频| 久久久久国产精品免费免费搜索| 日韩国产高清一区| 久久日韩视频| 午夜a成v人精品| 欧美性猛交久久久乱大交小说 | 萌白酱视频在线| 伊人久久大香线蕉综合四虎小说| 久久6精品影院| 国产真实乱人偷精品视频| 国产欧美三级| 国产精品视频自在线| 999精品国产| 99免费精品在线| 午夜精品视频在线观看一区二区| 免费黄色在线看| 亚洲成人精品影院| 免费一级特黄录像| 久久伦理中文字幕| 国产偷亚洲偷欧美偷精品| 精品一区二区6| 精品成人免费| 国产欧美日韩中文字幕| 免费国产精品视频| 中文av一区二区| 久久国产精品网| 日韩福利影视| 亚洲精品av在线| 中文字幕无码日韩专区免费| 国产一区二区精品| 91在线播放国产| 暖暖视频在线免费观看| 一区二区三区精密机械公司| 日本va中文字幕| 国产厕拍一区| 久久视频这里只有精品| 中文字幕一区在线播放| 国产盗摄精品一区二区三区在线 | 国产在线69| 色婷婷综合久久久中文一区二区| 国产精品999.| jlzzjlzz亚洲女人| 97av在线播放| 亚洲第一天堂网| 国产精品国产自产拍在线| www.玖玖玖| 粉嫩一区二区三区四区公司1| 日韩性xxxx爱| 国产精品xxxxxx| 成人激情综合网站| 好色先生视频污| 久久天堂av| 亚洲码在线观看| 日本熟妇毛耸耸xxxxxx| 国产专区综合网| 午夜欧美性电影| 香蕉视频亚洲一级| 亚洲精品综合精品自拍| 国产一级一级片| 国产一区999| 99re8这里只有精品| 99久久婷婷国产综合精品首页| 日韩av一卡二卡| 久久综合色综合| 国产成人在线视频网址| 中文字幕中文字幕一区三区| 久久麻豆视频| 啊v视频在线一区二区三区| 中文字幕无码乱码人妻日韩精品| 91色九色蝌蚪| 欧美xxxxx在线视频| 希岛爱理av免费一区二区| 45www国产精品网站| 午夜福利理论片在线观看| 欧美日韩精品国产| 亚洲精品理论片| 久久不射网站| 日本精品一区二区| 日韩欧美2区| 日韩在线视频一区| 国产老妇伦国产熟女老妇视频| 中文字幕一区二区5566日韩| 亚洲精品国产久| 欧美视频四区| 久久久久国产精品视频| 成人小电影网站| 国产一区二区三区在线观看网站 | 日韩一区二区在线看片| 麻豆精品一区二区三区视频| 丁香六月综合激情| 极品美女扒开粉嫩小泬| 在线日韩一区| 国产精品亚洲美女av网站| 欧美成人三区| 精品国产一区二区三区av性色 | 免费久久一级欧美特大黄| 桃花岛成人影院| 中文字幕亚洲激情| 99久久精品免费看国产交换| 亚洲自拍偷拍图区| 一区二区黄色片| 狠狠色狠狠色综合系列| 日本a在线天堂| 丝袜连裤袜欧美激情日韩| 国产精品福利在线观看网址| 国产高清一区二区三区视频| 亚洲成人a**站| 青青草视频在线观看免费| 中文字幕在线一区| 国产一级黄色录像| 日韩avvvv在线播放| 99中文字幕在线观看| 亚洲伊人春色| 亚洲一区二区三区视频播放| 欧美aa在线| 日韩三级成人av网| 四虎影院在线域名免费观看| 欧美日韩另类国产亚洲欧美一级| 免费看一级一片| 国产片一区二区三区| 中文字幕乱码在线人视频| 久久一综合视频| 国产 欧美 日本| 欧美日韩一二三四| 国产伦精品一区二区三区在线| 成人开心激情| 欧美精品18videos性欧美| av电影在线观看一区二区三区| 亚洲精品一线二线三线无人区| 中文字幕在线网站| 天天色图综合网| 欧美极品视频在线观看| 欧美国产日本韩| 日本黄色录像片| 国产综合色产在线精品| 欧美激情成人网| 最新亚洲激情| 400部精品国偷自产在线观看| 久久99国产精品视频| 国产精品免费区二区三区观看| 福利一区和二区| 欧美中文字幕精品| 91高清视频在线观看| 久久精品视频免费播放| 91美女视频在线| 亚洲欧美制服综合另类| 蜜桃视频污在线观看| 91精品国产一区二区| 中文字幕免费播放| 色综合夜色一区| 国产无遮挡又黄又爽| 亚洲老司机在线| 国产一区二区三区视频播放| 国产日产欧产精品推荐色 | 美女在线视频一区二区 | 在线视频日韩欧美| 免费av成人在线| 99视频精品免费| 香蕉亚洲视频| 亚洲精品无码久久久久久| 欧美午夜电影在线观看 | 秋霞在线一区二区| 成人情趣视频| 亚洲欧美日韩精品久久久| 国产日韩视频在线| 欧美一卡2卡3卡4卡无卡免费观看水多多 | 99久久这里只有精品| 色噜噜一区二区| 日韩免费看片| 亚洲精品视频一二三| 欧美一区电影| 亚洲欧美日韩国产成人综合一二三区| 禁断一区二区三区在线| 日韩av大全| 色乱码一区二区三区网站| 一本色道久久综合亚洲二区三区| 精品精品久久| 一区二区三区四区五区视频| 久久福利影院| 中文字幕乱码免费| 亚洲欧美一级二级三级| 男人的天堂avav| 一本久道久久久| 日韩欧美精品在线观看视频| 性欧美暴力猛交另类hd| 成人在线看视频| 日本成人在线一区| 亚洲一区二区三区观看| 国产剧情av麻豆香蕉精品| 青青草精品在线| 成人久久18免费网站麻豆| 在线精品一区二区三区| 久久综合九色综合97_久久久| 精品少妇人妻一区二区黑料社区 | 先锋在线资源一区二区三区| 欧美国产一级| 99视频精品全部免费看| 亚洲手机在线| 88av.com| 国产一区二区网址| 成人在线视频免费播放| 国产日产欧美一区二区视频| 91久久久久久久久久久久久久 | ww国产内射精品后入国产| 免费一区视频| 国产又黄又猛的视频| 成人免费看黄yyy456| www.中文字幕av| 亚洲视频在线一区观看| 亚洲精品www久久久久久| 欧美制服丝袜第一页| 国产视频一区二区三| 亚洲第一天堂av| 午夜不卡视频| 九九九热精品免费视频观看网站| 松下纱荣子在线观看| 国产在线精品自拍| 日韩av影院| 在线观看欧美一区| 99日韩精品| 婷婷中文字幕在线观看| 96av麻豆蜜桃一区二区| 久久国产高清视频| 日韩欧美亚洲范冰冰与中字| 91theporn国产在线观看| 亚洲精品不卡在线| 黄色网页在线观看| 日本电影亚洲天堂| 亚洲一区二区三区四区电影 | 国产高潮视频在线观看| 国产精品乱码一区二三区小蝌蚪| 精品无码人妻一区二区三| 欧美综合亚洲图片综合区| 开心激情综合网| 深夜福利91大全| 一区二区三区短视频| 91成人免费观看| 成人嫩草影院| 欧美成人精品欧美一级乱| 成人高清在线视频| 美女的奶胸大爽爽大片| 欧美日韩精品是欧美日韩精品| 四虎影院在线域名免费观看| 欧美黄色www| 精品精品视频| 一区二区国产日产| 日韩精彩视频在线观看| 久久久久久久无码| 亚洲一区在线观看免费观看电影高清| 亚洲 小说区 图片区| 亚洲欧洲视频在线| 理论片午夜视频在线观看| 成人av男人的天堂| 最新精品国产| 毛片毛片毛片毛| 国产精品欧美久久久久一区二区| 国产美女激情视频| 亚洲国产欧美一区| 678在线观看视频| 147欧美人体大胆444| 一区二区电影在线观看| 亚洲va在线va天堂va偷拍| 国产精品欧美久久久久一区二区| 欧美性受xxx黑人xyx性爽| 亚洲视频自拍偷拍| 四虎成人在线| 亚洲 国产 欧美一区| 免费观看成人av| 亚洲精品国产精品国自| 精品视频资源站| 婷婷五月在线视频| 国产精品一香蕉国产线看观看| 日韩欧美一区二区三区免费看| 亚欧在线免费观看| 中文字幕不卡一区| 一级片一区二区三区| 俺去了亚洲欧美日韩| 欧美激情精品| www.成年人视频| 91丨九色丨黑人外教| 欧美一区二区三区不卡视频| 亚洲视频欧美视频| 91大神在线观看线路一区| 一本久道久久综合| 国产精品1区2区3区| 精品久久免费视频| 亚洲国产精品一区二区久| 欧美aa在线| 日日夜夜精品网站| 精品夜夜嗨av一区二区三区| 91嫩草丨国产丨精品| 精品不卡在线视频| 秋霞伦理一区| 亚洲亚洲精品三区日韩精品在线视频| 久久99热99| 久久久久亚洲AV| 日韩精品中文字| 四虎精品在线观看| 97中文字幕在线| 久久久久99精品一区| 伊人色综合久久久| 欧美激情精品久久久久久大尺度| 精品少妇一区| 天天影视综合色| 亚洲美女精品一区| 欧美日韩国产中文字幕在线| 国产美女搞久久| 国产主播一区| 少妇久久久久久久久久| 在线成人免费观看| 国产拍在线视频| 一区二区三区不卡在线| 成人网在线播放| 18国产免费视频| 欧美激情免费看| 欧洲grand老妇人| 99国产精品免费视频| 日韩欧美黄色动漫| 日本资源在线| 五月天综合网| 99久久久无码国产精品| 国产精品久久影视| 青青久久av北条麻妃黑人| 欧美一区二区三区另类| 精品欧美一区二区久久久| 欧美大片在线观看| 91精品国产66| 精品国产一区三区| 亚洲色图20p|