WebAssembly 提供 block
指令,它可以建立一個區塊,在區塊中看不到進入區塊前於堆疊中置放的數值,可以想像成在既有的堆疊頂端,建立一個新的堆疊起點,在其中進行指令操作。
因此底下會出錯:
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block
i32.const 2
i32.add
call $log
end
)
(start $main)
)
因為區塊中看不到先前堆疊中已經置入的數值,區塊中在新堆疊起點之後,只置入了一個數值,而 i32.add
必須堆疊中有兩個數值,必須是底下才是正確的:
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block
i32.const 2
i32.const 3
i32.add
call $log
end
call $log
)
(start $main)
)
區塊若沒有定義結果型態,執行完區塊後,堆疊必須為空,否則會發生堆疊不為空的錯誤;區塊可以使用 result
定義回傳值,執行完區塊後堆疊可以留下一個數值,這個數值在離開區塊之後,會置入先前堆疊,可以繼續其他指令操作。例如:
(module
(import "env" "log" (func $log (param i32)))
(func $main
i32.const 1
block (result i32)
i32.const 2
i32.const 3
i32.add
end
i32.add
call $log ;; 顯示 6
)
(start $main)
)
在區塊中可以使用 br
分支(Branch)指令跳出區塊,br
可以接上數字,若數字為 n
,表示流程分支至往外第 n
層區塊的 end
之後開始執行,也就是在分支時,只能分支至外層。例如:
(module
(import "env" "log" (func $log (param i32)))
(func $main
block
block
br 0
i32.const 3
call $log
end
i32.const 2
call $log
end
i32.const 1
call $log
)
(start $main)
)
其中 br 0
表示流程分支至往外第 0 層的 end
,也就是目前區塊的 end
之後,因此顯示 2、1,如果將 br 0
改為 br 1
,表示流程分支至往外第 1 層的 end
,這時就只會顯示 1。
就撰寫上,使用數字並不方便,區塊也可以命名,br
時就可以指定名稱:
(module
(import "env" "log" (func $log (param i32)))
(func $main
block $B0
block $B1
br $B1
i32.const 3
call $log
end
i32.const 2
call $log
end
i32.const 1
call $log
)
(start $main)
)
同樣地,以上會顯示 2、1,若改為 br $B0
則會顯示 1。
在分支指令方面,有個 br_if
可以進行條件分支,br_if
會從堆疊中取出一個數值,若為 0 不做什麼事,若不為 0 就依指定進行分支,因此,可以用來實現一個 unless
(也就是 if
的相反):
(module
(import "env" "log" (func $log (param i32)))
(func $main
block $UNLESS_BLOCK
block $THEN
block $UNLESS
i32.const 0 ;; unless 條件不成立
br_if $THEN
end
;; unless 條件不成立執行的部份
i32.const 10
call $log
br $UNLESS_BLOCK
end
;; unless 條件成立執行的部份
i32.const 20
call $log
end
)
(start $main)
)
在上面的範例中,因為 i32.const 0
,br_if
不進行分支,繼續執行流程到 br $UNLESS_BLOCK
,分支至 $UNLESS_BLOCK
的 end
之後,因此不會執行到 $THEN
的 end
之後的部份,也就是結果顯示 10;若是改為 i32.const 1
,結果就會顯示 20。
在多個區塊形成巢狀的情況下,也許會有複雜的分支流程,這時可以使用 br_table
,它會從堆疊中取出一個數值,依數值決定要分支至哪個被列出的區塊。例如:
(module
(import "env" "log" (func $log (param i32)))
(func $main
(local $n i32)
(set_local $n (i32.const 0))
block $B0
block $B1
block $B2
get_local $n
br_table $B2 $B1 $B0 ;; 依 $n 的值分支
end
i32.const 2
call $log
end
i32.const 1
call $log
end
i32.const 0
call $log
)
(start $main)
)
$n
若為 0,就會分支至第一個列出的 $B2
,若為 1 就分支至 $B1
,依此類推。
若要進行條件判斷,WebAssembly 提供 if..else..end
、loop..end
條件分支,不必如上自行使用 block
來兜,實際上,if..else..end
、loop..end
(以及 func
)都會建立區塊,而 block
可以輔助 if..else..end
、loop..end
等,實現更多樣化的條件分支。