日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

《WebAssembly 权威指南》(6)在浏览器中运行遗留代码

發布時間:2024/3/24 HTML 120 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《WebAssembly 权威指南》(6)在浏览器中运行遗留代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


譯者注:這篇文章是《WebAssembly 權威指南》一書的第六章,介紹了如何使用 WebAssembly 在瀏覽器中運行遺留代碼,即已經存在的 C/C++ 代碼庫。文章以一個實際的例子,即使用 Emscripten 工具將 C++ 代碼編譯為 WebAssembly 模塊,并在瀏覽器中使用 JavaScript 調用它。文章詳細說明了 Emscripten 的工作原理、編譯選項、運行時環境和調試方法。

現在,我們再仔細地看看在瀏覽器中調用 C/C++ 代碼的過程。大多數編程語言的代碼都不是為了在瀏覽器中以下載的形式運行。但是,正如香港騎士(譯者注:The Hong Kong Cavaliers,電影《天生愛神》中的主角 Buckaroo Banzai 的樂隊名稱)的隊長那樣,偶爾你會發現自己出現在某個意想不到的新地方,而那里只有你。

我們對在瀏覽器中調用 C/C++ 代碼感興趣的原因是多方面的。但取代 JavaScript 并不是其中之一。至少對大多數人來說不是。相反,我們有大量的用 C 和 C++ 等語言編寫的遺留代碼。其中有很多是非常有用的,如果能在我們的網絡應用程序中使用這些代碼,那就太好了。其中一些可能是將組織與遺留系統聯系在一起的。能夠通過瀏覽器分發這些代碼將是一個很大的進步。

此外,有些問題根本不適合用 JavaScript 來寫。可以用另一種語言來編寫應用程序的這一部分,而不需要一個單獨的運行時,這是非常吸引人的。而且,正如我們的最后一個用例所表明的那樣,對于敏感和棘手的軟件(如加密算法)來說,有來自可信來源的可信代碼提供證明才是有價值的。能夠簡單地重新編譯來自你所認識的人的現有代碼,他們知道自己在做什么,這也是一種有用的能力。

在上一章中,我們展示了使用常規的支持 WebAssembly 的 C 編譯器(如 clang)和一些頭和庫的依賴性管理來實現基本的集成是可能的。然而,必須提供我們自己的標準庫版本,并手動將 C 代碼連接到所提供的 JavaScript 上,這樣做很快就會過時。

幸運的是,Emscript 項目?[1]?奠定了基礎,它比其他方式更容易。這并不奇怪,因為它的主要開發者 Alon Zakai 和 Luke Wagner 一直是這項工作的幕后推手,從 asm.js 開始,延伸到 WebAssembly MVP,再到推動規范的發展 ,一直持續到今天。Emscripten 工具鏈在這一過程中發揮了重要作用。

該項目是基于 LLVM 平臺的。在前面的章節中,我指出它最初有一個自定義的后端,用來生成 asm.js 的可優化的 JavaScript 子集。一旦 WebAssembly 平臺被定義,一個新的后端就能生成 Wasm 二進制文件。

不幸的是,這只是解決方案的一部分。還需要支持數據進出內存,鏈接模塊,包裝現有的庫,等等。一個提供用戶界面或監聽網絡請求的 C 語言程序經常在一個相當緊密的循環中響應輸入活動。鑒于瀏覽器默認為單線程環境,這種主循環會出現操作上的不匹配。Emscripten 工具鏈已被修改,以解決在試圖將本地 C/C++ 移植到 Web 環境中運行時可能出現的許多類型的問題。與大多數主題一樣,本書不可能全面介紹這個項目的所有內容,但我將嘗試讓你快速入門。

適當的 "Hello, World!"

所以,首先要承認:我們本可以在第二章中就在瀏覽器中擁有一個有效的、未經修改的 "Hello, World!" 的例子,早在 2000 年就有了。最后一次,我們將向你展示例 6-1 中的代碼。

例 6-1. 典型的 "Hello, World!" 程序用 C 語言表達

#include?<stdio.h> int?main?()?{printf?("Hello,?World!\n");return?0; }

使用 Emscripten C 語言編譯器(安裝說明見附件?[2]),我們只需要告訴它編譯 C 代碼并生成一些 JavaScript 腳手架。之后,它就會在 Node.js 中未經修改地運行。

brian@tweezer?~/g/w/s/ch06>?emcc?hello.c?-o?hello.js? brian@tweezer?~/g/w/s/ch06>?ls?-laF total?520 drwxr-xr-x?7?brian?staff?224?Mar?1?14:45?./ drwxr-xr-x?7?brian?staff?224?Mar?1?13:02?../ -rw-r--r--?1?brian?staff?121457?Mar?1?13:05?bootstrap.min.css? -rw-r--r--?1?brian?staff?76?Mar?1?13:02?hello.c? -rw-r--r--?1?brian?staff?388?Mar?1?13:07?hello.html? -rw-r--r--?1?brian?staff?121686?Mar?1?14:45?hello.js -rwxr-xr-x?1?brian?staff?11711?Mar?1?14:45?hello.wasm*? brian@tweezer?~/g/w/s/ch06>?node?hello.js Hello,?World!

例 6-2 中的 HTML 文件并不是由這個過程生成的,它與我們之前看到的文件有明顯的不同。有一個單一的?<script>?元素來加載我們生成的 JavaScript。我們沒有使用到目前為止使用過的 utils.js 文件。相反,我們有一個由前面的命令產生的更長的 JavaScript 文件。看看這個文件的清單!它超過了 120KB。它超過了 120 千字節!超過 2000 行代碼。如果你瀏覽下這個文件,你很快就會迷失。這就是我不想在前面的章節中從這里開始的原因。

例 6-2. 一個與我們所見過的 HTML 文件大不相同的文件

<!DOCTYPE?html> <html?lang="en"><head>?<meta?charset="utf-8"?/>?<link?rel="stylesheet"?href="bootstrap.min.css"?/>?<title>Hello,?World!</title>?</head>?<body>?<div?class="container">?<h1>Hello,?World!</h1>?</div>?<script?src="hello.js"></script>??</body> </html>

然而,如果我們通過 HTTP 提供這個目錄,打開瀏覽器,并打開 JavaScript 控制臺,你會看到非常類似圖 6-1 的東西。

圖 6-1. 你看到的 Hello, World!

如果你對?hello.wasm?文件使用 wasm-objdump 命令,你會注意到有一個導出的?main ()?函數。生成的代碼很快就超出了我們顯示整個文件的能力,所以我將只強調導出部分。

...Export?[13]:-?memory?[0]?->?"memory"-?func?[3]?<__wasm_call_ctors>?->?"__wasm_call_ctors"-?func?[5]?<main>?->?"main"-?func?[6]?<__errno_location>?->?"__errno_location"-?func?[50]?<fflush>?->?"fflush"-?func?[47]?<stackSave>?->?"stackSave"-?func?[48]?<stackRestore>?->?"stackRestore"-?func?[49]?<stackAlloc>?->?"stackAlloc"-?func?[44]?<emscripten_stack_init>?->?"emscripten_stack_init"-?func?[45]?<emscripten_stack_get_free>?->?"emscripten_stack_get_free"-?func?[46]?<emscripten_stack_get_end>?->?"emscripten_stack_get_end"-?table?[0]?->?"__indirect_function_table"-?func?[53]?<dynCall_jiji>?->?"dynCall_jiji" ...

你看,為了能讓這個 “Hello, world!” 運行生成了相當多的腳手架。這些細節相當復雜,但如果你想通過它來追蹤,我建議用 wasm2wat 生成相應的 Wat 文件。從那里,追蹤?main ()?函數(在前面的代碼樣本中編號為 5)。你將會看到如例 6-3 所示內容。

例 6-3. Wat 的 main 方法

... (func?(;5;)?(type?5)?(param?i32?i32)?(result?i32)?(local?i32) call?4 local.set?2 local.get?2 return)? ...

最終,你會發現自己回到了生成的 JavaScript 文件。在那里有一個叫做?fd_write?的函數,如例 6-4 所示。它被添加到一個名為?wasi_snapshot_preview1?的命名空間。顧名思義,這是一個我們將在后面的討論中涉及的預覽,但主要的一點是,Emscripten 工具鏈正在生成代碼,以解決我們在前面章節中看到的一些底層麻煩。我們將在第 10 章中發現與 Rust 生態系統類似的工具鏈。

例 6-4. printf 解決方案的一部分

