├── .github └── FUNDING.yml ├── LICENSE ├── README.adoc ├── src ├── package.lisp └── wasm-encoder.lisp ├── test ├── instructions.lisp ├── modules.lisp ├── test.lisp └── values.lisp └── wasm-encoder.asd /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: alex-gutev # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: alexgutev # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexander Gutev 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.adoc: -------------------------------------------------------------------------------- 1 | = WASM-ENCODER = 2 | :AUTHOR: Alexander Gutev 3 | :EMAIL: 4 | :toc: preamble 5 | :toclevels: 4 6 | :icons: font 7 | :idprefix: 8 | 9 | ifdef::env-github[] 10 | :tip-caption: :bulb: 11 | :note-caption: :information_source: 12 | :caution-caption: :fire: 13 | :important-caption: :exclamation: 14 | :warning-caption: :warning: 15 | endif::[] 16 | 17 | This is a simple library for serializing a representation of 18 | WebAssembly modules to the binary wasm format. 19 | 20 | == Documentation == 21 | 22 | [[value_types]] 23 | === Value Types === 24 | 25 | The value types of various entities, such as global variables, 26 | function arguments and return values, local and global variables, are 27 | identified by the following symbols: 28 | 29 | .Numeric Types 30 | `I32`:: 32-bit integer 31 | `I64`:: 64-bit integer 32 | `F32`:: 32-bit single precision floating point value 33 | `F64`:: 64-bit double precision floating point value 34 | 35 | .Reference Types 36 | `FUNCREF`:: Function reference 37 | `EXTERNREF`:: All reference objects that can be passed to WebAssembly 38 | 39 | NOTE: A symbol is considered equivalent to one of the above if its 40 | symbol name is equivalent, regardless of its package. Thus you don't 41 | have to import `I32`, etc from the `WASM-ENCODER` package. 42 | 43 | === Instructions === 44 | 45 | A WebAssembly instruction is represented by a symbol of which the 46 | symbol name is equal to the instruction's mnemonic. Any symbol, in any 47 | package, with a symbol name that is equal to a WebAssembly instruction 48 | mnemonic can be used. 49 | 50 | Instructions which accept static options are represented by a list in 51 | which the first element (the `CAR`) is the instruction mnemonic and 52 | the remaining elements are the options. The interpretation of the 53 | options depends on the instruction. 54 | 55 | ==== Branch Instructions ==== 56 | 57 | The `BR` and `BR_IF` branch instructions each take a single option, an 58 | unsigned integer interpreted as the index of the block which is the 59 | branch target. 60 | 61 | The `BR_TABLE` takes a variable number of options, where each option 62 | is an unsigned integer, interpreted as a block index. 63 | 64 | .Examples 65 | -------------------------------------------------- 66 | (BR 0) ;; Branch to block 0 67 | (BR_IF 2) ;; Branch to block 2 68 | (BR_TABLE 0 1 2) 69 | -------------------------------------------------- 70 | 71 | ==== Call Instructions ==== 72 | 73 | The `CALL` instruction takes a single option, an unsigned integer 74 | interpreted as the index of the function being called. 75 | 76 | The `CALL_INDIRECT` instruction takes a single option, an unsigned 77 | integer interpreted as the index, within the type section, of the 78 | function's type signature. 79 | 80 | .Examples 81 | -------------------------------------------------- 82 | (CALL 5) ;; Call function 5 83 | (CALL_INDIRECT 2) ;; Indirect call to a function with type signature index 2 84 | -------------------------------------------------- 85 | 86 | ==== Select Instruction ==== 87 | 88 | The select instruction takes an optional list of type identifier which 89 | represent the value types of the operands to be selected. If there is 90 | more than one type specifier then there is more than one value per 91 | operand. 92 | 93 | .Example 94 | -------------------------------------------------- 95 | (select f32 f64) 96 | -------------------------------------------------- 97 | 98 | In the above example `SELECT` consumes, off the stack, two operands, 99 | following the condition, each consisting of two values the first being 100 | an `F32` and the second being an `F64`. The two values of the result 101 | operand, of type `F32` and `F64`, are pushed back onto the stack. 102 | 103 | If `SELECT` is not given the type identifier of it's operand values, 104 | the operands must be of a numeric type. 105 | 106 | NOTE: In the current version of WebAssembly `SELECT` may only specify 107 | a single value type, for a single value per operand. This library 108 | supports `SELECT` with multiple operand value types. 109 | 110 | ==== Local and Global Variable Instructions ==== 111 | 112 | The instructions for retrieving/setting the value of a local/global 113 | variable, each take a single options, an unsigned integer interpreted 114 | as the local/global variable index. 115 | 116 | - `LOCAL.GET` 117 | - `LOCAL.SET` 118 | - `LOCAL.TEE` 119 | - `GLOBAL.GET` 120 | - `GLOBAL.SET` 121 | 122 | ==== Memory Instructions ==== 123 | 124 | Memory load/store instructions take an optional list specifying the 125 | expected alignment and offset. These operands take the following form: 126 | 127 | -------------------------------------------------- 128 | (I32.STORE (ALIGN a) (OFFSET o)) ;; Alignment = a, Offset = o 129 | -------------------------------------------------- 130 | 131 | The alignment option is a list of two elements where the first element 132 | is a symbol, with name `ALIGN`, and the second element is an unsigned 133 | integer specifying the alignment as a power of two. If the alignment 134 | option is omitted a default alignment of `2` is assumed. 135 | 136 | The offset option is a list of two elements where the first element is 137 | a symbol, with name `OFFSET`, and the second element is an unsigned 138 | integer specifying the offset. If this option is omitted a default 139 | offset of `0` is assumed. 140 | 141 | TIP: As with the instruction mnemonics any symbol, in any package, 142 | with symbol name `ALIGN` or `OFFSET` can be used to specify the 143 | alignment and offset options. 144 | 145 | The alignment and offset options can be specified in any order and 146 | either one, or both, can be omitted. If both options are omitted the 147 | instruction can either take the form of a list containing only the 148 | instruction mnemonic, or the instruction mnemonic symbol by itself. 149 | 150 | .Examples: 151 | -------------------------------------------------- 152 | (I32.LOAD (OFFSET 8)) ;; Offset = 8 153 | (I32.STORE (ALIGN 1)) ;; Alignment = 1 154 | I64.STORE ;; Default Alignment = 2 and Offset = 0 155 | -------------------------------------------------- 156 | 157 | The instructions falling within this group are all the typed `xx.LOAD` 158 | and `xx.STORE` instructions (where `xx` is the value type), including 159 | the instructions with a storage size which is smaller than the size of 160 | the type. 161 | 162 | ==== Constant Instructions ==== 163 | 164 | Constant instructions take a single option which is the literal 165 | constant value. 166 | 167 | - `I32.CONST` and `I64.CONST` take either a signed or unsigned 32-bit 168 | (64-bit in the case of `I64.CONST`) integer option. However, 169 | regardless of whether the operand value is signed or not, the value 170 | itself is always encoded as a signed integer in twos-complement. 171 | 172 | - `F32.CONST` takes a single precision floating-point 173 | (`SINGLE-FLOAT`) value as its option. 174 | 175 | - `F64.CONST` takes either a single or double precision floating-point 176 | value as its option. 177 | 178 | ==== Reference Instructions ==== 179 | 180 | The `REF.NULL` instruction takes a single option which is interpreted 181 | as the reference type identifier, either `FUNCREF` or `EXTERNREF`. See 182 | <>. 183 | 184 | The `REF.FUNC` instruction takes a single option, an unsigned integer 185 | interpreted as the index of the function, within the function section, 186 | to which to return a reference. 187 | 188 | ==== Memory and Table Instructions ==== 189 | 190 | The `MEMORY.INIT` and `DATA.DROP` instructions both take a single 191 | option, an unsigned integer which is interpreted as the index of a 192 | data segment within the data section. 193 | 194 | The `TABLE.INIT` and `TABLE.COPY` instructions take two options, both 195 | unsigned integers which are interpreted as a table index and element 196 | segment index. 197 | 198 | The `ELEM.DROP` instruction takes a single instruction, an unsigned 199 | integer interpreted as an element segment index. 200 | 201 | The `TABLE.GET`, `TABLE.SET`, `TABLE.SIZE`, `TABLE.GROW` and 202 | `TABLE.FILL` instructions each take a single option, an unsigned 203 | integer interpreted as a table index. 204 | 205 | ==== Structured Block Instructions ==== 206 | 207 | Structured block instructions are represented by a list with the block 208 | type in the first element being and the instructions, comprising the 209 | body of the block, in the remaining elements. 210 | 211 | The second element of the list may be either an instruction, in which 212 | case it is the first instruction of the block, or one of the 213 | following: 214 | 215 | `(RESULT type)`:: Indicates the type of value returned (on the stack) 216 | by the block, where `type` is the value type identifier, see 217 | <>. 218 | 219 | `(TYPE index)`:: Indicates the of the values consumed (from the stack) 220 | and returned by the block, where `index` is the index of a function 221 | type specifier, with the type section, see <>. 222 | 223 | If neither a result type nor type signature is specified, then it is 224 | assumed that the block neither consumes nor returns a value and hence 225 | does not have a return value type. 226 | 227 | The `BLOCK` and `LOOP` block instructions follow this representation 228 | exactly. 229 | 230 | .Example: Simple block (no result type) 231 | -------------------------------------------------- 232 | (block 233 | (local.get 1) 234 | (br_if 0) 235 | (call 0)) 236 | -------------------------------------------------- 237 | 238 | .Example: Block with result type 239 | -------------------------------------------------- 240 | (block (result i32) 241 | (local.get 1) 242 | (br_if 0) 243 | 244 | (local.get 2) 245 | (local.get 3) 246 | i32.add) 247 | -------------------------------------------------- 248 | 249 | .Example: Block with type signature 250 | -------------------------------------------------- 251 | (block (type 1) ;; (i32 i32 i32) => i32 252 | i32.add 253 | i32.mul) 254 | -------------------------------------------------- 255 | 256 | .Example: Simple loop 257 | -------------------------------------------------- 258 | (loop 259 | (call 0) 260 | (local.get 1) 261 | (i32.const 5) 262 | i32.lt 263 | (br_if 0)) 264 | -------------------------------------------------- 265 | 266 | The `IF` instruction is represented by a list of the following form: 267 | 268 | -------------------------------------------------- 269 | (IF (THEN instructions...) 270 | (ELSE instructions...)) 271 | -------------------------------------------------- 272 | 273 | Where `instructions` are the instructions comprising the body of the 274 | `THEN` and `ELSE` branches. The `(ELSE ...)` element may be omitted in 275 | which case the if instruction does not have an else branch. 276 | 277 | The `IF` instruction may also have an optional result type or type 278 | signature specified in the second element by `(RESULT type)` or `(TYPE 279 | index)`. If this is omitted the `IF` instruction is assumed to have no 280 | result type. 281 | 282 | .Example: If without else branch 283 | -------------------------------------------------- 284 | (local.get 0) 285 | (if (then (call 0)) 286 | -------------------------------------------------- 287 | 288 | .Example: If with else branch 289 | -------------------------------------------------- 290 | (local.get 0) 291 | (if (then (call 0)) 292 | (else (call 1))) 293 | -------------------------------------------------- 294 | 295 | .Example: If with result type 296 | -------------------------------------------------- 297 | (local.get 0) 298 | (i32.const 0) 299 | i32.ge 300 | 301 | (if (result i32) 302 | (then (local.get 0)) 303 | (else (local.get 0) 304 | (i32.const -1) 305 | i32.mul)) 306 | -------------------------------------------------- 307 | 308 | === Modules === 309 | 310 | A WebAssembly module is represented by the `WASM-MODULE` structure, 311 | which contains a slot for each section. A `WASM-MODULE` object can be 312 | serialized to an output stream using the `SERIALIZE-MODULE` function. 313 | 314 | ==== WASM-MODULE ==== 315 | 316 | Structure: `WASM-MODULE` 317 | 318 | Represents a WebAssembly module with a lot for each section: 319 | 320 | Slots: 321 | 322 | - `TYPES` 323 | - `IMPORTS` 324 | - `FUNCTIONS` 325 | - `TABLES` 326 | - `MEMORY` 327 | - `GLOBALS` 328 | - `EXPORTS` 329 | - `START` 330 | - `ELEMENTS` 331 | - `DATA` 332 | 333 | ==== SERIALIZE-MODULE ==== 334 | 335 | Function: `SERIALIZE-MODULE MODULE STREAM` 336 | 337 | Serialize a module to the wasm binary format and write the output to a 338 | given stream. 339 | 340 | `MODULE`:: The `WASM-MODULE` to serialize. 341 | `STREAM`:: Output stream to which to serialize the module. This must 342 | be a binary output stream with element type `(UNSIGNED-BYTE 8)`. 343 | 344 | [[type_section]] 345 | === Type Section === 346 | 347 | The `TYPES` slot, of `WASM-MODULE` ,is a list of `WASM-FUNCTION-TYPE` 348 | objects which represent the function type signatures of the module's 349 | functions. 350 | 351 | ==== WASM-FUNCTION-TYPE ==== 352 | 353 | Structure: `WASM-FUNCTION-TYPE` 354 | 355 | Represents a function type signature. 356 | 357 | Slots: 358 | 359 | `PARAMS`:: List of the argument types 360 | `RESULTS`:: List of the return value types 361 | 362 | === Imports Section === 363 | 364 | The `IMPORTS` slot, of `WASM-MODULE`, is a list of `WASM-IMPORT` 365 | objects which represent the module's imports. 366 | 367 | [[wasm_import]] 368 | ==== WASM-IMPORT ==== 369 | 370 | Structure: `WASM-IMPORT` 371 | 372 | Represents an imported entity. 373 | 374 | Slots: 375 | 376 | `MODULE`:: Module component (first level) of the import name 377 | 378 | `NAME`:: Name component (second level) of the import name 379 | 380 | `TYPE`:: Keyword identifying type of imported entity: 381 | + 382 | -- 383 | `:FUNC`:: The imported entity is a function 384 | `:TABLE`:: The imported entity is a table object 385 | `:MEMORY`:: The imported entity is a memory object 386 | `:GLOBAL`:: The imported entity is a global variable 387 | -- 388 | 389 | `DESC`:: Description of the imported entity, which depends on `TYPE`: 390 | + 391 | -- 392 | `:FUNC`:: Index of the function's type signature within the module's 393 | type section. 394 | 395 | `:TABLE`:: A `WASM-TABLE` object specifying the table type and limits. 396 | 397 | `:MEMORY`:: A `WASM-LIMIT` object specifying the memory limits. 398 | 399 | `:GLOBAL`:: A list of the form `(TYPE MUTABLE-P)` where `TYPE` is the 400 | value type of the variable and `MUTABLE-P` is a flag, which if true, 401 | indicates that the variable is mutable. 402 | -- 403 | 404 | === Functions === 405 | 406 | The slot `FUNCTIONS`, of `WASM-MODULE`, is a list of `WASM-FUNCTION` 407 | objects which represent the module's functions. 408 | 409 | ==== WASM-FUNCTION ==== 410 | 411 | Structure: `WASM-FUNCTION` 412 | 413 | Represents a function. 414 | 415 | Slots: 416 | 417 | `TYPE`:: Index of the function's type signature within the type section. 418 | 419 | `LOCALS`:: List of the types of the local variables. 420 | 421 | `CODE`:: List of instructions comprising the body of the function. 422 | 423 | 424 | === Memory and Table Sections === 425 | 426 | The `MEMORY` slot is a list of `WASM-LIMIT` objects which specify the 427 | limits of the module's memory objects. 428 | 429 | The slot `TABLES`, of `WASM-MODULE`, is a list of `WASM-TABLE` objects 430 | which specify the type and limits of the module's table objects. 431 | 432 | NOTE: In the current version of WebAssembly, modules may contain a 433 | maximum of one table and memory object. This library supports 434 | serializing modules with more than one memory and table object. 435 | 436 | ==== WASM-LIMIT ==== 437 | 438 | Structure: `WASM-LIMIT` 439 | 440 | Specifies the limits of a memory and table objects. 441 | 442 | Slots: 443 | 444 | `MIN`:: The lower-bound of the memory / table limit. Must be greater 445 | than or equal to 0. 446 | 447 | `MAX`:: The upper-bound of the limit. If NIL the limit has no 448 | upper-bound. 449 | 450 | 451 | ==== WASM-TABLE ==== 452 | 453 | Structure: `WASM-TABLE (:INCLUDE WASM-LIMIT)` 454 | 455 | Specifies the type and limits of a table object. Includes the slots of 456 | the structure `WASM-LIMIT`. 457 | 458 | Slots: 459 | 460 | `TYPE`:: Table element type, either `FUNCREF` (the default) or `EXTERNREF`. 461 | 462 | NOTE: In the current version of WebAssembly only tables of type 463 | `FUNCREF` are supported. 464 | 465 | 466 | === Global Variable Section === 467 | 468 | The `GLOBALS` slot, of `WASM-MODULE`, is a list of `WASM-GLOBAL` 469 | objects which represent the module's global variables. 470 | 471 | ==== WASM-GLOBAL ==== 472 | 473 | Structure: `WASM-GLOBAL` 474 | 475 | Represents a global variable 476 | 477 | .Slots 478 | 479 | `TYPE`:: Value type of the variable. 480 | 481 | `MUTABLE-P`:: Flag, which if true, indicates the variable is 482 | mutable. Otherwise the variable is immutable. 483 | 484 | `INIT`:: Expression which computes the variable's initial value. May 485 | be `NIL`. 486 | 487 | 488 | === Exports Section === 489 | 490 | The `EXPORTS` slot, of `WASM-MODULE`, is a list of `WASM-EXPORT` 491 | objects which represent the entities exported by the module. 492 | 493 | ==== WASM-EXPORT ==== 494 | 495 | Structure: `WASM-EXPORT` 496 | 497 | Represents an exported entity. 498 | 499 | .Slots 500 | 501 | `NAME`:: The name (as a string) under which the entity is exported. 502 | 503 | `TYPE`:: Keyword describing the type of entity. See the `TYPE` slot of 504 | <>. 505 | 506 | `INDEX`:: Index of the exported entity within its sections. 507 | 508 | 509 | === Start Function === 510 | 511 | The `START` slot, of `WASM-MODULE`, is the index of the function, with 512 | functions list in the `FUNCTIONS` slot, of the function which serves 513 | as the module's entry point. If `NIL` the module does not have an 514 | entry point. 515 | 516 | === Element Section === 517 | 518 | The `ELEMENTS` slot, of `WASM-MODULE`, is a list of `WASM-ELEMENT` 519 | objects which represent the module's element segments. 520 | 521 | Each element segment specifies the initial values of a range of 522 | elements in a table object. 523 | 524 | ==== WASM-ELEMENT ==== 525 | 526 | Structure `WASM-ELEMENT` 527 | 528 | Represents an element segment. 529 | 530 | .Slots 531 | 532 | `MODE`:: Keyword specifying the element segment mode. 533 | + 534 | -- 535 | `:ACTIVE`:: An 'active' segment , which is used to initialize the 536 | table elements during module instantiation. This is the default. 537 | 538 | `:PASSIVE`:: A 'passive' segment, which can be used to initialize the 539 | table elements at runtime with the `TABLE.INIT` instruction. 540 | 541 | `:DECLARATIVE`:: A 'declarative' segment, which is used only to 542 | forward declare the function references that will be added to the 543 | table, using the `REF.FUNC` instruction. 544 | -- 545 | 546 | `INDEX`:: Index of the table object, 0 by default, which this element 547 | initializes. This slot is only used when `MODE` is `:ACTIVE`. 548 | + 549 | -- 550 | NOTE: In the current version of WebAssembly the only valid index is 0. 551 | -- 552 | 553 | `OFFSET`:: Expression which computes the starting index of the 554 | location within the table where the elements in this segment are 555 | copied to. 556 | 557 | `INIT`:: Object specifying the values of the elements in this 558 | segment. This can be either a `WASM-ELEMENT-INIT-INDEX` or 559 | `WASM-ELEMENT-INIT-EXPRESSIONS` object. 560 | 561 | ==== WASM-ELEMENT-INIT-INDEX ==== 562 | 563 | Structure `WASM-ELEMENT-INIT-INDEX` 564 | 565 | Represents a table element segment where the initial element values 566 | are function indices. 567 | 568 | .Slots 569 | 570 | `FUNCTIONS`:: List of indices of the functions, within the function 571 | section (`FUNCTIONs` slot), to which the table elements are set. 572 | 573 | ==== WASM-ELEMENT-INIT-EXPRESSIONS ==== 574 | 575 | Structure `WASM-ELEMENT-INIT-EXPRESSIONS` 576 | 577 | Represents a table element segment where the initial element values 578 | are computed by expressions. 579 | 580 | .Slots 581 | 582 | `TYPE`:: Table element type, either `FUNCREF` (default) or 583 | `EXTERNREF`. 584 | 585 | `EXPRESSIONS`:: List of expressions which compute the initial element 586 | values. Each expression should leave a function reference on the 587 | stack, which is obtained with the `REF.FUNC` instruction. 588 | 589 | === Data Section === 590 | 591 | The `DATA` slot, of `WASM-MODULE`, is a list of `WASM-DATA` objects 592 | which represent the module's data segments. 593 | 594 | Each data segment specifies the initial values of a range of bytes 595 | in a memory object. 596 | 597 | ==== WASM-DATA ==== 598 | 599 | Structure `WASM-DATA` 600 | 601 | Represents a data segment. 602 | 603 | .Slots 604 | 605 | `MODE`:: Keyword specifying the element segment mode. 606 | + 607 | -- 608 | `:ACTIVE`:: An 'active' segment, which is used to initialize the 609 | memory object during module instantiation. This is the default. 610 | 611 | `:PASSIVE`:: A 'passive' segment, which can be used to initialize the 612 | memory object at runtime with the `MEMORY.INIT` instruction. 613 | -- 614 | 615 | `MEMORY`:: Index of the memory object, 0 by default, which this element 616 | initializes. This slot is only used when `MODE` is `:ACTIVE`. 617 | + 618 | -- 619 | NOTE: In the current version of WebAssembly the only valid index is 0. 620 | -- 621 | 622 | `OFFSET`:: Expression which computes the starting index of the 623 | location within the memory object where the bytes in this segment are 624 | copied to. 625 | 626 | `BYTES`:: Byte array containing the values to which the bytes in the 627 | memory object are set. 628 | 629 | -------------------------------------------------------------------------------- /src/package.lisp: -------------------------------------------------------------------------------- 1 | ;;; package.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020 Alexander Gutev 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 | 23 | 24 | (defpackage #:wasm-encoder 25 | (:use #:generic-cl 26 | #:agutil 27 | #:alexandria 28 | #:trivia 29 | 30 | #:ieee-floats 31 | #:babel 32 | #:babel-encodings) 33 | 34 | (:shadowing-import-from #:generic-cl 35 | #:emptyp 36 | #:multiply 37 | #:accumulate) 38 | 39 | (:import-from #:flexi-streams 40 | #:with-output-to-sequence) 41 | 42 | (:export 43 | ;; Types 44 | 45 | #:wasm-module 46 | #:wasm-module-types 47 | #:wasm-module-imports 48 | #:wasm-module-functions 49 | #:wasm-module-tabls 50 | #:wasm-module-memory 51 | #:wasm-module-globals 52 | #:wasm-module-exports 53 | #:wasm-module-start 54 | #:wasm-module-elements 55 | #:wasm-module-data 56 | #:wasm-module-data-count 57 | #:wasm-module-p 58 | #:make-wasm-module 59 | 60 | #:wasm-function-type 61 | #:wasm-function-type-params 62 | #:wasm-function-type-results 63 | #:wasm-function-type-p 64 | #:make-wasm-function-type 65 | 66 | #:wasm-import 67 | #:wasm-import-module 68 | #:wasm-import-name 69 | #:wasm-import-type 70 | #:wasm-import-desc 71 | #:wasm-import-p 72 | #:make-wasm-import 73 | 74 | #:wasm-export 75 | #:wasm-export-name 76 | #:wasm-export-type 77 | #:wasm-export-index 78 | #:wasm-export-p 79 | #:make-wasm-export 80 | 81 | #:wasm-limit 82 | #:wasm-limit-min 83 | #:wasm-limit-max 84 | #:wasm-limit-p 85 | #:make-wasm-limit 86 | 87 | #:wasm-global 88 | #:wasm-global-type 89 | #:wasm-global-mutable-p 90 | #:wasm-global-init 91 | #:wasm-global-p 92 | #:make-wasm-global 93 | 94 | #:wasm-element 95 | #:wasm-element-index 96 | #:wasm-element-offset 97 | #:wasm-element-mode 98 | #:wasm-element-init 99 | #:wasm-element-p 100 | #:make-wasm-element 101 | 102 | #:wasm-element-init-index 103 | #:wasm-element-init-index-functions 104 | #:wasm-element-init-index-p 105 | #:make-wasm-element-init-index 106 | 107 | #:wasm-element-init-expressions 108 | #:wasm-element-init-type 109 | #:wasm-element-init-expressions-expressions 110 | #:wasm-element-init-expressions-p 111 | #:make-wasm-element-init-expressions 112 | 113 | #:wasm-table 114 | #:wasm-table-type 115 | #:wasm-table-min 116 | #:wasm-table-max 117 | #:wasm-table-p 118 | #:make-wasm-table 119 | 120 | #:wasm-function 121 | #:wasm-function-type 122 | #:wasm-function-locals 123 | #:wasm-function-code 124 | #:wasm-function-p 125 | #:make-wasm-function 126 | 127 | #:wasm-data 128 | #:wasm-data-offset 129 | #:wasm-data-bytes 130 | #:wasm-data-memory 131 | #:wasm-data-mode 132 | #:wasm-data-p 133 | #:make-wasm-data 134 | 135 | ;; Functions 136 | 137 | #:serialize-module)) 138 | -------------------------------------------------------------------------------- /src/wasm-encoder.lisp: -------------------------------------------------------------------------------- 1 | ;;; wasm-encoder.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020 Alexander Gutev 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 | 23 | (in-package #:wasm-encoder) 24 | 25 | 26 | ;;; Types 27 | 28 | (defstruct wasm-module 29 | "Represents a WebAssembly module that can be serialized to the binary wasm format. 30 | 31 | Each slot contains a list of the entities comprising a particular 32 | section of the module. The entities are serialized in the order 33 | given in the list. 34 | 35 | TYPES is a list of `WASM-FUNCTION-TYPE' objects representing the 36 | function type signatures of the module's functions. 37 | 38 | IMPORTS is a list of `WASM-IMPORT' objects representing the 39 | module's imports. 40 | 41 | FUNCTIONS is a list of `WASM-FUNCTION' objects representing the 42 | module's functions, including both the signatures and code. 43 | 44 | TABLES is a list of `WASM-TABLE' objects specifying the limits, and 45 | types, of the module's tables. In the current version of 46 | WebAssembly, a module may have at most one table of type FUNCREF, 47 | however this library supports multiple tables of both type FUNCREF 48 | and EXTERNREF. 49 | 50 | MEMORY is a list of `WASM-LIMIT' objects specifying the limits of 51 | the module's memory objects. Currently a module may have at most 52 | one memory object, however this library supports multiple memory 53 | objects. 54 | 55 | GLOBALS is a list of `WASM-GLOBAL' objects representing the 56 | module's global variables. 57 | 58 | EXPORTS is a list `WASM-EXPORT' objects representing the module's 59 | exports. 60 | 61 | START is the index of the function serving as the module's entry 62 | point. If NIL the module does not have an entry point. 63 | 64 | ELEMENTS is a list of `WASM-ELEMET' objects representing the 65 | module's element segments, for initializing the module's tables. 66 | 67 | DATA is a list of `WASM-DATA' objects representing the module's 68 | data segments for initializing the module's memory. 69 | 70 | DATA-COUNT is a flag, which if true, a data count section is 71 | emitted, prior to the code section, storing the number of data 72 | segments." 73 | 74 | types 75 | imports 76 | functions 77 | tables 78 | memory 79 | globals 80 | exports 81 | start 82 | elements 83 | data 84 | (data-count t)) 85 | 86 | (defstruct wasm-function-type 87 | "Represents a function type signature. 88 | 89 | PARAMS is a list of the argument types. 90 | 91 | RESULTS is a list of the return value types." 92 | 93 | params 94 | results) 95 | 96 | (defstruct wasm-import 97 | "Represents an imported entity. 98 | 99 | The two-level import name is given by MODULE and NAME. 100 | 101 | TYPE is a keyword specifying the kind of entity that is imported: 102 | 103 | :FUNC - function 104 | :TABLE - table object 105 | :MEMORY - memory object 106 | :GLOBAL - global variable 107 | 108 | DESC is a description of the imported entity, which depends on TYPE: 109 | 110 | :FUNC - Index of the function's type signature within the 111 | module's type section. 112 | 113 | :TABLE - A `WASM-TABLE' object specifying the table type and 114 | limits. 115 | 116 | :MEMORY - A `WASM-LIMIT' object specifying the memory limits. 117 | 118 | :GLOBAL - A list of the form (TYPE MUTABLE-P) where TYPE is the 119 | value type of the variable and MUTABLE-P is a flag for 120 | whether the variable is mutable." 121 | 122 | module 123 | name 124 | type 125 | desc) 126 | 127 | (defstruct wasm-export 128 | "Represents an exported entity. 129 | 130 | NAME is the name at which the entity is exported. 131 | 132 | TYPE is a keyword specifying the kind of entity that is exported: 133 | 134 | :FUNC - function 135 | :TABLE - table object 136 | :MEMORY - memory object 137 | :GLOBAL - global variable 138 | 139 | INDEX is the index of the exported entity within the module 140 | section in which it is contained." 141 | 142 | name 143 | type 144 | index) 145 | 146 | 147 | (defstruct wasm-limit 148 | "Represents a limit of memory or table object. 149 | 150 | MIN is the lower bound, which must be greater than or equal to 0. 151 | 152 | MAX is the upper bound. If NIL the limit has no upper bound." 153 | 154 | min 155 | max) 156 | 157 | (defstruct wasm-global 158 | "Represents a global variable. 159 | 160 | TYPE is the value type of the variable. 161 | 162 | MUTABLE-P is a flag, which if true indicates the variable is 163 | mutable. 164 | 165 | INIT is an optional expression (a list of instructions) which 166 | compute the variables initial value. If NIL the variable has no 167 | initial value." 168 | 169 | type 170 | mutable-p 171 | init) 172 | 173 | (defstruct wasm-element 174 | "Represents an element segment. 175 | 176 | Element segments specify the initial values of table elements. 177 | 178 | MODE is a keyword specifying the element segment mode: 179 | 180 | :ACTIVE - An active element initialized on instantiation (the default) 181 | :PASSIVE - A passive element which can be used with TABLE.INIT 182 | :DECLARATIVE - A forward declaration of table contents. 183 | 184 | INDEX is the index of the table to initialize. This slot is only 185 | used when MODE is :ACTIVE. By default this is 0, which is the only 186 | valid index in the current version of WebAssembly. 187 | 188 | OFFSET is an expression (list of instructions) which compute the 189 | starting index of the range to initialize. This field is only used 190 | when MODE is :ACTIVE. 191 | 192 | INIT is an object specifying the initial values of the table 193 | elements, which can be of the following can be either a 194 | `WASM-ELEMENT-INIT-INDEX' or `WASM-ELEMENT-INIT-EXPRESSIONS' 195 | object." 196 | 197 | (mode :active) 198 | (index 0) 199 | offset 200 | init) 201 | 202 | (defstruct wasm-element-init-index 203 | "Represents a table element segment initialization with function indices. 204 | 205 | FUNCTIONS is a list of function indices to which the table elements 206 | are initialized, starting from the index OFFSET (of the WASM-ELEMENT object)." 207 | 208 | functions) 209 | 210 | (defstruct wasm-element-init-expressions 211 | "Represents a table element segment initialization with expressions. 212 | 213 | TYPE is the table element type, either FUNCREF (default) or 214 | EXTERNREF. In the current version of WebAssembly only FUNCREF is 215 | supported. 216 | 217 | EXPRESSIONS is a list of expressions, where each expression is a 218 | list of instructions, which compute the initial values of the table 219 | elements, starting from the index OFFSET (of the WASM-ELEMENT object)." 220 | 221 | (type 'funcref) 222 | expressions) 223 | 224 | (defstruct wasm-function 225 | "Represents a WebAssembly function. 226 | 227 | TYPE is the index of the functions type signature, within the type 228 | section. 229 | 230 | LOCALS is the list of the value types of the functions local 231 | variables. 232 | 233 | CODE is the list of instructions comprising the function." 234 | 235 | type 236 | locals 237 | code) 238 | 239 | (defstruct wasm-data 240 | "Represents a data segment. 241 | 242 | Data segments specify the initial values of memory objects. 243 | 244 | MODE is a keyword specifying the data segment mode: 245 | 246 | :ACTIVE - An active element initialized on module loading (the default) 247 | :PASSIVE - A passive element which can be used with MEMORY.INIT 248 | 249 | MEMORY is the index of the memory object to initialize. This field 250 | is only used when MODE is :ACTIVE. By default this is 0, which is 251 | the only valid index in the current version of WebAssembly. 252 | 253 | OFFSET is an expression (list of instructions) which compute the 254 | starting index of the range to initialize. This field is only used 255 | when MODE is :ACTIVE. 256 | 257 | BYTES is the byte array containing the values to which the bytes, 258 | starting at OFFSET (in the case of an active data segment), are 259 | initialized." 260 | 261 | (mode :active) 262 | offset 263 | bytes 264 | (memory 0)) 265 | 266 | (defstruct (wasm-table (:include wasm-limit)) 267 | "Represents a table object type. 268 | 269 | TYPE is the type of element stored in the table, either FUNCREF or 270 | EXTERNREF." 271 | 272 | (type 'funcref)) 273 | 274 | ;;;; Value Types 275 | 276 | (deftype u32 () 277 | `(integer 0 ,(1- (expt 2 32)))) 278 | 279 | (deftype i32 () 280 | `(integer ,(- (expt 2 31)) ,(1- (expt 2 31)))) 281 | 282 | (deftype u64 () 283 | `(integer 0 ,(1- (expt 2 64)))) 284 | 285 | (deftype i64 () 286 | `(integer ,(- (expt 2 63)) ,(1- (expt 2 63)))) 287 | 288 | 289 | ;;; Module Serialization 290 | 291 | (defun serialize-module (module stream) 292 | "Serialize a WebAssembly module to an output stream. 293 | 294 | MODULE is a `WASM-MODULE' object. 295 | 296 | STREAM is the output stream, which must be a binary stream with 297 | element type (unsigned-byte 8)." 298 | 299 | (write-sequence #(#x00 #x61 #x73 #x6D ; Magic Number 300 | #x01 #x00 #x00 #x00) ; Version Field 301 | stream) 302 | 303 | (with-struct-slots wasm-module- 304 | (types imports functions tables memory globals exports start elements data data-count) 305 | module 306 | 307 | ;; Function Type Indices 308 | (serialize-types types stream) 309 | 310 | ;; Import Section 311 | (serialize-imports imports stream) 312 | 313 | ;; Function Type Signatures 314 | (serialize-function-types 315 | (map #'wasm-function-type functions) 316 | stream) 317 | 318 | ;; Table and Memory Sections 319 | (serialize-table-types tables stream) 320 | (serialize-memory-types memory stream) 321 | 322 | ;; Global Variable Section 323 | (serialize-global-section globals stream) 324 | 325 | ;; Exports Section 326 | (serialize-export-section exports stream) 327 | 328 | ;; Start Section 329 | (serialize-start-section start stream) 330 | 331 | ;; Indirect Function Table Elements 332 | (serialize-table-elements elements stream) 333 | 334 | ;; Add data count section 335 | (when (and data-count (not (emptyp data))) 336 | (serialize-data-count-section (length data) stream)) 337 | 338 | ;; Function Code Section 339 | (serialize-functions functions stream) 340 | 341 | ;; Memory Data Section 342 | (serialize-data-section data stream))) 343 | 344 | 345 | ;;; Sections 346 | 347 | ;;;; Section Type Identifiers 348 | 349 | (defconstant +custom-section-id+ 0 350 | "Custom section identifier.") 351 | 352 | (defconstant +type-section-id+ 1 353 | "Function type signature section identifier.") 354 | 355 | (defconstant +import-section-id+ 2 356 | "Import section identifier.") 357 | 358 | (defconstant +function-section-id+ 3 359 | "Identifier of the section containing the types of the functions, 360 | in the module.") 361 | 362 | (defconstant +table-section-id+ 4 363 | "Table section identifier.") 364 | 365 | (defconstant +memory-section-id+ 5 366 | "Memory section identifier.") 367 | 368 | (defconstant +global-section-id+ 6 369 | "Global variable section identifier.") 370 | 371 | (defconstant +export-section-id+ 7 372 | "Export section identifier.") 373 | 374 | (defconstant +start-section-id+ 8 375 | "Module entry point section identifier.") 376 | 377 | (defconstant +element-section-id+ 9 378 | "Element section identifier.") 379 | 380 | (defconstant +code-section-id+ 10 381 | "Function body section identifier.") 382 | 383 | (defconstant +data-section-id+ 11 384 | "Data section identifier.") 385 | 386 | (defconstant +data-count-section-id+ 12 387 | "Data count section identifier.") 388 | 389 | 390 | ;;;; Serialization Functions 391 | 392 | (defun serialize-section (id fn stream) 393 | "Serialize a section, with identifier ID, to STREAM. 394 | 395 | FN is called with a single argument -- the stream to which the 396 | contents of the section should be written. The section id and the 397 | number of bytes are then written to STREAM followed by the bytes 398 | written by FN." 399 | 400 | (write-byte id stream) 401 | (serialize-with-byte-size fn stream)) 402 | 403 | (defun serialize-with-byte-size (fn stream) 404 | "Serialize a sequence of bytes to STREAM, preceded by the number of bytes. 405 | 406 | FN is called with a single argument -- the stream to which the 407 | contents of the section should be written. The number of bytes is 408 | then written to STREAM followed by the bytes written by FN." 409 | 410 | (let ((bytes 411 | (with-output-to-sequence (stream) 412 | (funcall fn stream)))) 413 | 414 | (serialize-u32 (length bytes) stream) 415 | (write-sequence bytes stream))) 416 | 417 | 418 | ;;;; Function Type Section 419 | 420 | (defun serialize-types (types stream) 421 | "Serialize the function type section consisting of the function type 422 | signatures, represented by WASM-FUNCTION-TYPE objects, in TYPES." 423 | 424 | (serialize-section 425 | +type-section-id+ 426 | 427 | (lambda (stream) 428 | (serialize-vector #'serialize-ftype types stream)) 429 | 430 | stream)) 431 | 432 | 433 | ;;;; Imports Section 434 | 435 | (defun serialize-imports (imports stream) 436 | "Serialize the import section, consisting of the import entries, 437 | represented by WASM-IMPORT objects, in IMPORTS." 438 | 439 | (serialize-section 440 | +import-section-id+ 441 | 442 | (lambda (stream) 443 | (serialize-vector #'serialize-import imports stream)) 444 | 445 | stream)) 446 | 447 | (defun serialize-import (import stream) 448 | "Serialize a single import entry IMPORT, represented by a 449 | WASM-IMPORT object." 450 | 451 | (check-type import wasm-import) 452 | 453 | (with-struct-slots wasm-import- (module name type desc) 454 | import 455 | 456 | (serialize-string module stream) 457 | (serialize-string name stream) 458 | 459 | (ecase type 460 | (:func 461 | (write-byte #x00 stream) 462 | (serialize-u32 desc stream)) 463 | 464 | (:table 465 | (write-byte #x01 stream) 466 | (serialize-table desc stream)) 467 | 468 | (:memory 469 | (write-byte #x02 stream) 470 | (serialize-memory desc stream)) 471 | 472 | (:global 473 | (write-byte #x03 stream) 474 | (serialize-global (first desc) (second desc) stream))))) 475 | 476 | 477 | ;;;; Table and Memory Sections 478 | 479 | (defun serialize-table-types (tables stream) 480 | "Serialize the table section, consisting of a table for each table 481 | limit, represented as a WASM-TABLE, given in TABLES." 482 | 483 | (serialize-section 484 | +table-section-id+ 485 | 486 | (lambda (stream) 487 | (serialize-vector #'serialize-table tables stream)) 488 | 489 | stream)) 490 | 491 | (defun serialize-memory-types (memory stream) 492 | "Serialize the memory section, consisting of a memory object for 493 | each limit, represented as a WASM-LIMIT, given in MEMORY." 494 | 495 | (serialize-section 496 | +memory-section-id+ 497 | 498 | (lambda (stream) 499 | (serialize-vector #'serialize-memory memory stream)) 500 | 501 | stream)) 502 | 503 | 504 | ;;;; Global Variable Section 505 | 506 | (defun serialize-global-section (globals stream) 507 | "Serialize the global variable section, consisting of the global 508 | variables, represented by WASM-GLOBAL objects, in GLOBALS." 509 | 510 | (flet ((serialize-global (global stream) 511 | (check-type global wasm-global) 512 | 513 | (with-struct-slots wasm-global- (type mutable-p init) 514 | global 515 | 516 | (serialize-global type mutable-p stream) 517 | (serialize-expression init stream)))) 518 | 519 | (serialize-section 520 | +global-section-id+ 521 | 522 | (lambda (stream) 523 | (serialize-vector #'serialize-global globals stream)) 524 | 525 | stream))) 526 | 527 | 528 | ;;;; Exports Section 529 | 530 | (defun serialize-export-section (exports stream) 531 | "Serialize the export section consisting of the entries, represented 532 | by WASM-EXPORT objects, in EXPORTS." 533 | 534 | (serialize-section 535 | +export-section-id+ 536 | 537 | (lambda (stream) 538 | (serialize-vector #'serialize-export exports stream)) 539 | 540 | stream)) 541 | 542 | (defun serialize-export (export stream) 543 | "Serialize a single export entry, represented as a WASM-EXPORT 544 | object." 545 | 546 | (check-type export wasm-export) 547 | 548 | (with-struct-slots wasm-export- (name type index) export 549 | (serialize-string name stream) 550 | 551 | (write-byte 552 | (ecase type 553 | (:func #x00) 554 | (:table #x01) 555 | (:memory #x02) 556 | (:global #x03)) 557 | stream) 558 | 559 | (serialize-u32 index stream))) 560 | 561 | 562 | ;;;; Start Section 563 | 564 | (defun serialize-start-section (index stream) 565 | (when index 566 | (check-type index (integer 0)) 567 | 568 | (serialize-section 569 | +start-section-id+ 570 | 571 | (lambda (stream) 572 | (serialize-u32 index stream)) 573 | 574 | stream))) 575 | 576 | 577 | ;;;; Table Element Section 578 | 579 | (defun serialize-table-elements (elements stream) 580 | "Serialize the table element section, consisting of the table 581 | element initializations, represented by WASM-ELEMNT objects, in 582 | ELEMENTS." 583 | 584 | (serialize-section 585 | +element-section-id+ 586 | 587 | (lambda (stream) 588 | (serialize-vector #'serialize-table-element elements stream)) 589 | 590 | stream)) 591 | 592 | (defun serialize-table-element (table stream) 593 | "Serialize a single table element initialization entry, represented 594 | by a WASM-ELEMENT object." 595 | 596 | (check-type table wasm-element) 597 | 598 | (with-struct-slots wasm-element- (index mode offset init) table 599 | (check-type index (integer 0)) 600 | (check-type mode (member :active :passive :declarative)) 601 | (check-type init (or wasm-element-init-index wasm-element-init-expressions)) 602 | 603 | ;; Serialize Type 604 | (write-byte (table-element-type-code table) stream) 605 | 606 | (when (= mode :active) 607 | (when (plusp index) 608 | (serialize-u32 index stream)) 609 | 610 | (serialize-expression offset stream)) 611 | 612 | (serialize-table-element-init mode index init stream))) 613 | 614 | (defun serialize-table-element-init (mode index init stream) 615 | "Serialize a table element initializer. 616 | 617 | MODE is the table element mode. 618 | 619 | INDEX is the table index. 620 | 621 | INIT is the table element initializer. 622 | 623 | STREAM is the output stream" 624 | 625 | (cond 626 | ((wasm-element-init-index-p init) 627 | 628 | (when (or (/= mode :active) (plusp index)) 629 | (write-byte #x00 stream)) 630 | 631 | (serialize-vector #'serialize-u32 (wasm-element-init-index-functions init) stream)) 632 | 633 | (t 634 | (when (or (/= mode :active) 635 | (plusp index) 636 | (/= 'funcref (intern-symbol (wasm-element-init-expressions-type init)))) 637 | 638 | (serialize-ref-type (wasm-element-init-expressions-type init) stream)) 639 | (serialize-vector #'serialize-expression (wasm-element-init-expressions-expressions init) stream)))) 640 | 641 | (defun table-element-type-code (element) 642 | "Return the type code for a table element. 643 | 644 | ELEMENT is the table element. 645 | 646 | Returns the type code as a byte." 647 | 648 | (with-struct-slots wasm-element- (index mode offset init) 649 | element 650 | 651 | (logior 652 | (if (member mode '(:passive :declarative)) 1 0) 653 | (if (or (= mode :declarative) (plusp index)) 654 | 2 0) 655 | (if (wasm-element-init-expressions-p init) 4 0)))) 656 | 657 | 658 | ;;;; Function Sections 659 | 660 | (defun serialize-function-types (types stream) 661 | "Serialize the function type section (containing the indices of the 662 | type signatures of the functions defined in the module), consisting 663 | of the function type indices in TYPES." 664 | 665 | (serialize-section 666 | +function-section-id+ 667 | 668 | (lambda (stream) 669 | (serialize-vector #'serialize-u32 types stream)) 670 | 671 | stream)) 672 | 673 | (defun serialize-functions (functions stream) 674 | "Serialize the code section (containing the actual instructions 675 | comprising the functions in the module), consisting of the 676 | functions, represented by WASM-FUNCTION objects, in FUNCTIONS." 677 | 678 | (serialize-section 679 | +code-section-id+ 680 | 681 | (lambda (stream) 682 | (serialize-vector #'serialize-function functions stream)) 683 | 684 | stream)) 685 | 686 | (defun serialize-function (function stream) 687 | "Serialize the a single function, represented by a WASM-FUNCTION 688 | object. Only the local variable types and instructions of the 689 | function are serialized." 690 | 691 | (labels ((serialize-local (local stream) 692 | (destructuring-bind (type . count) local 693 | (serialize-u32 count stream) 694 | (serialize-type type stream))) 695 | 696 | (group-locals (locals) 697 | (let ((it (iterator locals))) 698 | (loop 699 | until (endp it) 700 | collect (group-next it)))) 701 | 702 | (group-next (it) 703 | (loop 704 | with type = (at it) 705 | for count = 0 then (1+ count) 706 | until (or (endp it) (/= (at it) type)) 707 | do (advance it) 708 | finally (return (cons type count))))) 709 | 710 | (check-type function wasm-function) 711 | 712 | (with-struct-slots wasm-function- (locals code) 713 | function 714 | 715 | (serialize-with-byte-size 716 | (lambda (stream) 717 | (serialize-vector #'serialize-local (group-locals locals) stream) 718 | (serialize-expression code stream)) 719 | 720 | stream)))) 721 | 722 | 723 | ;;; Data Section 724 | 725 | (defun serialize-data-count-section (count stream) 726 | "Serialize the data count section. 727 | 728 | COUNT is the number of data segments. 729 | 730 | STREAM is the output stream." 731 | 732 | (serialize-section 733 | +data-count-section-id+ 734 | 735 | (lambda (stream) 736 | (serialize-u32 count stream)) 737 | 738 | stream)) 739 | 740 | (defun serialize-data-section (data stream) 741 | "Serialize the data section (containing the initialization of the 742 | module's memory), with the memory initialization 743 | entries (represented by WASM-DATA objects) in DATA." 744 | 745 | (serialize-section 746 | +data-section-id+ 747 | 748 | (lambda (stream) 749 | (serialize-vector #'serialize-data data stream)) 750 | 751 | stream)) 752 | 753 | (defun serialize-data (data stream) 754 | "Serialize a data segment." 755 | 756 | (check-type data wasm-data) 757 | 758 | (with-struct-slots wasm-data- (offset bytes memory mode) 759 | data 760 | 761 | (check-type memory (integer 0)) 762 | (check-type mode (member :active :passive)) 763 | 764 | (write-byte (data-segment-type-code data) stream) 765 | 766 | (when (= mode :active) 767 | (when (plusp memory) (serialize-u32 memory stream)) 768 | (serialize-expression offset stream)) 769 | 770 | (serialize-vector #'write-byte bytes stream))) 771 | 772 | (defun data-segment-type-code (data) 773 | "Determine the type code of a data segment." 774 | 775 | (with-struct-slots wasm-data- (mode memory) data 776 | (logior 777 | (if (= mode :passive) 1 0) 778 | (if (plusp memory) 2 0)))) 779 | 780 | 781 | ;;; Numbers 782 | 783 | ;;;; Floating Point 784 | 785 | (defun serialize-float (value stream) 786 | "Serialize a single FLOAT to IEEE 32-bit floating point 787 | representation." 788 | 789 | (check-type value single-float) 790 | 791 | (serialize-32-bit (encode-float32 value) stream)) 792 | 793 | (defun serialize-double-float (value stream) 794 | "Serialize a DOUBLE-FLOAT to IEEE 64-bit floating point 795 | representation." 796 | 797 | (check-type value float) 798 | 799 | (serialize-64-bit (encode-float64 value) stream)) 800 | 801 | 802 | ;;;; Integers 803 | 804 | (defun serialize-u32 (value stream) 805 | (check-type value u32) 806 | 807 | (serialize-unsigned value stream)) 808 | 809 | (defun serialize-i32 (value stream) 810 | (check-type value i32) 811 | 812 | (serialize-signed value stream)) 813 | 814 | 815 | (defun serialize-unsigned (value stream) 816 | "Serialize an unsigned integer to LEB128 format" 817 | 818 | (check-type value (integer 0)) 819 | 820 | (nlet encode ((n value)) 821 | (let ((b (ldb (byte 7 0) n)) 822 | (n (ash n -7))) 823 | (cond 824 | ((zerop n) 825 | (write-byte b stream)) 826 | 827 | (t 828 | (write-byte (logior b #x80) stream) 829 | (encode n)))))) 830 | 831 | (defun serialize-signed (value stream) 832 | "Serialize a signed integer to LEB128 format" 833 | 834 | (nlet encode ((n value)) 835 | (let ((b (ldb (byte 7 0) n)) 836 | (n (ash n -7))) 837 | 838 | (cond 839 | ((or (and (zerop n) (not (logbitp 6 b))) 840 | (and (= n -1) (logbitp 6 b))) 841 | (write-byte b stream)) 842 | 843 | (t 844 | (write-byte (logior b #x80) stream) 845 | (encode n)))))) 846 | 847 | 848 | (defun serialize-32-bit (value stream) 849 | "Serialize an integer value to 4 bytes in little endian order." 850 | 851 | (check-type value integer) 852 | 853 | (write-byte (ldb (byte 8 0) value) stream) 854 | (write-byte (ldb (byte 8 8) value) stream) 855 | (write-byte (ldb (byte 8 16) value) stream) 856 | (write-byte (ldb (byte 8 24) value) stream)) 857 | 858 | (defun serialize-64-bit (value stream) 859 | "Serialize an integer value to 8 bytes in little endian order." 860 | 861 | (check-type value integer) 862 | 863 | (write-byte (ldb (byte 8 0) value) stream) 864 | (write-byte (ldb (byte 8 8) value) stream) 865 | (write-byte (ldb (byte 8 16) value) stream) 866 | (write-byte (ldb (byte 8 24) value) stream) 867 | 868 | (write-byte (ldb (byte 8 32) value) stream) 869 | (write-byte (ldb (byte 8 40) value) stream) 870 | (write-byte (ldb (byte 8 48) value) stream) 871 | (write-byte (ldb (byte 8 56) value) stream)) 872 | 873 | 874 | ;;;; Strings 875 | 876 | (defun serialize-string (value stream) 877 | "Serialize a string to a UTF-8 encoded string without BOM." 878 | 879 | (let ((octets (string-to-octets value :encoding :utf-8 :use-bom nil))) 880 | (serialize-u32 (length octets) stream) 881 | (write-sequence octets stream))) 882 | 883 | 884 | ;;; Vectors 885 | 886 | (defun serialize-vector (fn vector stream) 887 | "Serialize a vector of values. 888 | 889 | The length VECTOR is written to STREAM first, after which FN is 890 | called on each element of the vector. FN should write each element 891 | it is called on to the stream passed to it in the second argument" 892 | 893 | (serialize-u32 (length vector) stream) 894 | (foreach (rcurry fn stream) vector)) 895 | 896 | 897 | ;;; Types 898 | 899 | (defun serialize-type (type stream) 900 | "Serialize a WebAssembly value type." 901 | 902 | (write-byte 903 | (ecase (intern-symbol type) 904 | (i32 #x7F) 905 | (i64 #x7E) 906 | (f32 #x7D) 907 | (f64 #x7C) 908 | 909 | (funcref #x70) 910 | (externref #x6F)) 911 | 912 | stream)) 913 | 914 | (defun serialize-ref-type (type stream) 915 | "Serialize a WebAssembly function reference type." 916 | 917 | (write-byte 918 | (ecase (intern-symbol type) 919 | (funcref #x70) 920 | (externref #x6F)) 921 | 922 | stream)) 923 | 924 | (defun serialize-ftype (type stream) 925 | "Serialize a function type signature, represented by a 926 | WASM-FUNCTION-TYPE object." 927 | 928 | (check-type type wasm-function-type) 929 | 930 | (with-struct-slots wasm-function-type- (params results) 931 | type 932 | 933 | (write-byte #x60 stream) 934 | (serialize-vector #'serialize-type params stream) 935 | (serialize-vector #'serialize-type results stream))) 936 | 937 | (defun intern-symbol (sym) 938 | "Intern the symbol SYM in the package WASM-ENCODER." 939 | 940 | (intern (symbol-name sym) (find-package :wasm-encoder))) 941 | 942 | (defun intern-symbols (syms) 943 | "If SYMS is a symbol intern it into WASM-ENCODER. If SYMS is a list, 944 | apply INTERN-SYMBOLS on each of element otherwise return SYMS as 945 | is." 946 | 947 | (match syms 948 | ((type list) 949 | (map #'intern-symbols syms)) 950 | 951 | ((type symbol) 952 | (intern-symbol syms)) 953 | 954 | (_ syms))) 955 | 956 | 957 | ;;; Memory and Table Declarations 958 | 959 | (defun serialize-memory (limit stream) 960 | "Serialize a memory object entry with limit, represented by a 961 | WASM-LIMIT object, given by LIMIT." 962 | 963 | (serialize-limit limit stream)) 964 | 965 | (defun serialize-table (table stream) 966 | "Serialize a table object entry with limit, represented by a 967 | WASM-LIMIT object, given by LIMIT." 968 | 969 | (check-type table wasm-table) 970 | 971 | (serialize-ref-type (wasm-table-type table) stream) 972 | (serialize-limit table stream)) 973 | 974 | (defun serialize-limit (limit stream) 975 | "Serialize a memory/table limit represented by a WASM-LIMIT object." 976 | 977 | (check-type limit wasm-limit) 978 | 979 | (with-struct-slots wasm-limit- (min max) limit 980 | (cond 981 | (max 982 | (write-byte #x01 stream) 983 | (serialize-u32 min stream) 984 | (serialize-u32 max stream)) 985 | 986 | (t 987 | (write-byte #x00 stream) 988 | (serialize-u32 min stream))))) 989 | 990 | 991 | ;;; Global Variables 992 | 993 | (defun serialize-global (type mutable? stream) 994 | "Serialize a global variable with value type TYPE. If MUTABLE? is 995 | true a mutable global variable is serialized." 996 | 997 | (serialize-type type stream) 998 | (write-byte 999 | (if mutable? #x01 #x00) 1000 | stream)) 1001 | 1002 | 1003 | ;;; Instructions 1004 | 1005 | (defconstant +op-codes+ 1006 | '((unreachable . #x00) 1007 | (nop . #x01) 1008 | 1009 | (block . #x02) 1010 | (loop . #x03) 1011 | (if . #x04) 1012 | 1013 | (br . #x0C) 1014 | (br_if . #x0D) 1015 | (br_table . #x0E) 1016 | (return . #x0F) 1017 | (call . #x10) 1018 | (call_indirect . #x11) 1019 | 1020 | (drop . #x1A) 1021 | (select . #x1B) 1022 | ((select) . #x1C) 1023 | 1024 | (local.get . #x20) 1025 | (local.set . #x21) 1026 | (local.tee . #x22) 1027 | (global.get . #x23) 1028 | (global.set . #x24) 1029 | 1030 | (table.get . #x25) 1031 | (table.set . #x26) 1032 | 1033 | (i32.load . #x28) 1034 | (i64.load . #x29) 1035 | (f32.load . #x2A) 1036 | (f64.load . #x2B) 1037 | 1038 | (i32.load8_s . #x2C) 1039 | (i32.load8_u . #x2D) 1040 | (i32.load16_s . #x2E) 1041 | (i32.load16_u . #x2F) 1042 | 1043 | (i64.load8_s . #x30) 1044 | (i64.load8_u . #x31) 1045 | (i64.load16_s . #x32) 1046 | (i64.load16_u . #x33) 1047 | (i64.load32_s . #x34) 1048 | (i64.load32_u . #x35) 1049 | 1050 | (i32.store . #x36) 1051 | (i64.store . #x37) 1052 | (f32.store . #x38) 1053 | (f64.store . #x39) 1054 | 1055 | (i32.store8 . #x3A) 1056 | (i32.store16 . #x3B) 1057 | (i64.store8 . #x3C) 1058 | (i64.store16 . #x3D) 1059 | (i64.store32 . #x3E) 1060 | 1061 | (memory.size . #x3F) 1062 | (memory.grow . #x40) 1063 | 1064 | (i32.const . #x41) 1065 | (i64.const . #x42) 1066 | (f32.const . #x43) 1067 | (f64.const . #x44) 1068 | 1069 | (i32.eqz . #x45) 1070 | (i32.eq . #x46) 1071 | (i32.ne . #x47) 1072 | (i32.lt_s . #x48) 1073 | (i32.lt_u . #x49) 1074 | (i32.gt_s . #x4A) 1075 | (i32.gt_u . #x4B) 1076 | (i32.le_s . #x4C) 1077 | (i32.le_u . #x4D) 1078 | (i32.ge_s . #x4E) 1079 | (i32.ge_u . #x4F) 1080 | 1081 | (i64.eqz . #x50) 1082 | (i64.eq . #x51) 1083 | (i64.ne . #x52) 1084 | (i64.lt_s . #x53) 1085 | (i64.lt_u . #x54) 1086 | (i64.gt_s . #x55) 1087 | (i64.gt_u . #x56) 1088 | (i64.le_s . #x57) 1089 | (i64.le_u . #x58) 1090 | (i64.ge_s . #x59) 1091 | (i64.ge_u . #x5A) 1092 | 1093 | (f32.eq . #x5B) 1094 | (f32.ne . #x5C) 1095 | (f32.lt . #x5D) 1096 | (f32.gt . #x5E) 1097 | (f32.le . #x5F) 1098 | (f32.ge . #x60) 1099 | 1100 | (f64.eq . #x61) 1101 | (f64.ne . #x62) 1102 | (f64.lt . #x63) 1103 | (f64.gt . #x64) 1104 | (f64.le . #x65) 1105 | (f64.ge . #x66) 1106 | 1107 | (i32.clz . #x67) 1108 | (i32.ctz . #x68) 1109 | (i32.popcnt . #x69) 1110 | (i32.add . #x6A) 1111 | (i32.sub . #x6B) 1112 | (i32.mul . #x6C) 1113 | (i32.div_s . #x6D) 1114 | (i32.div_u . #x6E) 1115 | (i32.rem_s . #x6F) 1116 | (i32.rem_u . #x70) 1117 | (i32.and . #x71) 1118 | (i32.or . #x72) 1119 | (i32.xor . #x73) 1120 | (i32.shl . #x74) 1121 | (i32.shr_s . #x75) 1122 | (i32.shr_u . #x76) 1123 | (i32.rotl . #x77) 1124 | (i32.rotr . #x78) 1125 | 1126 | (i64.clz . #x79) 1127 | (i64.ctz . #x7A) 1128 | (i64.popcnt . #x7B) 1129 | (i64.add . #x7C) 1130 | (i64.sub . #x7D) 1131 | (i64.mul . #x7E) 1132 | (i64.div_s . #x7F) 1133 | (i64.div_u . #x80) 1134 | (i64.rem_s . #x81) 1135 | (i64.rem_u . #x82) 1136 | (i64.and . #x83) 1137 | (i64.or . #x84) 1138 | (i64.xor . #x85) 1139 | (i64.shl . #x86) 1140 | (i64.shr_s . #x87) 1141 | (i64.shr_u . #x88) 1142 | (i64.rotl . #x89) 1143 | (i64.rotr . #x8A) 1144 | 1145 | (f32.abs . #x8B) 1146 | (f32.neg . #x8C) 1147 | (f32.ceil . #x8D) 1148 | (f32.floor . #x8E) 1149 | (f32.trunc . #x8F) 1150 | (f32.nearest . #x90) 1151 | (f32.sqrt . #x91) 1152 | (f32.add . #x92) 1153 | (f32.sub . #x93) 1154 | (f32.mul . #x94) 1155 | (f32.div . #x95) 1156 | (f32.min . #x96) 1157 | (f32.max . #x97) 1158 | (f32.copysign . #x98) 1159 | 1160 | (f64.abs . #x99) 1161 | (f64.neg . #x9A) 1162 | (f64.ceil . #x9B) 1163 | (f64.trunc . #x9C) 1164 | (f64.floor . #x9D) 1165 | (f64.nearest . #x9E) 1166 | (f64.sqrt . #x9F) 1167 | (f64.add . #xA0) 1168 | (f64.sub . #xA1) 1169 | (f64.mul . #xA2) 1170 | (f64.div . #xA3) 1171 | (f64.min . #xA4) 1172 | (f64.max . #xA5) 1173 | (f64.copysign . #xA6) 1174 | 1175 | (i32.wrap_i64 . #xA7) 1176 | (i32.trunc_f32_s . #xA8) 1177 | (i32.trunc_f32_u . #xA9) 1178 | (i32.trunc_f64_s . #xAA) 1179 | (i32.trunc_f64_u . #xAB) 1180 | (i64.extend_i32_s . #xAC) 1181 | (i64.extend_i32_u . #xAD) 1182 | (i64.trunc_f32_s . #xAE) 1183 | (i64.trunc_f32_u . #xAF) 1184 | (i64.trunc_f64_s . #xB0) 1185 | (i64.trunc_f64_u . #xB1) 1186 | 1187 | (f32.convert_i32_s . #xB2) 1188 | (f32.convert_i32_u . #xB3) 1189 | (f32.convert_i64_s . #xB4) 1190 | (f32.convert_i64_u . #xB5) 1191 | (f32.demote_f64 . #xB6) 1192 | 1193 | (f64.convert_i32_s . #xB7) 1194 | (f64.convert_i32_u . #xB8) 1195 | (f64.convert_i64_s . #xB9) 1196 | (f64.convert_i64_u . #xBA) 1197 | (f64.promote_f64 . #xBB) 1198 | 1199 | (i32.reinterpret_f32 . #xBC) 1200 | (i64.reinterpret_f64 . #xBD) 1201 | (f32.reinterpret_i32 . #xBE) 1202 | (f64.reinterpret_i64 . #xBF) 1203 | 1204 | (i32.extend8_s . #xC0) 1205 | (i32.extend16_s . #xC1) 1206 | (i64.extend8_s . #xC2) 1207 | (i64.extend16_s . #xC3) 1208 | (i64.extend32_s . #xC4) 1209 | 1210 | (ref.null . #xD0) 1211 | (ref.is_null . #xD1) 1212 | (ref.func . #xD2) 1213 | 1214 | (i32.trunc_sat_f32_s . (#xFC #X00)) 1215 | (i32.trunc_sat_f32_u . (#xFC #X01)) 1216 | (i32.trunc_sat_f64_s . (#xFC #X02)) 1217 | (i32.trunc_sat_f64_u . (#xFC #X03)) 1218 | (i64.trunc_sat_f32_s . (#xFC #X04)) 1219 | (i64.trunc_sat_f32_u . (#xFC #X05)) 1220 | (i64.trunc_sat_f64_s . (#xFC #X06)) 1221 | (i64.trunc_sat_f64_u . (#xFC #X07)) 1222 | 1223 | (memory.init . (#xFC #x08)) 1224 | (data.drop . (#xFC #x09)) 1225 | (memory.copy . (#xFC #x0A)) 1226 | (memory.fill . (#xFC #x0B)) 1227 | 1228 | (table.init . (#xFC #x0C)) 1229 | (elem.drop . (#xFC #x0D)) 1230 | (table.copy . (#xFC #x0E)) 1231 | (table.grow . (#xFC #x0F)) 1232 | (table.size . (#xFC #x10)) 1233 | (table.fill . (#xFC #x11))) 1234 | 1235 | "WebAssembly Instruction Opcode Map.") 1236 | 1237 | (defun serialize-instructions (instructions stream) 1238 | "Serialize the instructions, in INSTRUCTIONS, to STREAM." 1239 | 1240 | (foreach (rcurry #'serialize-instruction stream) instructions)) 1241 | 1242 | (defun serialize-instruction (instruction stream) 1243 | "Serialize a WebAssembly instruction to STREAM. The instruction may 1244 | either be a symbol, naming the instruction, or a list in which the 1245 | first element is the symbol naming the instruction and the 1246 | remaining elements are the instruction operands." 1247 | 1248 | (match instruction 1249 | ((or (list* op operands) op) 1250 | 1251 | (let* ((op (intern-symbol op)) 1252 | (opcode (or 1253 | (when (listp instruction) (get (list op) +op-codes+)) 1254 | (get op +op-codes+)))) 1255 | 1256 | (assert opcode (opcode) "Unknown instruction ~a" op) 1257 | 1258 | (write-sequence (ensure-list opcode) stream) 1259 | (serialize-instruction-operands op (intern-symbols operands) stream))))) 1260 | 1261 | (defgeneric serialize-instruction-operands (op operands stream) 1262 | (:documentation 1263 | "Serialize the operands (OPERANDS) of the instruction OP.")) 1264 | 1265 | (defmethod serialize-instruction-operands ((op t) operands stream) 1266 | "Default Method, does nothing." 1267 | 1268 | (declare (ignore operands stream)) 1269 | (assert (null operands) (operands) "Instruction ~a does not take immediate operands" op) 1270 | nil) 1271 | 1272 | ;;;; Blocks 1273 | 1274 | (defmethod serialize-instruction-operands ((op (eql 'block)) body stream) 1275 | (serialize-block body stream)) 1276 | 1277 | (defmethod serialize-instruction-operands ((op (eql 'loop)) body stream) 1278 | (serialize-block body stream)) 1279 | 1280 | 1281 | (defun serialize-block (body stream) 1282 | "Serialize a structured block instruction containing the 1283 | instructions in BODY." 1284 | 1285 | (match body 1286 | ((list* (list 'result type) body) 1287 | (serialize-type type stream) 1288 | (serialize-instructions body stream)) 1289 | 1290 | ((list* (list 'type index) body) 1291 | (check-type index (integer 0)) 1292 | (serialize-signed index stream) 1293 | (serialize-instructions body stream)) 1294 | 1295 | (_ 1296 | (serialize-nil-type stream) 1297 | (serialize-instructions body stream))) 1298 | 1299 | (write-byte #x0B stream)) 1300 | 1301 | (defun serialize-nil-type (stream) 1302 | (write-byte #x40 stream)) 1303 | 1304 | 1305 | ;;;; Parametric Instructions 1306 | 1307 | (defmethod serialize-instruction-operands ((op (eql 'select)) operands stream) 1308 | "Serialize the value types of the a select t* instruction" 1309 | 1310 | (unless (emptyp operands) 1311 | (serialize-vector #'serialize-type operands stream))) 1312 | 1313 | 1314 | ;;;; If/Then/Else Blocks 1315 | 1316 | (defmethod serialize-instruction-operands ((op (eql 'if)) body stream) 1317 | "Serialize the body of an IF structured instruction." 1318 | 1319 | (flet ((serialize-body (body) 1320 | (ematch body 1321 | ((list (list* 'then then) 1322 | (list* 'else else)) 1323 | 1324 | (serialize-instructions then stream) 1325 | (write-byte #x05 stream) 1326 | (serialize-instructions else stream)) 1327 | 1328 | ((list (list* 'then then)) 1329 | (serialize-instructions then stream))))) 1330 | 1331 | (match body 1332 | ((list* (list 'result type) body) 1333 | (serialize-type type stream) 1334 | (serialize-body body)) 1335 | 1336 | ((list* (list 'type index) body) 1337 | (check-type index (integer 0)) 1338 | (serialize-signed index stream) 1339 | (serialize-body body)) 1340 | 1341 | (_ 1342 | (serialize-nil-type stream) 1343 | (serialize-body body))) 1344 | 1345 | (write-byte #x0B stream))) 1346 | 1347 | 1348 | ;;;; Branch Instructions 1349 | 1350 | (defmethod serialize-instruction-operands ((op (eql 'br)) operands stream) 1351 | (serialize-branch operands stream)) 1352 | 1353 | (defmethod serialize-instruction-operands ((op (eql 'br_if)) operands stream) 1354 | (serialize-branch operands stream)) 1355 | 1356 | (defmethod serialize-instruction-operands ((op (eql 'call)) operands stream) 1357 | (serialize-branch operands stream)) 1358 | 1359 | (defun serialize-branch (operands stream) 1360 | "Serialize the branch index of a branch instruction." 1361 | 1362 | (destructuring-bind (index) operands 1363 | (check-type index (integer 0)) 1364 | 1365 | (serialize-u32 index stream))) 1366 | 1367 | 1368 | (defmethod serialize-instruction-operands ((op (eql 'br_table)) operands stream) 1369 | "Serialize the branches indices of a branch table instruction." 1370 | 1371 | (assert (not (emptyp operands))) 1372 | 1373 | (serialize-u32 (1- (length operands)) stream) 1374 | (foreach (rcurry #'serialize-u32 stream) operands)) 1375 | 1376 | 1377 | ;;;; Call Indirect 1378 | 1379 | (defmethod serialize-instruction-operands ((op (eql 'call_indirect)) operands stream) 1380 | "Serialize the type signature of a call indirect instruction." 1381 | 1382 | (destructuring-bind (type &optional (table 0)) operands 1383 | (check-type type (integer 0)) 1384 | 1385 | (serialize-u32 type stream) 1386 | (write-byte table stream))) 1387 | 1388 | 1389 | ;;;; Variables 1390 | 1391 | (defmethod serialize-instruction-operands ((op (eql 'local.get)) operands stream) 1392 | (serialize-variable-instruction operands stream)) 1393 | 1394 | (defmethod serialize-instruction-operands ((op (eql 'local.set)) operands stream) 1395 | (serialize-variable-instruction operands stream)) 1396 | 1397 | (defmethod serialize-instruction-operands ((op (eql 'local.tee)) operands stream) 1398 | (serialize-variable-instruction operands stream)) 1399 | 1400 | (defmethod serialize-instruction-operands ((op (eql 'global.get)) operands stream) 1401 | (serialize-variable-instruction operands stream)) 1402 | 1403 | (defmethod serialize-instruction-operands ((op (eql 'global.set)) operands stream) 1404 | (serialize-variable-instruction operands stream)) 1405 | 1406 | (defmethod serialize-variable-instruction (operands stream) 1407 | "Serialize the local variable index of a local variable 1408 | instruction." 1409 | 1410 | (destructuring-bind (index) operands 1411 | (check-type index (integer 0)) 1412 | 1413 | (serialize-u32 index stream))) 1414 | 1415 | 1416 | ;;;; Tables 1417 | 1418 | (defmethod serialize-instruction-operands ((op (eql 'table.get)) operands stream) 1419 | (serialize-table-instruction operands stream)) 1420 | 1421 | (defmethod serialize-instruction-operands ((op (eql 'table.set)) operands stream) 1422 | (serialize-table-instruction operands stream)) 1423 | 1424 | (defmethod serialize-table-instruction (operands stream) 1425 | "Serialize the table index of a table instruction." 1426 | 1427 | (destructuring-bind (index) operands 1428 | (check-type index (integer 0)) 1429 | 1430 | (serialize-u32 index stream))) 1431 | 1432 | (defmethod serialize-instruction-operands ((op (eql 'table.init)) operands stream) 1433 | (destructuring-bind (table elem) operands 1434 | (check-type table (integer 0)) 1435 | (check-type elem (integer 0)) 1436 | 1437 | (serialize-u32 elem stream) 1438 | (serialize-u32 table stream))) 1439 | 1440 | (defmethod serialize-instruction-operands ((op (eql 'table.copy)) operands stream) 1441 | (destructuring-bind (elem table) operands 1442 | (check-type elem (integer 0)) 1443 | (check-type table (integer 0)) 1444 | 1445 | (serialize-u32 elem stream) 1446 | (serialize-u32 table stream))) 1447 | 1448 | (defmethod serialize-instruction-operands ((op (eql 'elem.drop)) operands stream) 1449 | (serialize-table-instruction operands stream)) 1450 | 1451 | (defmethod serialize-instruction-operands ((op (eql 'table.grow)) operands stream) 1452 | (serialize-table-instruction operands stream)) 1453 | 1454 | (defmethod serialize-instruction-operands ((op (eql 'table.size)) operands stream) 1455 | (serialize-table-instruction operands stream)) 1456 | 1457 | (defmethod serialize-instruction-operands ((op (eql 'table.fill)) operands stream) 1458 | (serialize-table-instruction operands stream)) 1459 | 1460 | ;;;; Memory Load and Store 1461 | 1462 | (defmethod serialize-instruction-operands ((op (eql 'i32.load)) operands stream) 1463 | (serialize-memory-instruction operands stream)) 1464 | 1465 | (defmethod serialize-instruction-operands ((op (eql 'i64.load)) operands stream) 1466 | (serialize-memory-instruction operands stream)) 1467 | 1468 | (defmethod serialize-instruction-operands ((op (eql 'f32.load)) operands stream) 1469 | (serialize-memory-instruction operands stream)) 1470 | 1471 | (defmethod serialize-instruction-operands ((op (eql 'f64.load)) operands stream) 1472 | (serialize-memory-instruction operands stream)) 1473 | 1474 | (defmethod serialize-instruction-operands ((op (eql 'i32.load8_s)) operands stream) 1475 | (serialize-memory-instruction operands stream)) 1476 | 1477 | (defmethod serialize-instruction-operands ((op (eql 'i32.load8_u)) operands stream) 1478 | (serialize-memory-instruction operands stream)) 1479 | 1480 | (defmethod serialize-instruction-operands ((op (eql 'i32.load16_s)) operands stream) 1481 | (serialize-memory-instruction operands stream)) 1482 | 1483 | (defmethod serialize-instruction-operands ((op (eql 'i32.load16_u)) operands stream) 1484 | (serialize-memory-instruction operands stream)) 1485 | 1486 | (defmethod serialize-instruction-operands ((op (eql 'i64.load8_s)) operands stream) 1487 | (serialize-memory-instruction operands stream)) 1488 | 1489 | (defmethod serialize-instruction-operands ((op (eql 'i64.load8_u)) operands stream) 1490 | (serialize-memory-instruction operands stream)) 1491 | 1492 | (defmethod serialize-instruction-operands ((op (eql 'i64.load16_s)) operands stream) 1493 | (serialize-memory-instruction operands stream)) 1494 | 1495 | (defmethod serialize-instruction-operands ((op (eql 'i64.load16_u)) operands stream) 1496 | (serialize-memory-instruction operands stream)) 1497 | 1498 | (defmethod serialize-instruction-operands ((op (eql 'i64.load32_s)) operands stream) 1499 | (serialize-memory-instruction operands stream)) 1500 | 1501 | (defmethod serialize-instruction-operands ((op (eql 'i64.load32_u)) operands stream) 1502 | (serialize-memory-instruction operands stream)) 1503 | 1504 | (defmethod serialize-instruction-operands ((op (eql 'i32.store)) operands stream) 1505 | (serialize-memory-instruction operands stream)) 1506 | 1507 | (defmethod serialize-instruction-operands ((op (eql 'i64.store)) operands stream) 1508 | (serialize-memory-instruction operands stream)) 1509 | 1510 | (defmethod serialize-instruction-operands ((op (eql 'f32.store)) operands stream) 1511 | (serialize-memory-instruction operands stream)) 1512 | 1513 | (defmethod serialize-instruction-operands ((op (eql 'f64.store)) operands stream) 1514 | (serialize-memory-instruction operands stream)) 1515 | 1516 | (defmethod serialize-instruction-operands ((op (eql 'i32.store8)) operands stream) 1517 | (serialize-memory-instruction operands stream)) 1518 | 1519 | (defmethod serialize-instruction-operands ((op (eql 'i32.store16)) operands stream) 1520 | (serialize-memory-instruction operands stream)) 1521 | 1522 | (defmethod serialize-instruction-operands ((op (eql 'i64.store8)) operands stream) 1523 | (serialize-memory-instruction operands stream)) 1524 | 1525 | (defmethod serialize-instruction-operands ((op (eql 'i64.store16)) operands stream) 1526 | (serialize-memory-instruction operands stream)) 1527 | 1528 | (defmethod serialize-instruction-operands ((op (eql 'i64.store32)) operands stream) 1529 | (serialize-memory-instruction operands stream)) 1530 | 1531 | (defun serialize-memory-instruction (operands stream) 1532 | "Serialize the offset and alignment operands of a memory load/store 1533 | instruction." 1534 | 1535 | (let ((alignment (or (second (assoc 'align operands)) 2)) 1536 | (offset (or (second (assoc 'offset operands)) 0))) 1537 | 1538 | (check-type alignment (integer 0)) 1539 | (check-type offset (integer 0)) 1540 | 1541 | (serialize-u32 alignment stream) 1542 | (serialize-u32 offset stream))) 1543 | 1544 | 1545 | ;;;; Memory Pages 1546 | 1547 | (defmethod serialize-instruction-operands ((op (eql 'memory.size)) operands stream) 1548 | (assert (null operands)) 1549 | (write-byte #x00 stream)) 1550 | 1551 | (defmethod serialize-instruction-operands ((op (eql 'memory.grow)) operands stream) 1552 | (assert (null operands)) 1553 | (write-byte #x00 stream)) 1554 | 1555 | (defmethod serialize-instruction-operands ((op (eql 'memory.copy)) operands stream) 1556 | (assert (null operands)) 1557 | (write-sequence #(#x00 #x00) stream)) 1558 | 1559 | (defmethod serialize-instruction-operands ((op (eql 'memory.fill)) operands stream) 1560 | (assert (null operands)) 1561 | (write-byte #x00 stream)) 1562 | 1563 | (defmethod serialize-instruction-operands ((op (eql 'memory.init)) operands stream) 1564 | (destructuring-bind (dataidx) operands 1565 | (check-type dataidx (integer 0)) 1566 | (serialize-u32 dataidx stream) 1567 | (write-byte #x00 stream))) 1568 | 1569 | (defmethod serialize-instruction-operands ((op (eql 'data.drop)) operands stream) 1570 | (destructuring-bind (dataidx) operands 1571 | (check-type dataidx (integer 0)) 1572 | (serialize-u32 dataidx stream))) 1573 | 1574 | ;;; Constants 1575 | 1576 | (defmethod serialize-instruction-operands ((op (eql 'i32.const)) operands stream) 1577 | (destructuring-bind (constant) operands 1578 | (etypecase constant 1579 | (i32 (serialize-i32 constant stream)) 1580 | (u32 (serialize-signed (logior (- (expt 2 32)) constant) stream))))) 1581 | 1582 | (defmethod serialize-instruction-operands ((op (eql 'i64.const)) operands stream) 1583 | (destructuring-bind (constant) operands 1584 | (etypecase constant 1585 | (i64 (serialize-signed constant stream)) 1586 | (u64 (serialize-signed (logior (- (expt 2 64)) constant) stream))))) 1587 | 1588 | (defmethod serialize-instruction-operands ((op (eql 'f32.const)) operands stream) 1589 | (destructuring-bind (constant) operands 1590 | (serialize-float constant stream))) 1591 | 1592 | (defmethod serialize-instruction-operands ((op (eql 'f64.const)) operands stream) 1593 | (destructuring-bind (constant) operands 1594 | (serialize-double-float constant stream))) 1595 | 1596 | 1597 | ;;; Reference Instructions 1598 | 1599 | (defmethod serialize-instruction-operands ((op (eql 'ref.null)) operands stream) 1600 | (destructuring-bind (type) operands 1601 | (serialize-ref-type type stream))) 1602 | 1603 | (defmethod serialize-instruction-operands ((op (eql 'ref.func)) operands stream) 1604 | (destructuring-bind (index) operands 1605 | (serialize-u32 index stream))) 1606 | 1607 | 1608 | ;;; Expressions 1609 | 1610 | (defun serialize-expression (instructions stream) 1611 | "Serialize a list of instructions (an expression) followed by the 1612 | block end instruction." 1613 | 1614 | (serialize-instructions instructions stream) 1615 | (write-byte #x0B stream)) 1616 | -------------------------------------------------------------------------------- /test/instructions.lisp: -------------------------------------------------------------------------------- 1 | ;;; instructions.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020-2021 Alexander Gutev 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 | 23 | (defpackage wasm-encoder/test.instructions 24 | (:use #:generic-cl 25 | #:wasm-encoder 26 | #:agutil 27 | #:alexandria 28 | #:trivia 29 | 30 | #:fiveam 31 | #:wasm-encoder/test) 32 | 33 | (:shadowing-import-from #:generic-cl 34 | #:emptyp 35 | #:multiply 36 | #:accumulate) 37 | 38 | (:import-from #:flexi-streams 39 | #:with-output-to-sequence) 40 | 41 | (:import-from #:wasm-encoder 42 | #:serialize-u32 43 | #:serialize-i32 44 | 45 | #:serialize-float 46 | #:serialize-double-float 47 | 48 | #:serialize-string 49 | #:serialize-vector 50 | 51 | #:serialize-type 52 | #:serialize-ftype 53 | 54 | #:serialize-limit 55 | #:serialize-memory 56 | #:serialize-table 57 | 58 | #:serialize-global 59 | 60 | #:serialize-instruction 61 | #:serialize-instructions 62 | #:serialize-expression 63 | 64 | #:serialize-types 65 | #:serialize-imports 66 | #:serialize-function-types 67 | #:serialize-table-types 68 | #:serialize-memory-types 69 | #:serialize-global-section 70 | #:serialize-export-section 71 | #:serialize-start-section 72 | #:serialize-table-elements 73 | #:serialize-functions 74 | #:serialize-data-section)) 75 | 76 | (in-package #:wasm-encoder/test.instructions) 77 | 78 | 79 | ;;; Test Suite Definition 80 | 81 | (def-suite instructions 82 | :description "Test encoding of instructions" 83 | :in wasm-encoder) 84 | 85 | (in-suite instructions) 86 | 87 | 88 | ;;; Test Control Instructions 89 | 90 | (test unreachable 91 | "Test serialization of UNREACHABLE" 92 | 93 | (test-encoding stream 94 | (serialize-instruction 'unreachable stream) 95 | #(#x00))) 96 | 97 | (test nop 98 | "Test serialization of NOP" 99 | 100 | (test-encoding stream 101 | (serialize-instruction 'nop stream) 102 | #(#x01))) 103 | 104 | (test return 105 | "Test serialization of RETURN" 106 | 107 | (test-encoding stream 108 | (serialize-instruction 'return stream) 109 | #(#x0F))) 110 | 111 | 112 | ;;; Test Branch Instructions 113 | 114 | (test br 115 | "Test serialization of BR" 116 | 117 | (test-encoding stream 118 | (serialize-instruction '(br 0) stream) 119 | #(#x0C 0)) 120 | 121 | (test-encoding stream 122 | (serialize-instruction '(br 10) stream) 123 | #(#x0C 10)) 124 | 125 | (test-encoding stream 126 | (serialize-instruction '(br 300) stream) 127 | #(#x0C #xAC #x02))) 128 | 129 | (test br-errors 130 | "Test errors in serialization of BR" 131 | 132 | (test-error stream 133 | (serialize-instruction '(br -10) stream) 134 | type-error) 135 | 136 | (test-error stream 137 | (serialize-instruction 'br stream) 138 | error)) 139 | 140 | (test br_if 141 | "Test serialization of BR_IF" 142 | 143 | (test-encoding stream 144 | (serialize-instruction '(br_if 0) stream) 145 | #(#x0D 0)) 146 | 147 | (test-encoding stream 148 | (serialize-instruction '(br_if 10) stream) 149 | #(#x0D 10)) 150 | 151 | (test-encoding stream 152 | (serialize-instruction '(br_if 300) stream) 153 | #(#x0D #xAC #x02))) 154 | 155 | (test br_if-errors 156 | "Test errors in serialization of BR_IF" 157 | 158 | (test-error stream 159 | (serialize-instruction '(br_if -10) stream) 160 | type-error) 161 | 162 | (test-error stream 163 | (serialize-instruction 'br_if stream) 164 | error)) 165 | 166 | (test br_table 167 | "Test serialization of BR_TABLE" 168 | 169 | (test-encoding stream 170 | (serialize-instruction '(br_table 1 0 5) stream) 171 | 172 | #(#x0E #x02 1 0 5)) 173 | 174 | (test-encoding stream 175 | (serialize-instruction '(br_table 1 5 300 0 4) stream) 176 | 177 | #(#x0E #x04 1 5 #xAC #x02 0 4))) 178 | 179 | (test br_table-errors 180 | "Test errors in serialization of BR_TABLE" 181 | 182 | (test-error stream 183 | (serialize-instruction '(br_table 1 6 -3 4) stream) 184 | type-error) 185 | 186 | (test-error stream 187 | (serialize-instruction 'br_table stream) 188 | error)) 189 | 190 | 191 | ;;; Test Call Instructions 192 | 193 | (test call 194 | "Test serialization of CALL" 195 | 196 | (test-encoding stream 197 | (serialize-instruction '(call 0) stream) 198 | #(#x10 0)) 199 | 200 | (test-encoding stream 201 | (serialize-instruction '(call 10) stream) 202 | #(#x10 10)) 203 | 204 | (test-encoding stream 205 | (serialize-instruction '(call 301) stream) 206 | #(#x10 #xAD #x02))) 207 | 208 | (test call-errors 209 | "Test errors in serialization of CALL" 210 | 211 | (test-error stream 212 | (serialize-instruction '(call -10) stream) 213 | type-error) 214 | 215 | (test-error stream 216 | (serialize-instruction 'call stream) 217 | error)) 218 | 219 | (test call_indirect 220 | "Test serialization of CALL_INDIRECT" 221 | 222 | (test-encoding stream 223 | (serialize-instruction '(call_indirect 0) stream) 224 | #(#x11 0 #x00)) 225 | 226 | (test-encoding stream 227 | (serialize-instruction '(call_indirect 10) stream) 228 | #(#x11 10 #x00)) 229 | 230 | (test-encoding stream 231 | (serialize-instruction '(call_indirect 301) stream) 232 | #(#x11 #xAD #x02 #x00))) 233 | 234 | (test call_indirect-errors 235 | "Test errors in serialization of CALL_INDIRECT" 236 | 237 | (test-error stream 238 | (serialize-instruction '(call_indirect -10) stream) 239 | type-error) 240 | 241 | (test-error stream 242 | (serialize-instruction 'call_indirect stream) 243 | error)) 244 | 245 | 246 | ;;; Test Structured Blocks 247 | 248 | (test block-no-result-type 249 | "Test serialization of BLOCK with no result type" 250 | 251 | (test-encoding stream 252 | (serialize-instruction 253 | '(block 254 | nop 255 | unreachable 256 | (br 1)) 257 | stream) 258 | 259 | #(#x02 #x40 260 | #x01 261 | #x00 262 | #x0C 1 263 | #x0B))) 264 | 265 | (test block-with-result-type 266 | "Test serialization of BLOCK with result type" 267 | 268 | (test-encoding stream 269 | (serialize-instruction 270 | '(block (result i32) 271 | nop 272 | (br 1) 273 | unreachable) 274 | stream) 275 | 276 | #(#x02 #x7F 277 | #x01 278 | #x0C 1 279 | #x00 280 | #x0B))) 281 | 282 | (test block-with-type-index 283 | "Test serialization of BLOCK with function type index" 284 | 285 | (test-encoding stream 286 | (serialize-instruction 287 | '(block (type #x77) 288 | nop 289 | (br 1) 290 | unreachable) 291 | stream) 292 | 293 | #(#x02 #xF7 #x00 294 | #x01 295 | #x0C 1 296 | #x00 297 | #x0B))) 298 | 299 | (test loop-no-result-type 300 | "Test serialization of LOOP with no result type" 301 | 302 | (test-encoding stream 303 | (serialize-instruction 304 | '(loop 305 | nop 306 | unreachable 307 | (br 1)) 308 | stream) 309 | 310 | #(#x03 #x40 311 | #x01 312 | #x00 313 | #x0C 1 314 | #x0B))) 315 | 316 | (test loop-with-result-type 317 | "Test serialization of LOOP with result type" 318 | 319 | (test-encoding stream 320 | (serialize-instruction 321 | '(loop (result i32) 322 | nop 323 | (br 1) 324 | unreachable) 325 | stream) 326 | 327 | #(#x03 #x7F 328 | #x01 329 | #x0C 1 330 | #x00 331 | #x0B))) 332 | 333 | (test loop-with-type-index 334 | "Test serialization of LOOP with function type index" 335 | 336 | (test-encoding stream 337 | (serialize-instruction 338 | '(loop (type #x35) 339 | nop 340 | (br 1) 341 | unreachable) 342 | stream) 343 | 344 | #(#x03 #x35 345 | #x01 346 | #x0C 1 347 | #x00 348 | #x0B))) 349 | 350 | (test if-no-else-no-result-type 351 | "Test serialization of IF without else branch without result type" 352 | 353 | (test-encoding stream 354 | (serialize-instruction 355 | '(if 356 | (then 357 | nop 358 | unreachable 359 | (br 1))) 360 | stream) 361 | 362 | #(#x04 #x40 363 | #x01 364 | #x00 365 | #x0C 1 366 | #x0B))) 367 | 368 | (test if-no-else-with-result-type 369 | "Test serialization of IF without else branch with result type" 370 | 371 | (test-encoding stream 372 | (serialize-instruction 373 | '(if (result i32) 374 | (then 375 | nop 376 | (br 1) 377 | unreachable)) 378 | stream) 379 | 380 | #(#x04 #x7F 381 | #x01 382 | #x0C 1 383 | #x00 384 | #x0B))) 385 | 386 | (test if-no-else-with-type-index 387 | "Test serialization of IF without else branch with function type index" 388 | 389 | (test-encoding stream 390 | (serialize-instruction 391 | '(if (type #x3F2) 392 | (then 393 | nop 394 | (br 1) 395 | unreachable)) 396 | stream) 397 | 398 | #(#x04 #xF2 #x07 399 | #x01 400 | #x0C 1 401 | #x00 402 | #x0B))) 403 | 404 | (test if-else-no-result-type 405 | "Test serialization of IF with else branch without result type" 406 | 407 | (test-encoding stream 408 | (serialize-instruction 409 | '(if 410 | (then 411 | nop 412 | unreachable 413 | (br 1)) 414 | 415 | (else 416 | (br 2) 417 | nop)) 418 | stream) 419 | 420 | #(#x04 #x40 421 | #x01 422 | #x00 423 | #x0C 1 424 | 425 | #x05 426 | #x0C 2 427 | #x01 428 | 429 | #x0B))) 430 | 431 | (test if-else-with-result-type 432 | "Test serialization of IF with else branch with result type" 433 | 434 | (test-encoding stream 435 | (serialize-instruction 436 | '(if (result i32) 437 | (then 438 | nop 439 | (br 1) 440 | unreachable) 441 | 442 | (else 443 | (br 2) 444 | nop)) 445 | stream) 446 | 447 | #(#x04 #x7F 448 | #x01 449 | #x0C 1 450 | #x00 451 | 452 | #x05 453 | #x0C 2 454 | #x01 455 | 456 | #x0B))) 457 | 458 | (test if-else-with-type-index 459 | "Test serialization of IF with else branch with function type index" 460 | 461 | (test-encoding stream 462 | (serialize-instruction 463 | '(if (type #x40) 464 | (then 465 | nop 466 | (br 1) 467 | unreachable) 468 | 469 | (else 470 | (br 2) 471 | nop)) 472 | stream) 473 | 474 | #(#x04 #xC0 #x0 475 | #x01 476 | #x0C 1 477 | #x00 478 | 479 | #x05 480 | #x0C 2 481 | #x01 482 | 483 | #x0B))) 484 | 485 | 486 | ;;; Test Reference Instructions 487 | 488 | (test ref.null 489 | "Test serialization of REF.NULL" 490 | 491 | (test-encoding stream 492 | (serialize-instruction '(ref.null funcref) stream) 493 | #(#xD0 #x70)) 494 | 495 | (test-encoding stream 496 | (serialize-instruction '(ref.null externref) stream) 497 | #(#xD0 #x6F))) 498 | 499 | (test ref.is_null 500 | "Test serialization of REF.IS_NULL" 501 | 502 | (test-encoding stream 503 | (serialize-instruction 'ref.is_null stream) 504 | #(#xD1))) 505 | 506 | (test ref.func 507 | "Test serialization of REF.FUNC" 508 | 509 | (test-encoding stream 510 | (serialize-instruction '(ref.func #xF7) stream) 511 | #(#xD2 #xF7 #x01))) 512 | 513 | 514 | ;;; Test Parametric Instructions 515 | 516 | (test drop 517 | "Test serialization of DROP" 518 | 519 | (test-encoding stream 520 | (serialize-instruction 'drop stream) 521 | #(#x1A))) 522 | 523 | (test select 524 | "Test serialization of SELECT" 525 | 526 | (test-encoding stream 527 | (serialize-instruction 'select stream) 528 | #(#x1B))) 529 | 530 | (test multiple-select 531 | "Test serialization of SELECT with multiple values." 532 | 533 | ;; Currently this is not semantically valid WASM but the encoded is 534 | ;; valid and may be come semantically valid in a future version of 535 | ;; the spec. 536 | 537 | (test-encoding stream 538 | (serialize-instruction '(select i64 i32) stream) 539 | #(#x1C #x02 #x7E #x7F))) 540 | 541 | 542 | ;;; Test Variable Instructions 543 | 544 | (test local.get 545 | "Test serialization of LOCAL.GET" 546 | 547 | (test-encoding stream 548 | (serialize-instruction '(local.get 5) stream) 549 | #(#x20 #x05)) 550 | 551 | (test-encoding stream 552 | (serialize-instruction '(local.get 301) stream) 553 | #(#x20 #xAD #x02)) 554 | 555 | (test-error stream 556 | (serialize-instruction '(local.get -5) stream) 557 | type-error)) 558 | 559 | (test local.set 560 | "Test serialization of LOCAL.SET" 561 | 562 | (test-encoding stream 563 | (serialize-instruction '(local.set 5) stream) 564 | #(#x21 #x05)) 565 | 566 | (test-encoding stream 567 | (serialize-instruction '(local.set 301) stream) 568 | #(#x21 #xAD #x02)) 569 | 570 | (test-error stream 571 | (serialize-instruction '(local.set -5) stream) 572 | type-error)) 573 | 574 | (test local.tee 575 | "Test serialization of LOCAL.TEE" 576 | 577 | (test-encoding stream 578 | (serialize-instruction '(local.tee 5) stream) 579 | #(#x22 #x05)) 580 | 581 | (test-encoding stream 582 | (serialize-instruction '(local.tee 301) stream) 583 | #(#x22 #xAD #x02)) 584 | 585 | (test-error stream 586 | (serialize-instruction '(local.tee -5) stream) 587 | type-error)) 588 | 589 | (test global.get 590 | "Test serialization of GLOBAL.GET" 591 | 592 | (test-encoding stream 593 | (serialize-instruction '(global.get 5) stream) 594 | #(#x23 #x05)) 595 | 596 | (test-encoding stream 597 | (serialize-instruction '(global.get 301) stream) 598 | #(#x23 #xAD #x02)) 599 | 600 | (test-error stream 601 | (serialize-instruction '(global.get -5) stream) 602 | type-error)) 603 | 604 | (test global.set 605 | "Test serialization of GLOBAL.SET" 606 | 607 | (test-encoding stream 608 | (serialize-instruction '(global.set 5) stream) 609 | #(#x24 #x05)) 610 | 611 | (test-encoding stream 612 | (serialize-instruction '(global.set 301) stream) 613 | #(#x24 #xAD #x02)) 614 | 615 | (test-error stream 616 | (serialize-instruction '(global.set -5) stream) 617 | type-error)) 618 | 619 | 620 | ;;; Test Table Instructions 621 | 622 | (test table.get 623 | "Test serialization of TABLE.GET" 624 | 625 | (test-encoding stream 626 | (serialize-instruction '(table.get 0) stream) 627 | 628 | #(#x25 #x00)) 629 | 630 | (test-encoding stream 631 | (serialize-instruction '(table.get #xE5) stream) 632 | 633 | #(#x25 #xE5 #x01))) 634 | 635 | (test table.set 636 | "Test serialization of TABLE.SET" 637 | 638 | (test-encoding stream 639 | (serialize-instruction '(table.set 0) stream) 640 | 641 | #(#x26 #x00)) 642 | 643 | (test-encoding stream 644 | (serialize-instruction '(table.set #xE5) stream) 645 | 646 | #(#x26 #xE5 #x01))) 647 | 648 | (test table.init 649 | "Test serialization of TABLE.INIT" 650 | 651 | (test-encoding stream 652 | (serialize-instruction '(table.init 0 #xC7) stream) 653 | 654 | #(#xFC #x0C #xC7 #x01 #x00)) 655 | 656 | (test-encoding stream 657 | (serialize-instruction '(table.init #xC5 #x34) stream) 658 | 659 | #(#xFC #x0C #x34 #xC5 #x01))) 660 | 661 | (test elem.drop 662 | "Test serialization of ELEM.DROP" 663 | 664 | (test-encoding stream 665 | (serialize-instruction '(elem.drop 0) stream) 666 | 667 | #(#xFC #x0D #x00)) 668 | 669 | (test-encoding stream 670 | (serialize-instruction '(elem.drop #xE5) stream) 671 | 672 | #(#xFC #x0D #xE5 #x01))) 673 | 674 | (test table.copy 675 | "Test serialization of TABLE.COPY" 676 | 677 | (test-encoding stream 678 | (serialize-instruction '(table.copy 0 1) stream) 679 | 680 | #(#xFC #x0E #x00 #x01)) 681 | 682 | (test-encoding stream 683 | (serialize-instruction '(table.copy #xC5 #xE4) stream) 684 | 685 | #(#xFC #x0E #xC5 #x01 #xE4 #x01))) 686 | 687 | (test table.grow 688 | "Test serialization of TABLE.GROW" 689 | 690 | (test-encoding stream 691 | (serialize-instruction '(table.grow 0) stream) 692 | 693 | #(#xFC #x0F #x00)) 694 | 695 | (test-encoding stream 696 | (serialize-instruction '(table.grow #xE5) stream) 697 | 698 | #(#xFC #x0F #xE5 #x01))) 699 | 700 | (test table.size 701 | "Test serialization of TABLE.SIZE" 702 | 703 | (test-encoding stream 704 | (serialize-instruction '(table.size 0) stream) 705 | 706 | #(#xFC #x10 #x00)) 707 | 708 | (test-encoding stream 709 | (serialize-instruction '(table.size #xE5) stream) 710 | 711 | #(#xFC #x10 #xE5 #x01))) 712 | 713 | (test table.fill 714 | "Test serialization of TABLE.FILL" 715 | 716 | (test-encoding stream 717 | (serialize-instruction '(table.fill 0) stream) 718 | 719 | #(#xFC #x11 #x00)) 720 | 721 | (test-encoding stream 722 | (serialize-instruction '(table.fill #xC3) stream) 723 | 724 | #(#xFC #x11 #xC3 #x01))) 725 | 726 | 727 | ;;; Test Load Instructions 728 | 729 | (test i32.load-no-offset-alignment 730 | "Test serialization of I32.LOAD without offset or alignment" 731 | 732 | (test-encoding stream 733 | (serialize-instruction 'i32.load stream) 734 | 735 | #(#x28 #x02 #x00))) 736 | 737 | (test i32.load-with-offset 738 | "Test serialization of I32.LOAD with offset" 739 | 740 | (test-encoding stream 741 | (serialize-instruction '(i32.load (offset 16)) stream) 742 | 743 | #(#x28 #x02 #x10))) 744 | 745 | (test i32.load-alignment 746 | "Test serialization of I32.LOAD with alignment" 747 | 748 | (test-encoding stream 749 | (serialize-instruction '(i32.load (align 1)) stream) 750 | 751 | #(#x28 #x01 #x00))) 752 | 753 | (test i32.load-offset-alignment 754 | "Test serialization of I32.LOAD with offset and alignment" 755 | 756 | (test-encoding stream 757 | (serialize-instruction '(i32.load (offset 5) (align 1)) stream) 758 | 759 | #(#x28 #x01 #x05))) 760 | 761 | (test i32.load-errors 762 | "Test errors in serialization of I32.LOAD" 763 | 764 | (test-error stream 765 | (serialize-instruction '(i32.load (offset -1)) stream) 766 | type-error)) 767 | 768 | (test i64.load-no-offset-alignment 769 | "Test serialization of I64.LOAD without offset or alignment" 770 | 771 | (test-encoding stream 772 | (serialize-instruction 'i64.load stream) 773 | 774 | #(#x29 #x02 #x00))) 775 | 776 | (test i65.load-with-offset 777 | "Test serialization of I64.LOAD with offset" 778 | 779 | (test-encoding stream 780 | (serialize-instruction '(i64.load (offset 16)) stream) 781 | 782 | #(#x29 #x02 #x10))) 783 | 784 | (test i64.load-alignment 785 | "Test serialization of I64.LOAD with alignment" 786 | 787 | (test-encoding stream 788 | (serialize-instruction '(i64.load (align 1)) stream) 789 | 790 | #(#x29 #x01 #x00))) 791 | 792 | (test i64.load-offset-alignment 793 | "Test serialization of I64.LOAD with offset and alignment" 794 | 795 | (test-encoding stream 796 | (serialize-instruction '(i64.load (offset 5) (align 1)) stream) 797 | 798 | #(#x29 #x01 #x05))) 799 | 800 | (test i64.load-errors 801 | "Test errors in serialization of I64.LOAD" 802 | 803 | (test-error stream 804 | (serialize-instruction '(i64.load (offset -1)) stream) 805 | type-error)) 806 | 807 | (test f32.load-no-offset-alignment 808 | "Test serialization of F32.LOAD without offset or alignment" 809 | 810 | (test-encoding stream 811 | (serialize-instruction 'f32.load stream) 812 | 813 | #(#x2A #x02 #x00))) 814 | 815 | (test f32.load-with-offset 816 | "Test serialization of F32.LOAD with offset" 817 | 818 | (test-encoding stream 819 | (serialize-instruction '(f32.load (offset 16)) stream) 820 | 821 | #(#x2A #x02 #x10))) 822 | 823 | (test F32.load-alignment 824 | "Test serialization of F32.LOAD with alignment" 825 | 826 | (test-encoding stream 827 | (serialize-instruction '(f32.load (align 1)) stream) 828 | 829 | #(#x2A #x01 #x00))) 830 | 831 | (test f32.load-offset-alignment 832 | "Test serialization of F32.LOAD with offset and alignment" 833 | 834 | (test-encoding stream 835 | (serialize-instruction '(f32.load (offset 5) (align 1)) stream) 836 | 837 | #(#x2A #x01 #x05))) 838 | 839 | (test f32.load-errors 840 | "Test errors in serialization of F32.LOAD" 841 | 842 | (test-error stream 843 | (serialize-instruction '(f32.load (offset -1)) stream) 844 | type-error)) 845 | 846 | (test f64.load-no-offset-alignment 847 | "Test serialization of F64.LOAD without offset or alignment" 848 | 849 | (test-encoding stream 850 | (serialize-instruction 'f64.load stream) 851 | 852 | #(#x2B #x02 #x00))) 853 | 854 | (test f64.load-with-offset 855 | "Test serialization of F64.LOAD with offset" 856 | 857 | (test-encoding stream 858 | (serialize-instruction '(f64.load (offset 16)) stream) 859 | 860 | #(#x2B #x02 #x10))) 861 | 862 | (test F64.load-alignment 863 | "Test serialization of F64.LOAD with alignment" 864 | 865 | (test-encoding stream 866 | (serialize-instruction '(f64.load (align 1)) stream) 867 | 868 | #(#x2B #x01 #x00))) 869 | 870 | (test f64.load-offset-alignment 871 | "Test serialization of F64.LOAD with offset and alignment" 872 | 873 | (test-encoding stream 874 | (serialize-instruction '(f64.load (offset 5) (align 1)) stream) 875 | 876 | #(#x2B #x01 #x05))) 877 | 878 | (test f64.load-errors 879 | "Test errors in serialization of F64.LOAD" 880 | 881 | (test-error stream 882 | (serialize-instruction '(f64.load (offset -1)) stream) 883 | type-error)) 884 | 885 | (test i32.load8_s-no-offset-alignment 886 | "Test serialization of I32.LOAD8_S without offset or alignment" 887 | 888 | (test-encoding stream 889 | (serialize-instruction 'i32.load8_s stream) 890 | 891 | #(#x2C #x02 #x00))) 892 | 893 | (test i32.load8_s-with-offset 894 | "Test serialization of I32.LOAD8_S with offset" 895 | 896 | (test-encoding stream 897 | (serialize-instruction '(i32.load8_s (offset 16)) stream) 898 | 899 | #(#x2C #x02 #x10))) 900 | 901 | (test i32.load8_s-alignment 902 | "Test serialization of I32.LOAD8_S with alignment" 903 | 904 | (test-encoding stream 905 | (serialize-instruction '(i32.load8_s (align 1)) stream) 906 | 907 | #(#x2C #x01 #x00))) 908 | 909 | (test i32.load8_s-offset-alignment 910 | "Test serialization of I32.LOAD8_S with offset and alignment" 911 | 912 | (test-encoding stream 913 | (serialize-instruction '(i32.load8_s (offset 5) (align 1)) stream) 914 | 915 | #(#x2C #x01 #x05))) 916 | 917 | (test i32.load8_s-errors 918 | "Test errors in serialization of I32.LOAD8_S" 919 | 920 | (test-error stream 921 | (serialize-instruction '(i32.load8_s (offset -1)) stream) 922 | type-error)) 923 | 924 | (test i32.load8_u-no-offset-alignment 925 | "Test serialization of I32.LOAD8_U without offset or alignment" 926 | 927 | (test-encoding stream 928 | (serialize-instruction 'i32.load8_u stream) 929 | 930 | #(#x2D #x02 #x00))) 931 | 932 | (test i32.load8_u-with-offset 933 | "Test serialization of I32.LOAD8_U with offset" 934 | 935 | (test-encoding stream 936 | (serialize-instruction '(i32.load8_u (offset 16)) stream) 937 | 938 | #(#x2D #x02 #x10))) 939 | 940 | (test i32.load8_u-alignment 941 | "Test serialization of I32.LOAD8_U with alignment" 942 | 943 | (test-encoding stream 944 | (serialize-instruction '(i32.load8_u (align 1)) stream) 945 | 946 | #(#x2D #x01 #x00))) 947 | 948 | (test i32.load8_u-offset-alignment 949 | "Test serialization of I32.LOAD8_U with offset and alignment" 950 | 951 | (test-encoding stream 952 | (serialize-instruction '(i32.load8_u (offset 5) (align 1)) stream) 953 | 954 | #(#x2D #x01 #x05))) 955 | 956 | (test i32.load8_u-errors 957 | "Test errors in serialization of I32.LOAD8_U" 958 | 959 | (test-error stream 960 | (serialize-instruction '(i32.load8_u (offset -1)) stream) 961 | type-error)) 962 | 963 | (test i32.load16_s-no-offset-alignment 964 | "Test serialization of I32.LOAD16_S without offset or alignment" 965 | 966 | (test-encoding stream 967 | (serialize-instruction 'i32.load16_s stream) 968 | 969 | #(#x2E #x02 #x00))) 970 | 971 | (test i32.load16_s-with-offset 972 | "Test serialization of I32.LOAD16_S with offset" 973 | 974 | (test-encoding stream 975 | (serialize-instruction '(i32.load16_s (offset 16)) stream) 976 | 977 | #(#x2E #x02 #x10))) 978 | 979 | (test i32.load16_s-alignment 980 | "Test serialization of I32.LOAD16_S with alignment" 981 | 982 | (test-encoding stream 983 | (serialize-instruction '(i32.load16_s (align 1)) stream) 984 | 985 | #(#x2E #x01 #x00))) 986 | 987 | (test i32.load16_s-offset-alignment 988 | "Test serialization of I32.LOAD16_S with offset and alignment" 989 | 990 | (test-encoding stream 991 | (serialize-instruction '(i32.load16_s (offset 5) (align 1)) stream) 992 | 993 | #(#x2E #x01 #x05))) 994 | 995 | (test i32.load16_s-errors 996 | "Test errors in serialization of I32.LOAD16_S" 997 | 998 | (test-error stream 999 | (serialize-instruction '(i32.load16_s (offset -1)) stream) 1000 | type-error)) 1001 | 1002 | (test i32.load16_u-no-offset-alignment 1003 | "Test serialization of I32.LOAD16_U without offset or alignment" 1004 | 1005 | (test-encoding stream 1006 | (serialize-instruction 'i32.load16_u stream) 1007 | 1008 | #(#x2F #x02 #x00))) 1009 | 1010 | (test i32.load16_u-with-offset 1011 | "Test serialization of I32.LOAD16_U with offset" 1012 | 1013 | (test-encoding stream 1014 | (serialize-instruction '(i32.load16_u (offset 16)) stream) 1015 | 1016 | #(#x2F #x02 #x10))) 1017 | 1018 | (test i32.load16_u-alignment 1019 | "Test serialization of I32.LOAD16_U with alignment" 1020 | 1021 | (test-encoding stream 1022 | (serialize-instruction '(i32.load16_u (align 1)) stream) 1023 | 1024 | #(#x2F #x01 #x00))) 1025 | 1026 | (test i32.load16_u-offset-alignment 1027 | "Test serialization of I32.LOAD16_U with offset and alignment" 1028 | 1029 | (test-encoding stream 1030 | (serialize-instruction '(i32.load16_u (offset 5) (align 1)) stream) 1031 | 1032 | #(#x2F #x01 #x05))) 1033 | 1034 | (test i32.load16_u-errors 1035 | "Test errors in serialization of I32.LOAD16_U" 1036 | 1037 | (test-error stream 1038 | (serialize-instruction '(i32.load16_u (offset -1)) stream) 1039 | type-error)) 1040 | 1041 | (test i64.load8_s-no-offset-alignment 1042 | "Test serialization of I64.LOAD8_S without offset or alignment" 1043 | 1044 | (test-encoding stream 1045 | (serialize-instruction 'i64.load8_s stream) 1046 | 1047 | #(#x30 #x02 #x00))) 1048 | 1049 | (test i64.load8_s-with-offset 1050 | "Test serialization of I64.LOAD8_S with offset" 1051 | 1052 | (test-encoding stream 1053 | (serialize-instruction '(i64.load8_s (offset 16)) stream) 1054 | 1055 | #(#x30 #x02 #x10))) 1056 | 1057 | (test i64.load8_s-alignment 1058 | "Test serialization of I64.LOAD8_S with alignment" 1059 | 1060 | (test-encoding stream 1061 | (serialize-instruction '(i64.load8_s (align 1)) stream) 1062 | 1063 | #(#x30 #x01 #x00))) 1064 | 1065 | (test i64.load8_s-offset-alignment 1066 | "Test serialization of I64.LOAD8_S with offset and alignment" 1067 | 1068 | (test-encoding stream 1069 | (serialize-instruction '(i64.load8_s (offset 5) (align 1)) stream) 1070 | 1071 | #(#x30 #x01 #x05))) 1072 | 1073 | (test i64.load8_s-errors 1074 | "Test errors in serialization of I64.LOAD8_S" 1075 | 1076 | (test-error stream 1077 | (serialize-instruction '(i64.load8_s (offset -1)) stream) 1078 | type-error)) 1079 | 1080 | (test i64.load8_u-no-offset-alignment 1081 | "Test serialization of I64.LOAD8_U without offset or alignment" 1082 | 1083 | (test-encoding stream 1084 | (serialize-instruction 'i64.load8_u stream) 1085 | 1086 | #(#x31 #x02 #x00))) 1087 | 1088 | (test i64.load8_u-with-offset 1089 | "Test serialization of I64.LOAD8_U with offset" 1090 | 1091 | (test-encoding stream 1092 | (serialize-instruction '(i64.load8_u (offset 16)) stream) 1093 | 1094 | #(#x31 #x02 #x10))) 1095 | 1096 | (test i64.load8_u-alignment 1097 | "Test serialization of I64.LOAD8_U with alignment" 1098 | 1099 | (test-encoding stream 1100 | (serialize-instruction '(i64.load8_u (align 1)) stream) 1101 | 1102 | #(#x31 #x01 #x00))) 1103 | 1104 | (test i64.load8_u-offset-alignment 1105 | "Test serialization of I64.LOAD8_U with offset and alignment" 1106 | 1107 | (test-encoding stream 1108 | (serialize-instruction '(i64.load8_u (offset 5) (align 1)) stream) 1109 | 1110 | #(#x31 #x01 #x05))) 1111 | 1112 | (test i64.load8_u-errors 1113 | "Test errors in serialization of I64.LOAD8_U" 1114 | 1115 | (test-error stream 1116 | (serialize-instruction '(i64.load8_u (offset -1)) stream) 1117 | type-error)) 1118 | 1119 | (test i64.load16_s-no-offset-alignment 1120 | "Test serialization of I64.LOAD16_S without offset or alignment" 1121 | 1122 | (test-encoding stream 1123 | (serialize-instruction 'i64.load16_s stream) 1124 | 1125 | #(#x32 #x02 #x00))) 1126 | 1127 | (test i64.load16_s-with-offset 1128 | "Test serialization of I64.LOAD16_S with offset" 1129 | 1130 | (test-encoding stream 1131 | (serialize-instruction '(i64.load16_s (offset 16)) stream) 1132 | 1133 | #(#x32 #x02 #x10))) 1134 | 1135 | (test i64.load16_s-alignment 1136 | "Test serialization of I64.LOAD16_S with alignment" 1137 | 1138 | (test-encoding stream 1139 | (serialize-instruction '(i64.load16_s (align 1)) stream) 1140 | 1141 | #(#x32 #x01 #x00))) 1142 | 1143 | (test i64.load16_s-offset-alignment 1144 | "Test serialization of I64.LOAD16_S with offset and alignment" 1145 | 1146 | (test-encoding stream 1147 | (serialize-instruction '(i64.load16_s (offset 5) (align 1)) stream) 1148 | 1149 | #(#x32 #x01 #x05))) 1150 | 1151 | (test i64.load16_s-errors 1152 | "Test errors in serialization of I64.LOAD16_S" 1153 | 1154 | (test-error stream 1155 | (serialize-instruction '(i64.load16_s (offset -1)) stream) 1156 | type-error)) 1157 | 1158 | (test i64.load16_u-no-offset-alignment 1159 | "Test serialization of I64.LOAD16_U without offset or alignment" 1160 | 1161 | (test-encoding stream 1162 | (serialize-instruction 'i64.load16_u stream) 1163 | 1164 | #(#x33 #x02 #x00))) 1165 | 1166 | (test i64.load16_u-with-offset 1167 | "Test serialization of I64.LOAD16_U with offset" 1168 | 1169 | (test-encoding stream 1170 | (serialize-instruction '(i64.load16_u (offset 16)) stream) 1171 | 1172 | #(#x33 #x02 #x10))) 1173 | 1174 | (test i64.load16_u-alignment 1175 | "Test serialization of I64.LOAD16_U with alignment" 1176 | 1177 | (test-encoding stream 1178 | (serialize-instruction '(i64.load16_u (align 1)) stream) 1179 | 1180 | #(#x33 #x01 #x00))) 1181 | 1182 | (test i64.load16_u-offset-alignment 1183 | "Test serialization of I64.LOAD16_U with offset and alignment" 1184 | 1185 | (test-encoding stream 1186 | (serialize-instruction '(i64.load16_u (offset 5) (align 1)) stream) 1187 | 1188 | #(#x33 #x01 #x05))) 1189 | 1190 | (test i64.load16_u-errors 1191 | "Test errors in serialization of I64.LOAD16_U" 1192 | 1193 | (test-error stream 1194 | (serialize-instruction '(i64.load16_u (offset -1)) stream) 1195 | type-error)) 1196 | 1197 | (test i64.load32_s-no-offset-alignment 1198 | "Test serialization of I64.LOAD32_S without offset or alignment" 1199 | 1200 | (test-encoding stream 1201 | (serialize-instruction 'i64.load32_s stream) 1202 | 1203 | #(#x34 #x02 #x00))) 1204 | 1205 | (test i64.load32_s-with-offset 1206 | "Test serialization of I64.LOAD32_S with offset" 1207 | 1208 | (test-encoding stream 1209 | (serialize-instruction '(i64.load32_s (offset 16)) stream) 1210 | 1211 | #(#x34 #x02 #x10))) 1212 | 1213 | (test i64.load32_s-alignment 1214 | "Test serialization of I64.LOAD32_S with alignment" 1215 | 1216 | (test-encoding stream 1217 | (serialize-instruction '(i64.load32_s (align 1)) stream) 1218 | 1219 | #(#x34 #x01 #x00))) 1220 | 1221 | (test i64.load32_s-offset-alignment 1222 | "Test serialization of I64.LOAD32_S with offset and alignment" 1223 | 1224 | (test-encoding stream 1225 | (serialize-instruction '(i64.load32_s (offset 5) (align 1)) stream) 1226 | 1227 | #(#x34 #x01 #x05))) 1228 | 1229 | (test i64.load32_s-errors 1230 | "Test errors in serialization of I64.LOAD32_S" 1231 | 1232 | (test-error stream 1233 | (serialize-instruction '(i64.load32_s (offset -1)) stream) 1234 | type-error)) 1235 | 1236 | (test i64.load64_s-no-offset-alignment 1237 | "Test serialization of I64.LOAD64_S without offset or alignment" 1238 | 1239 | (test-encoding stream 1240 | (serialize-instruction 'i64.load32_u stream) 1241 | 1242 | #(#x35 #x02 #x00))) 1243 | 1244 | (test i64.load_64s-with-offset 1245 | "Test serialization of I64.LOAD_64S with offset" 1246 | 1247 | (test-encoding stream 1248 | (serialize-instruction '(i64.load32_u (offset 16)) stream) 1249 | 1250 | #(#x35 #x02 #x10))) 1251 | 1252 | (test i64.load_64s-alignment 1253 | "Test serialization of I64.LOAD_64S with alignment" 1254 | 1255 | (test-encoding stream 1256 | (serialize-instruction '(i64.load32_u (align 1)) stream) 1257 | 1258 | #(#x35 #x01 #x00))) 1259 | 1260 | (test i64.load_64s-offset-alignment 1261 | "Test serialization of I64.LOAD_64S with offset and alignment" 1262 | 1263 | (test-encoding stream 1264 | (serialize-instruction '(i64.load32_u (offset 5) (align 1)) stream) 1265 | 1266 | #(#x35 #x01 #x05))) 1267 | 1268 | (test i64.load_64s-errors 1269 | "Test errors in serialization of I64.LOAD_64S" 1270 | 1271 | (test-error stream 1272 | (serialize-instruction '(i64.load32_u (offset -1)) stream) 1273 | type-error)) 1274 | 1275 | 1276 | ;;; Test Store Instructions 1277 | 1278 | (test i32.store-no-offset-alignment 1279 | "Test serialization of I32.store without offset or alignment" 1280 | 1281 | (test-encoding stream 1282 | (serialize-instruction 'i32.store stream) 1283 | 1284 | #(#x36 #x02 #x00))) 1285 | 1286 | (test i32.store-with-offset 1287 | "Test serialization of I32.store with offset" 1288 | 1289 | (test-encoding stream 1290 | (serialize-instruction '(i32.store (offset 16)) stream) 1291 | 1292 | #(#x36 #x02 #x10))) 1293 | 1294 | (test i32.store-alignment 1295 | "Test serialization of I32.store with alignment" 1296 | 1297 | (test-encoding stream 1298 | (serialize-instruction '(i32.store (align 1)) stream) 1299 | 1300 | #(#x36 #x01 #x00))) 1301 | 1302 | (test i32.store-offset-alignment 1303 | "Test serialization of I32.store with offset and alignment" 1304 | 1305 | (test-encoding stream 1306 | (serialize-instruction '(i32.store (offset 5) (align 1)) stream) 1307 | 1308 | #(#x36 #x01 #x05))) 1309 | 1310 | (test i32.store-errors 1311 | "Test errors in serialization of I32.store" 1312 | 1313 | (test-error stream 1314 | (serialize-instruction '(i32.store (offset -1)) stream) 1315 | type-error)) 1316 | 1317 | (test i64.store-no-offset-alignment 1318 | "Test serialization of I64.store without offset or alignment" 1319 | 1320 | (test-encoding stream 1321 | (serialize-instruction 'i64.store stream) 1322 | 1323 | #(#x37 #x02 #x00))) 1324 | 1325 | (test i64.store-with-offset 1326 | "Test serialization of I64.store with offset" 1327 | 1328 | (test-encoding stream 1329 | (serialize-instruction '(i64.store (offset 16)) stream) 1330 | 1331 | #(#x37 #x02 #x10))) 1332 | 1333 | (test i64.store-alignment 1334 | "Test serialization of I64.store with alignment" 1335 | 1336 | (test-encoding stream 1337 | (serialize-instruction '(i64.store (align 1)) stream) 1338 | 1339 | #(#x37 #x01 #x00))) 1340 | 1341 | (test i64.store-offset-alignment 1342 | "Test serialization of I64.store with offset and alignment" 1343 | 1344 | (test-encoding stream 1345 | (serialize-instruction '(i64.store (offset 5) (align 1)) stream) 1346 | 1347 | #(#x37 #x01 #x05))) 1348 | 1349 | (test i64.store-errors 1350 | "Test errors in serialization of I64.store" 1351 | 1352 | (test-error stream 1353 | (serialize-instruction '(i64.store (offset -1)) stream) 1354 | type-error)) 1355 | 1356 | (test f32.store-no-offset-alignment 1357 | "Test serialization of F32.store without offset or alignment" 1358 | 1359 | (test-encoding stream 1360 | (serialize-instruction 'f32.store stream) 1361 | 1362 | #(#x38 #x02 #x00))) 1363 | 1364 | (test f32.store-with-offset 1365 | "Test serialization of F32.store with offset" 1366 | 1367 | (test-encoding stream 1368 | (serialize-instruction '(f32.store (offset 16)) stream) 1369 | 1370 | #(#x38 #x02 #x10))) 1371 | 1372 | (test f32.store-alignment 1373 | "Test serialization of F32.store with alignment" 1374 | 1375 | (test-encoding stream 1376 | (serialize-instruction '(f32.store (align 1)) stream) 1377 | 1378 | #(#x38 #x01 #x00))) 1379 | 1380 | (test f32.store-offset-alignment 1381 | "Test serialization of F32.store with offset and alignment" 1382 | 1383 | (test-encoding stream 1384 | (serialize-instruction '(f32.store (offset 5) (align 1)) stream) 1385 | 1386 | #(#x38 #x01 #x05))) 1387 | 1388 | (test f32.store-errors 1389 | "Test errors in serialization of F32.store" 1390 | 1391 | (test-error stream 1392 | (serialize-instruction '(f32.store (offset -1)) stream) 1393 | type-error)) 1394 | 1395 | (test f64.store-no-offset-alignment 1396 | "Test serialization of F64.store without offset or alignment" 1397 | 1398 | (test-encoding stream 1399 | (serialize-instruction 'f64.store stream) 1400 | 1401 | #(#x39 #x02 #x00))) 1402 | 1403 | (test f64.store-with-offset 1404 | "Test serialization of F64.store with offset" 1405 | 1406 | (test-encoding stream 1407 | (serialize-instruction '(f64.store (offset 16)) stream) 1408 | 1409 | #(#x39 #x02 #x10))) 1410 | 1411 | (test F64.store-alignment 1412 | "Test serialization of F64.store with alignment" 1413 | 1414 | (test-encoding stream 1415 | (serialize-instruction '(f64.store (align 1)) stream) 1416 | 1417 | #(#x39 #x01 #x00))) 1418 | 1419 | (test f64.store-offset-alignment 1420 | "Test serialization of F64.store with offset and alignment" 1421 | 1422 | (test-encoding stream 1423 | (serialize-instruction '(f64.store (offset 5) (align 1)) stream) 1424 | 1425 | #(#x39 #x01 #x05))) 1426 | 1427 | (test f64.store-errors 1428 | "Test errors in serialization of F64.store" 1429 | 1430 | (test-error stream 1431 | (serialize-instruction '(f64.store (offset -1)) stream) 1432 | type-error)) 1433 | 1434 | (test i32.store8-no-offset-alignment 1435 | "Test serialization of I32.store8 without offset or alignment" 1436 | 1437 | (test-encoding stream 1438 | (serialize-instruction 'i32.store8 stream) 1439 | 1440 | #(#x3A #x02 #x00))) 1441 | 1442 | (test i32.store8-with-offset 1443 | "Test serialization of I32.store8 with offset" 1444 | 1445 | (test-encoding stream 1446 | (serialize-instruction '(i32.store8 (offset 16)) stream) 1447 | 1448 | #(#x3A #x02 #x10))) 1449 | 1450 | (test i32.store8-alignment 1451 | "Test serialization of I32.store8 with alignment" 1452 | 1453 | (test-encoding stream 1454 | (serialize-instruction '(i32.store8 (align 1)) stream) 1455 | 1456 | #(#x3A #x01 #x00))) 1457 | 1458 | (test i32.store8-offset-alignment 1459 | "Test serialization of I32.store8 with offset and alignment" 1460 | 1461 | (test-encoding stream 1462 | (serialize-instruction '(i32.store8 (offset 5) (align 1)) stream) 1463 | 1464 | #(#x3A #x01 #x05))) 1465 | 1466 | (test i32.store8-errors 1467 | "Test errors in serialization of I32.store8" 1468 | 1469 | (test-error stream 1470 | (serialize-instruction '(i32.store8 (offset -1)) stream) 1471 | type-error)) 1472 | 1473 | (test i32.store16-no-offset-alignment 1474 | "Test serialization of I32.store16 without offset or alignment" 1475 | 1476 | (test-encoding stream 1477 | (serialize-instruction 'i32.store16 stream) 1478 | 1479 | #(#x3B #x02 #x00))) 1480 | 1481 | (test i32.store16-with-offset 1482 | "Test serialization of I32.store16 with offset" 1483 | 1484 | (test-encoding stream 1485 | (serialize-instruction '(i32.store16 (offset 16)) stream) 1486 | 1487 | #(#x3B #x02 #x10))) 1488 | 1489 | (test i32.store16-alignment 1490 | "Test serialization of I32.store16 with alignment" 1491 | 1492 | (test-encoding stream 1493 | (serialize-instruction '(i32.store16 (align 1)) stream) 1494 | 1495 | #(#x3B #x01 #x00))) 1496 | 1497 | (test i32.store16-offset-alignment 1498 | "Test serialization of I32.store16 with offset and alignment" 1499 | 1500 | (test-encoding stream 1501 | (serialize-instruction '(i32.store16 (offset 5) (align 1)) stream) 1502 | 1503 | #(#x3B #x01 #x05))) 1504 | 1505 | (test i32.store16-errors 1506 | "Test errors in serialization of I32.LOAD16_S" 1507 | 1508 | (test-error stream 1509 | (serialize-instruction '(i32.store16 (offset -1)) stream) 1510 | type-error)) 1511 | 1512 | (test i64.store8-no-offset-alignment 1513 | "Test serialization of I64.store8 without offset or alignment" 1514 | 1515 | (test-encoding stream 1516 | (serialize-instruction 'i64.store8 stream) 1517 | 1518 | #(#x3C #x02 #x00))) 1519 | 1520 | (test i64.store8-with-offset 1521 | "Test serialization of I64.store8 with offset" 1522 | 1523 | (test-encoding stream 1524 | (serialize-instruction '(i64.store8 (offset 16)) stream) 1525 | 1526 | #(#x3C #x02 #x10))) 1527 | 1528 | (test i64.store8-alignment 1529 | "Test serialization of I64.store8 with alignment" 1530 | 1531 | (test-encoding stream 1532 | (serialize-instruction '(i64.store8 (align 1)) stream) 1533 | 1534 | #(#x3C #x01 #x00))) 1535 | 1536 | (test i64.store8-offset-alignment 1537 | "Test serialization of I64.store8 with offset and alignment" 1538 | 1539 | (test-encoding stream 1540 | (serialize-instruction '(i64.store8 (offset 5) (align 1)) stream) 1541 | 1542 | #(#x3C #x01 #x05))) 1543 | 1544 | (test i64.store8-errors 1545 | "Test errors in serialization of I64.store8" 1546 | 1547 | (test-error stream 1548 | (serialize-instruction '(i64.store8 (offset -1)) stream) 1549 | type-error)) 1550 | 1551 | (test i64.store16-no-offset-alignment 1552 | "Test serialization of I64.store16 without offset or alignment" 1553 | 1554 | (test-encoding stream 1555 | (serialize-instruction 'i64.store16 stream) 1556 | 1557 | #(#x3D #x02 #x00))) 1558 | 1559 | (test i64.store16-with-offset 1560 | "Test serialization of I64.store16 with offset" 1561 | 1562 | (test-encoding stream 1563 | (serialize-instruction '(i64.store16 (offset 16)) stream) 1564 | 1565 | #(#x3D #x02 #x10))) 1566 | 1567 | (test i64.store16-alignment 1568 | "Test serialization of I64.store16 with alignment" 1569 | 1570 | (test-encoding stream 1571 | (serialize-instruction '(i64.store16 (align 1)) stream) 1572 | 1573 | #(#x3D #x01 #x00))) 1574 | 1575 | (test i64.store16-offset-alignment 1576 | "Test serialization of I64.store16 with offset and alignment" 1577 | 1578 | (test-encoding stream 1579 | (serialize-instruction '(i64.store16 (offset 5) (align 1)) stream) 1580 | 1581 | #(#x3D #x01 #x05))) 1582 | 1583 | (test i64.store16-errors 1584 | "Test errors in serialization of I64.store16" 1585 | 1586 | (test-error stream 1587 | (serialize-instruction '(i64.store16 (offset -1)) stream) 1588 | type-error)) 1589 | 1590 | (test i64.store32-no-offset-alignment 1591 | "Test serialization of I64.store32 without offset or alignment" 1592 | 1593 | (test-encoding stream 1594 | (serialize-instruction 'i64.store32 stream) 1595 | 1596 | #(#x3E #x02 #x00))) 1597 | 1598 | (test i64.store32-with-offset 1599 | "Test serialization of I64.store32 with offset" 1600 | 1601 | (test-encoding stream 1602 | (serialize-instruction '(i64.store32 (offset 16)) stream) 1603 | 1604 | #(#x3E #x02 #x10))) 1605 | 1606 | (test i64.store32-alignment 1607 | "Test serialization of I64.store32 with alignment" 1608 | 1609 | (test-encoding stream 1610 | (serialize-instruction '(i64.store32 (align 1)) stream) 1611 | 1612 | #(#x3E #x01 #x00))) 1613 | 1614 | (test i64.store32-offset-alignment 1615 | "Test serialization of I64.store32 with offset and alignment" 1616 | 1617 | (test-encoding stream 1618 | (serialize-instruction '(i64.store32 (offset 5) (align 1)) stream) 1619 | 1620 | #(#x3E #x01 #x05))) 1621 | 1622 | (test i64.store32-errors 1623 | "Test errors in serialization of I64.store32" 1624 | 1625 | (test-error stream 1626 | (serialize-instruction '(i64.store32 (offset -1)) stream) 1627 | type-error)) 1628 | 1629 | 1630 | ;;; Test Memory Instructions 1631 | 1632 | (test memory.size 1633 | "Test serialization of MEMORY.SIZE" 1634 | 1635 | (test-encoding stream 1636 | (serialize-instruction 'memory.size stream) 1637 | #(#x3F #x00))) 1638 | 1639 | (test memory.grow 1640 | "Test serialization of MEMORY.GROW" 1641 | 1642 | (test-encoding stream 1643 | (serialize-instruction 'memory.grow stream) 1644 | #(#x40 #x00))) 1645 | 1646 | (test memory.init 1647 | "Test serialization of MEMORY.INIT" 1648 | 1649 | (test-encoding stream 1650 | (serialize-instruction '(memory.init #x03) stream) 1651 | #(#xFC #x08 #x03 #x00)) 1652 | 1653 | (test-encoding stream 1654 | (serialize-instruction '(memory.init #xC7) stream) 1655 | #(#xFC #x08 #xC7 #x01 #x00))) 1656 | 1657 | (test data.drop 1658 | "Test serialization of DATA.DROP" 1659 | 1660 | (test-encoding stream 1661 | (serialize-instruction '(data.drop #x03) stream) 1662 | #(#xFC #x09 #x03)) 1663 | 1664 | (test-encoding stream 1665 | (serialize-instruction '(data.drop #xC7) stream) 1666 | #(#xFC #x09 #xC7 #x01))) 1667 | 1668 | (test memory.copy 1669 | "Test serialization of MEMORY.COPY" 1670 | 1671 | (test-encoding stream 1672 | (serialize-instruction 'memory.copy stream) 1673 | #(#xFC #x0A #x00 #x00))) 1674 | 1675 | (test memory.fill 1676 | "Test serialization of MEMORY.FILL" 1677 | 1678 | (test-encoding stream 1679 | (serialize-instruction 'memory.fill stream) 1680 | #(#xFC #x0B #x00))) 1681 | 1682 | 1683 | ;;; Test Constant Instructions 1684 | 1685 | (test i32.const 1686 | "Test serialization of I32.CONST" 1687 | 1688 | (test-encoding stream 1689 | (serialize-instruction '(i32.const 1) stream) 1690 | #(#x41 1)) 1691 | 1692 | (test-encoding stream 1693 | (serialize-instruction '(i32.const -5) stream) 1694 | #(#x41 #x7B)) 1695 | 1696 | (test-encoding stream 1697 | (serialize-instruction '(i32.const 350) stream) 1698 | 1699 | #(#x41 #xDE #x02)) 1700 | 1701 | (test-encoding stream 1702 | (serialize-instruction '(i32.const -350) stream) 1703 | 1704 | #(#x41 #xA2 #x7D)) 1705 | 1706 | (test-encoding stream 1707 | (serialize-instruction `(i32.const ,(1- (expt 2 32))) stream) 1708 | 1709 | #(#x41 #x7F)) 1710 | 1711 | (test-encoding stream 1712 | (serialize-instruction `(i32.const ,(expt 2 31)) stream) 1713 | 1714 | #(#x41 #x80 #x80 #x80 #x80 #x78)) 1715 | 1716 | (test-encoding stream 1717 | (serialize-instruction `(i32.const ,(+ (expt 2 31) (expt 2 23) 4 1)) stream) 1718 | 1719 | #(#x41 #x85 #x80 #x80 #x84 #x78))) 1720 | 1721 | (test i64.const 1722 | "Test serialization of I64.CONST" 1723 | 1724 | (test-encoding stream 1725 | (serialize-instruction '(i64.const 1) stream) 1726 | #(#x42 1)) 1727 | 1728 | (test-encoding stream 1729 | (serialize-instruction '(i64.const -5) stream) 1730 | #(#x42 #x7B)) 1731 | 1732 | (test-encoding stream 1733 | (serialize-instruction '(i64.const 350) stream) 1734 | 1735 | #(#x42 #xDE #x02)) 1736 | 1737 | (test-encoding stream 1738 | (serialize-instruction '(i64.const -350) stream) 1739 | 1740 | #(#x42 #xA2 #x7D)) 1741 | 1742 | (test-encoding stream 1743 | (serialize-instruction `(i64.const ,(1- (expt 2 64))) stream) 1744 | 1745 | #(#x42 #x7F)) 1746 | 1747 | (test-encoding stream 1748 | (serialize-instruction `(i64.const ,(expt 2 63)) stream) 1749 | 1750 | #(#x42 #x80 #x80 #x80 #x80 #x80 #x80 #x80 #x80 #x80 #x7F)) 1751 | 1752 | (test-encoding stream 1753 | (serialize-instruction `(i64.const ,(+ (expt 2 63) (expt 2 43) (expt 2 32) 16 1)) stream) 1754 | 1755 | #(#x42 #x91 #x80 #x80 #x80 #x90 #x80 #x82 #x80 #x80 #x7F))) 1756 | 1757 | (test f32.const 1758 | "Test serialization of F32.CONST" 1759 | 1760 | (test-encoding stream 1761 | (serialize-instruction '(f32.const 1.5) stream) 1762 | 1763 | #(#x43 #x00 #x00 #xC0 #x3F))) 1764 | 1765 | (test f64.const 1766 | "Test serialization of F64.CONST" 1767 | 1768 | (test-encoding stream 1769 | (serialize-instruction '(f64.const 1.5) stream) 1770 | 1771 | #(#x44 #x00 #x00 #x00 #x00 #x00 #x00 #xF8 #x3F))) 1772 | 1773 | 1774 | ;;; Test Arithmetic Instructions 1775 | 1776 | (test arithmetic-instructions 1777 | "Test serialization of arithmetic instructions" 1778 | 1779 | (test-encode-instruction i32.eqz #x45) 1780 | (test-encode-instruction i32.eq #x46) 1781 | (test-encode-instruction i32.ne #x47) 1782 | (test-encode-instruction i32.lt_s #x48) 1783 | (test-encode-instruction i32.lt_u #x49) 1784 | (test-encode-instruction i32.gt_s #x4A) 1785 | (test-encode-instruction i32.gt_u #x4B) 1786 | (test-encode-instruction i32.le_s #x4C) 1787 | (test-encode-instruction i32.le_u #x4D) 1788 | (test-encode-instruction i32.ge_s #x4E) 1789 | (test-encode-instruction i32.ge_u #x4F) 1790 | 1791 | (test-encode-instruction i64.eqz #x50) 1792 | (test-encode-instruction i64.eq #x51) 1793 | (test-encode-instruction i64.ne #x52) 1794 | (test-encode-instruction i64.lt_s #x53) 1795 | (test-encode-instruction i64.lt_u #x54) 1796 | (test-encode-instruction i64.gt_s #x55) 1797 | (test-encode-instruction i64.gt_u #x56) 1798 | (test-encode-instruction i64.le_s #x57) 1799 | (test-encode-instruction i64.le_u #x58) 1800 | (test-encode-instruction i64.ge_s #x59) 1801 | (test-encode-instruction i64.ge_u #x5A) 1802 | 1803 | (test-encode-instruction f32.eq #x5B) 1804 | (test-encode-instruction f32.ne #x5C) 1805 | (test-encode-instruction f32.lt #x5D) 1806 | (test-encode-instruction f32.gt #x5E) 1807 | (test-encode-instruction f32.le #x5F) 1808 | (test-encode-instruction f32.ge #x60) 1809 | 1810 | (test-encode-instruction f64.eq #x61) 1811 | (test-encode-instruction f64.ne #x62) 1812 | (test-encode-instruction f64.lt #x63) 1813 | (test-encode-instruction f64.gt #x64) 1814 | (test-encode-instruction f64.le #x65) 1815 | (test-encode-instruction f64.ge #x66) 1816 | 1817 | (test-encode-instruction i32.clz #x67) 1818 | (test-encode-instruction i32.ctz #x68) 1819 | (test-encode-instruction i32.popcnt #x69) 1820 | (test-encode-instruction i32.add #x6A) 1821 | (test-encode-instruction i32.sub #x6B) 1822 | (test-encode-instruction i32.mul #x6C) 1823 | (test-encode-instruction i32.div_s #x6D) 1824 | (test-encode-instruction i32.div_u #x6E) 1825 | (test-encode-instruction i32.rem_s #x6F) 1826 | (test-encode-instruction i32.rem_u #x70) 1827 | (test-encode-instruction i32.and #x71) 1828 | (test-encode-instruction i32.or #x72) 1829 | (test-encode-instruction i32.xor #x73) 1830 | (test-encode-instruction i32.shl #x74) 1831 | (test-encode-instruction i32.shr_s #x75) 1832 | (test-encode-instruction i32.shr_u #x76) 1833 | (test-encode-instruction i32.rotl #x77) 1834 | (test-encode-instruction i32.rotr #x78) 1835 | 1836 | (test-encode-instruction i64.clz #x79) 1837 | (test-encode-instruction i64.ctz #x7A) 1838 | (test-encode-instruction i64.popcnt #x7B) 1839 | (test-encode-instruction i64.add #x7C) 1840 | (test-encode-instruction i64.sub #x7D) 1841 | (test-encode-instruction i64.mul #x7E) 1842 | (test-encode-instruction i64.div_s #x7F) 1843 | (test-encode-instruction i64.div_u #x80) 1844 | (test-encode-instruction i64.rem_s #x81) 1845 | (test-encode-instruction i64.rem_u #x82) 1846 | (test-encode-instruction i64.and #x83) 1847 | (test-encode-instruction i64.or #x84) 1848 | (test-encode-instruction i64.xor #x85) 1849 | (test-encode-instruction i64.shl #x86) 1850 | (test-encode-instruction i64.shr_s #x87) 1851 | (test-encode-instruction i64.shr_u #x88) 1852 | (test-encode-instruction i64.rotl #x89) 1853 | (test-encode-instruction i64.rotr #x8A) 1854 | 1855 | (test-encode-instruction f32.abs #x8B) 1856 | (test-encode-instruction f32.neg #x8C) 1857 | (test-encode-instruction f32.ceil #x8D) 1858 | (test-encode-instruction f32.floor #x8E) 1859 | (test-encode-instruction f32.trunc #x8F) 1860 | (test-encode-instruction f32.nearest #x90) 1861 | (test-encode-instruction f32.sqrt #x91) 1862 | (test-encode-instruction f32.add #x92) 1863 | (test-encode-instruction f32.sub #x93) 1864 | (test-encode-instruction f32.mul #x94) 1865 | (test-encode-instruction f32.div #x95) 1866 | (test-encode-instruction f32.min #x96) 1867 | (test-encode-instruction f32.max #x97) 1868 | (test-encode-instruction f32.copysign #x98) 1869 | 1870 | (test-encode-instruction f64.abs #x99) 1871 | (test-encode-instruction f64.neg #x9A) 1872 | (test-encode-instruction f64.ceil #x9B) 1873 | (test-encode-instruction f64.trunc #x9C) 1874 | (test-encode-instruction f64.floor #x9D) 1875 | (test-encode-instruction f64.nearest #x9E) 1876 | (test-encode-instruction f64.sqrt #x9F) 1877 | (test-encode-instruction f64.add #xA0) 1878 | (test-encode-instruction f64.sub #xA1) 1879 | (test-encode-instruction f64.mul #xA2) 1880 | (test-encode-instruction f64.div #xA3) 1881 | (test-encode-instruction f64.min #xA4) 1882 | (test-encode-instruction f64.max #xA5) 1883 | (test-encode-instruction f64.copysign #xA6) 1884 | 1885 | (test-encode-instruction i32.wrap_i64 #xA7) 1886 | (test-encode-instruction i32.trunc_f32_s #xA8) 1887 | (test-encode-instruction i32.trunc_f32_u #xA9) 1888 | (test-encode-instruction i32.trunc_f64_s #xAA) 1889 | (test-encode-instruction i32.trunc_f64_u #xAB) 1890 | (test-encode-instruction i64.extend_i32_s #xAC) 1891 | (test-encode-instruction i64.extend_i32_u #xAD) 1892 | (test-encode-instruction i64.trunc_f32_s #xAE) 1893 | (test-encode-instruction i64.trunc_f32_u #xAF) 1894 | (test-encode-instruction i64.trunc_f64_s #xB0) 1895 | (test-encode-instruction i64.trunc_f64_u #xB1) 1896 | 1897 | (test-encode-instruction f32.convert_i32_s #xB2) 1898 | (test-encode-instruction f32.convert_i32_u #xB3) 1899 | (test-encode-instruction f32.convert_i64_s #xB4) 1900 | (test-encode-instruction f32.convert_i64_u #xB5) 1901 | (test-encode-instruction f32.demote_f64 #xB6) 1902 | 1903 | (test-encode-instruction f64.convert_i32_s #xB7) 1904 | (test-encode-instruction f64.convert_i32_u #xB8) 1905 | (test-encode-instruction f64.convert_i64_s #xB9) 1906 | (test-encode-instruction f64.convert_i64_u #xBA) 1907 | (test-encode-instruction f64.promote_f64 #xBB) 1908 | 1909 | (test-encode-instruction i32.reinterpret_f32 #xBC) 1910 | (test-encode-instruction i64.reinterpret_f64 #xBD) 1911 | (test-encode-instruction f32.reinterpret_i32 #xBE) 1912 | (test-encode-instruction f64.reinterpret_i64 #xBF) 1913 | 1914 | (test-encode-instruction i32.extend8_s #xC0) 1915 | (test-encode-instruction i32.extend16_s #xC1) 1916 | (test-encode-instruction i64.extend8_s #xC2) 1917 | (test-encode-instruction i64.extend16_s #xC3) 1918 | (test-encode-instruction i64.extend32_s #xC4) 1919 | 1920 | (test-encode-instruction i32.trunc_sat_f32_s #xFC #x00) 1921 | (test-encode-instruction i32.trunc_sat_f32_u #xFC #x01) 1922 | (test-encode-instruction i32.trunc_sat_f64_s #xFC #x02) 1923 | (test-encode-instruction i32.trunc_sat_f64_u #xFC #x03) 1924 | (test-encode-instruction i64.trunc_sat_f32_s #xFC #x04) 1925 | (test-encode-instruction i64.trunc_sat_f32_u #xFC #x05) 1926 | (test-encode-instruction i64.trunc_sat_f64_s #xFC #x06) 1927 | (test-encode-instruction i64.trunc_sat_f64_u #xFC #x07)) 1928 | 1929 | 1930 | ;;; Test Serialization of Expressions 1931 | 1932 | (test expressions 1933 | "Test serialization of expressions" 1934 | 1935 | (test-encoding stream 1936 | (serialize-expression 1937 | '((i32.const 1) 1938 | (i32.const 2) 1939 | i32.add) 1940 | stream) 1941 | 1942 | #(#x41 1 1943 | #x41 2 1944 | #x6A 1945 | #x0B))) 1946 | -------------------------------------------------------------------------------- /test/modules.lisp: -------------------------------------------------------------------------------- 1 | ;;; modules.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020-2021 Alexander Gutev 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 | 23 | (defpackage wasm-encoder/test.modules 24 | (:use #:generic-cl 25 | #:wasm-encoder 26 | #:agutil 27 | #:alexandria 28 | #:trivia 29 | 30 | #:fiveam 31 | #:wasm-encoder/test) 32 | 33 | (:shadowing-import-from #:generic-cl 34 | #:emptyp 35 | #:multiply 36 | #:accumulate) 37 | 38 | (:import-from #:flexi-streams 39 | #:with-output-to-sequence) 40 | 41 | (:import-from #:wasm-encoder 42 | #:serialize-u32 43 | #:serialize-i32 44 | 45 | #:serialize-float 46 | #:serialize-double-float 47 | 48 | #:serialize-string 49 | #:serialize-vector 50 | 51 | #:serialize-type 52 | #:serialize-ftype 53 | 54 | #:serialize-limit 55 | #:serialize-memory 56 | #:serialize-table 57 | 58 | #:serialize-global 59 | 60 | #:serialize-instruction 61 | #:serialize-instructions 62 | #:serialize-expression 63 | 64 | #:serialize-types 65 | #:serialize-imports 66 | #:serialize-function-types 67 | #:serialize-table-types 68 | #:serialize-memory-types 69 | #:serialize-global-section 70 | #:serialize-export-section 71 | #:serialize-start-section 72 | #:serialize-table-elements 73 | #:serialize-functions 74 | #:serialize-data-section)) 75 | 76 | (in-package #:wasm-encoder/test.modules) 77 | 78 | 79 | ;;; Test Suite Definition 80 | 81 | (def-suite modules 82 | :description "Test encoding of modules and section" 83 | :in wasm-encoder) 84 | 85 | (in-suite modules) 86 | 87 | 88 | ;;;; Test Serialization of Sections 89 | 90 | (test section-type 91 | "Test serialization of type sections" 92 | 93 | (test-encoding stream 94 | (serialize-types 95 | (list (make-wasm-function-type :params '(i32 i32) :results '(i32)) 96 | (make-wasm-function-type :results '(i32))) 97 | 98 | stream) 99 | 100 | #(#x01 #x0B #x02 101 | #x60 #x02 #x7F #x7F #x01 #x7F 102 | #x60 #x00 #x01 #x7F))) 103 | 104 | (test section-imports 105 | "Test serialization of imports sections" 106 | 107 | (test-encoding stream 108 | (serialize-imports 109 | (list (make-wasm-import :module "runtime" 110 | :name "fn" 111 | :type :func 112 | :desc 5) 113 | 114 | (make-wasm-import :module "env" 115 | :name "table" 116 | :type :table 117 | :desc (make-wasm-table :min 2)) 118 | 119 | (make-wasm-import :module "env" 120 | :name "memory" 121 | :type :memory 122 | :desc (make-wasm-limit :min 1)) 123 | 124 | (make-wasm-import :module "env" 125 | :name "stack" 126 | :type :global 127 | :desc '(i32 t))) 128 | stream) 129 | 130 | #(#x02 #x37 ; Section ID 2, 54 bytes 131 | #x04 ; 4 Imports 132 | 133 | ;; Import 1: runtime.fn 134 | #x07 #x72 #x75 #x6E #x74 #x69 #x6D #x65 ; "runtime" 135 | #x02 #x66 #x6E ; "fn" 136 | #x00 #x05 ; Function type 5 137 | 138 | ;; Import 2: env.table 139 | #x03 #x65 #x6E #x76 ; "env" 140 | #x05 #x74 #x61 #x62 #x6C #x65 ; "table" 141 | #x01 #x70 #x00 #x02 ; Table type funcref, min: 0 142 | 143 | ;; Import 3: env.memory 144 | #x03 #x65 #x6E #x76 ; "env" 145 | #x06 #x6D #x65 #x6D #x6F #x72 #x79 ; "memory" 146 | #x02 #x00 #x01 ; Memory type, min: 0 147 | 148 | ;; Import 4: env.stack 149 | #x03 #x65 #x6E #x76 ; "env" 150 | #x05 #x73 #x74 #x61 #x63 #x6B ; "stack" 151 | #x03 #x7F #x01))) ; Global type i32 mutable 152 | 153 | (test section-function 154 | "Test serialization of function sections" 155 | 156 | (test-encoding stream 157 | (serialize-function-types '(5 1 3) stream) 158 | 159 | #(#x03 #x04 ; Section ID 3, 4 bytes 160 | #x03 #x05 #x01 #x03))) ; 3 Type Indices: [5, 1, 3] 161 | 162 | (test section-table 163 | "Test serialization of table section" 164 | 165 | (test-encoding stream 166 | (serialize-table-types 167 | (list (make-wasm-table :min 2 :max 100)) 168 | stream) 169 | 170 | #(#x04 #x05 ; Section ID 4, 5 bytes 171 | #x01 #x70 #x01 2 100))) ; 1 Table: funcref, min: 2, max: 100 172 | 173 | (test section-memory 174 | "Test serialization of memory section" 175 | 176 | (test-encoding stream 177 | (serialize-memory-types 178 | (list (make-wasm-limit :min 3)) 179 | stream) 180 | 181 | #(#x05 #x03 ; Section ID 5, 3 bytes 182 | #x01 #x00 3))) ; 1 Memory, min: 3 183 | 184 | (test section-global 185 | "Test serialization of globals section" 186 | 187 | (test-encoding stream 188 | (serialize-global-section 189 | (list (make-wasm-global :type 'i32 :init '((i32.const 3))) 190 | (make-wasm-global :type 'i64 :mutable-p t)) 191 | stream) 192 | 193 | #(#x06 #x09 ; Section ID 6, 9 bytes 194 | #x02 ; 2 Globals 195 | 196 | #x7F #x00 #x41 3 #x0B ; I32 Immutable Global 197 | #x7E #x01 #x0B))) ; I64 Mutable Global 198 | 199 | (test section-exports 200 | "Test serialization of exports section" 201 | 202 | (test-encoding stream 203 | (serialize-export-section 204 | (list (make-wasm-export :name "f1" :type :func :index 2) 205 | (make-wasm-export :name "tab" :type :table :index 0) 206 | (make-wasm-export :name "mem" :type :memory :index 0) 207 | (make-wasm-export :name "stack" :type :global :index 6)) 208 | stream) 209 | 210 | #(#x07 #x1A ; Section ID 7, 26 bytes 211 | #x04 ; 4 Exports 212 | 213 | ;; f1 214 | #x02 #x66 #x31 ; name "f1" 215 | #x00 #x02 ; Function 2 216 | 217 | ;; tab 218 | #x03 #x74 #x61 #x62 ; name "tab" 219 | #x01 #x00 ; Table 0 220 | 221 | ;; mem 222 | #x03 #x6D #x65 #x6D ; name "tab" 223 | #x02 #x00 ; Memory 0 224 | 225 | ;; stack 226 | #x05 #x73 #x74 #x61 #x63 #x6B ; name "stack" 227 | #x03 #x06))) ; Global 6 228 | 229 | (test section-start 230 | "Test serialization of start section" 231 | 232 | (test-encoding stream 233 | (serialize-start-section 5 stream) 234 | 235 | #(#x08 #x01 #x05))) 236 | 237 | (test section-element 238 | "Test serialization of table elements" 239 | 240 | (test-encoding stream 241 | (serialize-table-elements 242 | (list 243 | ;; Active Elements 244 | ;; Table 0 245 | ;; Index initialization 246 | 247 | (make-wasm-element 248 | :index 0 249 | :offset '((i32.const 2)) 250 | :init 251 | (make-wasm-element-init-index 252 | :functions '(5 1 3))) 253 | 254 | (make-wasm-element 255 | :index 0 256 | :offset '((i32.const 100)) 257 | :mode :active 258 | :init 259 | (make-wasm-element-init-index 260 | :functions '(9 8))) 261 | 262 | ;; Active element 263 | ;; Table 5 264 | ;; Index initialization 265 | 266 | (make-wasm-element 267 | :index 5 268 | :offset '((i32.const 7)) 269 | :init 270 | (make-wasm-element-init-index 271 | :functions '(#xFC 1 3))) 272 | 273 | ;; Active element 274 | ;; Table 0 275 | ;; Expression initialization 276 | 277 | (make-wasm-element 278 | :index 0 279 | :offset '((i32.const #xB6)) 280 | :mode :active 281 | :init 282 | (make-wasm-element-init-expressions 283 | :type 'funcref 284 | :expressions '(((ref.func 0)) ((ref.func 9))))) 285 | 286 | 287 | ;; Active element 288 | ;; Table 2 289 | ;; Expression initialization 290 | 291 | (make-wasm-element 292 | :index 2 293 | :offset '((i32.const #xB6)) 294 | :mode :active 295 | :init 296 | (make-wasm-element-init-expressions 297 | :type 'funcref 298 | :expressions '(((ref.func 0)) ((ref.func 6)))))) 299 | 300 | stream) 301 | 302 | #(#x09 #x36 ; Section ID 9 303 | #x05 ; Five Element Segments 304 | 305 | ;; Element 1 306 | 307 | #x00 ; Table 0 308 | #x41 #x02 #x0B ; i32.const 2 309 | #x03 5 1 3 ; Functions [5, 1, 3] 310 | 311 | ;; Element 2 312 | 313 | #x00 ; Table 0 314 | #x41 #xE4 #x0 #x0B ; i32.const 100 315 | #x02 9 8 ; Functions [9, 8] 316 | 317 | ;; Element 4 318 | 319 | #x02 #x05 ; Active element table 5 320 | #x41 #x07 #x0B ; i32.const 7 321 | #x00 322 | #x03 #xFC #x01 #x01 #x03 ; Functions [0xFC, 1, 3] 323 | 324 | ;; Element 6 325 | 326 | #x04 ; Active Element table 0 327 | #x41 #xB6 #x01 #x0B ; i32.const #xB6 328 | #x02 #xD2 #x00 #x0B #xD2 #x09 #x0B ; FUNC.REF Expressions [0, 9] 329 | 330 | 331 | ;; Element 8 332 | 333 | #x06 #x02 ; Active Element table 2 334 | #x41 #xB6 #x01 #x0B ; i32.const #xB6 335 | #x70 ; Type FUNCREF 336 | #x02 #xD2 #x00 #x0B #xD2 #x06 #x0B))) ; FUNC.REF Expressions [0, 6] 337 | 338 | (test section-element-passive 339 | "Test serialization of element sections with passive elements" 340 | 341 | (test-encoding stream 342 | (serialize-table-elements 343 | (list 344 | ;; Passive Element 345 | ;; Index initialization 346 | 347 | (make-wasm-element 348 | :mode :passive 349 | :init 350 | (make-wasm-element-init-index 351 | :functions '(1 2 3))) 352 | 353 | ;; Passive element 354 | ;; Expression initialization 355 | 356 | (make-wasm-element 357 | :offset '((i32.const #xB6)) 358 | :mode :passive 359 | :init 360 | (make-wasm-element-init-expressions 361 | :type 'externref 362 | :expressions '(((ref.func 0)) ((ref.func 9)))))) 363 | stream) 364 | 365 | #(#x09 #x10 ; Section ID 9 366 | #x02 ; Two Table Element Segments 367 | 368 | ;; Element 1 369 | 370 | #x01 #x00 ; Passive Element 371 | #x03 #x01 #x02 #x03 ; Functions [1, 2, 3] 372 | 373 | ;; Element 2 374 | 375 | #x05 ; Passive Element 376 | #x6F ; Type EXTERNREF 377 | #x02 #xD2 #x00 #x0B #xD2 #x09 #x0B))) 378 | 379 | (test section-element-declarative 380 | "Test serialization of element sections with declarative elements" 381 | 382 | (test-encoding stream 383 | (serialize-table-elements 384 | (list 385 | ;; Declarative element 386 | ;; Index initialization 387 | 388 | (make-wasm-element 389 | :mode :declarative 390 | :init 391 | (make-wasm-element-init-index 392 | :functions '(1 2 3))) 393 | 394 | ;; Declarative element 395 | ;; Expression initialization 396 | 397 | (make-wasm-element 398 | :offset '((i32.const #xB6)) 399 | :mode :declarative 400 | :init 401 | (make-wasm-element-init-expressions 402 | :type 'funcref 403 | :expressions '(((ref.func 0)) ((ref.func 7)))))) 404 | 405 | stream) 406 | 407 | #(#x09 #x10 ; Section ID 9 408 | #x02 ; Two Table Element Segments 409 | 410 | ;; Element 1 411 | 412 | #x03 #x00 ; Declarative Element 413 | #x03 #x01 #x02 #x03 ; Functions [1, 2, 3] 414 | 415 | ;; Element 2 416 | 417 | #x07 ; Declarative Element 418 | #x70 ; Type FUNCREF 419 | #x02 #xD2 #x00 #x0B #xD2 #x07 #x0B))) ; FUNC.REF Expressions [0, 9] 420 | 421 | (test section-code 422 | "Test serialization of code section" 423 | 424 | (test-encoding stream 425 | (serialize-functions 426 | (list (make-wasm-function :type 0 427 | :code 428 | '((local.get 0) 429 | (local.get 1) 430 | i32.add)) 431 | 432 | (make-wasm-function :type 1 433 | :locals '(i32 i32 f32 i64 i64 i64) 434 | :code 435 | '((local.get 0) 436 | (local.set 2) 437 | 438 | (local.get 1) 439 | (local.tee 3)))) 440 | stream) 441 | 442 | #(#x0A #x1A ; Section ID 10 443 | #x02 ; 2 Functions 444 | 445 | #x07 ; Code size = 7 bytes 446 | #x00 ; No Locals 447 | #x20 #x00 448 | #x20 #x01 449 | #x6A 450 | #x0B 451 | 452 | #x10 ; Code Size = 16 Bytes 453 | #x03 #x02 #x7F #x01 #x7D #x03 #x7E ; 3 Locals (2 x i32) (1 x f32) (3 x i64) 454 | #x20 #x00 455 | #x21 #x02 456 | #x20 #x01 457 | #x22 #x03 458 | #x0B))) 459 | 460 | (test section-data 461 | "Test serialization of data section" 462 | 463 | (test-encoding stream 464 | (serialize-data-section 465 | (list (make-wasm-data :offset '((i32.const 16)) 466 | :bytes #(1 5 9 45) 467 | :memory 0) 468 | 469 | (make-wasm-data :offset '((i32.const 0)) 470 | :bytes #(7 8 9) 471 | :memory 0) 472 | 473 | (make-wasm-data :offset '((i32.const 3)) 474 | :bytes #(1 2 3 4 5) 475 | :memory #x8C) 476 | 477 | (make-wasm-data :mode :passive 478 | :bytes #(1 2 3 4 5))) 479 | 480 | stream) 481 | 482 | #(#x0B #x25 ; Section ID 11 483 | #x04 ; 2 Data Segments 484 | 485 | #x00 486 | #x41 #x10 #x0B ; i32.const 16 487 | #x04 1 5 9 45 488 | 489 | #x00 490 | #x41 #x0 #x0B ; i32.const 0 491 | #x03 7 8 9 492 | 493 | #x02 494 | #x8C #x01 495 | #x41 #x03 #x0B 496 | #x05 1 2 3 4 5 497 | 498 | #x01 499 | #x05 1 2 3 4 5))) 500 | -------------------------------------------------------------------------------- /test/test.lisp: -------------------------------------------------------------------------------- 1 | ;;; test.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020 Alexander Gutev 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 | 23 | (defpackage wasm-encoder/test 24 | (:use #:generic-cl 25 | #:wasm-encoder 26 | #:agutil 27 | #:alexandria 28 | #:trivia 29 | 30 | #:fiveam) 31 | 32 | (:shadowing-import-from #:generic-cl 33 | #:emptyp 34 | #:multiply 35 | #:accumulate) 36 | 37 | (:import-from #:flexi-streams 38 | #:with-output-to-sequence) 39 | 40 | (:import-from #:wasm-encoder 41 | #:serialize-u32 42 | #:serialize-i32 43 | 44 | #:serialize-float 45 | #:serialize-double-float 46 | 47 | #:serialize-string 48 | #:serialize-vector 49 | 50 | #:serialize-type 51 | #:serialize-ftype 52 | 53 | #:serialize-limit 54 | #:serialize-memory 55 | #:serialize-table 56 | 57 | #:serialize-global 58 | 59 | #:serialize-instruction 60 | #:serialize-instructions 61 | #:serialize-expression 62 | 63 | #:serialize-types 64 | #:serialize-imports 65 | #:serialize-function-types 66 | #:serialize-table-types 67 | #:serialize-memory-types 68 | #:serialize-global-section 69 | #:serialize-export-section 70 | #:serialize-start-section 71 | #:serialize-table-elements 72 | #:serialize-functions 73 | #:serialize-data-section) 74 | 75 | (:export 76 | #:test-encoding 77 | #:test-error 78 | #:test-encode-instruction 79 | #:wasm-encoder 80 | #:test-wasm-encoder)) 81 | 82 | (in-package #:wasm-encoder/test) 83 | 84 | 85 | ;;; Test Utilities 86 | 87 | (defmacro test-encoding (stream &body (form expected)) 88 | `(is (= ,expected 89 | (with-output-to-sequence (,stream) ,form)))) 90 | 91 | (defmacro test-error (stream &body (form type)) 92 | `(signals ,type 93 | (with-output-to-sequence (,stream) ,form) 94 | ,(format nil "~a results in ~a" form type))) 95 | 96 | (defmacro test-encode-instruction (op &rest opcodes) 97 | `(test-encoding stream 98 | (serialize-instruction ',op stream) 99 | #(,@opcodes))) 100 | 101 | 102 | ;;; Test Suite Definition 103 | 104 | (def-suite wasm-encoder 105 | :description "Master test suite for wasm-encoder") 106 | 107 | (in-suite wasm-encoder) 108 | 109 | (defun test-wasm-encoder () 110 | (run! 'wasm-encoder)) 111 | -------------------------------------------------------------------------------- /test/values.lisp: -------------------------------------------------------------------------------- 1 | ;;; values.lisp 2 | ;;; 3 | ;;; Copyright (c) 2020-2021 Alexander Gutev 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 | 23 | (defpackage wasm-encoder/test.values 24 | (:use #:generic-cl 25 | #:wasm-encoder 26 | #:agutil 27 | #:alexandria 28 | #:trivia 29 | 30 | #:fiveam 31 | #:wasm-encoder/test) 32 | 33 | (:shadowing-import-from #:generic-cl 34 | #:emptyp 35 | #:multiply 36 | #:accumulate) 37 | 38 | (:import-from #:flexi-streams 39 | #:with-output-to-sequence) 40 | 41 | (:import-from #:wasm-encoder 42 | #:serialize-u32 43 | #:serialize-i32 44 | 45 | #:serialize-float 46 | #:serialize-double-float 47 | 48 | #:serialize-string 49 | #:serialize-vector 50 | 51 | #:serialize-type 52 | #:serialize-ftype 53 | 54 | #:serialize-limit 55 | #:serialize-memory 56 | #:serialize-table 57 | 58 | #:serialize-global 59 | 60 | #:serialize-instruction 61 | #:serialize-instructions 62 | #:serialize-expression 63 | 64 | #:serialize-types 65 | #:serialize-imports 66 | #:serialize-function-types 67 | #:serialize-table-types 68 | #:serialize-memory-types 69 | #:serialize-global-section 70 | #:serialize-export-section 71 | #:serialize-start-section 72 | #:serialize-table-elements 73 | #:serialize-functions 74 | #:serialize-data-section)) 75 | 76 | (in-package #:wasm-encoder/test.values) 77 | 78 | 79 | ;;; Test Suite Definition 80 | 81 | (def-suite values 82 | :description "Test encoding of basic values" 83 | :in wasm-encoder) 84 | 85 | (in-suite values) 86 | 87 | 88 | ;;; Test Serialization of Values 89 | 90 | (test values-unsigned-integer 91 | "Test serialization of unsigned integer values" 92 | 93 | (test-encoding stream (serialize-u32 #x18 stream) #(#x18)) 94 | (test-encoding stream (serialize-u32 #x7D stream) #(#x7D)) 95 | (test-encoding stream (serialize-u32 64 stream) #(64)) 96 | (test-encoding stream (serialize-u32 #xA52 stream) #(#xD2 #x14)) 97 | 98 | (test-encoding stream 99 | (serialize-u32 #xA5B23408 stream) 100 | #(#x88 #xE8 #xC8 #xAD #x0A)) 101 | 102 | (test-encoding stream 103 | (serialize-u32 (1- (expt 2 32)) stream) 104 | #(#xFF #xFF #xFF #xFF #x0F))) 105 | 106 | (test values-unsigned-integer-range-checks 107 | "Test range checks in serialization of unsigned integer values" 108 | 109 | (test-error stream (serialize-u32 (expt 2 32) stream) type-error) 110 | (test-error stream (serialize-u32 -10 stream) type-error)) 111 | 112 | (test values-signed-integer 113 | "Test serialization of signed integer values" 114 | 115 | (test-encoding stream (serialize-i32 #x18 stream) #(#x18)) 116 | (test-encoding stream (serialize-i32 #x7D stream) #(#xFD #x00)) 117 | (test-encoding stream (serialize-i32 64 stream) #(#xC0 #x00)) 118 | (test-encoding stream (serialize-i32 #xA52 stream) #(#xD2 #x14)) 119 | 120 | (test-encoding stream (serialize-i32 -15 stream) #(#x71)) 121 | (test-encoding stream (serialize-i32 -1234 stream) #(#xAE #x76))) 122 | 123 | (test values-signed-integer-range-checkes 124 | "Test range checks in serialization of signed integer values" 125 | 126 | (test-error stream (serialize-i32 (expt 2 31) stream) type-error) 127 | (test-error stream (serialize-i32 (- (1+ (expt 2 31))) stream) type-error)) 128 | 129 | (test values-float-single 130 | "Test serialization of single precision (32 bit) floating point values" 131 | 132 | (test-encoding stream (serialize-float 7.125 stream) #(#x00 #x00 #xE4 #x40)) 133 | (test-encoding stream (serialize-float 12.576 stream) #(#x4c #x37 #x49 #x41)) 134 | 135 | (test-encoding stream (serialize-float -7.125 stream) #(#x00 #x00 #xE4 #xC0)) 136 | (test-encoding stream (serialize-float -12.576 stream) #(#x4c #x37 #x49 #xC1)) 137 | 138 | (test-error stream (serialize-float 112.765d0 stream) type-error)) 139 | 140 | (test values-float-double 141 | "Test serialization of double precision (64 bit) floating point values" 142 | 143 | (test-encoding stream (serialize-double-float 35.0625d0 stream) 144 | #(#x00 #x00 #x00 #x00 #x00 #x88 #x41 #x40)) 145 | 146 | (test-encoding stream (serialize-double-float 163.8973d0 stream) 147 | #(#x6D #x56 #x7D #xAE #xB6 #x7C #x64 #x40)) 148 | 149 | 150 | (test-encoding stream (serialize-double-float -35.0625d0 stream) 151 | #(#x00 #x00 #x00 #x00 #x00 #x88 #x41 #xC0)) 152 | 153 | (test-encoding stream (serialize-double-float -163.8973d0 stream) 154 | #(#x6D #x56 #x7D #xAE #xB6 #x7C #x64 #xC0))) 155 | 156 | (test values-strings 157 | "Test serialization of strings" 158 | 159 | (test-encoding stream 160 | (serialize-string "hello" stream) 161 | #(#x05 #x68 #x65 #x6C #x6C #x6F)) 162 | 163 | ;; Test non-ASCII characters 164 | (test-encoding stream 165 | (serialize-string "Уникод" stream) 166 | #(#x0C #xD0 #xA3 #xD0 #xBD #xD0 #xB8 #xD0 #xBA #xD0 #xBE #xD0 #xB4))) 167 | 168 | (test values-vectors 169 | "Test serialization of vectors" 170 | 171 | (test-encoding stream 172 | (serialize-vector #'write-byte #(#x0A #xB0 #xCD) stream) 173 | #(#x03 #x0A #xB0 #xCD)) 174 | 175 | (test-encoding stream 176 | (serialize-vector #'write-byte #(#x0A) stream) 177 | #(#x01 #x0A)) 178 | 179 | (test-encoding stream 180 | (serialize-vector #'write-byte #() stream) 181 | #(#x00))) 182 | 183 | 184 | ;;; Test Serialization of Types 185 | 186 | (test types-values 187 | "Test serialization of value types" 188 | 189 | (test-encoding stream (serialize-type 'i32 stream) #(#x7F)) 190 | (test-encoding stream (serialize-type 'i64 stream) #(#x7E)) 191 | (test-encoding stream (serialize-type 'f32 stream) #(#x7D)) 192 | (test-encoding stream (serialize-type 'f64 stream) #(#x7C)) 193 | 194 | (test-encoding stream (serialize-type 'funcref stream) #(#x70)) 195 | (test-encoding stream (serialize-type 'externref stream) #(#x6F))) 196 | 197 | (test types-values-error 198 | "Test errors signalled when serializing unknown types" 199 | 200 | (test-error stream (serialize-type 'not-a-type stream) error)) 201 | 202 | (test types-function 203 | "Test serialization of function types" 204 | 205 | (test-encoding stream 206 | 207 | (serialize-ftype 208 | (make-wasm-function-type :params '(i32 i32) :results '(i32)) 209 | stream) 210 | 211 | #(#x60 #x02 #x7F #x7F #x01 #x7F)) 212 | 213 | (test-encoding stream 214 | 215 | (serialize-ftype 216 | (make-wasm-function-type :params '(i32 f32 i64 f64) :results '(f64)) 217 | stream) 218 | 219 | #(#x60 #x04 #x7F #x7D #x7E #x7C #x01 #x7C)) 220 | 221 | (test-encoding stream 222 | 223 | (serialize-ftype 224 | (make-wasm-function-type :params '(i32 i64 i32)) 225 | stream) 226 | 227 | #(#x60 #x03 #x7F #x7E #x7F #x00)) 228 | 229 | (test-encoding stream 230 | 231 | (serialize-ftype 232 | (make-wasm-function-type :params nil :results nil) 233 | stream) 234 | 235 | #(#x60 #x00 #x00)) 236 | 237 | (test-encoding stream 238 | (serialize-ftype 239 | (make-wasm-function-type :params '(i32 i32) :results '(funcref externref)) 240 | stream) 241 | 242 | #(#x60 #x02 #x7F #x7F #x02 #x70 #x6F))) 243 | 244 | (test types-limits 245 | "Test serialization of limit types" 246 | 247 | (test-encoding stream 248 | 249 | (serialize-limit 250 | (make-wasm-limit :min 3) 251 | stream) 252 | 253 | #(#x00 #x03)) 254 | 255 | (test-encoding stream 256 | 257 | (serialize-limit 258 | (make-wasm-limit :min 100 :max 350) 259 | stream) 260 | 261 | #(#x01 100 #xDE #x02))) 262 | 263 | (test types-limits-errors 264 | "Test errors in serialization of limit types" 265 | 266 | (test-error stream 267 | 268 | (serialize-limit 269 | (make-wasm-limit :min -3 :max (expt 2 35)) 270 | stream) 271 | 272 | type-error)) 273 | 274 | (test types-memory 275 | "Test serialization of memory types" 276 | 277 | (test-encoding stream 278 | 279 | (serialize-memory 280 | (make-wasm-limit :min 1 :max 3) 281 | stream) 282 | 283 | #(#x01 #x01 #x03)) 284 | 285 | (test-encoding stream 286 | 287 | (serialize-memory 288 | (make-wasm-limit :min 1) 289 | stream) 290 | 291 | #(#x00 #x01))) 292 | 293 | (test types-tables 294 | "Test serialization of table types" 295 | 296 | (test-encoding stream 297 | (serialize-table 298 | (make-wasm-table :min 10) 299 | stream) 300 | 301 | #(#x70 #x00 10)) 302 | 303 | (test-encoding stream 304 | (serialize-table 305 | (make-wasm-table :min 10 :max 100) 306 | stream) 307 | 308 | #(#x70 #x01 10 100)) 309 | 310 | (test-encoding stream 311 | (serialize-table 312 | (make-wasm-table :type 'externref :min 10 :max 100) 313 | stream) 314 | 315 | #(#x6F #x01 10 100))) 316 | 317 | (test types-tables-errors 318 | "Test errors in serialization of table types" 319 | 320 | (test-error stream 321 | (serialize-table 322 | (make-wasm-table :type 'i32 :min 10) 323 | stream) 324 | 325 | error)) 326 | 327 | (test types-globals 328 | "Test serialization of global variable types" 329 | 330 | (test-encoding stream 331 | (serialize-global 'i32 nil stream) 332 | 333 | #(#x7F #x00)) 334 | 335 | (test-encoding stream 336 | (serialize-global 'i64 t stream) 337 | 338 | #(#x7E #x01)) 339 | 340 | (test-encoding stream 341 | (serialize-global 'funcref t stream) 342 | 343 | #(#x70 #x01))) 344 | -------------------------------------------------------------------------------- /wasm-encoder.asd: -------------------------------------------------------------------------------- 1 | ;;; wasm-encoder.asd 2 | ;;; 3 | ;;; Copyright (c) 2020 Alexander Gutev 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 | 23 | 24 | (asdf:defsystem #:wasm-encoder 25 | :description "Library for serializing WebAssembly modules to binary .wasm files" 26 | :author "Alexander Gutev " 27 | :license "MIT" 28 | :version "0.2" 29 | :serial t 30 | :depends-on (#:generic-cl 31 | #:agutil 32 | #:alexandria 33 | #:trivia 34 | 35 | #:ieee-floats 36 | #:babel 37 | #:flexi-streams) 38 | 39 | :components ((:module 40 | "src" 41 | :serial t 42 | :components 43 | ((:file "package") 44 | (:file "wasm-encoder")))) 45 | 46 | :in-order-to ((asdf:test-op (asdf:test-op :wasm-encoder/test)))) 47 | 48 | (asdf:defsystem #:wasm-encoder/test 49 | :description "Tests for wasm-encoder." 50 | :author "Alexander Gutev" 51 | :license "MIT" 52 | :depends-on (#:wasm-encoder #:fiveam) 53 | 54 | :components ((:module 55 | "test" 56 | :serial t 57 | :components 58 | ((:file "test") 59 | (:file "values") 60 | (:file "instructions") 61 | (:file "modules")))) 62 | 63 | :perform (asdf:test-op :after (op c) 64 | (uiop:symbol-call '#:wasm-encoder/test '#:test-wasm-encoder))) 65 | --------------------------------------------------------------------------------