├── test ├── test_helper.exs ├── wasm_values_test.exs └── wasm_modules_test.exs ├── .gitignore ├── mix.exs ├── mix.lock ├── LICENSE ├── README.md └── lib └── wasm.ex /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /cover 3 | /deps 4 | erl_crash.dump 5 | *.ez 6 | doc -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Wasm.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :wasm, 6 | description: "WASM compiler for Elixir", 7 | version: "0.2.0", 8 | elixir: "~> 1.1-dev", 9 | build_embedded: Mix.env == :prod, 10 | start_permanent: Mix.env == :prod, 11 | source_url: "https://github.com/jamen/elixir-wasm", 12 | deps: deps(), 13 | package: package() 14 | ] 15 | end 16 | 17 | defp deps do 18 | [{:ex_doc, "~> 0.19", only: :dev}] 19 | end 20 | 21 | defp package do 22 | [name: :wasm, 23 | maintainers: ["Jamen Marz (https://git.io/jamen)"], 24 | licenses: ["MIT"], 25 | links: %{ "GitHub" => "https://github.com/jamen/elixir-wasm" }] 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/wasm_values_test.exs: -------------------------------------------------------------------------------- 1 | defmodule WasmValueTest do 2 | use ExUnit.Case 3 | 4 | test "unsigned leb128 encoding" do 5 | assert(Wasm.encode_integer({:u32, 10}) == <<10>>) 6 | assert(Wasm.encode_integer({:u32, 320}) == <<192, 2>>) 7 | assert(Wasm.encode_integer({:u32, 9_019_283_812_387}) == <<163, 224, 212, 185, 191, 134, 2>>) 8 | end 9 | 10 | test "signed leb128 encoding" do 11 | assert(Wasm.encode_integer({:s32, 10}) == <<10>>) 12 | assert(Wasm.encode_integer({:s32, 320}) == <<192, 2>>) 13 | assert(Wasm.encode_integer({:s32, 9_019_283_812_387}) == <<163, 224, 212, 185, 191, 134, 2>>) 14 | assert(Wasm.encode_integer({:s32, -10}) == <<118>>) 15 | assert(Wasm.encode_integer({:s32, -320}) == <<192, 125>>) 16 | 17 | assert( 18 | Wasm.encode_integer({:s32, -9_019_283_812_387}) == <<221, 159, 171, 198, 192, 249, 125>> 19 | ) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"}, 3 | "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jamen Marz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # elixir-wasm 3 | 4 | Functions and types for encoding WebAssembly. 5 | 6 | For more information, see the [WebAssembly spec](https://github.com/WebAssembly/spec), the [Binary section](http://webassembly.github.io/spec/core/bikeshed/index.html#binary-format%E2%91%A0), and the [types documented for this module](https://hexdocs.pm/wasm/Wasm.html). 7 | 8 | ## Scope 9 | 10 | This module **does not compile Elixir to WebAssembly**, it lets Elixir encode a WebAssembly module using tuples of instructions. 11 | 12 | Please see [ElixirScript](https://github.com/elixirscript/elixirscript), where Elixir [will eventually](https://github.com/elixirscript/elixirscript/issues/454) compile to WebAssembly using this module. 13 | 14 | ## Documentation 15 | 16 | See the [Hexdocs](https://hexdocs.pm/wasm). 17 | 18 | ## Testing 19 | 20 | The tests compare Elixir-compiled WASM and WAT-compiled WASM using the command `wat2wasm` (from the [WebAssembly Binary Toolkit](https://github.com/WebAssembly/wabt)), so this needs to be installed or else the tests will fail. 21 | 22 | After the tests, you can inspect the binaries at `_build/test/*.wasm` with `wasm2wat`, `hexdump`, etc. -------------------------------------------------------------------------------- /test/wasm_modules_test.exs: -------------------------------------------------------------------------------- 1 | defmodule WasmModulesTest do 2 | use ExUnit.Case 3 | 4 | test "simple module" do 5 | assert_modules_equal( 6 | {:module, 7 | [ 8 | type_sec: [ 9 | {:func_type, [], [:i32]} 10 | ], 11 | func_sec: [ 12 | {:type_index, 0} 13 | ], 14 | export_sec: [ 15 | {:export, {:name, "test"}, {:func_index, 0}} 16 | ], 17 | code_sec: [ 18 | {:code, {:func, [], {:expr, [{:i32_const, 1}, :return]}}} 19 | ] 20 | ]}, 21 | """ 22 | (module 23 | (func (export "test") (result i32) 24 | (i32.const 1) 25 | return 26 | ) 27 | ) 28 | """ 29 | ) 30 | end 31 | 32 | defp assert_modules_equal(wasm, wat) do 33 | id = :crypto.hash(:sha224, wat) |> Base.url_encode64(padding: false) 34 | wat_input = Path.join([Mix.Project.build_path(), "wat-" <> id <> ".wat"]) 35 | wat_output = Path.join([Mix.Project.build_path(), "wat-" <> id <> ".wasm"]) 36 | elixir_output = Path.join([Mix.Project.build_path(), "elixir-" <> id <> ".wasm"]) 37 | File.write!(wat_input, wat) 38 | 39 | case System.cmd("wat2wasm", [wat_input, "-o", wat_output]) do 40 | {_msg, 0} -> 41 | wat_binary = File.read!(wat_output) 42 | elixir_binary = Wasm.encode(wasm) 43 | File.write!(elixir_output, elixir_binary) 44 | assert(wat_binary == elixir_binary) 45 | 46 | {msg, code} -> 47 | {:error, code, msg} 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/wasm.ex: -------------------------------------------------------------------------------- 1 | defmodule Wasm do 2 | import Enum, only: [map_join: 2] 3 | import Bitwise 4 | 5 | @moduledoc """ 6 | Functions and types for encoding WebAssembly. 7 | 8 | For more information, see the [WebAssembly spec](https://github.com/WebAssembly/spec), the [Binary section](http://webassembly.github.io/spec/core/bikeshed/index.html#binary-format%E2%91%A0), and the [types documented for this module](https://hexdocs.pm/wasm/Wasm.html). 9 | 10 | ## Scope 11 | 12 | This module **does not compile Elixir to WebAssembly**, it allows Elixir to encode a WebAssembly module, using tuples that resemble WebAssembly. It would be a building block to accomplishing the an Elixir-to-WASM compiler. 13 | 14 | Please see [ElixirScript](https://github.com/elixirscript/elixirscript), where Elixir [will eventually](https://github.com/elixirscript/elixirscript/issues/454) compile to WebAssembly. 15 | """ 16 | 17 | @magic <<0x00, 0x61, 0x73, 0x6D>> 18 | @version <<0x01, 0x00, 0x00, 0x00>> 19 | 20 | @type wasm_module :: {:module, [wasm_section]} 21 | @spec encode(wasm_module) :: binary 22 | 23 | def encode({:module, sections} = wasm_module) do 24 | @magic <> @version <> map_join(sections, &encode_section/1) 25 | end 26 | 27 | @type wasm_integer :: 28 | {:u32, non_neg_integer} 29 | | {:u64, non_neg_integer} 30 | | {:s32, integer} 31 | | {:s64, integer} 32 | @spec encode_integer(wasm_integer) :: binary 33 | 34 | # https://webassembly.github.io/spec/core/bikeshed/index.html#integers 35 | defp encode_integer({name, value}) do 36 | case name do 37 | :u32 -> leb128(value, 0, <<>>, 128) 38 | :u64 -> leb128(value, 0, <<>>, 128) 39 | :s32 -> leb128(value, 0, <<>>, 64) 40 | :s64 -> leb128(value, 0, <<>>, 64) 41 | :i32 -> leb128(value, 0, <<>>, 64) 42 | :i64 -> leb128(value, 0, <<>>, 64) 43 | end 44 | end 45 | 46 | # The LEB128 encoder inspired from [funbox/eleb128](https://github.com/funbox/eleb128/blob/7aadf28a239d2f5bdee431e407a7f43dcdbf4b5f/src/eleb128.erl) and ["LEB128" on Wikipedia](https://en.wikipedia.org/wiki/LEB128). 47 | defp leb128(value, shift, acc, max) when -max <= value >>> shift and value >>> shift < max do 48 | acc <> <<0::1, value >>> shift::7>> 49 | end 50 | 51 | defp leb128(value, shift, acc, max) do 52 | leb128(value, shift + 7, acc <> <<1::1, value >>> shift::7>>, max) 53 | end 54 | 55 | @type wasm_float :: {:f32, float} | {:f64, float} 56 | @spec encode_float(wasm_float) :: binary 57 | 58 | # http://webassembly.github.io/spec/core/bikeshed/index.html#floating-point%E2%91%A0 59 | defp encode_float({name, value}) do 60 | case name do 61 | :f32 -> <> 62 | :f64 -> <> 63 | end 64 | end 65 | 66 | @type wasm_name :: {:name, String.t()} 67 | @spec encode_name(wasm_name) :: binary 68 | 69 | # http://webassembly.github.io/spec/core/bikeshed/index.html#names 70 | defp encode_name({:name, name}) do 71 | encode_integer({:u32, byte_size(name)}) <> name 72 | end 73 | 74 | @type wasm_value_type :: :i32 | :i64 | :f32 | :f64 75 | @spec encode_value_type(wasm_value_type) :: binary 76 | 77 | # http://webassembly.github.io/spec/core/bikeshed/index.html#value-types 78 | defp encode_value_type(name) do 79 | case name do 80 | :i32 -> <<0x7F>> 81 | :i64 -> <<0xFE>> 82 | :f32 -> <<0x7D>> 83 | :f64 -> <<0x7C>> 84 | end 85 | end 86 | 87 | @type wasm_result_type :: {:result, [wasm_value_type]} 88 | @spec encode_result_type(wasm_result_type) :: binary 89 | 90 | # http://webassembly.github.io/spec/core/bikeshed/index.html#result-types 91 | defp encode_result_type({:result_type, value}) do 92 | case value do 93 | [] -> <<0x40>> 94 | [type] -> encode_value_type(type) 95 | end 96 | end 97 | 98 | @type wasm_func_type :: {:func_type, [wasm_value_type], [wasm_value_type]} 99 | @spec encode_func_type(wasm_func_type) :: binary 100 | 101 | # http://webassembly.github.io/spec/core/bikeshed/index.html#function-types 102 | defp encode_func_type({:func_type, param_types, result_types}) do 103 | <<0x60>> <> 104 | encode_vec(param_types, &encode_value_type/1) <> 105 | encode_vec(result_types, &encode_value_type/1) 106 | end 107 | 108 | @type wasm_limits :: {:limits, non_neg_integer} | {:limits, non_neg_integer, non_neg_integer} 109 | @spec encode_limits(wasm_limits) :: binary 110 | 111 | # http://webassembly.github.io/spec/core/bikeshed/index.html#limits 112 | defp encode_limits({:limits, min, max}) do 113 | <<0x01>> <> encode_integer({:u32, min}) <> encode_integer({:u32, max}) 114 | end 115 | 116 | defp encode_limits({:limits, min}) do 117 | <<0x00>> <> encode_integer({:u32, min}) 118 | end 119 | 120 | @type wasm_mem_type :: {:mem_type, [wasm_limits]} 121 | @spec encode_mem_type(wasm_mem_type) :: binary 122 | 123 | # http://webassembly.github.io/spec/core/bikeshed/index.html#memory-types 124 | defp encode_mem_type({:mem_type, limits}) do 125 | encode_limits(limits) 126 | end 127 | 128 | @type wasm_table_type :: {:table_type, wasm_elem_type, wasm_limits} 129 | @spec encode_table_type(wasm_table_type) :: binary 130 | 131 | # http://webassembly.github.io/spec/core/bikeshed/index.html#table-types 132 | defp encode_table_type({:table_type, elemtype, limits}) do 133 | encode_elem_type(elemtype) <> encode_limits(limits) 134 | end 135 | 136 | @type wasm_elem_type :: :elem_type 137 | @spec encode_elem_type(wasm_elem_type) :: binary 138 | 139 | # http://webassembly.github.io/spec/core/bikeshed/index.html#table-types 140 | defp encode_elem_type(:elem_type) do 141 | <<0x70>> 142 | end 143 | 144 | @type wasm_global_type :: {:global_type, :const | :var, wasm_value_type} 145 | @spec encode_global_type(wasm_global_type) :: binary 146 | 147 | # http://webassembly.github.io/spec/core/bikeshed/index.html#global-types 148 | defp encode_global_type({:global_type, :const, valtype}) do 149 | <<0x00>> <> encode_value_type(valtype) 150 | end 151 | 152 | defp encode_global_type({:global_type, :var, valtype}) do 153 | <<0x01>> <> encode_value_type(valtype) 154 | end 155 | 156 | @type wasm_instr :: 157 | atom 158 | | {atom, wasm_result_type, [wasm_instr]} 159 | | {atom, wasm_index} 160 | | {atom, [wasm_index], wasm_index} 161 | | {atom, wasm_integer, wasm_integer} 162 | | {atom, integer} 163 | | {atom, [wasm_instr]} 164 | @spec encode_instr(wasm_instr) :: binary 165 | 166 | # http://webassembly.github.io/spec/core/binary/instructions.html 167 | defp encode_instr(instr) do 168 | case instr do 169 | # Control instructions. [Spec.](http://webassembly.github.io/spec/core/bikeshed/index.html#control-instructions) 170 | :unreachable -> 171 | <<0x00>> 172 | 173 | :nop -> 174 | <<0x01>> 175 | 176 | {:block, result_type, instrs} -> 177 | <<0x02>> <> encode_result_type(result_type) <> map_join(instrs, &encode_instr/1) 178 | 179 | {:loop, result_type, instrs} -> 180 | <<0x03>> <> encode_result_type(result_type) <> map_join(instrs, &encode_instr/1) 181 | 182 | {:if, result_type, instrs} -> 183 | <<0x04>> <> encode_result_type(result_type) <> map_join(instrs, &encode_instr/1) 184 | 185 | {:if, result_type, consequent, alternate} -> 186 | <<0x04>> <> 187 | encode_result_type(result_type) <> 188 | map_join(consequent, &encode_instr/1) <> 189 | <<0x05>> <> map_join(alternate, &encode_instr/1) <> <<0x0B>> 190 | 191 | {:br, label_index} -> 192 | <<0x0C>> <> encode_index(label_index) 193 | 194 | {:br_if, label_index} -> 195 | <<0x0D>> <> encode_index(label_index) 196 | 197 | {:br_table, label_indices, label_index} -> 198 | <<0x0E>> <> map_join(label_indices, &encode_index/1) <> encode_index(label_index) 199 | 200 | :return -> 201 | <<0x0F>> 202 | 203 | {:call, func_index} -> 204 | <<0x10>> <> encode_index(func_index) 205 | 206 | {:call_indirect, type_index} -> 207 | <<0x11>> <> encode_index(type_index) 208 | 209 | # Parameteric instructions. [Spec.](http://webassembly.github.io/spec/core/bikeshed/index.html#parametric-instructions) 210 | :drop -> 211 | <<0x1A>> 212 | 213 | :select -> 214 | <<0x1B>> 215 | 216 | # Variable instructions. [Spec.](http://webassembly.github.io/spec/core/bikeshed/index.html#variable-instructions) 217 | {:get_local, local_index} -> 218 | <<0x20>> <> encode_index(local_index) 219 | 220 | {:set_local, local_index} -> 221 | <<0x21>> <> encode_index(local_index) 222 | 223 | {:tee_local, local_index} -> 224 | <<0x22>> <> encode_index(local_index) 225 | 226 | {:get_global, global_index} -> 227 | <<0x23>> <> encode_index(global_index) 228 | 229 | {:set_global, global_index} -> 230 | <<0x24>> <> encode_index(global_index) 231 | 232 | # Memory instructions. [Spec.](http://webassembly.github.io/spec/core/bikeshed/index.html#memory-instructions) 233 | {:i32_load, align, offset} -> 234 | mem_instr(<<0x28>>, align, offset) 235 | 236 | {:i64_load, align, offset} -> 237 | mem_instr(<<0x29>>, align, offset) 238 | 239 | {:f32_load, align, offset} -> 240 | mem_instr(<<0x2A>>, align, offset) 241 | 242 | {:f64_load, align, offset} -> 243 | mem_instr(<<0x2B>>, align, offset) 244 | 245 | {:i32_load8_s, align, offset} -> 246 | mem_instr(<<0x2C>>, align, offset) 247 | 248 | {:i32_load8_u, align, offset} -> 249 | mem_instr(<<0x2D>>, align, offset) 250 | 251 | {:i32_load16_s, align, offset} -> 252 | mem_instr(<<0x2E>>, align, offset) 253 | 254 | {:i32_load16_u, align, offset} -> 255 | mem_instr(<<0x2F>>, align, offset) 256 | 257 | {:i64_load8_s, align, offset} -> 258 | mem_instr(<<0x30>>, align, offset) 259 | 260 | {:i64_load8_u, align, offset} -> 261 | mem_instr(<<0x31>>, align, offset) 262 | 263 | {:i64_load16_s, align, offset} -> 264 | mem_instr(<<0x32>>, align, offset) 265 | 266 | {:i64_load16_u, align, offset} -> 267 | mem_instr(<<0x33>>, align, offset) 268 | 269 | {:i64_load32_s, align, offset} -> 270 | mem_instr(<<0x34>>, align, offset) 271 | 272 | {:i64_load32_u, align, offset} -> 273 | mem_instr(<<0x35>>, align, offset) 274 | 275 | {:i32_store, align, offset} -> 276 | mem_instr(<<0x36>>, align, offset) 277 | 278 | {:i64_store, align, offset} -> 279 | mem_instr(<<0x37>>, align, offset) 280 | 281 | {:f32_store, align, offset} -> 282 | mem_instr(<<0x38>>, align, offset) 283 | 284 | {:f64_store, align, offset} -> 285 | mem_instr(<<0x39>>, align, offset) 286 | 287 | {:i32_store8, align, offset} -> 288 | mem_instr(<<0x3A>>, align, offset) 289 | 290 | {:i32_store16, align, offset} -> 291 | mem_instr(<<0x3B>>, align, offset) 292 | 293 | {:i64_store8, align, offset} -> 294 | mem_instr(<<0x3C>>, align, offset) 295 | 296 | {:i64_store16, align, offset} -> 297 | mem_instr(<<0x3D>>, align, offset) 298 | 299 | {:i64_store32, align, offset} -> 300 | mem_instr(<<0x3E>>, align, offset) 301 | 302 | :memory_size -> 303 | <<0x3F, 0x00>> 304 | 305 | :memory_grow -> 306 | <<0x40, 0x00>> 307 | 308 | # Numberic instructions. [Spec.](http://webassembly.github.io/spec/core/bikeshed/index.html#numeric-instructions) 309 | {:i32_const, integer} -> 310 | <<0x41>> <> encode_integer({:i32, integer}) 311 | 312 | {:i64_const, integer} -> 313 | <<0x42>> <> encode_integer({:i64, integer}) 314 | 315 | {:f32_const, float} -> 316 | <<0x43>> <> encode_float({:f32, float}) 317 | 318 | {:f64_const, float} -> 319 | <<0x44>> <> encode_float({:f64, float}) 320 | 321 | :i32_eqz -> 322 | <<0x45>> 323 | 324 | :i32_eq -> 325 | <<0x46>> 326 | 327 | :i32_ne -> 328 | <<0x47>> 329 | 330 | :i32_lt_s -> 331 | <<0x48>> 332 | 333 | :i32_lt_u -> 334 | <<0x49>> 335 | 336 | :i32_gt_s -> 337 | <<0x4A>> 338 | 339 | :i32_gt_u -> 340 | <<0x4B>> 341 | 342 | :i32_le_s -> 343 | <<0x4C>> 344 | 345 | :i32_le_u -> 346 | <<0x4D>> 347 | 348 | :i32_ge_s -> 349 | <<0x4E>> 350 | 351 | :i32_ge_u -> 352 | <<0x4F>> 353 | 354 | :i32_clz -> 355 | <<0x67>> 356 | 357 | :i32_ctz -> 358 | <<0x68>> 359 | 360 | :i32_popcnt -> 361 | <<0x69>> 362 | 363 | :i32_add -> 364 | <<0x6A>> 365 | 366 | :i32_sub -> 367 | <<0x6B>> 368 | 369 | :i32_mul -> 370 | <<0x6C>> 371 | 372 | :i32_div_s -> 373 | <<0x6D>> 374 | 375 | :i32_div_u -> 376 | <<0x6E>> 377 | 378 | :i32_rem_s -> 379 | <<0x6F>> 380 | 381 | :i32_rem_u -> 382 | <<0x70>> 383 | 384 | :i32_add -> 385 | <<0x71>> 386 | 387 | :i32_or -> 388 | <<0x72>> 389 | 390 | :i32_xor -> 391 | <<0x73>> 392 | 393 | :i32_shl -> 394 | <<0x74>> 395 | 396 | :i32_shr_s -> 397 | <<0x75>> 398 | 399 | :i32_shr_u -> 400 | <<0x76>> 401 | 402 | :i32_rotl -> 403 | <<0x77>> 404 | 405 | :i32_rotr -> 406 | <<0x78>> 407 | 408 | :i64_eqz -> 409 | <<0x50>> 410 | 411 | :i64_eq -> 412 | <<0x51>> 413 | 414 | :i64_ne -> 415 | <<0x52>> 416 | 417 | :i64_lt_s -> 418 | <<0x53>> 419 | 420 | :i64_lt_u -> 421 | <<0x54>> 422 | 423 | :i64_gt_s -> 424 | <<0x55>> 425 | 426 | :i64_gt_u -> 427 | <<0x56>> 428 | 429 | :i64_le_s -> 430 | <<0x57>> 431 | 432 | :i64_le_u -> 433 | <<0x58>> 434 | 435 | :i64_ge_s -> 436 | <<0x59>> 437 | 438 | :i64_ge_u -> 439 | <<0x5A>> 440 | 441 | :i64_clz -> 442 | <<0x79>> 443 | 444 | :i64_ctz -> 445 | <<0x7A>> 446 | 447 | :i64_popcnt -> 448 | <<0x7B>> 449 | 450 | :i64_add -> 451 | <<0x7C>> 452 | 453 | :i64_sub -> 454 | <<0x7D>> 455 | 456 | :i64_mul -> 457 | <<0x7E>> 458 | 459 | :i64_div_s -> 460 | <<0x7F>> 461 | 462 | :i64_div_u -> 463 | <<0x80>> 464 | 465 | :i64_rem_s -> 466 | <<0x81>> 467 | 468 | :i64_rem_u -> 469 | <<0x82>> 470 | 471 | :i64_add -> 472 | <<0x83>> 473 | 474 | :i64_or -> 475 | <<0x84>> 476 | 477 | :i64_xor -> 478 | <<0x85>> 479 | 480 | :i64_shl -> 481 | <<0x86>> 482 | 483 | :i64_shr_s -> 484 | <<0x87>> 485 | 486 | :i64_shr_u -> 487 | <<0x88>> 488 | 489 | :i64_rotl -> 490 | <<0x89>> 491 | 492 | :i64_rotr -> 493 | <<0x8A>> 494 | 495 | :f32_eq -> 496 | <<0x5B>> 497 | 498 | :f32_ne -> 499 | <<0x5C>> 500 | 501 | :f32_lt -> 502 | <<0x5D>> 503 | 504 | :f32_gt -> 505 | <<0x5E>> 506 | 507 | :f32_le -> 508 | <<0x5F>> 509 | 510 | :f32_ge -> 511 | <<0x60>> 512 | 513 | :f32_abs -> 514 | <<0x8B>> 515 | 516 | :f32_neg -> 517 | <<0x8C>> 518 | 519 | :f32_ceil -> 520 | <<0x8D>> 521 | 522 | :f32_floor -> 523 | <<0x8E>> 524 | 525 | :f32_trunc -> 526 | <<0x8F>> 527 | 528 | :f32_nearest -> 529 | <<0x90>> 530 | 531 | :f32_sqrt -> 532 | <<0x91>> 533 | 534 | :f32_add -> 535 | <<0x92>> 536 | 537 | :f32_sub -> 538 | <<0x93>> 539 | 540 | :f32_mul -> 541 | <<0x94>> 542 | 543 | :f32_div -> 544 | <<0x95>> 545 | 546 | :f32_min -> 547 | <<0x96>> 548 | 549 | :f32_max -> 550 | <<0x97>> 551 | 552 | :f32_copysign -> 553 | <<0x98>> 554 | 555 | :f64_eq -> 556 | <<0x61>> 557 | 558 | :f64_ne -> 559 | <<0x62>> 560 | 561 | :f64_lt -> 562 | <<0x63>> 563 | 564 | :f64_gt -> 565 | <<0x64>> 566 | 567 | :f64_le -> 568 | <<0x65>> 569 | 570 | :f64_ge -> 571 | <<0x66>> 572 | 573 | :f64_abs -> 574 | <<0x99>> 575 | 576 | :f64_neg -> 577 | <<0x9A>> 578 | 579 | :f64_ceil -> 580 | <<0x9B>> 581 | 582 | :f64_floor -> 583 | <<0x9C>> 584 | 585 | :f64_trunc -> 586 | <<0x9D>> 587 | 588 | :f64_nearest -> 589 | <<0x9E>> 590 | 591 | :f64_sqrt -> 592 | <<0x9F>> 593 | 594 | :f64_add -> 595 | <<0xA0>> 596 | 597 | :f64_sub -> 598 | <<0xA1>> 599 | 600 | :f64_mul -> 601 | <<0xA2>> 602 | 603 | :f64_div -> 604 | <<0xA3>> 605 | 606 | :f64_min -> 607 | <<0xA4>> 608 | 609 | :f64_max -> 610 | <<0xA5>> 611 | 612 | :f64_copysign -> 613 | <<0xA6>> 614 | 615 | :i32_wrap_i64 -> 616 | <<0xA7>> 617 | 618 | :i32_trunc_s_f32 -> 619 | <<0xA8>> 620 | 621 | :i32_trunc_u_f32 -> 622 | <<0xA9>> 623 | 624 | :i32_trunc_s_f64 -> 625 | <<0xAA>> 626 | 627 | :i32_trunc_u_f64 -> 628 | <<0xAB>> 629 | 630 | :i64_extend_s_i32 -> 631 | <<0xAC>> 632 | 633 | :i64_extend_u_i32 -> 634 | <<0xAD>> 635 | 636 | :i64_trunc_s_f32 -> 637 | <<0xAE>> 638 | 639 | :i64_trunc_u_f32 -> 640 | <<0xAF>> 641 | 642 | :i64_trunc_s_f64 -> 643 | <<0xB0>> 644 | 645 | :i64_trunc_u_f64 -> 646 | <<0xB1>> 647 | 648 | :f32_convert_s_i32 -> 649 | <<0xB2>> 650 | 651 | :f32_convert_u_i32 -> 652 | <<0xB3>> 653 | 654 | :f32_convert_s_i64 -> 655 | <<0xB4>> 656 | 657 | :f32_convert_u_i64 -> 658 | <<0xB5>> 659 | 660 | :f32_demote_f64 -> 661 | <<0xB6>> 662 | 663 | :f64_convert_s_i32 -> 664 | <<0xB7>> 665 | 666 | :f64_convert_u_i32 -> 667 | <<0xB8>> 668 | 669 | :f64_convert_s_i64 -> 670 | <<0xB9>> 671 | 672 | :f64_convert_u_i64 -> 673 | <<0xBA>> 674 | 675 | :f64_promote_f32 -> 676 | <<0xBB>> 677 | 678 | :i32_reinterpret_f32 -> 679 | <<0xBC>> 680 | 681 | :i64_reinterpret_f64 -> 682 | <<0xBD>> 683 | 684 | :f32_reinterpret_i32 -> 685 | <<0xBE>> 686 | 687 | :f64_reinterpret_i64 -> 688 | <<0xBF>> 689 | 690 | # Expressions 691 | {:expr, instrs} -> 692 | map_join(instrs, &encode_instr/1) <> <<0x0B>> 693 | end 694 | end 695 | 696 | defp mem_instr(opcode, align, offset) do 697 | opcode <> encode_integer(align) <> encode_integer(offset) 698 | end 699 | 700 | @type wasm_index :: 701 | {:type_index, non_neg_integer} 702 | | {:func_index, non_neg_integer} 703 | | {:table_index, non_neg_integer} 704 | | {:mem_index, non_neg_integer} 705 | | {:global_index, non_neg_integer} 706 | | {:local_index, non_neg_integer} 707 | | {:label_index, non_neg_integer} 708 | @spec encode_index(wasm_index) :: binary 709 | 710 | # http://webassembly.github.io/spec/core/bikeshed/index.html#indices 711 | defp encode_index({name, value}) do 712 | case name do 713 | :type_index -> encode_integer({:u32, value}) 714 | :func_index -> encode_integer({:u32, value}) 715 | :table_index -> encode_integer({:u32, value}) 716 | :mem_index -> encode_integer({:u32, value}) 717 | :global_index -> encode_integer({:u32, value}) 718 | :local_index -> encode_integer({:u32, value}) 719 | :label_index -> encode_integer({:u32, value}) 720 | end 721 | end 722 | 723 | @type wasm_section :: 724 | {:custom_sec, wasm_custom} 725 | | {:type_sec, [wasm_func_type]} 726 | | {:import_sec, [wasm_import]} 727 | | {:func_sec, [wasm_index]} 728 | | {:table_sec, [wasm_table]} 729 | | {:memory_sec, [wasm_mem]} 730 | | {:global_sec, [wasm_global]} 731 | | {:export_sec, [wasm_export]} 732 | | {:start_sec, wasm_start} 733 | | {:elem_sec, [wasm_elem]} 734 | | {:code_sec, [wasm_code]} 735 | | {:data_sec, [wasm_data]} 736 | @spec encode_section(wasm_section) :: binary 737 | 738 | # http://webassembly.github.io/spec/core/bikeshed/index.html#sections) 739 | defp encode_section({name, content}) do 740 | {section_id, result} = 741 | case name do 742 | :custom_sec -> {0, encode_custom(content)} 743 | :type_sec -> {1, encode_vec(content, &encode_func_type/1)} 744 | :import_sec -> {2, encode_vec(content, &encode_import/1)} 745 | :func_sec -> {3, encode_vec(content, &encode_index/1)} 746 | :table_sec -> {4, encode_vec(content, &encode_table/1)} 747 | :memory_sec -> {5, encode_vec(content, &encode_mem/1)} 748 | :global_sec -> {6, encode_vec(content, &encode_global/1)} 749 | :export_sec -> {7, encode_vec(content, &encode_export/1)} 750 | :start_sec -> {8, encode_start(content)} 751 | :elem_sec -> {9, encode_vec(content, &encode_elem/1)} 752 | :code_sec -> {10, encode_vec(content, &encode_code/1)} 753 | :data_sec -> {11, encode_vec(content, &encode_data/1)} 754 | end 755 | 756 | <> <> encode_integer({:u32, byte_size(result)}) <> result 757 | end 758 | 759 | @type wasm_custom :: {:custom, wasm_name, binary} 760 | @spec encode_custom(wasm_custom) :: binary 761 | 762 | # http://webassembly.github.io/spec/core/bikeshed/index.html#custom-section 763 | defp encode_custom({:custom, name, bytes}) do 764 | encode_name(name) <> bytes 765 | end 766 | 767 | @type wasm_import :: {:import, wasm_import_desc} 768 | @type wasm_import_desc :: 769 | wasm_name | wasm_name | wasm_index | wasm_table_type | wasm_mem_type | wasm_global_type 770 | @spec encode_import(wasm_import) :: binary 771 | 772 | # http://webassembly.github.io/spec/core/bikeshed/index.html#import-section 773 | defp encode_import({:import, mod, name, desc}) do 774 | encode_name(mod) <> 775 | encode_name(name) <> 776 | case desc do 777 | {:type_index, _t} -> <<0x00>> <> encode_index(desc) 778 | {:table_type, _tt} -> <<0x01>> <> encode_table_type(desc) 779 | {:mem_type, _mt} -> <<0x02>> <> encode_mem_type(desc) 780 | {:global_type, _gt} -> <<0x03>> <> encode_global_type(desc) 781 | end 782 | end 783 | 784 | @type wasm_table :: {:table, wasm_table_type} 785 | @spec encode_table(wasm_table) :: binary 786 | 787 | # http://webassembly.github.io/spec/core/bikeshed/index.html#table-section 788 | defp encode_table({:table, table_type}) do 789 | encode_table_type(table_type) 790 | end 791 | 792 | @type wasm_mem :: {:mem, wasm_mem_type} 793 | @spec encode_mem(wasm_mem) :: binary 794 | 795 | # http://webassembly.github.io/spec/core/bikeshed/index.html#memory-section 796 | defp encode_mem({:mem, mem_type}) do 797 | encode_mem_type(mem_type) 798 | end 799 | 800 | @type wasm_global :: {:global, wasm_global_type, wasm_instr} 801 | @spec encode_global(wasm_global) :: binary 802 | 803 | # http://webassembly.github.io/spec/core/bikeshed/index.html#global-section 804 | defp encode_global({:global, global_type, expr}) do 805 | encode_global_type(global_type) <> encode_instr(expr) 806 | end 807 | 808 | @type wasm_export :: {:export, wasm_name, wasm_index} 809 | @spec encode_export(wasm_export) :: binary 810 | 811 | # http://webassembly.github.io/spec/core/bikeshed/index.html#export-section 812 | defp encode_export({:export, name, desc}) do 813 | encode_name(name) <> 814 | case desc do 815 | {:func_index, _t} -> <<0x00>> <> encode_index(desc) 816 | {:table_index, _tt} -> <<0x01>> <> encode_index(desc) 817 | {:mem_index, _mt} -> <<0x02>> <> encode_index(desc) 818 | {:global_index, _gt} -> <<0x03>> <> encode_index(desc) 819 | end 820 | end 821 | 822 | @type wasm_start :: {:start, wasm_index} 823 | @spec encode_start(wasm_start) :: binary 824 | 825 | # http://webassembly.github.io/spec/core/bikeshed/index.html#start-section 826 | defp encode_start({:start, func_index}) do 827 | encode_index(func_index) 828 | end 829 | 830 | @type wasm_elem :: {:elem, wasm_index, wasm_instr, [wasm_index]} 831 | @spec encode_elem(wasm_elem) :: binary 832 | 833 | # http://webassembly.github.io/spec/core/bikeshed/index.html#element-section 834 | defp encode_elem({:elem, table_index, expr, init}) do 835 | encode_index(table_index) <> encode_instr(expr) <> map_join(init, &encode_index/1) 836 | end 837 | 838 | @type wasm_code :: {:code, [wasm_func]} 839 | @type wasm_func :: {:func, [wasm_locals], wasm_instr} 840 | @type wasm_locals :: {:locals, wasm_integer, wasm_value_type} 841 | @spec encode_code(wasm_code) :: binary 842 | 843 | # http://webassembly.github.io/spec/core/bikeshed/index.html#code-section) 844 | defp encode_code({:code, code}) do 845 | result = encode_func(code) 846 | encode_integer({:u32, byte_size(result)}) <> result 847 | end 848 | 849 | defp encode_func({:func, locals, expr}) do 850 | encode_vec(locals, &encode_locals/1) <> encode_instr(expr) 851 | end 852 | 853 | defp encode_locals({:locals, n, value_type}) do 854 | encode_integer(n) <> encode_value_type(value_type) 855 | end 856 | 857 | @type wasm_data :: {:data, [wasm_index], wasm_instr, binary} 858 | @spec encode_data(wasm_data) :: binary 859 | 860 | # http://webassembly.github.io/spec/core/bikeshed/index.html#data-section 861 | defp encode_data({:data, data, expr, bytes}) do 862 | encode_index(data) <> encode_instr(expr) <> encode_integer({:u32, byte_size(bytes)}) <> bytes 863 | end 864 | 865 | defp encode_vec(items, encode_elem) when is_list(items) do 866 | encode_integer({:u32, length(items)}) <> map_join(items, encode_elem) 867 | end 868 | end --------------------------------------------------------------------------------