... function?_fd_write?(fd,?iov,?iovcnt,?pnum)?{//hack?to?support?printf?in?SYSCALLS_REQUIRE_FILESYSTEM=0varnum?=?0;for?(vari?=?0;?i?<?iovcnt;?i++)?{var?ptr?=?HEAP32?[(((iov)?+?(i?*?8))?>>?2)];var?len?=?HEAP32?[(((iov)?+?(i?*?8?+?4))?>>?2)];for?(varj?=?0;?j?<?len;?j++)?{SYSCALLS.printChar?(fd,?HEAPU8?[ptr?+?j]);}num?+=?len;}HEAP32?[((pnum)?>>?2)]?=?numreturn?0; } ...

當然,我們沒有必要深入了解這一切到底是如何進行的。重要的是你要明白,我們實際上不是在典型的標準庫意義上調用?printf (),而是這個函數被改寫成了調用生成的代碼。在瀏覽器中,它將把字符路由到與開發者工具相關的 JavaScript 控制臺。在 Node.js 環境中,它將被路由到底層系統控制臺。在這個階段,重要的是,我們的傳統應用程序不必修改就可以在這個新環境中運行,但我們也沒有被直接運行本地 C 和 C++ 的可怕前景所困擾。我們在可移植性、安全性和性能之間取得了重要的平衡,這正是 WebAssembly 的意義所在。

生成的代碼在 JavaScript 中有一個 Module 對象,它定義了我們的 WebAssembly 代碼將占用的運行環境。在 JavaScript 文件的頂部有一些注釋,描述了這個對象和它作為兩個世界之間的接口的作用。然而,為了保持事情的可控性,我們將專注于其中更小的部分。

我們可以選擇的方法之一是使用編譯器指令來打開或壓制某些生成行為。例如,我們可能不希望我們的 C 程序在加載 JavaScript 代碼時立即運行。如果你嘗試在沒有?INVOKE_RUN=0?指令的情況下進行編譯,你會看到典型的問候語,就像你在前面的例子中一樣。在下面的片段中,注意到在 Node.js 中加載代碼時,沒有任何東西被打印到命令行。

brian@tweezer?~/g/w/s/ch06>?emcc?hello.c?-o?hello.js?-s?INVOKE_RUN=0? brian@tweezer?~/g/w/s/ch06>?node?hello.js brian@tweezer?~/g/w/s/ch06>

很明顯,如果你抑制了自動執行,你將希望能夠指示應用程序何時可執行。這可以通過另一個指令來實現:

brian@tweezer?~/g/w/s/ch06>?emcc?hello.c?-o?hello.js?? -s?INVOKE_RUN=0?-s?EXPORTED_RUNTIME_METHODS="['callMain']"

在例 6-5 中,你可以看到我們調用?main ()?函數來響應一個按鈕的點擊。

例 6-5. 一個延遲的?main ()?方法調用

<!DOCTYPE?html> <html?lang="en"><head>?<meta?charset="utf-8"?/>?<link?rel="stylesheet"?href="bootstrap.min.css"?/>?<title>Hello,?World!</title>?</head>?<body>?<div?class="container">?<h1>Hello,?World!</h1>?<button?id="press">Press?Me</button>?</div>?<script?src="hello.js"></script>?<script> var?button?=?document.getElementById?("press");?button.onclick?=?function?()?{try?{?Module.callMain?(); }?catch?(re)?{};};</script>??</body> </html>

在圖 6-2 中,可以看到當按鈕被按下時,友好的信息被打印到控制臺。Firefox 沒有顯示每條相同的信息,但它顯示我已經在右邊按了七次按鈕。你的瀏覽器可能會在每次調用時顯示一條打印信息。

圖 6-2. 按下按鈕所觸發的 Hello, World!

移植第三方代碼

我們現在要深入研究將一些現有的代碼引入瀏覽器。這段代碼從未打算在 web 上運行,它所做的事情通常不會在瀏覽器中運行,例如向文件系統寫入。不要擔心,我們不會破壞瀏覽器的安全模型,但你會看到這段 C++ 代碼基本上可以不加修改地運行。

Emscripten 有大量的選項用于將第三方代碼移植到 WebAssembly 中。它可以有效地替代 cc、make 和 configure,這通常使移植過程變得簡單。在現實中,你很可能要通過自己的方式來解決遇到的問題,但你可能會驚訝于這個過程是如此的簡單。該項目的網站?[3]?有很多幫助文檔。然而,我最喜歡的主題介紹是 Robert Aboukhalil 的?Level Up With WebAssembly 材料?[4]。他告訴你如何將幾個不同的開源項目移植到 WebAssembly,以便在瀏覽器上運行。這包括像俄羅斯方塊、乒乓和吃豆人等游戲。與其嘗試重新創造他已經完成的杰作,我將專注于一個相對簡單和干凈的項目。

我花了一些時間來尋找好的候選代碼。我想找一些內容豐富但又不至于過于復雜的代碼。最終,我在?Arash Partow 的網站?[5]?上找到了他收集的優雅、干凈、適當授權和有用的 C++ 代碼。在那里你會發現相當多有趣的材料。我原本打算使用計算幾何庫,但 Bitmap 庫更適合于這本書。

首先,從?Partow 的網站?[6]?下載代碼。下載 ZIP 文件, 解壓縮,你會看到三個文件。Makefile 是一個老式的 Unix 構建文件,它有組裝有關軟件的指示。我們稍后將探討這個過程。bitmap_image.hpp?文件是主庫,bitmap_test.cpp?是一個全面的測試集合,用于生成一堆有趣的 Windows 位圖圖像。這段代碼不需要任何特定平臺的庫。

brian@tweezer?~/g/w/s/c/bitmap>?ls?-alF? total?536 -rw-r--r--@?1?brian??staff???770B?Dec?31??1999?Makefile -rw-r--r--@?1?brian??staff???242K?Dec?31??1999?bitmap_image.hpp -rw-r--r--@?1?brian??staff????20K?Dec?31??1999?bitmap_test.cpp

我把一些注釋和許可證的細節從例 6-6 中刪除了,為了節省空間。剩下的是構建測試程序的規則結構,即?bitmap_test。Makefile 的工作方式是建立一個目標,然后是建立目標的依賴關系和規則。作為一種慣例,通常有一個 All 規則,指定前面提到的目標文件名。它依賴于?.cpp?和?.hpp?文件。如果這兩個文件中的任何一個被修改了,我們的可執行文件就需要被重新構建。要做到這一點,make 工具將用 OPTIONS 變量中的選項執行 COMPILER 變量中的文件。作為一個 C/C++ 程序,它還需要與 LINKER_OPT 變量中指定的庫鏈接。在這種情況下,我們要與標準的 C++ 庫和基本的數學函數集合進行鏈接。在庫方面,這是最獨立的了。clean 目標只是刪除了衍生的結果。

Makefile 通常對空格和制表符比較敏感。確保使用制表符來開始縮進的規則行。本書倉庫中的代碼就是這樣做的,但如果你以任何方式修改它,你要確保使用制表符。

例 6-6. 測試程序的 Makefile

COMPILER OPTIONS LINKER_OPT =?-c++ =?-ansi?-pedantic-errors?-Wall?-Wall?-Werror?-Wextra?-o =?-L/usr/lib?-lstdc++?-lm all:?bitmap_test bitmap_test:?bitmap_test.cpp?bitmap_image.hpp$(COMPILER)?$(OPTIONS)?bitmap_test?bitmap_test.cpp?$(LINKER_OPT) clean:?rm?-f?core?*.o?*.bak?*stackdump?*~

只要你安裝了一個正常的 C++ 環境,你就應該能夠構建測試程序。

brian@tweezer?~/g/w/s/c/bitmap>?make c++?-ansi?-pedantic-errors?-Wall?-Wall?-Werror?-Wextra?-o?bitmap_test?? bitmap_test.cpp?-L/usr/lib?-lstdc++?-lm?brian@tweezer?~/g/w/s/c/bitmap>?ls?-alF? total?944 drwxr-xr-x@??6?brian??staff?????192?Mar??6?14:35?./ drwxr-xr-x@?11?brian??staff?????352?Mar??6?13:56?../ -rw-r--r--@??1?brian??staff?????770?Dec?31??1999?Makefile -rw-r--r--@??1?brian??staff??247721?Dec?31??1999?bitmap_image.hpp -rwxr-xr-x???1?brian??staff??205032?Mar??6?14:35?bitmap_test* -rw-r--r--@??1?brian??staff???20479?Dec?31??1999?bitmap_test.cpp

現在這個測試程序要求在當前目錄下有一個?image.bmp?文件的例子。我只是在網上找了一個,然后用這個名字。運行該程序后,你將得到一大堆生成的圖像,如圖 6-3 所示。

圖 6-3. 由 bitmap_test 可執行文件生成的圖像

好的,所以它可以工作。它是干凈的代碼。我不打算教你 C++,也不打算帶你看代碼,但我會給你看一些工作實例,你可以在完全不理解發生了什么的情況下進行嘗試。

首先要做的是。我們需要修改 Makefile 以使用 Emscripten 編譯器,而不是你用來構建測試程序的東西。這就像更新 COMPILER 變量一樣簡單,如例 6-7 所示。

例 6-7. 為我們的測試程序更新 Makefile,以便用 Emscripten 編譯。

... COMPILER??????=?-em++ ...

Makefile 的 clean 步驟并沒有刪除可執行文件。所以手動刪除?bitmap_test(或者更好的是修改 Makefile!),現在重新運行 make。你應該看到類似下面的東西。

brian@tweezer?~/g/w/c/bitmap>?make em++?-ansi?-pedantic-errors?-Wall?-Wall?-Werror?-Wextra?-o?bitmap_test???bitmap_test.cpp?-L/usr/lib?-lstdc++?-lm brian@tweezer?~/g/w/c/bitmap>?ls?-alF total?1848 drwxr-xr-x@?8?brian?staff????256????Mar??6?15:21?./ drwxr-xr-x?12?brian?staff????384????Mar??6?14:47?.../ -rw-r-r--@??1?brian?staff????771????Mar??6?15:20?Makefile -rw-r-r--@??1?brian?staff?247721????Dec?31?1999?bitmap_image.hpp -rw-r--r--?1?brian?staff?248314????Mar??6?15:21?bitmap_test -rw-r-r--@??1?brian?staff??20479????Dec??31?1999?bitmap_test.cpp -rwxr-xr-x?1?brian?staff?296743????Mar??6?15:21?bitmap_test.wasm*. -rw-r-r--@??1?brian?staff?120054????Mar??6?14:39?image.bmp

呃,這很容易。不幸的是,我們還沒有完全完成。雖然這確實是在編譯,但由于各種原因,它是無法工作的。其中第一個原因是,該庫希望能夠寫入文件系統。這一點應該不足為奇,因為這是不可能的。然而,有一個非常酷的文件系統抽象,它可以通過添加編譯器指令來寫入本地存儲。現在,就像處理?printf () ?的調用一樣,Emscripten 工具鏈將模擬一個文件系統。通過在你的 Makefile 中添加指令?-s FORCE_FILESYSTEM=1?來解鎖這一支持。我將在下面向你展示最終的形式。

第二個問題是,默認生成的 Memory 實例將不允許增長。如果我們期望這個庫能在內存中生成一些相當大的圖像,那么它就需要足夠的內存。所以,我們可以使用另一個指令來允許它這樣做。這是我在第 4 章中向你展示的如何手動操作的東西。這是 Emscripten 可以為我們處理的細節。為了對這個過程有更多的控制,我們將告訴 Emscripten 不要自動退出程序,并導出?main ()?方法,這樣我們就可以在需要時調用它。因為我們不是生成一個獨立的二進制文件,我們還要告訴 Emscripten 編譯器生成一個叫做?bitmap_test.js?的 JavaScript 文件。bitmap_test?規則的命令現在應該如例 6-8 所示。

例 6-8. 修改后的 Makefile 帶有我們所有的 Emscripten 選項

bitmap_test:?bitmap_test.cpp?bitmap_image.hpp $(COMPILER)?$(OPTIONS)?bitmap_test.js?bitmap_test.cpp?$(LINKER_OPT)?-s?FORCE_FILESYSTEM=1?-s?ALLOW_MEMORY_GROWTH=1??-s?INVOKE_RUN=0?-s?EXPORTED_RUNTIME_METHODS="['callMain']"

這就解決了妨礙該例子工作的具體問題。然而,還有一個問題。這個程序運行了 20 個相對耗時的測試。由于 JavaScript 是一個單線程的環境,當 WebAssembly 模塊運行時,瀏覽器很可能會抓狂,因為花了太長時間。

我們最終會解決這個問題,但目前,我只是要刪除對其余測試的調用,只調用我最喜歡的?test20 ()。

main ()?方法現在如例 6-9 所示。

例 6-9. 調用一個測試的 main 方法

int?main?()?{test20?();return?0;? }

如果你重新運行 make 命令,你應該看到生成的 Wasm 和 JavaScript 文件。我將生成一些基本的 HTML 腳手架供我們使用。在例 6-10 中,你可以看到我有一個按鈕和一個?<canvas>?元素,我們將用它來渲染位圖。現在,把這個文件和你的 Wasm 和 JavaScript 文件保存在同一個目錄中,并像我們在書中所做的那樣,通過 HTTP 提供給它。

例 6-10. 為我們的位圖生成器提供 HTML 腳手架

<!DOCTYPE?html> <html?lang="en"><head>?<meta?charset="utf-8"?/>?<title>C++-rendered?Image?in?the?Browser</title>?</head>?<body>?<div?class="container">?<h1>C++-rendered?Image?in?the?Browser</h1>?</div>?<button?id="load">Load</button>?<canvas?id="output"></canvas>?<script?src="bitmap_test.js"></script>?<script> var?button?=?document.getElementById?("load");?button.onclick?=?function?()?{Module.callMain?();console.log?("Done?rendering.");};</script>??</body> </html>

一旦你把 HTML 加載到你的瀏覽器,打開開發者控制臺并按下按鈕。這將生成各種文件,并將它們寫到 "磁盤" 上。這將需要一點時間,我料想你的瀏覽器會抱怨這一點。只要告訴它在它要求的時候等待就可以了。一旦完成,你應該看到信息被打印到控制臺。此時,在控制臺中,你可以做一些可能讓你吃驚的事情,如圖 6-4 所示。

圖 6-4. 在瀏覽器中向文件系統寫文件

我們的第三方代碼使用標準 C++ 庫來向 "文件系統" 寫入。Emscripten 在瀏覽器的本地存儲上提供了一個抽象層,使之成為可能。從 C++ 中,我們可以很容易地把它讀回來。在 JavaScript 中,這并不難,如例 6-11。

例 6-11. 使用文件系統抽象從 "磁盤" 中讀取 "文件" 的 JavaScript

<-?undefined>>?image <-?Uint8Array?(2880054)?[66,?77,?54,?242,?43,?0,?0,?0,?0,?0,?...]

我們剛剛通過調用?FS.readFile ()?函數得到了一個 Uint8Array。這將使我們很容易處理來自文件的字節。只有一個問題。瀏覽器不支持顯示 Windows 位圖文件!幸運的是,這是一種有記錄的格式,有人為我們做了件好事,提供了相應的代碼。我們可以依靠一些現有的 C 或 C++ 代碼,但只是為了向你展示一些選擇,我們將使用 JavaScript 代碼?[7]

幸運的是,在第 4 章你已經具備了理解例 6-12 中的大部分內容的能力。我們把從?FS.readFile ()?函數中返回的 ArrayBuffer 傳遞給一個叫做?getMBP ()?的方法。這將在緩沖區周圍創建一個 DataView,并在把它們塞進一個更容易理解的 JavaScript 表示法之前拉出各種圖像細節。

一旦讀入位圖文件,我們通過同一網站的?convertToImageData ()?函數將 JavaScript 結構轉換成 ImageData 實例。之后 ,我們設置?<canvas>?的大小以匹配其高度和寬度,并使用其?putImageData ()?方法來渲染像素。

例 6-12. 用 JavaScript 讀回我們的位圖文件,并在?<canvas>?中渲染它

<script>//?Code?taken?from?https://tinyurl.com/bitmap-in-javascript?//?Written?by?Ian?Elliottfunction?getBMP?(buffer)?{var?datav?=?new?DataView?(buffer);var?bitmap?=?{};bitmap.fileheader?=?{};bitmap.fileheader.bfType?=?datav.getUint16?(0,?true);bitmap.fileheader.bfSize?=?datav.getUint32?(2,?true);bitmap.fileheader.bfReserved1?=?datav.getUint16?(6,?true);bitmap.fileheader.bfReserved2?=?datav.getUint16?(8,?true);bitmap.fileheader.bfOffBits?=?datav.getUint32?(10,?true);bitmap.infoheader?=?{};bitmap.infoheader.biSize?=?datav.getUint32?(14,?true);bitmap.infoheader.biWidth?=?datav.getUint32?(18,?true);bitmap.infoheader.biHeight?=?datav.getUint32?(22,?true);bitmap.infoheader.biPlanes?=?datav.getUint16?(26,?true);bitmap.infoheader.biBitCount?=?datav.getUint16?(28,?true);bitmap.infoheader.biCompression?=?datav.getUint32?(30,?true);bitmap.infoheader.biSizeImage?=?datav.getUint32?(34,?true);bitmap.infoheader.biXPelsPerMeter?=?datav.getUint32?(38,?true);bitmap.infoheader.biYPelsPerMeter?=?datav.getUint32?(42,?true);bitmap.infoheader.biClrUsed?=?datav.getUint32?(46,?true);bitmap.infoheader.biClrImportant?=?datav.getUint32?(50,?true);var?start?=?bitmap.fileheader.bfOffBits;bitmap.stride?=?Math.floor?((bitmap.infoheader.biBitCount??*bitmap.infoheader.biWidth?+?31)?/?32)?*?4;bitmap.pixels?=?new?Uint8Array?(buffer,?start);return?bitmap;}//?Code?taken?from?https://tinyurl.com/bitmap-in-javascript?//?Written?by?Ian?Elliottfunction?convertToImageData?(bitmap)?{var?canvas?=?document.createElement?("canvas");var?ctx?=?canvas.getContext?("2d");var?width?=?bitmap.infoheader.biWidth;var?height?=?bitmap.infoheader.biHeight;var?imageData?=?ctx.createImageData?(width,?height);var?data?=?imageData.data;var?bmpdata?=?bitmap.pixels;var?stride?=?bitmap.stride;for?(var?y?=?0;?y?<?height;?++y)?{for?(var?x?=?0;?x?<?width;?++x)?{var?index1?=?(x+width*(height-y))*4;var?index2?=?x?*?3?+?stride?*?y;data?[index1]?=?bmpdata?[index2?+?2];data?[index1?+?1]?=?bmpdata?[index2?+?1];data?[index1?+?2]?=?bmpdata?[index2];data?[index1?+?3]?=?255;}}return?imageData;}var?button?=?document.getElementById?("load");button.onclick?=?function?()?{Module.callMain?();var?canvas?=?document.getElementById?("output");var?context?=?canvas.getContext?('2d');var?image?=?FS.readFile?("./test20_julia_set_vga.bmp");var?bmp?=?getBMP?(image.buffer);var?imageData?=?convertToImageData?(bmp);canvas.width?=?bmp.infoheader.biWidth;canvas.height?=?bmp.infoheader.biHeight;context.putImageData?(imageData,?0,?0);console.log?(image);};</script></body> </html>

調用我們的 C++ 應用程序,并在通過 JavaScript 讀回結果后在畫布上渲染,其結果如圖 6-5 所示。

圖 6-5. 在畫布中渲染位圖文件的結果

我希望你至少有一點印象。要在瀏覽器中運行這段 C++ 代碼,我們只需要做很少的事情,這一點非常酷!在性能和線程方面仍有一些問題,但你已經從兩個數字相加的過程一路走來,走了很長一段路。

我們可以執行命令中添加一個參數,以選擇要運行的測試。目前,我們不擔心在樣本圖像中讀取的測試。

為了接受命令行上的參數,我們需要將?main ()?方法修改為如例 6-13 所示。

例 6-13. 修改了?main ()?方法,以接受測試選擇的參數

int?main?(int?argc,?char?**argv) {int?which?=?20;if?(argc>?1)?{std::string::size_type?sz;which?=?std::stoi?(argv?[1],?&sz);}switch?(which)?{case?0:case?1:case?2:case?3:case?4:case?5:case?6:case?7:case?8:case?10:case?11:case?12:case?13:case?16:printf?("Sorry,?%?s?requires?reading?in?a?file?which?we?are?not?supporting?yet.\n",?argv?[1]);break;case?9:test09?();break;case?14:test14?();break;case?15:test15?();break;case?17:test17?();break;case?18:test18?();break;case?19:test19?();break;case?20:test20?();break;default:printf?("Sorry,?%?s?is?an?unknown?test?number.\n",?argv?[1]);}return?0; }

我們首先注意到的是,main ()?的簽名已經被修改為接受一個整數作為命令行參數,實際上是一個字符串數組。請記住,在 C/C++ 中,這是作為一個指針指向一堆指針,這就是為什么有兩個星號的原因。我們可以像對數組那樣對它們進行索引。

默認情況下,第一個參數將是可執行文件的名稱。由于我們從 0 開始計數,第一個傳入的參數將在 1 的位置。我們設置一個默認的測試數字為 20,因為我已經表示這是我最喜歡的測試。然而,如果你傳入一個代表數字的字符串,它將被轉換為一個整數。一旦我們確定了是否使用默認值,我們就在這個值上切換。如前所述,我們跳過需要輸入圖像的測試。還有其他幾個你可以運行的。

{{<callout note 提示>}}

如果你要在本地代碼和 WebAssembly 之間來回轉移,你可能想在這時維護兩個不同的 Makefile。你可以創建靈活的 Makefiles,支持兩個目標。你可以用?-f <file>?參數來指定使用哪個文件,如下面的例子所示。

{{}}

如果你愿意,可以重新編譯本地可執行文件并嘗試新的參數處理:

brian@tweezer?~/g/w/s/c/bitmap>?make?-f?Makefile.orig c++?-ansi?-pedantic-errors?-Wall?-Wall?-Werror?-Wextra?-o?bitmap_test?? bitmap_test.cpp?-L/usr/lib?-lstdc++?-lm brian@tweezer?~/g/w/s/c/bitmap>?./bitmap_test?1 1?requires?reading?in?a?file?which?we?don't?support?yet. brian@tweezer?~/g/w/s/c/bitmap>?./bitmap_test?9 brian@tweezer?~/g/w/s/c/bitmap>?ls?-laF total?7608 drwxr-xr-x@?14?brian?staff?????448?Mar??7?22:55?./ drwxr-xr-x??12?brian?staff?????384?Mar??6?14:45?../ -rw-r--r--@??1?brian?staff?????893?Mar??7?17:43?Makefile -rw-r--r--@??1?brian?staff?????776?Mar??7?20:10?Makefile.orig -rw-r--r--@??1?brian?staff??247721?Dec?31??1999?bitmap_image.hpp -rwxr-xr-x???1?brian?staff?205264??Mar??7?22:55?bitmap_test* -rw-r--r--@??1?brian?staff???20954?Mar??7?20:16?bitmap_test.cpp -rw-r--r--???1?brian?staff??249546?Mar??7?20:16?bitmap_test.js? -rw-r--r--@??1?brian?staff??120054?Mar??6?14:39?image.bmp -rw-r--r--???1?brian?staff????3127?Mar??7?17:26?index.html -rw-r--r--???1?brian?staff?3000054?Mar??7?22:55?test09_color_map_image.bmp

好消息是,我們不需要對 JavaScript 代碼做太多的改動!因為簽名已經改變了。現在我們可以傳入字符串,以調用?main ()?方法。與其在 HTML 中輸出只有適度差異的 JavaScript,在圖 6-6 中你可以看到從開發者控制臺輸出的帶參數的可執行程序的調用結果。

圖 6-6. 在瀏覽器中用命令行參數調用我們的位圖生成器

除了根據命令行參數選擇測試外,你可能還想把測試當作函數來運行。然而,這需要更多工作。

首先在我們的測試文件中添加一個名為?run_test ()?的方法用來接受一個參數。在這一點上沒有必要重復實際的代碼,所以我們將只是打印出一個字符串,表明請求運行的是哪個測試。如例 6-14,你可以看到這個函數的定義。

void?run_test?(int?i)?{printf?("Running?test?%?d!\n",?i); }

默認情況下,只有?main ()?方法被導出,因為那是我們需要啟動程序的唯一函數。我們需要添加一個?EXPORTED_FUNCTIONS?指令,如下所示。函數名的定義有一個前導下劃線字符。如果你想讓?main ()?仍然可以被調用,你需要把它也包括進來,但在例 6-15 中我們沒有這樣做。

例 6-15. 修改后的 Makefile 導出更多方法

bitmap_test:?bitmap_test.cpp?bitmap_image.hpp$(COMPILER)?$(OPTIONS)?bitmap_test.js?bitmap_test.cpp?$(LINKER_OPT)?-s?FORCE_FILESYSTEM=1?-s?ALLOW_MEMORY_GROWTH=1?-s?INVOKE_RUN=0?-s?EXPORTED_FUNCTIONS="['_main',?'_run_test']" -s?EXPORTED_RUNTIME_METHODS="['callMain']"

不幸的是,這樣做是行不通的,因為我們使用的是 C++。生成的函數名會被編譯器進一步篡改,其原因不值得在此贅述。為了避免這個問題,我們需要告訴編譯器抑制這種行為并使用 C 語言連接。要使用這種行為,我們需要修改函數定義,使之看起來如例 6-16 所示。

例 6-16. 導出一個函數以便從 JavaScript 中調用,并使用 C 語言鏈接

extern?"C" void?run_test?(int?i)?{printf?("Running?test?%?d!\n",?i); }

這應該可以解決這個問題,但我還要做一個改動,向你展示另一個選擇。

Emscripten 工具鏈中有一個便捷的方法叫 cwrap,它可以為調用一個特定的 C 函數生成一個 JavaScript 函數。我們把它添加到例 6-17 的?EXPORTED_RUNTIME_METHODS?指令中。

例 6-17. 更新了 Makefile 以使用 cwrap

bitmap_test:?bitmap_test.cpp?bitmap_image.hpp$(COMPILER)?$(OPTIONS)?bitmap_test.js?bitmap_test.cpp?$(LINKER_OPT)?-s?FORCE_FILESYSTEM=1?-s?ALLOW_MEMORY_GROWTH=1?-s?INVOKE_RUN=0?-s?EXPORTED_FUNCTIONS="['_main',?'_run_test']"?-s?EXPORTED_RUNTIME_METHODS="['callMain',?'cwrap']"

如果你重新構建和加載 HTML,你將能夠從 JavaScript 開發者控制臺調用這個函數。其結果如圖 6-7 所示。

圖 6-7. 從 JavaScript 中直接通過 cwrap () 調用我們的函數

請注意,cwrap ()?的調用返回一個適當的 JavaScript 函數,我們可以像往常一樣使用。你可以把 switch 語句移到這個方法中,同樣能用來調用任意的測試。

作為練習,嘗試添加一個方法,寫出一個名為?image.bmp?的位圖文件。從你的 C++ 代碼中導出這個方法并從瀏覽器中調用它。這可以讓你在需要它的測試中讀回該文件。你可以修改 switch 語句以允許調用這些方法。

最后,想象一些其他的用戶界面元素,允許你挑選要運行的測試。一旦運行,設想在?<canvas>?元素中顯示一個文件列表。你幾乎擁有做這件事所需要的所有組件,所以請試一試吧!

libsodium

在本章結束之前,我想請你注意一個叫做 libsodium 的項目。我們不打算直接用它做任何事情,但它展示了通過 WebAssembly 將 C 和 C++ 等語言與瀏覽器混合的能力。

它基于網絡和加密庫(NaCl),這是一個高性能的現代加密庫,由深諳此道的人編寫。NaCl 的許多功能還不一定能用于 JavaScript 運行時。新的密碼套件,包括帶有附加數據的認證加密(AEAD),可能會在它們被移植到 JavaScript 或通過操作系統提供給瀏覽器之前發布在這里。

第二個動機是,NaCl 庫的作者知道他們在做什么。用一個糟糕的實現來破壞一個加密庫的功效是非常容易的。即使是比較兩個哈希值是否相同這樣微妙的事情,如果實施不當也會泄露細節。令人沮喪的是,這種比較的正確實現將與開發人員通常比較兩個哈希值的方式相悖。我的觀點是,NaCl 代碼庫是有出處的。如果一個沒有背景了解這些細節的 JavaScript 開發人員試圖實現這些功能,那么它很有可能存在這些漏洞。當你擁有一個可信賴的代碼庫時,能夠重新編譯并直接使用它是考慮本章主題的另一個原因。

所以,libsodium 旨在通過 WebAssembly 將 NaCl 庫導出到 JavaScript 環境中,而不需要重寫代碼或在性能上妥協。它被作為 WebAssembly 項目來維護。我認為,一旦人們對以這種方式使用 WebAssembly 有了更好的認識,我們就會看到更多的項目可以作為本地庫或 WebAssembly 模塊使用,這取決于你的配置需求。這將是代碼重用的一個好機會。我們將在第 10 章看到這種方法的另一個例子。

在那之前,還有更多關于 WebAssembly 的知識需要學習。

引用鏈接

[1]?Emscript 項目:?https://emscripten.org/
[2]?附件:?../appendix/
[3]?該項目的網站:?https://emscripten.org/docs/compiling/Building-Projects.html
[4]?Level Up With WebAssembly 材料:?https://www.levelupwasm.com/
[5]?Arash Partow 的網站:?https://www.partow.net/
[6]?Partow 的網站:?http://www.partow.net/programming/bitmap/index.html
[7]?使用 JavaScript 代碼:?https://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html

獲取更多云原生社區資訊,加入微信群,請加入云原生社區,點擊閱讀原文了解更多。

總結

以上是生活随笔為你收集整理的《WebAssembly 权威指南》(6)在浏览器中运行遗留代码的全部內容,希望文章能夠幫你解決所遇到的問題。

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

蜜桃视频在线视频 | 国产精品免费不卡 | 国产精品久久久久影院 | 97人人添人澡人人爽超碰动图 | 国产精品久久久久av福利动漫 | 久久精品国产亚洲aⅴ | 五月婷婷欧美 | 亚洲精品免费在线观看视频 | 五月天亚洲激情 | 国产剧情av在线播放 | 午夜精品久久久久 | 91看片一区二区三区 | 亚洲精品综合一区二区 | 日韩精品中文字幕av | 欧美午夜性 | 久久久亚洲精华液 | 黄色亚洲片 | 日韩黄色软件 | 人交video另类hd | 国产精品久久久久久电影 | 四虎永久国产精品 | 久草精品视频在线播放 | 91一区啪爱嗯打偷拍欧美 | 最近中文国产在线视频 | 欧美天堂久久 | 午夜久久福利视频 | 亚洲国产理论片 | 婷婷丁香七月 | 欧美极品少妇xbxb性爽爽视频 | 久久只精品99品免费久23小说 | 在线观看日韩精品 | 欧美日韩国产一区二区三区 | 国产亚洲日 | 婷婷久久综合九色综合 | 午夜.dj高清免费观看视频 | 一级片视频在线 | 亚洲国产黄色 | japanesefreesex中国少妇 | 欧美中文字幕久久 | 又黄又网站 | 国产女教师精品久久av | 国产小视频在线看 | 亚洲成人av一区 | 一区二区三区在线播放 | 久久高清免费观看 | 手机版av在线 | 亚洲,国产成人av | 91看片在线观看 | 亚洲免费av一区二区 | 丁香婷婷网 | 国产区久久 | 夜色资源站wwwcom | 成人黄色视 | 国产精品 中文在线 | 欧美一二区视频 | 久久五月天色综合 | 欧美精品v国产精品 | 日韩国产精品一区 | 粉嫩av一区二区三区四区在线观看 | 天天艹天天 | 韩国av免费看 | 91成熟丰满女人少妇 | 国产一级在线观看 | 久草精品视频在线观看 | 午夜影院三级 | 午夜视频在线观看欧美 | 亚洲国产最新 | 国产精品久久久久久久久久尿 | 九精品 | 久久综合狠狠综合久久激情 | 天堂av免费看 | 一区二区三区精品在线视频 | 国产乱视频| 国产女v资源在线观看 | 人人澡人摸人人添学生av | 99 色| 国产va饥渴难耐女保洁员在线观看 | 色综合小说 | www.天天色 | 精品国产乱码久久久久久浪潮 | 网站在线观看日韩 | 国产一区二区高清 | 亚洲天堂视频在线 | 亚洲 欧洲av| 色九九影院 | 国产午夜在线观看 | 国产a级片免费观看 | 激情五月综合 | 欧美老女人xx | 欧美中文字幕第一页 | 99热这里| 99精品欧美一区二区蜜桃免费 | 99久久久久免费精品国产 | 欧美日韩在线精品一区二区 | 免费av黄色 | 成人免费观看电影 | 色婷婷狠狠操 | 在线国产欧美 | 久久成人高清视频 | 黄色影院在线播放 | 日韩在线高清视频 | 精品久久久久久久久久 | 四虎永久国产精品 | 在线观看免费黄色 | 免费av试看 | 97高清视频 | 国产91小视频 | 精品国产亚洲在线 | 国产成人一区二区三区影院在线 | 久久精品99国产 | 国产人免费人成免费视频 | 国产精品一区二区三区在线播放 | 在线视频观看你懂的 | 欧洲亚洲国产视频 | 精品久久国产一区 | 一区二区三区日韩视频在线观看 | 奇米影视777四色米奇影院 | 亚洲精品色 | 国产精品青草综合久久久久99 | 久草影视在线 | 亚洲激情综合 | 人人爽人人爽人人片 | 一级一片免费视频 | 国产精品18久久久久vr手机版特色 | 草免费视频 | 在线成人免费电影 | 91.麻豆视频 | 色偷偷97| 久久国内精品视频 | 国产一线二线三线性视频 | 久久久久久毛片精品免费不卡 | 97成人资源站 | 亚洲一区欧美激情 | 69国产在线观看 | 三级小视频在线观看 | 91在线入口 | 日本精品午夜 | 四虎天堂 | www日韩| 久久精品一区二区三区中文字幕 | 久草在线中文888 | 黄色毛片视频免费观看中文 | 成人h视频在线播放 | 91av亚洲 | 国产精品第一页在线观看 | 国产成人一区二区三区免费看 | 中文字幕乱码一区二区 | 免费中午字幕无吗 | 激情视频二区 | 欧美日产在线观看 | 婷婷视频在线播放 | 日韩欧美在线观看一区 | 99中文在线 | 91在线资源 | 国产玖玖精品视频 | 在线观看成人一级片 | 久久国产福利 | 狠狠色丁香婷婷综合最新地址 | 欧美精品久久久久久久免费 | 国产精品视频免费观看 | 欧美日韩另类在线 | 精品伊人久久久 | 久久精品成人欧美大片古装 | 福利久久| 欧美精品亚州精品 | 久久综合五月天婷婷伊人 | 五月天久久 | 欧美日韩久久不卡 | 一级黄色片在线免费看 | 在线观看免费 | 99精品欧美一区二区三区黑人哦 | 草久视频在线观看 | 九月婷婷色 | 色视频网站免费观看 | 亚洲春色综合另类校园电影 | 国产经典 欧美精品 | 99视频在线观看免费 | 96亚洲精品久久久蜜桃 | 日韩毛片在线一区二区毛片 | 中文字幕频道 | 亚洲一级片av | 亚洲在线综合 | 91免费在线播放 | 激情综合网色播五月 | 精品国产一二三四区 | www.五月婷婷.com | 国产精品久久久久永久免费 | 久久国产精品一二三区 | 精品国产乱子伦一区二区 | 丝袜美腿在线播放 | 中文字幕在线播放一区 | 91在线精品秘密一区二区 | 久久久影院官网 | 欧美激情视频免费看 | 欧美国产精品久久久久久免费 | 国产亚洲精品中文字幕 | 久久精品在线免费观看 | 日韩在线观看三区 | 免费视频18 | 天天草天天摸 | 九草在线观看 | 亚洲精品成人免费 | 中国一级特黄毛片大片久久 | 国产操在线 | 91亚色视频 | 国产精品久久久久久电影 | 色多视频在线观看 | 成人黄色在线观看视频 | 91精选 | 婷婷播播网 | 欧美日韩视频在线播放 | 亚洲 欧美 综合 在线 精品 | 日本久久精品 | 亚洲五月花| 日韩欧美综合在线视频 | 欧美激情综合五月色丁香 | 国产三级精品三级在线观看 | 精品国产欧美一区二区 | 97在线精品 | 天天色天天射天天干 | 亚州性色 | 91热爆视频 | 成年人网站免费在线观看 | 亚洲视频免费在线观看 | 国产色就色 | 天天综合网在线观看 | 久久综合免费 | 亚洲人人射| 黄色av网站在线观看 | 国产.精品.日韩.另类.中文.在线.播放 | 91视频在线观看下载 | 欧美日韩亚洲第一页 | 久久久久这里只有精品 | 国产精品久久久久久999 | 国产中文字幕视频在线观看 | 欧美日韩二三区 | 国产中文字幕久久 | 成人国产精品一区 | 成人免费看片网址 | 黄色av一级 | 免费看毛片在线 | av在线永久免费观看 | 久久综合久久久久88 | 免费h在线观看 | 成人免费影院 | 亚洲一区二区91 | 人人爱人人爽 | 国产91亚洲 | 国产亚洲精品久久久久久移动网络 | 亚洲精品456在线播放第一页 | 欧美亚洲成人免费 | 欧美超碰在线 | 亚洲成人家庭影院 | 欧美精品一区二区蜜臀亚洲 | 99久久久国产精品免费99 | 黄色片网站av | 麻豆视频免费观看 | 午夜三级影院 | 手机看片1042 | 91人人澡人人爽人人精品 | 五月综合激情网 | 精品久久免费 | 九九九视频精品 | 精品国产乱码久久久久久三级人 | 三级毛片视频 | 国产二区免费视频 | 日韩动漫免费观看高清完整版在线观看 | 蜜臀av麻豆 | 欧美日韩性生活 | 一二区精品 | 久久久九九 | 亚洲午夜久久久久久久久电影网 | 精品女同一区二区三区在线观看 | 亚洲丁香日韩 | 激情久久一区二区三区 | 免费不卡中文字幕视频 | 成人黄色短片 | 日韩免费播放 | 日韩在线大片 | 中文字幕欲求不满 | 色婷婷久久一区二区 | 成人高清在线 | 免费a级观看 | 欧美成年黄网站色视频 | 婷婷激情在线观看 | 日韩av电影网站在线观看 | 丰满少妇久久久 | 成人动图| 综合国产在线观看 | 色丁香婷婷 | 国产精品普通话 | 啪啪动态视频 | 天天看天天干 | 伊人狠狠操 | 亚洲视频一| 91在线中文 | 麻豆mv在线观看 | 婷婷色六月天 | 一区二区三区四区五区在线视频 | 国产成人精品av在线 | 中文字幕中文字幕在线中文字幕三区 | 日韩 在线观看 | 久久综合九色综合欧美就去吻 | 日日干日日色 | 欧美aaa大片 | 欧美a级片免费看 | 国产黄在线免费观看 | 国产剧情一区 | 午夜免费福利片 | 欧美精品v国产精品v日韩精品 | 免费看毛片在线 | 久草在线视频在线观看 | 欧美91精品国产自产 | 女人18片 | 免费高清在线观看成人 | 在线黄网站 | 狂野欧美激情性xxxx | 麻豆视频www | 天天干,天天射,天天操,天天摸 | 成人a v视频 | 韩国av一区二区三区在线观看 | 婷婷丁香六月天 | 人人澡视频 | 成年人免费电影在线观看 | 人人艹人人 | 国产麻豆果冻传媒在线观看 | 国产精品久久久久久久久毛片 | 麻豆精品传媒视频 | 伊香蕉大综综综合久久啪 | 国产免费久久av | 久草网站在线 | 天天操网站 | 狠狠网亚洲精品 | 免费看一级一片 | 四虎影视精品成人 | 国产情侣一区 | 激情综合网色播五月 | 91av资源在线 | 日韩免费av片 | 国产精品久久久久一区二区三区 | 国产高清第一页 | 永久精品视频 | 欧美性爽爽 | 在线观看视频福利 | 日韩在线电影一区 | 91视频下载| www99久久 | 久草影视在线 | 五月天视频网站 | 色妞色视频一区二区三区四区 | 成人午夜电影免费在线观看 | 国产亚洲精品中文字幕 | 综合色在线 | 国产精品美女久久久久久免费 | 97电影院网 | 91在线入口 | 国产在线精品国自产拍影院 | 精品一区二区精品 | 青青久草在线视频 | 成年人免费观看在线视频 | 日韩免费区 | 精品国产一区二区三区日日嗨 | 日韩中文字幕视频在线 | 91最新地址永久入口 | 天天综合网在线观看 | 天天操天天射天天插 | 日韩欧美99| 亚洲国产无| 中文字幕一区在线 | 欧美爽爽爽 | 久久99精品国产麻豆婷婷 | 特级西西444www高清大视频 | 久久久久福利视频 | 久久久久久久久久网站 | 黄色的网站在线 | 91香蕉视频黄色 | 99精品小视频| 欧美国产不卡 | 97在线观看免费观看 | 久久精品一二三区 | 97视频在线观看播放 | 在线国产欧美 | 91一区一区三区 | 99视| 亚洲一区二区三区四区精品 | 青春草视频在线播放 | 手机在线看片日韩 | 欧美日韩高清国产 | 欧美另类交在线观看 | 天天激情站 | 亚洲另类交 | 日韩av不卡在线观看 | www.香蕉视频在线观看 | 激情综合一区 | 欧美激情h | 国产黄色精品在线 | 国产剧情一区二区在线观看 | 91精品一区在线观看 | 久久久国产一区二区三区四区小说 | 夜夜夜精品 | 精精国产xxxx视频在线播放 | 欧美a在线看 | 欧美一级特黄高清视频 | 啪啪免费观看网站 | 免费在线观看一级片 | 91亚洲欧美 | 黄网站色视频免费观看 | 国产精品入口a级 | 就要色综合| 久久99精品久久久久久三级 | 91视频网址入口 | 99国产精品一区二区 | 日本中文字幕观看 | 国内精品久久天天躁人人爽 | 在线午夜电影神马影院 | 天天色.com | 中文字幕av免费在线观看 | 在线免费观看国产黄色 | 天天干天天玩天天操 | 天堂视频一区 | 久99久精品视频免费观看 | 国产精品一区二区吃奶在线观看 | 免费亚洲精品视频 | 天天做天天看 | 中文字幕在线资源 | 黄色影院在线免费观看 | 亚洲人成在线观看 | 99久久久精品 | 亚洲精品久久久久58 | 国产在线欧美日韩 | 人人添人人澡 | 日韩免费一级电影 | 91福利视频免费观看 | 欧美精品久久久久久久 | 久草在线视频中文 | 狠狠色噜噜狠狠狠合久 | 六月婷婷久香在线视频 | 日韩av区 | 久久久久一区 | 日韩在线免费高清视频 | 精品视频久久久 | 亚洲国产精品成人综合 | 久久国产视频网站 | 天天av天天 | 亚洲精品字幕 | 麻豆精品在线 | 手机av在线免费观看 | 精品久久一区二区三区 | 日韩免费精品 | 五月天丁香亚洲 | 国产日韩精品一区二区三区 | 国模精品在线 | 中国一级片免费看 | 亚洲片在线资源 | 黄色一及电影 | 久久黄网站 | 97色婷婷成人综合在线观看 | 国产第一页在线观看 | 粉嫩av一区二区三区四区在线观看 | 日韩欧美一区二区三区视频 | av色综合| av电影 一区二区 | 免费看的黄色的网站 | 黄色成人在线观看 | 天天玩天天干天天操 | 亚洲欧美日韩在线看 | 久久久久久久看片 | 午夜国产一区二区三区四区 | 日韩免费高清在线 | 欧美男同视频网站 | 久久久久成 | 中文字幕精品视频 | 精品中文字幕在线观看 | 久久精品日产第一区二区三区乱码 | 成年人看片网站 | 免费看的黄色录像 | 免费情趣视频 | 久久97超碰 | 日韩一区二区久久 | 婷婷网址 | 97超碰人人干 | 成人在线视频在线观看 | 三级黄色大片在线观看 | 久久人人艹 | 精品亚洲国产视频 | av不卡免费看 | 久久成人国产精品免费软件 | 在线观看91精品国产网站 | 免费观看www7722午夜电影 | 国产欧美精品在线观看 | 日本韩国精品一区二区在线观看 | 成人91av| 麻豆av电影 | 夜夜高潮夜夜爽国产伦精品 | 久久久久久久久久久免费视频 | 九九精品久久久 | 免费国产亚洲视频 | 在线导航福利 | 亚洲激情久久 | 91桃色国产在线播放 | 日韩在线电影观看 | 日日躁夜夜躁xxxxaaaa | 一区二区中文字幕在线观看 | 天天操天天怕 | 亚洲成人精品国产 | 欧美午夜久久 | 婷婷丁香花五月天 | 日韩美女av在线 | 精品久久久国产 | 久久另类视频 | 国产成人一区二区在线观看 | 天天综合视频在线观看 | 天天射射天天 | 久久成年人 | 欧美日韩另类在线 | 欧美色精品天天在线观看视频 | 免费看色的网站 | 在线精品观看国产 | 久久露脸国产精品 | 欧美日韩在线视频观看 | 色综合中文综合网 | 亚洲国产大片 | 国外成人在线视频网站 | 天天操天天色综合 | 色噜噜狠狠狠狠色综合 | av一本久道久久波多野结衣 | 天天干天天拍天天操天天拍 | 91色一区二区三区 | 天天插一插 | 深夜免费福利视频 | 国产成人在线精品 | 精品国产中文字幕 | 四虎成人在线 | 日日爽日日操 | 97超碰人人澡人人爱 | 在线视频电影 | 日韩欧美国产视频 | 色综合久久久久久久久五月 | 九九久久久久久久久激情 | 日日夜夜天天操 | 国产精品欧美久久久久天天影视 | 99精品成人 | 久久久www成人免费精品 | 亚洲国产成人精品久久 | 国产视频 久久久 | 国产不卡一 | 人人玩人人添人人 | 日韩高清在线一区 | 中文字幕日本电影 | 亚洲成人免费在线观看 | 国产精品亚洲视频 | 正在播放一区 | 粉嫩av一区二区三区四区 | 免费久久网 | 在线成人高清电影 | 久久999精品 | 国产高清中文字幕 | 久久综合欧美 | 久草在线看片 | 国产综合久久 | 在线免费观看欧美日韩 | 在线观看免费黄视频 | 粉嫩一二三区 | 99精品视频免费 | 午夜av大片 | 久久久精品网 | 99久久精品久久久久久动态片 | 午夜性生活 | 婷婷 中文字幕 | 欧洲一区二区三区精品 | av成人免费在线看 | 国产精品地址 | 日韩三级免费观看 | 久久综合之合合综合久久 | 人人添人人澡人人澡人人人爽 | 毛片在线网 | 亚洲影院国产 | 丁香婷婷网 | 国产日韩精品一区二区三区 | 中文字幕欧美激情 | 色夜视频 | 国产一级精品绿帽视频 | 亚洲精品视频在线免费播放 | 精品久久久免费视频 | 久久另类小说 | 国产成人av福利 | 伊人激情综合 | 久久99网| 国产综合激情 | 97精品在线 | 五月婷香 | 亚洲天天在线日亚洲洲精 | 91激情 | 久久亚洲人 | 精品自拍sae8—视频 | 日韩网页 | 2021av在线| 日韩在线视频看看 | 久久99亚洲网美利坚合众国 | 麻豆观看 | 日韩免费观看一区二区三区 | av一区二区在线观看中文字幕 | 国产剧情在线一区 | 日韩综合色 | 色综合久久久久久久久五月 | 天天干天天做 | 91久久人澡人人添人人爽欧美 | 日韩在线观看影院 | 97人人模人人爽人人喊网 | 午夜美女网站 | 精品国产_亚洲人成在线 | 日韩在线视频精品 | 日本黄色免费播放 | 国产一区二区不卡视频 | 国产免费观看视频 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产又粗又猛又黄又爽视频 | 91久草视频 | 伊人热| a色网站| 久久人人爽人人人人片 | 午夜在线免费观看视频 | 国产午夜精品免费一区二区三区视频 | 久久乱码卡一卡2卡三卡四 五月婷婷久 | 国产成人性色生活片 | 亚洲每日更新 | 国产免费xvideos视频入口 | 欧美日韩精品区 | 日韩a级黄色片 | 97视频在线免费播放 | 午夜精品一区二区三区免费视频 | 天天天天天天天操 | 黄色小说18 | 奇米影视8888在线观看大全免费 | 国产免费又爽又刺激在线观看 | 免费看成人 | 美女福利视频在线 | 色婷婷综合久久久中文字幕 | 精品国产成人在线 | 欧美激情一区不卡 | 岛国精品一区二区 | 日韩精品一区二区三区在线播放 | 在线视频 91| 黄色软件视频网站 | 亚洲精品乱码久久久久久高潮 | 狠狠干干| 国产亚洲精品久久久久久 | 色偷偷人人澡久久超碰69 | 四虎www com| 亚洲黄色小说网 | 日日夜夜网 | 五月婷婷在线播放 | 国产a国产a国产a | 激情视频免费在线观看 | 日日天天干 | 久久久久国产精品免费 | 亚洲免费在线视频 | 亚洲三级网 | 国产精品99久久久久久大便 | 中文字幕一区二区三区四区久久 | 日韩电影一区二区在线 | 国产999久久久 | 中文字幕在线一二 | 亚洲电影黄色 | 五月综合婷 | 婷婷丁香激情网 | 午夜三级毛片 | 久久国产精品免费一区 | 国产精品18久久久久白浆 | 国产专区日韩专区 | 91九色在线 | 亚洲综合视频在线 | 国产一区 在线播放 | 98福利在线| 国产精品久久久久一区 | 亚洲高清视频一区二区三区 | 91传媒激情理伦片 | 99精品热视频 | 在线黄色免费 | 久草资源在线观看 | 日韩免费电影网站 | 一区 二区 精品 | 日本少妇高清做爰视频 | 国产精品免费不卡 | 国产在线观看国语版免费 | 国产精品久久二区 | 最近日本中文字幕a | 国产色综合天天综合网 | 一区二区三区日韩精品 | 色婷婷97| 成人免费网站视频 | 九九热视频在线免费观看 | 国产成人免费在线观看 | 在线观看国产中文字幕 | 99在线观看 | 丰满少妇在线观看 | 国产免费高清视频 | 中文字幕在线第一页 | 天天干天天操天天入 | 午夜av激情 | 激情综合色综合久久综合 | 操老逼免费视频 | 久久精品最新 | 精品成人久久 | 国模视频一区二区 | 人人插人人干 | 日韩亚洲在线 | 国产精品久久嫩一区二区免费 | 国产精品免费久久 | 日韩av中文在线 | 香蕉视频在线网站 | 国产人免费人成免费视频 | 五月婷婷中文 | 国产高清视频在线观看 | 日韩欧美精品在线视频 | 国产999精品久久久久久麻豆 | 激情www| 中文字幕在线观看视频免费 | 久久99在线视频 | 亚洲欧美综合精品久久成人 | 免费碰碰 | av三级在线看 | 日韩中文免费视频 | 久久99精品热在线观看 | 天天艹天天 | 中文字幕一区二区三区在线观看 | 久久免费在线观看视频 | 日韩电影一区二区三区 | 69久久夜色精品国产69 | 欧美婷婷色 | 欧美精品v国产精品 | www久久精品 | 久久久久99999 | 成人av片在线观看 | 国产婷婷在线观看 | 精品一区久久 | 丁香婷婷激情 | 日本中文字幕在线看 | 婷婷新五月 | 91黄色成人 | 国产99久久九九精品免费 | 久久久久福利视频 | 国产日韩欧美在线观看 | 久久久久久毛片精品免费不卡 | 精壮的侍卫呻吟h | 欧美成人h版电影 | 69欧美视频 | 国产麻豆精品久久一二三 | 国产成人精品av在线 | 国产精品18久久久久久久久久久久 | 91精品国产一区二区在线观看 | 91在线入口 | av电影不卡在线 | 国产美女久久 | 午夜精品电影 | 中文字幕在线看视频国产 | 91av资源网 | 欧美成人精品xxx | 91精品国产乱码在线观看 | 午夜精品一区二区三区在线 | 成人国产精品电影 | 69av在线视频| 手机av在线网站 | 国产又粗又硬又长又爽的视频 | 超碰个人在线 | www.天天草| 国产精品久久久久久久久久久久午夜 | 成人黄色片在线播放 | 久久久免费播放 | 久久精品看 | 国产1区在线观看 | 久久男人中文字幕资源站 | 日韩美在线 | 亚洲码国产日韩欧美高潮在线播放 | 91福利视频网站 | 亚洲视频一级 | 四虎影视8848dvd | 亚州国产精品 | 亚洲国产成人在线 | 久久99国产精品免费网站 | 久久精品久久99精品久久 | 日韩精品久久久 | 免费看一级片 | 精品久久久精品 | 青青河边草免费直播 | 精品久久久久久一区二区里番 | 99精品免费久久久久久久久日本 | 国产在线精品一区二区 | 天天夜夜操 | 激情av资源网| 国产美女免费看 | 久久九九精品 | 日韩视频一区二区三区在线播放免费观看 | 国产精品久久久久久久99 | 欧美精品久久久久久久久久白贞 | 成人小视频在线 | 超碰在线日本 | 中文字幕电影在线 | 丁香午夜婷婷 | 久久影视一区 | 国产午夜av | 欧美一区日韩精品 | 在线观看爱爱视频 | 国产精品嫩草影院123 | 日本黄色免费在线 | 日韩美女黄色片 | 日韩伦理片一区二区三区 | 天天爱天天射 | 中文网丁香综合网 | 亚洲视频分类 | 国产黄色观看 | 国产精品成人国产乱一区 | 亚洲精品乱码久久久久久蜜桃欧美 | 麻豆精品在线 | 欧美日韩另类视频 | 国产精品久久一 | 亚洲欧美视频 | 久久激五月天综合精品 | 欧女人精69xxxxxx | 美女一区网站 | 国产成人在线综合 | 综合色播 | 国产在线国偷精品产拍 | 91av99| 777xxx欧美| 天天插天天爱 | 超碰在线网 | 欧美一级性生活视频 | 亚洲成年人免费网站 | 久久九九国产视频 | 国产日产精品久久久久快鸭 | 精品麻豆入口免费 | 久久久国际精品 | 欧美成人h版 | 久久久www免费电影网 | 99久久久久久久久久 | 99精品国产兔费观看久久99 | 99精品久久99久久久久 | 在线不卡视频 | 日韩在线观看的 | 亚洲精品中文字幕视频 | 成人黄色电影在线观看 | 中文字幕黄色网址 | 久久免费毛片 | 色狠狠久久av五月综合 | 不卡精品视频 | 在线免费观看黄色 | 片网站 | 91探花系列在线播放 | 在线观看免费91 | 日韩精品免费一线在线观看 | 美女久久久久久久久久久 | 99久久久国产精品免费观看 | 西西4444www大胆无视频 | 国产99在线免费 | 91精品啪在线观看国产 | 国产高清中文字幕 | 久久精品国产精品亚洲精品 | 日韩在线视频看看 | 久久久久久久久爱 | 高清一区二区三区 | 精品视频区 | 亚洲国产高清在线 | 成人一级电影在线观看 | 久福利 | 中文字幕在线中文 | 亚洲一级久久 | 久久久久久久综合色一本 | 天天射天天舔天天干 | 国产一区二区免费 | 在线午夜电影神马影院 | 丁香六月激情 | 黄色三级免费看 | 日本丰满少妇免费一区 | 日韩视频一区二区三区 | 亚洲在线精品视频 | 五月婷婷视频 | 日韩欧美xxx| 亚洲综合精品在线 | 91九色精品国产 | 91最新网址在线观看 | 日韩手机在线观看 | 久久综合久久鬼 | 狠狠干在线播放 | 欧美福利视频 | 久草精品视频 | 精品国产视频在线 | 久久视频中文字幕 | 久久99精品久久久久婷婷 | 亚州av一区 | 日日干天天| 97精品在线观看 | 精品视频国产 | 中文字幕国语官网在线视频 | 国产亚洲欧美一区 | 69国产精品成人在线播放 | 欧美亚洲精品一区 | 精品久久久久一区二区国产 | 丁香久久 | 精品亚洲网 | 在线免费观看麻豆 | 91亚洲夫妻 | 亚洲国产精品小视频 | 国产黄免费在线观看 | 欧美激情精品久久久久 | 国产精品二区三区 | 日本一区二区不卡高清 | 在线观看免费版高清版 | 日本一区二区高清不卡 | 久久久久久久久艹 | 四虎国产永久在线精品 | 国产精品久久久777 成人手机在线视频 | 久久久久综合网 | 国产成人精品电影久久久 | 久草国产视频 | 天天干天天做天天操 | 激情网综合 | 在线三级av| 成人app在线免费观看 | 欧美黄色软件 | 中文字幕亚洲欧美 | 国产视频手机在线 | 久久久久免费精品视频 | 国产专区精品视频 | 亚洲一级在线观看 | 婷婷丁香六月天 | 日本九九视频 | 超碰人人草 | 97超碰人| 精品久久中文 | 久草在线91| 欧美性成人 | 国产特级毛片aaaaaa高清 | 手机在线黄色网址 | 国产一级性生活 | 国产精品第一页在线 | 久久伊人操| 在线观看中文字幕视频 | 麻豆一精品传二传媒短视频 | 天天超碰 | 亚洲精品高清视频在线观看 | 亚洲精品1区2区3区 超碰成人网 | 毛片基地黄久久久久久天堂 | 亚洲h色精品| 亚洲丝袜中文 | 中文字幕在线观看三区 | 免费国产在线精品 | 色吊丝在线永久观看最新版本 | 欧美中文字幕久久 | 亚洲闷骚少妇在线观看网站 | 草莓视频在线观看免费观看 | 国产成人精品综合久久久久99 | 中文字幕第一页在线vr | 国产精品久久久久影院 | 亚洲aⅴ乱码精品成人区 | 日本午夜在线亚洲.国产 | av看片在线| 2018好看的中文在线观看 | 欧美一级免费高清 | 国产精品精品国产色婷婷 | 日韩成人在线一区二区 | 欧美精品乱码久久久久久按摩 | 91视频xxxx| 天天爽夜夜爽人人爽曰av | 97天堂网| 日韩在线观看电影 | 日本精品在线看 | 日韩av在线高清 | 国产区网址 | 日韩二三区 | 在线观看亚洲电影 | 国产日韩在线视频 | 日韩激情视频 | 欧美成人亚洲成人 | 91免费高清观看 | 欧美日韩a视频 | 日韩中字在线 | 久久看视频 | 在线观看免费福利 | 黄www在线观看 | 国产专区精品视频 | 综合久久影院 | 噜噜色官网 | 97福利 | 国产二区视频在线观看 | 成人aⅴ视频 | 久久99国产精品 | 亚洲最快最全在线视频 | 国产精品视频内 | 国产69精品久久久久久久久久 | 日韩美在线观看 | 欧美一级视频免费 | 99久久99久久精品 | 午夜久久电影网 | av免费看av | 99久国产 | av短片在线观看 | 国产精品女人久久久 |