preamble.js 中的 JavaScript API 提供程式化的存取方式,用於與編譯後的 C 程式碼互動,包括:呼叫編譯後的 C 函數、存取記憶體、將指標轉換為 JavaScript Strings
以及將 Strings
轉換為指標 (具有不同的編碼/格式),以及其他便利函數。
我們稱之為「preamble.js
」,因為 Emscripten 的輸出 JS 在較高的層級上,包含前導程式碼 (來自 src/preamble.js
)、編譯後的程式碼,然後是後導程式碼。(更詳細地說,前導程式碼包含工具函數和設定,而後導程式碼則連結各部分並處理應用程式的執行。)
前導程式碼包含在輸出 JS 中,然後由編譯器與您新增的任何 --pre-js
和 --post-js
檔案,以及任何 JavaScript 函式庫 ( --js-library
) 中的程式碼一起進行最佳化。這表示您可以直接呼叫前導程式碼的方法,編譯器會看到您需要它們,而不會將它們移除視為未使用。
如果您想從編譯器看不到的地方呼叫前導程式碼方法,例如 HTML 上的另一個腳本標籤,則需要將它們匯出。若要這麼做,請將它們新增至 EXPORTED_RUNTIME_METHODS
(例如,-sEXPORTED_RUNTIME_METHODS=ccall,cwrap
將匯出 ccall
和 cwrap
)。匯出後,您可以在 Module
物件上存取它們 (例如 Module.ccall
)。
注意
如果您嘗試使用 Module.ccall
或其他未匯出的執行階段方法,則會收到錯誤。在以 -sASSERTIONS
建構的版本中,編譯器會發出程式碼來顯示有用的錯誤訊息,這會說明您需要匯出它。一般而言,如果您看到任何奇怪的事情,最好以斷言來建構。
ccall
(ident, returnType, argTypes, args, opts)¶從 JavaScript 呼叫編譯的 C 函數。
此函數從 JavaScript 執行編譯後的 C 函數,並傳回結果。C++ 名稱修飾表示無法呼叫「正常」的 C++ 函數;該函數必須定義在 .c 檔案中,或是以 extern "C"
定義的 C++ 函數。
returnType
和 argTypes
可讓您指定參數和傳回值的類型。可能的類型為 "number"
、"string"
、"array"
或 "boolean"
,對應到適當的 JavaScript 類型。針對任何數值類型或 C 指標,使用 "number"
,針對表示字串的 C char*
使用 string
,針對布林類型使用 "boolean"
,針對包含 8 位元整數資料的 JavaScript 陣列和類型化陣列使用 "array"
,也就是說,資料會寫入 8 位元整數的 C 陣列中;特別是如果您在此處提供類型化陣列,則它必須是 Uint8Array 或 Int8Array。如果您想接收其他資料類型的陣列,您可以手動配置記憶體並寫入,然後在此處提供指標 (作為 "number"
,因為指標只是數字)。
// Call C from JavaScript
var result = Module.ccall('c_add', // name of C function
'number', // return type
['number', 'number'], // argument types
[10, 20]); // arguments
// result is 30
注意
ccall
使用 C 堆疊來儲存暫時值。如果您傳遞字串,則只有在呼叫完成之前才「存活」。如果正在呼叫的程式碼儲存指標以供稍後使用,則它可能會指向無效的資料。
如果您需要字串永久存在,您可以使用 _malloc
和 stringToUTF8()
建立字串。但是,您稍後必須手動刪除它!
LLVM 最佳化可以內嵌和移除函數,之後您將無法呼叫它們。同樣地,由 *Closure Compiler* 縮小化的函數名稱是無法存取的。在任一種情況下,解決方案是在您叫用 *emcc* 時,將函數新增至 EXPORTED_FUNCTIONS
清單
-sEXPORTED_FUNCTIONS=_main,_myfunc"
(請注意,我們也會匯出 main
- 如果我們不這麼做,編譯器會假設我們不需要它。) 匯出的函數接著可以正常呼叫
a_result = Module.ccall('myfunc', 'number', ['number'], [10])
ident – 要呼叫的 C 函數的名稱。
returnType – 函數的傳回類型。請注意,不支援 array
,因為我們無法得知陣列的長度。對於 void 函數,這可以是 null
(請注意:JavaScript null
值,而不是包含單字「null」的字串)。
注意
64 位元整數會變成兩個 32 位元參數,分別用於低位元和高位元 (因為 64 位元整數無法以 JavaScript 數字表示)。
argTypes – 函數的引數類型陣列 (如果沒有引數,則可以省略此陣列)。
args – 函數的引數陣列,作為原生 JavaScript 值 (如同 returnType
)。請注意,字串引數會儲存在堆疊上 (JavaScript 字串會變成堆疊上的 C 字串)。
函數呼叫的結果,作為原生 JavaScript 值 (如同 returnType
),或者,如果設定了 async
選項,則為結果的 JavaScript Promise。
一個可選的選項物件。它可以包含下列屬性
async
:如果為 true
,則表示 ccall 將執行非同步操作。這假設您是以 asyncify 支援來建構。
注意
非同步呼叫目前不支援 Promise 錯誤處理。
cwrap
(ident, returnType, argTypes)¶傳回 C 函數的原生 JavaScript 包裝函式。
這類似於 ccall()
,但傳回一個 JavaScript 函數,可以根據需要重複使用多次。C 函數可以定義在 C 檔案中,或是以 extern "C"
定義的 C 相容 C++ 函數 (以防止名稱修飾)。
// Call C from JavaScript
var c_javascript_add = Module.cwrap('c_add', // name of C function
'number', // return type
['number', 'number']); // argument types
// Call c_javascript_add normally
console.log(c_javascript_add(10, 20)); // 30
console.log(c_javascript_add(20, 30)); // 50
注意
cwrap
使用 C 堆疊來儲存臨時值。如果您傳遞一個字串,那麼它只會在呼叫完成前「存活」。如果被呼叫的程式碼儲存該指標以便稍後使用,它可能會指向無效的資料。如果您需要一個永遠存在的字串,您可以使用 _malloc
和 stringToUTF8()
等方式建立它。但是,您必須稍後手動刪除它!
若要包裝函式,必須在呼叫 emcc 時將其新增至 EXPORTED_FUNCTIONS
清單中。如果函式未匯出,最佳化可能會移除它,而且 cwrap
將無法在執行時找到它。(在啟用 ASSERTIONS
的建置中,如果發生這種情況,cwrap
將會顯示錯誤;在沒有斷言的發行版本中,嘗試包裝不存在的函式將會產生錯誤,可能會傳回 undefined,或是傳回一個實際呼叫時會發生錯誤的函式,這取決於 cwrap
如何最佳化。)
cwrap
實際上並不會呼叫編譯過的程式碼(只有呼叫它傳回的包裝器才會這麼做)。這表示可以提早呼叫 cwrap
,在執行階段完全初始化之前(當然,呼叫傳回的包裝函式必須等待執行階段,就像一般呼叫編譯過的程式碼一樣)。
-sEXPORTED_FUNCTIONS=_main,_myfunc
匯出的函式可以像平常一樣呼叫
my_func = Module.cwrap('myfunc', 'number', ['number'])
my_func(12)
ident – 要呼叫的 C 函數的名稱。
returnType – 函式的傳回類型。可以是 "number"
、"string"
或 "array"
,它們對應到適當的 JavaScript 類型(任何 C 指標都使用 "number"
,而 JavaScript 陣列和類型陣列則使用 "array"
;請注意,陣列是 8 位元的),或是對於 void 函式,它可以是 null
(注意:是 JavaScript 的 null
值,而不是包含單字「null」的字串)。
argTypes – 函式引數的類型陣列(如果沒有引數,則可以省略)。類型與 returnType
中的相同,除了不支援 array
,因為我們無法知道陣列的長度)。
opts – 一個可選的選項物件,請參閱 ccall()
。
一個可以用於執行 C 函式的 JavaScript 函式。
setValue
(ptr, value, type[, noSafe])¶在執行時設定特定記憶體位址的值。
注意
setValue()
和 getValue()
只會執行對齊的寫入和讀取。
type
是一個 LLVM IR 類型(i8
、i16
、i32
、i64
、float
、double
或指標類型,例如 i8*
或僅 *
),而不是 ccall()
或 cwrap()
中使用的 JavaScript 類型。這是一個較低層級的操作,我們需要關心所使用的特定類型。
ptr – 一個代表記憶體位址的指標(數字)。
value – 要儲存的值
type – 以字串形式表示的 LLVM IR 類型(請參閱上方的「注意」)。
noSafe (bool) – 開發人員應忽略此變數。它僅在 SAFE_HEAP
編譯模式中使用,在此模式下,它可以幫助避免在某些特殊使用案例中產生無限遞迴。
getValue
(ptr, type[, noSafe])¶在執行時取得特定記憶體位址的值。
注意
setValue()
和 getValue()
只會執行對齊的寫入和讀取!
type
是一個 LLVM IR 類型(i8
、i16
、i32
、i64
、float
、double
或指標類型,例如 i8*
或僅 *
),而不是 ccall()
或 cwrap()
中使用的 JavaScript 類型。這是一個較低層級的操作,我們需要關心所使用的特定類型。
ptr – 一個代表記憶體位址的指標(數字)。
type – 以字串形式表示的 LLVM IR 類型(請參閱上方的「注意」)。
noSafe (bool) – 開發人員應忽略此變數。它僅在 SAFE_HEAP
編譯模式中使用,在此模式下,它可以幫助避免在某些特殊使用案例中產生無限遞迴。
儲存在指定記憶體位址的值。
UTF8ToString
(ptr[, maxBytesToRead])¶給定一個指向 Emscripten HEAP 中以 null 結尾的 UTF8 編碼字串的指標 ptr
,會傳回該字串的副本,作為 JavaScript String
物件。
ptr – 指向 Emscripten HEAP 中以 null 結尾的 UTF8 編碼字串的指標。
maxBytesToRead – 一個可選的長度,指定要讀取的最大位元組數。您可以省略此參數,以掃描字串直到第一個 0 位元組。如果傳遞了 maxBytesToRead,且 [ptr, ptr+maxBytesToReadr)
中的字串在中間包含 null 位元組,則字串將在該位元組索引處截斷(即 maxBytesToRead 不會產生長度精確為 [ptr, ptr+maxBytesToRead)
的字串)。注意:頻繁使用具有和不具有 maxBytesToRead 的 UTF8ToString()
可能會使 JS JIT 最佳化失效,因此值得考慮一致地使用一種或另一種樣式。
一個 JavaScript String
物件
stringToUTF8
(str, outPtr, maxBytesToWrite)¶將指定的 JavaScript String
物件 str
複製到 Emscripten HEAP 中位址為 outPtr
的位置,以 null 結尾並以 UTF8 格式編碼。
此複製最多需要在 HEAP 中佔用 str.length*4+1
個位元組的空間。您可以使用 lengthBytesUTF8()
函式來計算編碼字串所需(不包含 null 終止符)的確切位元組數。
str (String) – 一個 JavaScript String
物件。
outPtr – 指向從 str
複製的資料的指標,以 UTF8 格式編碼並以 null 結尾。
maxBytesToWrite – 此函式最多可以寫出的位元組數限制。如果字串長度超過此限制,則輸出將被截斷。輸出的字串將始終以 null 結尾,即使發生截斷,只要 maxBytesToWrite > 0
即可。
UTF16ToString
(ptr)¶給定一個指向 Emscripten HEAP 中以 null 結尾的 UTF16LE 編碼字串的指標 ptr
,會傳回該字串的副本,作為 JavaScript String
物件。
ptr – 指向 Emscripten HEAP 中以 null 結尾的 UTF16LE 編碼字串的指標。
一個 JavaScript String
物件
stringToUTF16
(str, outPtr, maxBytesToWrite)¶將指定的 JavaScript String
物件 str
複製到 Emscripten HEAP 中位址為 outPtr
的位置,以 null 結尾並以 UTF16LE 格式編碼。
此複製將確切需要 HEAP 中 (str.length+1)*2
個位元組的空間。
str (String) – 一個 JavaScript String
物件。
outPtr – 指向從 str
複製的資料的指標,以 UTF16LE 格式編碼並以 null 結尾。
maxBytesToWrite – 此函式最多可以寫出的位元組數限制。如果字串長度超過此限制,則輸出將被截斷。輸出的字串將始終以 null 結尾,即使發生截斷,只要 maxBytesToWrite >= 2
,以便有空間容納 null 終止符。
UTF32ToString
(ptr)¶給定一個指向 Emscripten HEAP 中以 null 結尾的 UTF32LE 編碼字串的指標 ptr
,會傳回該字串的副本,作為 JavaScript String
物件。
ptr – 指向 Emscripten HEAP 中以 null 結尾的 UTF32LE 編碼字串的指標。
一個 JavaScript String
物件。
stringToUTF32
(str, outPtr, maxBytesToWrite)¶將給定的 JavaScript String
物件 str
複製到 Emscripten HEAP 中的 outPtr
位址,並以 UTF32LE 格式編碼,以 null 結尾。
複製時在 HEAP 中最多需要 (str.length+1)*4
個位元組的空間,但可能會使用較少,因為 str.length
不會回傳字串中的字元數,而是回傳字串中的 UTF-16 碼元數。您可以使用 lengthBytesUTF32()
函數來計算編碼字串所需的確切位元組數(不包含 null 終止符)。
str (String) – 一個 JavaScript String
物件。
outPtr – 指向從 str
複製的資料的指標,以 UTF32LE 格式編碼,並以 null 結尾。
maxBytesToWrite – 此函數最多可以寫出的位元組數限制。如果字串長度超過此值,則輸出將被截斷。輸出的字串始終會以 null 結尾,即使發生截斷也是如此,只要 maxBytesToWrite >= 4
,以便有空間容納 null 終止符。
AsciiToString
(ptr)¶將 ASCII 或 Latin-1 編碼的字串轉換為 JavaScript String 物件。
ptr – 要轉換為 String
的指標。
一個 JavaScript String
,包含來自 ptr
的資料。
字串
intArrayFromString
(stringy, dontAddNull[, length])¶此函數將 JavaScript 字串轉換為 C 樣式的數字陣列,並以 0 結尾。
stringy (字串) – 要轉換的字串。
dontAddNull (布林值) – 如果為 true
,則新陣列不會以零結尾。
length – 陣列的長度(可選)。
從 stringy
建立的陣列。
intArrayToString
(array)¶此函數從以零結尾的 C 樣式數字陣列建立 JavaScript 字串。
array – 要轉換的陣列。
一個 String
,包含 array
的內容。
writeArrayToMemory
(array, buffer)¶將陣列寫入堆積中指定的位址。請注意,在寫入陣列之前,應先為其分配記憶體。
array – 要寫入記憶體的陣列。
buffer (數字) – 要寫入 array
的位址(數字)。
請注意,通常執行相依性由檔案封裝器和系統的其他部分管理。開發人員很少直接使用此 API。
addRunDependency
(id)¶將 id
新增至執行相依性清單中。
這會新增一個執行相依性並遞增執行相依性計數器。
id (字串) – 代表操作的任意 ID。
removeRunDependency
(id)¶從執行相依性清單中移除指定的 id
。
id (字串) – 要移除的特定相依性的識別符(使用 addRunDependency()
新增)。
stackTrace
()¶回傳目前的堆疊追蹤。
注意
至少在 IE10 和 Safari 6 上無法使用堆疊追蹤。
目前的堆疊追蹤(如果有的話)。
Emscripten 記憶體表示法 使用類型化陣列緩衝區 (ArrayBuffer
) 來表示記憶體,透過不同的檢視可以存取不同的類型。以下列出了存取不同類型記憶體的檢視。
HEAP8
¶8 位元有符號記憶體的檢視。
HEAP16
¶16 位元有符號記憶體的檢視。
HEAP32
¶32 位元有符號記憶體的檢視。
HEAPU8
¶8 位元無符號記憶體的檢視。
HEAPU16
¶16 位元無符號記憶體的檢視。
HEAPU32
¶32 位元無符號記憶體的檢視。
HEAPF32
¶32 位元浮點數記憶體的檢視。
HEAPF64
¶64 位元浮點數記憶體的檢視。