From 8958ed1957538df362d9de576dab13b1f6984216 Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 5 Jan 2024 04:28:40 +0100 Subject: [PATCH 01/47] add work in progress --- lvtrun/README.md | 397 +++++++++++++++++++++++++++++++++ lvtrun/app/Main.hs | 15 +- lvtrun/app/WasmMod/Header.hs | 29 +++ lvtrun/app/WasmMod/Module.hs | 36 +++ lvtrun/app/WasmMod/Sections.hs | 33 +++ lvtrun/lvtrun.cabal | 8 +- lvtrun/package.yaml | 1 + lvtrun/scripts/build.ps1 | 1 + lvtrun/stack.yaml | 3 +- lvtrun/stack.yaml.lock | 20 ++ lvtrun/test/HexFile | Bin 0 -> 5008 bytes lvtrun/test/test.cpp | 22 ++ lvtrun/test/test.wasm | Bin 0 -> 830 bytes 13 files changed, 561 insertions(+), 4 deletions(-) create mode 100644 lvtrun/app/WasmMod/Header.hs create mode 100644 lvtrun/app/WasmMod/Module.hs create mode 100644 lvtrun/app/WasmMod/Sections.hs create mode 100644 lvtrun/scripts/build.ps1 create mode 100644 lvtrun/stack.yaml.lock create mode 100644 lvtrun/test/HexFile create mode 100644 lvtrun/test/test.cpp create mode 100644 lvtrun/test/test.wasm diff --git a/lvtrun/README.md b/lvtrun/README.md index 163ee90..eb356f5 100644 --- a/lvtrun/README.md +++ b/lvtrun/README.md @@ -1 +1,398 @@ # lvtrun + +# ----------------------- 0 ----------------------- + +00 61 73 6d # magic number, always 00 61 73 6d, otherwise the file is invalid +01 00 00 00 # version, always 01 00 00 00, otherwise the file is invalid + +# ----------------------- 1 ----------------------- + +# 01 is the id of the id for type section +01 11 04 +# 11 is the length of the section = 17 in decimal +# 04 is the number of types in the section + +60 00 01 7f +# 60 is the type "function" +# 00 number of parameters +# 01 number of results +# 0x7f is the type "i32" + +60 00 00 +# 0 parameter and 0 result +60 01 7f 00 +# 1 parameter i32 and 0 result +60 01 7f 01 7f +# 1 parameter i32 and 1 result i32 + +(type $t0 (func (result i32))) +(type $t1 (func)) +(type $t2 (func (param i32))) +(type $t3 (func (param i32) (result i32))) + +# ----------------------- 2 ----------------------- + +02 +# 02 is the id of the import section +24 01 16 77 +61 73 69 5f +73 6e 61 70 +73 68 6f 74 +5f 70 72 65 +76 69 65 77 +31 09 70 72 +6f 63 5f 65 +78 69 74 00 +02 + +# ----------------------- 3 ----------------------- + +# section 3 is the function section with 17 bytes of length +03 11 +10 01 00 01 +01 01 02 02 +00 01 00 00 +00 00 02 03 00 + +# ----------------------- 4 ----------------------- the table section + +04 +# 05 = length of the section +05 +# 01 = number of table (its a vector) +01 +# 70 = reftype {can be 70 = funcref or 6f = externref} +70 +# 01 is the indiacator to say "0x00 = min x, max empty or 0x01 = min x, max y" +01 +# 02 is the limit min and max of the table +02 02 + +# ----------------------- 5 ----------------------- the memory section + +05 +06 + +# 01 = number of memory (its a vector) +01 +01 c7 +02 c7 02 + +# ----------------------- 6 ----------------------- the global section + +06 +12 + +03 7f 01 +41 c7 c7 04 +0b 7f 01 41 +00 0b 7f 01 +41 00 0b + +# ----------------------- 7 ----------------------- + +# export type id +# 00 = function +# 01 = table +# 02 = memory +# 03 = global + + +07 +# 182 bytes of length +b6 +# 010c = 12 exports +01 0c + +# 06 is the length of the name +06 +# 5f 5f 69 6e 64 65 is the name "memory" +6d 65 6d 6f 72 79 +# 02 is export type "memory" +# 00 means export = first vector of memory +02 00 + + +# 19 hex = size in dec = 25 char for the name = __indirect_function +19 +# "_ _ i n d i r e c t _ f u n c t i o n _ t a b l e" +5f 5f 69 6e 64 69 72 65 63 74 5f 66 75 6e 63 74 69 6f 6e 5f 74 61 62 6c 65 +# 01 is export type "table" +01 00 + + +# size 6 +06 +# name "start" +5f 73 74 61 72 74 +# its a type function with id 03 +00 03 + + +# size = 16 name = __errno_location " +10 +5f 5f 65 72 72 6e 6f 5f 6c 6f 63 61 74 69 6f 6e +# export function of id 8 +00 08 + +# size = 21 name "emcrypten_stack_init" +15 +65 6d 73 63 72 69 70 74 65 6e 5f 73 74 61 63 6b 5f 69 6e 69 74 +# export function of id 9 +00 09 + +# size 25 name = "emscripten_stack_get_free" +19 +65 6d 73 63 72 69 70 74 65 6e 5f 73 74 61 63 6b 5f 67 65 74 5f 66 72 65 65 +# export function of id 10 +00 0a + +# size 25 name = "emcrypten_stack_get_base" +19 65 6d 73 63 72 69 70 74 65 6e 5f 73 74 61 63 6b 5f 67 65 74 5f 62 61 73 65 +# export function of id 11 +00 0b + +# size = "emcrypten_stack_get_end" +18 65 6d 73 63 72 69 70 74 65 6e 5f 73 74 61 63 6b 5f 67 65 74 5f 65 6e 64 +# export function of id 12 +00 0c + +# size = 9 name = "stackSave" +09 73 74 61 63 6b 53 61 76 65 +# export function of id 13 +00 0d + +# size = 12 name = "stackRestore" +0c 73 74 61 63 6b 52 65 73 74 6f 72 65 +# export function of id 14 +00 0e + +# size = 10 name = "stackAlloc" +0a 73 74 61 63 6b 41 6c 6c 6f 63 +# export function of id 15 +00 0f + +# size = 28 name = "emscripten_get_sbrk_ptr" +1c +65 6d 73 63 72 69 70 74 65 6e 5f 73 74 61 63 6b 5f 67 65 74 5f 63 75 72 72 65 6e 74 +# export function of id 16 +00 10 + +# ----------------------- 9 ----------------------- + +09 07 +01 00 41 01 +0b 01 01 + +# ----------------------- 10 ----------------------- + +# refer to: +https://webassembly.github.io/spec/core/binary/instructions.html + +0a +# size = cb = 203 +cb 01 + +# 16 func +10 + +# --- first func --------- + +# first function has a size of 4 +04 +# number of local +00 +# 10 = call +10 09 = call func id 9 +# 0b means end of expression +0b + +# --- second func --------- + +# 25 = length of 37 +25 +# number of local variable of type ( see below ) +01 +# 5 locals variable of type i32 (7f) (because 1 variable local of type x ^) +05 7f +# 23 = global.get = take the index u32 of a symbol in the global section +23 00 +# 21 = local.set = extract top stack value and store it in a local variable +21 00 +# +41 10 = i32.const of value 16 +21 01 = local.set localidx 1 = localvar +20 00 = local.get localidx 0 = localvar +20 01 = local.get localidx 1 = localvar +6b = i32.sub +21 02 = local.set localidx 2 = localvar +41 00 = i32.const of value 0 +21 03 = local.set localidx 3 = localvar +20 02 = local.get localidx 2 = localvar +20 03 = local.get localidx 3 = localvar +36 02 0c = i32.store align 02 offset 0c = 12 +41 00 = i32.const of value 0 +21 04 = local.set localidx 4 = localvar +20 04 = local.get localidx 4 = localvar +0f = return +0b + +# --- third func --------- + +11 +# 0 local variable +00 +# block + 02 + 40 = memory.grow ? + 41 01 = i32.const of value 1 + 45 = i32.eqz + 0d 00 = br_if label 0 + 10 01 = call func id 1 + 0b +10 02 = call func id 2 +10 06 = call func id 6 +00 = unreachable +0b + +# --- fourth func --------- + +02 +00 +0b + +# --- fifth func --------- + +2b = size 43 +# 1 local variable of type i32 (7f) +01 01 7f +41 00 = i32.const of value 0 +21 00 = local.set id 0 (the local variable) + 02 = begin block + 40 = memory.grow ? + 41 00 = i32.const of value 0 + 41 00 = i32.const of value 0 + 4d = i32.le_u + 0d 00 = br_if label 0 + 03 = loop + 40 = memory.grow ? + 20 00 = local.get 0 + 41 7c = i32.const of value -4 ? + 6a = i32.add + 22 00 = local.tee 0 + 28 02 00 = i32.load align 02 offset 00 + 11 01 00 = call_indirect index typeidx 01 typeidx 00 + 20 00 = local.get 0 + 41 00 = i32.const of value 0 + 4b = i32.gt_u + 0d 00 = br_if label 0 + 0b + 0b +10 04 = call func id 4 +0b + +# --- sixth func --------- + +0d +00 +10 04 = call func id 4 +10 05 = call func id 5 +10 04 = call func id 4 +20 00 = local.get 0 +10 07 = call func id 7 +00 = unreachable +0b 07 00 20 00 10 00 00 +0b + +# --- seventh func --------- + +06 +00 +41 = i32.const +c7 c7 04 = value (maybe 65536) +0b + +# --- eighth func --------- + +12 +00 +41 c7 c7 04 = i32.const value (maybe 65536) +24 02 = global.set 2 +41 00 = i32.const of value 0 +41 0f = i32.const of value 15 +6a = i32.add +41 70 = i32.const of value (maybe -16) +71 = i32.and +24 01 = global.set 1 +0b + +# --- ninth func --------- + +07 +# no local variable +00 +23 00 = global.get 0 +23 01 = global.get 1 +6b = i32.sub +0b + +# --- tenth func --------- + +04 +00 +23 02 = global.get 2 +0b + +# --- eleventh func --------- + +04 +00 +23 01 = global.get 1 +0b + +# --- twelfth func --------- + +04 +00 +23 00 = global.get 0 +0b + +# --- thirteenth func --------- + +06 +00 +20 00 = local.get 0 +24 00 = global.set 0 +0b + +# --- fourteenth func --------- + +12 +# number of local variable of type ( see below ) +01 +# 2 locals variable of type i32 (7f) (because 1 variable local of type x ^) +02 7f +23 00 = global.get 0 +20 00 = local.get 0 +6b = i32.sub +41 70 = i32.const of value +71 = i32.and +22 01 = local.tee 1 +24 00 = global.set 0 +20 01 = local.get 1 + + +# ----------------------- 11 ----------------------- + +# id 11 +0b +# size 4 +04 +# Bitfield. 0 = passive segment, 1 = active segment = presence of an explicit memory index +00 +# i32.const +23 +# principal memory +00 +# i32.store +0b diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 0726d58..6869bb3 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -7,7 +7,18 @@ module Main (main) where -import Lib +import qualified Data.ByteString as BS +import WasmMod.Module +import WasmMod.Header + +getFileContent :: String -> IO BS.ByteString +getFileContent path = BS.readFile path main :: IO () -main = someFunc +main = do + wasmFile <- getFileContent "./test/test.wasm" + let wasmMod = loadModule wasmFile + print wasmMod + if isHeaderValid $ header wasmMod + then putStrLn "Header is valid" + else putStrLn "Header is invalid" diff --git a/lvtrun/app/WasmMod/Header.hs b/lvtrun/app/WasmMod/Header.hs new file mode 100644 index 0000000..45606c1 --- /dev/null +++ b/lvtrun/app/WasmMod/Header.hs @@ -0,0 +1,29 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Header +-} + +module WasmMod.Header + ( + ModHeader(..), + getModuleHeader, + isHeaderValid + ) +where + +import qualified Data.ByteString as BS (ByteString, take, drop, pack) + +data ModHeader = ModHeader { + magicNumber :: BS.ByteString, + version :: BS.ByteString +} deriving (Show) + +getModuleHeader :: BS.ByteString -> ModHeader +getModuleHeader bytes = ModHeader (BS.take 4 bytes) (BS.take 4 $ BS.drop 4 bytes) + +isHeaderValid :: ModHeader -> Bool +isHeaderValid header = + magicNumber header == BS.pack [0x00, 0x61, 0x73, 0x6d] && + version header == BS.pack [0x01, 0x00, 0x00, 0x00] diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs new file mode 100644 index 0000000..1e14453 --- /dev/null +++ b/lvtrun/app/WasmMod/Module.hs @@ -0,0 +1,36 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Module +-} + +module WasmMod.Module + ( + WasmModule(..), + loadModule + ) +where + +import qualified Data.ByteString as BS (ByteString, unpack) +import Numeric (showHex) + +import WasmMod.Header +import WasmMod.Sections + +data WasmModule = WasmModule { + header :: ModHeader, + sections :: [Section] +} + +instance Show WasmModule where + show wasmMod = "Wasm Module Header:\n" ++ + " Magic Number: " ++ (concat $ map (\x -> showHex x " ") $ + BS.unpack $ magicNumber $ header wasmMod) ++ "\n" ++ + " Version: " ++ (concat $ map (\x -> showHex x " ") $ + BS.unpack $ version $ header wasmMod) ++ "\n" ++ + " Sections: " ++ (show $ sections wasmMod) ++ "\n" + + +loadModule :: BS.ByteString -> WasmModule +loadModule bytes = WasmModule (getModuleHeader bytes) [] diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs new file mode 100644 index 0000000..8bcbe58 --- /dev/null +++ b/lvtrun/app/WasmMod/Sections.hs @@ -0,0 +1,33 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Sections +-} + +module WasmMod.Sections where + +import qualified Data.ByteString as BS (ByteString) + +data SectionID = + Custom + | Type + | Import + | Function + | Table + | Memory + | Global + | Export + | Start + | Element + | Code + | Data + | DataCount + | Invalid + deriving (Show, Eq) + +data Section = Section { + id :: SectionID, + size :: Int, + content :: BS.ByteString +} deriving (Show) diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 4e28df2..2239e9c 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack @@ -35,11 +35,15 @@ library ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints build-depends: base >=4.7 && <5 + , bytestring default-language: Haskell2010 executable lvtrun-exe main-is: Main.hs other-modules: + WasmMod.Header + WasmMod.Module + WasmMod.Sections Paths_lvtrun autogen-modules: Paths_lvtrun @@ -48,6 +52,7 @@ executable lvtrun-exe ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 + , bytestring , lvtrun default-language: Haskell2010 @@ -63,5 +68,6 @@ test-suite lvtrun-test ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 + , bytestring , lvtrun default-language: Haskell2010 diff --git a/lvtrun/package.yaml b/lvtrun/package.yaml index e1a5e8f..b0d5b04 100644 --- a/lvtrun/package.yaml +++ b/lvtrun/package.yaml @@ -21,6 +21,7 @@ description: Please see the README on GitHub at = 4.7 && < 5 +- bytestring ghc-options: - -Wall diff --git a/lvtrun/scripts/build.ps1 b/lvtrun/scripts/build.ps1 new file mode 100644 index 0000000..176db1c --- /dev/null +++ b/lvtrun/scripts/build.ps1 @@ -0,0 +1 @@ +stack build --copy-bins --local-bin-path . diff --git a/lvtrun/stack.yaml b/lvtrun/stack.yaml index b2997b7..a37bfe6 100644 --- a/lvtrun/stack.yaml +++ b/lvtrun/stack.yaml @@ -40,7 +40,8 @@ packages: # - git: https://github.com/commercialhaskell/stack.git # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a # -# extra-deps: [] +extra-deps: + - bytestring-0.12.0.2 # Override default flag values for local packages and extra-deps # flags: {} diff --git a/lvtrun/stack.yaml.lock b/lvtrun/stack.yaml.lock new file mode 100644 index 0000000..11e1cde --- /dev/null +++ b/lvtrun/stack.yaml.lock @@ -0,0 +1,20 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: +- completed: + hackage: bytestring-0.12.0.2@sha256:9fc077ff5b7ed2246773c3ac4370ef8822e4834d4587522b68ae551a5968fb86,7891 + pantry-tree: + sha256: 05b5b3ef529f137062c632c38d9c94482ee25bcc0438d51a4be5448dafe690c9 + size: 4355 + original: + hackage: bytestring-0.12.0.2 +snapshots: +- completed: + sha256: e176944bc843f740e05242fa7a66ca1f440c127e425254f7f1257f9b19add23f + size: 712153 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/0.yaml + original: + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/0.yaml diff --git a/lvtrun/test/HexFile b/lvtrun/test/HexFile new file mode 100644 index 0000000000000000000000000000000000000000..34e4b4f854e41a08a6c12539bdad3d5a7304fb29 GIT binary patch literal 5008 zcmc(jNsin&5JkB*;5+mYq^z`LAIDSO{{(IG@#%v>XBONWw>Jb4jN!+RL0R?V->q%! zpLVpppJ#g+_iyced)MX2e4i&T^L6hw=zJJ<+x3D+t&G#^Cj=1 zd>t=GkFmxfX7zUK@3(fpw0PNjrsN6gHRn%V@?oG23*UNyKeftvY&s zJk_t!y;J$rS}UVe|LVYedJ3JdqY7fE_igZIMrK#5e;+xjA1jf3={_GdlilxMw}I!& zh#s6@2WF5T?b|r*bno^6nVzG{mCt=htYfNaC)ihDwl`9JJyW&XSI59}UK6J6)Jb>Q zIlQJD^$*X-)m`LbM_(x>{BnO}1}uB}Ih1ZS<3KJgYjp9daw{vSN|LxR-Ad?#zf1W6JhtR{bP1! z>fWdov8&1VcYbC~SIxO|Rt}MGjm}*4-2kGZAKafu-@a7AbIzlk<8<%W>))-2(+Y-l zx6De_?UXMdv*|vsyUyGrfsNc@N%lTp-$dfZ+w26HQFCi}c)d2i9ys^0Py7u0RwR0hoQSvn9-Z80?V<^uyw*FmFTJYqu#K5|rU{Z&v-_(6{u2ZBtK$ zufKInT&&`aIf}eSVQ<76I=Lf1M?b^^n<1~Y7RX;3__NiWbiAc|#VLn1I+~BD(sgEy zj&YS&RQCTPD&@~mfn?QpbJkcO^w-@pY;y|K2{z;_w#m}ovxE(q0fWE2!w($3J&#}$ zNtmfjikvff@p_EePwnWUym}YU^t^6>nWOU+Tjc9h*1oC}G;?1)AYVN=J7(l`c&}4j z@grY7JjSZ??#$}5&bvOVrhe3{(_O8QTZQDMe$4_&n6EP~66=M$c8eQ3JlA~TWbI_C zkj}L3Vwh95_DP-V>KXabD(7_@c!!hphRBIhJ2~GVU-9pb3PP~m$8Y1hqr%}t9tqjy z4%^u(#>Ib?+(w^gj_~FjC3@xOEriIc{{7mebu-Ah-t;rA&g*xGOiqNi@pa1=3jJ_p z*3Z3}-Tlg|VvqsO@Z;uJJ~iuW?(}f(b+Sao>9@FYo8KqD;*1}yJjFni;3vno{tC|D z&e+QQqh|guVnLdvJA8*tV^k;eE3keZn|Cp_bmy*cI6RNdor(U%pvg=mJmd+7d%-1+F2Ru?CE2ma_(oup1+Yq02DHpk-a@0&jt)Q(s8@)C z%#W4;d`Q4qGUPvQUcxy+h9CmoC!mz}lx%w1*B*nOfC|LZ0Lq8s4>&uv1xZYLlkfHm il!-aaaid(A(^zmU;{wA1)C@_!0>egzU!oxXv;P1dp`N|~ literal 0 HcmV?d00001 From 6a6bafbb5027af7afaa2e2bc9e75b7493d038634 Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 5 Jan 2024 04:40:37 +0100 Subject: [PATCH 02/47] move getFileContent in loadModule --- lvtrun/app/Main.hs | 10 +--------- lvtrun/app/WasmMod/Module.hs | 10 +++++++--- lvtrun/app/WasmMod/Sections.hs | 7 ++++++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 6869bb3..023667b 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -7,18 +7,10 @@ module Main (main) where -import qualified Data.ByteString as BS import WasmMod.Module import WasmMod.Header -getFileContent :: String -> IO BS.ByteString -getFileContent path = BS.readFile path - main :: IO () main = do - wasmFile <- getFileContent "./test/test.wasm" - let wasmMod = loadModule wasmFile + wasmMod <- loadModule "./test/test.wasm" print wasmMod - if isHeaderValid $ header wasmMod - then putStrLn "Header is valid" - else putStrLn "Header is invalid" diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index 1e14453..1728212 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -12,7 +12,7 @@ module WasmMod.Module ) where -import qualified Data.ByteString as BS (ByteString, unpack) +import qualified Data.ByteString as BS (ByteString, unpack, readFile) import Numeric (showHex) import WasmMod.Header @@ -31,6 +31,10 @@ instance Show WasmModule where BS.unpack $ version $ header wasmMod) ++ "\n" ++ " Sections: " ++ (show $ sections wasmMod) ++ "\n" +getFileContent :: String -> IO BS.ByteString +getFileContent path = BS.readFile path -loadModule :: BS.ByteString -> WasmModule -loadModule bytes = WasmModule (getModuleHeader bytes) [] +loadModule :: String -> IO WasmModule +loadModule filePath = do + bytes <- getFileContent filePath + return $ WasmModule (getModuleHeader bytes) [] diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs index 8bcbe58..03db388 100644 --- a/lvtrun/app/WasmMod/Sections.hs +++ b/lvtrun/app/WasmMod/Sections.hs @@ -5,7 +5,12 @@ -- Sections -} -module WasmMod.Sections where +module WasmMod.Sections + ( + SectionID(..), + Section(..) + ) +where import qualified Data.ByteString as BS (ByteString) From 570e147c38eb4fba2eb6a87fa5dc57ee16389b6f Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 5 Jan 2024 05:57:23 +0100 Subject: [PATCH 03/47] add better error control --- lvtrun/app/Errors.hs | 19 +++++++++++++++++++ lvtrun/app/Main.hs | 1 - lvtrun/app/WasmMod/Module.hs | 7 ++++++- lvtrun/lvtrun.cabal | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 lvtrun/app/Errors.hs diff --git a/lvtrun/app/Errors.hs b/lvtrun/app/Errors.hs new file mode 100644 index 0000000..f2bca94 --- /dev/null +++ b/lvtrun/app/Errors.hs @@ -0,0 +1,19 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Errors +-} + +module Errors + ( + exitWithError + ) +where + +import System.Exit (exitWith, ExitCode(..)) + +exitWithError :: String -> IO a +exitWithError msg = do + putStrLn msg + exitWith $ ExitFailure 84 diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 023667b..c52cbd3 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -8,7 +8,6 @@ module Main (main) where import WasmMod.Module -import WasmMod.Header main :: IO () main = do diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index 1728212..c102d50 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -13,10 +13,12 @@ module WasmMod.Module where import qualified Data.ByteString as BS (ByteString, unpack, readFile) +import Control.Monad (when) import Numeric (showHex) import WasmMod.Header import WasmMod.Sections +import Errors data WasmModule = WasmModule { header :: ModHeader, @@ -37,4 +39,7 @@ getFileContent path = BS.readFile path loadModule :: String -> IO WasmModule loadModule filePath = do bytes <- getFileContent filePath - return $ WasmModule (getModuleHeader bytes) [] + let modHeader = getModuleHeader bytes + when (not $ isHeaderValid modHeader) $ exitWithError "Invalid header" + let modSections = [] + return $ WasmModule modHeader modSections diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 2239e9c..f0f3dcb 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -41,6 +41,7 @@ library executable lvtrun-exe main-is: Main.hs other-modules: + Errors WasmMod.Header WasmMod.Module WasmMod.Sections From 30fc7b6fac3c92db8dd7cbfe0435abadb2d3f1a7 Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 5 Jan 2024 07:57:31 +0100 Subject: [PATCH 04/47] add first version of getLEB128 --- lvtrun/app/WasmMod/Header.hs | 8 +-- lvtrun/app/WasmMod/Module.hs | 21 ++++---- lvtrun/app/WasmMod/Sections.hs | 93 ++++++++++++++++++++++++++++++++-- lvtrun/lvtrun.cabal | 3 ++ lvtrun/package.yaml | 1 + lvtrun/stack.yaml | 1 + lvtrun/stack.yaml.lock | 7 +++ 7 files changed, 116 insertions(+), 18 deletions(-) diff --git a/lvtrun/app/WasmMod/Header.hs b/lvtrun/app/WasmMod/Header.hs index 45606c1..22d18fc 100644 --- a/lvtrun/app/WasmMod/Header.hs +++ b/lvtrun/app/WasmMod/Header.hs @@ -8,20 +8,20 @@ module WasmMod.Header ( ModHeader(..), - getModuleHeader, + getModHeader, isHeaderValid ) where -import qualified Data.ByteString as BS (ByteString, take, drop, pack) +import qualified Data.ByteString.Lazy as BS (ByteString, take, drop, pack) data ModHeader = ModHeader { magicNumber :: BS.ByteString, version :: BS.ByteString } deriving (Show) -getModuleHeader :: BS.ByteString -> ModHeader -getModuleHeader bytes = ModHeader (BS.take 4 bytes) (BS.take 4 $ BS.drop 4 bytes) +getModHeader :: BS.ByteString -> ModHeader +getModHeader bytes = ModHeader (BS.take 4 bytes) (BS.take 4 $ BS.drop 4 bytes) isHeaderValid :: ModHeader -> Bool isHeaderValid header = diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index c102d50..f9d905e 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -12,7 +12,7 @@ module WasmMod.Module ) where -import qualified Data.ByteString as BS (ByteString, unpack, readFile) +import qualified Data.ByteString.Lazy as BS (ByteString, unpack, readFile) import Control.Monad (when) import Numeric (showHex) @@ -26,20 +26,21 @@ data WasmModule = WasmModule { } instance Show WasmModule where - show wasmMod = "Wasm Module Header:\n" ++ - " Magic Number: " ++ (concat $ map (\x -> showHex x " ") $ + show wasmMod = "\n[ Wasm Module Header ]\n" ++ + "- Magic Number: " ++ (concat $ map (\x -> showHex x " ") $ BS.unpack $ magicNumber $ header wasmMod) ++ "\n" ++ - " Version: " ++ (concat $ map (\x -> showHex x " ") $ + "- Version: " ++ (concat $ map (\x -> showHex x " ") $ BS.unpack $ version $ header wasmMod) ++ "\n" ++ - " Sections: " ++ (show $ sections wasmMod) ++ "\n" + "- Sections: " ++ (show $ sections wasmMod) ++ "\n" getFileContent :: String -> IO BS.ByteString getFileContent path = BS.readFile path loadModule :: String -> IO WasmModule loadModule filePath = do - bytes <- getFileContent filePath - let modHeader = getModuleHeader bytes - when (not $ isHeaderValid modHeader) $ exitWithError "Invalid header" - let modSections = [] - return $ WasmModule modHeader modSections + bytes <- getFileContent filePath + let modHeader = getModHeader bytes + when (not $ isHeaderValid modHeader) $ exitWithError "Invalid header" + let modSections = getModSections bytes + when (not $ areSectionsValid modSections) $ exitWithError "Invalid sections" + return $ WasmModule modHeader modSections diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs index 03db388..f75a438 100644 --- a/lvtrun/app/WasmMod/Sections.hs +++ b/lvtrun/app/WasmMod/Sections.hs @@ -8,11 +8,18 @@ module WasmMod.Sections ( SectionID(..), - Section(..) + Section(..), + areSectionsValid, + getModSections ) where -import qualified Data.ByteString as BS (ByteString) +import qualified Data.ByteString.Lazy as BS (ByteString, head, drop, take, null, unpack) +import Data.Binary.Get +import Data.Bits +import Data.Word (Word8, Word64) +import Data.Int (Int32, Int64) +import Numeric (showHex) data SectionID = Custom @@ -32,7 +39,85 @@ data SectionID = deriving (Show, Eq) data Section = Section { - id :: SectionID, + identifier :: SectionID, size :: Int, content :: BS.ByteString -} deriving (Show) +} + +instance Show Section where + show section = + "\nSection " ++ (show $ identifier section) ++ + " Size: " ++ (show $ size section) ++ + " Content: " ++ (concat $ map (\x -> showHex x " ") (BS.unpack $ content section)) + +areSectionsValid :: [Section] -> Bool +areSectionsValid sections = True + +getSectionID :: Word8 -> SectionID +getSectionID 1 = Type +getSectionID 2 = Import +getSectionID 3 = Function +getSectionID 4 = Table +getSectionID 5 = Memory +getSectionID 6 = Global +getSectionID 7 = Export +getSectionID 8 = Start +getSectionID 9 = Element +getSectionID 10 = Code +getSectionID 11 = Data +getSectionID 12 = DataCount +getSectionID _ = Invalid + +getLEB128 :: Get Int +getLEB128 = do + byte <- getWord8 + let value = fromIntegral (byte .&. 0x7F) + if byte `testBit` 7 + then do + next <- getLEB128 + return $ value .|. (next `shiftL` 7) + else + return value + +getSectionSize :: BS.ByteString -> Int +getSectionSize bytes = runGet getLEB128 bytes + +-- Returns the number of bytes used to encode the leb128 +getLEB128Size' :: Get Int64 +getLEB128Size' = do + byte <- getWord8 + let value = fromIntegral (byte .&. 0x7F) + if byte `testBit` 7 + then do + next <- getLEB128Size' + return (next + 1) + else + return 1 + +getLEB128Size :: BS.ByteString -> Int64 +getLEB128Size bytes = runGet getLEB128Size' bytes + +getSection :: BS.ByteString -> (Section, BS.ByteString) +getSection bytes = (Section id size content, rest) + where + id = getSectionID (BS.head bytes) + nbByteEncoded = getLEB128Size (BS.drop 1 bytes) + size = getSectionSize (BS.take (fromIntegral nbByteEncoded) (BS.drop 1 bytes)) + content = BS.take (fromIntegral size) (BS.drop (fromIntegral nbByteEncoded + 1) bytes) + rest = BS.drop (nbByteEncoded + 1 + fromIntegral size) bytes + +removeHeader :: BS.ByteString -> BS.ByteString +removeHeader bytes = BS.drop 8 bytes + +getModSections' :: BS.ByteString -> [Section] +getModSections' = do + let getModSections'' bytes = + if BS.null bytes + then [] + else + let (section, rest) = getSection bytes + in section : getModSections'' rest + getModSections'' + +getModSections :: BS.ByteString -> [Section] +getModSections bytes = getModSections' (removeHeader bytes) diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index f0f3dcb..c024715 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -35,6 +35,7 @@ library ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints build-depends: base >=4.7 && <5 + , binary , bytestring default-language: Haskell2010 @@ -53,6 +54,7 @@ executable lvtrun-exe ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 + , binary , bytestring , lvtrun default-language: Haskell2010 @@ -69,6 +71,7 @@ test-suite lvtrun-test ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 + , binary , bytestring , lvtrun default-language: Haskell2010 diff --git a/lvtrun/package.yaml b/lvtrun/package.yaml index b0d5b04..25995fc 100644 --- a/lvtrun/package.yaml +++ b/lvtrun/package.yaml @@ -22,6 +22,7 @@ description: Please see the README on GitHub at = 4.7 && < 5 - bytestring +- binary ghc-options: - -Wall diff --git a/lvtrun/stack.yaml b/lvtrun/stack.yaml index a37bfe6..c49e528 100644 --- a/lvtrun/stack.yaml +++ b/lvtrun/stack.yaml @@ -42,6 +42,7 @@ packages: # extra-deps: - bytestring-0.12.0.2 + - binary-0.8.9.1 # Override default flag values for local packages and extra-deps # flags: {} diff --git a/lvtrun/stack.yaml.lock b/lvtrun/stack.yaml.lock index 11e1cde..9f9dca0 100644 --- a/lvtrun/stack.yaml.lock +++ b/lvtrun/stack.yaml.lock @@ -11,6 +11,13 @@ packages: size: 4355 original: hackage: bytestring-0.12.0.2 +- completed: + hackage: binary-0.8.9.1@sha256:81f468c1c75fd6535152ab69b2d32ac6cfcc03e345267b069abe4da56ec95801,6523 + pantry-tree: + sha256: 956ecd662408f69615977b87a92e042abcdc447b7824b8aabf5788c4393c10c5 + size: 1976 + original: + hackage: binary-0.8.9.1 snapshots: - completed: sha256: e176944bc843f740e05242fa7a66ca1f440c127e425254f7f1257f9b19add23f From 2a5ce2023a5cf37aef8fde44fae034f4e7234609 Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 5 Jan 2024 23:39:06 +0100 Subject: [PATCH 05/47] add better leb128 --- lvtrun/app/WasmMod/Leb128.hs | 46 +++++++++++++++++++++++++++++++++ lvtrun/app/WasmMod/Sections.hs | 45 ++++++-------------------------- lvtrun/lvtrun.cabal | 1 + lvtrun/test/HexFile | Bin 5008 -> 5168 bytes 4 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 lvtrun/app/WasmMod/Leb128.hs diff --git a/lvtrun/app/WasmMod/Leb128.hs b/lvtrun/app/WasmMod/Leb128.hs new file mode 100644 index 0000000..c16d011 --- /dev/null +++ b/lvtrun/app/WasmMod/Leb128.hs @@ -0,0 +1,46 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Leb128 +-} + +module WasmMod.Leb128 + ( + getLEB128, + extractLEB128 + ) +where + +import Data.Binary.Get +import Data.Bits +import Data.Int (Int32, Int64) +import qualified Data.ByteString.Lazy as BS (ByteString, drop) + +getLEB128 :: Get Int +getLEB128 = do + byte <- getWord8 + let value = fromIntegral (byte .&. 0x7F) + if byte `testBit` 7 + then do + next <- getLEB128 + return $ value .|. (next `shiftL` 7) + else + return value + +extractLEB128' :: Get (Int64, Int64) +extractLEB128' = do + byte <- getWord8 + let value = fromIntegral (byte .&. 0x7F) + if byte `testBit` 7 + then do + (next, size) <- extractLEB128' + return (value .|. (next `shiftL` 7), size + 1) + else + return (value, 1) + +--function that returns the value and the rest of the bytestring +extractLEB128 :: BS.ByteString -> (Int64, BS.ByteString) +extractLEB128 bytes = do + let (value, size) = runGet extractLEB128' bytes + (value, BS.drop size bytes) diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs index f75a438..b13b0a1 100644 --- a/lvtrun/app/WasmMod/Sections.hs +++ b/lvtrun/app/WasmMod/Sections.hs @@ -17,9 +17,11 @@ where import qualified Data.ByteString.Lazy as BS (ByteString, head, drop, take, null, unpack) import Data.Binary.Get import Data.Bits -import Data.Word (Word8, Word64) import Data.Int (Int32, Int64) import Numeric (showHex) +import WasmMod.Leb128 +import Data.Word +import WasmMod.Leb128 data SectionID = Custom @@ -68,43 +70,12 @@ getSectionID 11 = Data getSectionID 12 = DataCount getSectionID _ = Invalid -getLEB128 :: Get Int -getLEB128 = do - byte <- getWord8 - let value = fromIntegral (byte .&. 0x7F) - if byte `testBit` 7 - then do - next <- getLEB128 - return $ value .|. (next `shiftL` 7) - else - return value - -getSectionSize :: BS.ByteString -> Int -getSectionSize bytes = runGet getLEB128 bytes - --- Returns the number of bytes used to encode the leb128 -getLEB128Size' :: Get Int64 -getLEB128Size' = do - byte <- getWord8 - let value = fromIntegral (byte .&. 0x7F) - if byte `testBit` 7 - then do - next <- getLEB128Size' - return (next + 1) - else - return 1 - -getLEB128Size :: BS.ByteString -> Int64 -getLEB128Size bytes = runGet getLEB128Size' bytes - getSection :: BS.ByteString -> (Section, BS.ByteString) -getSection bytes = (Section id size content, rest) - where - id = getSectionID (BS.head bytes) - nbByteEncoded = getLEB128Size (BS.drop 1 bytes) - size = getSectionSize (BS.take (fromIntegral nbByteEncoded) (BS.drop 1 bytes)) - content = BS.take (fromIntegral size) (BS.drop (fromIntegral nbByteEncoded + 1) bytes) - rest = BS.drop (nbByteEncoded + 1 + fromIntegral size) bytes +getSection bytes = do + let id = getSectionID (BS.head bytes) + let (size, rest) = extractLEB128 (BS.drop 1 bytes) + let content = BS.take (fromIntegral size) rest + (Section id (fromIntegral size) content, BS.drop (fromIntegral size) rest) removeHeader :: BS.ByteString -> BS.ByteString removeHeader bytes = BS.drop 8 bytes diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index c024715..25d062b 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -44,6 +44,7 @@ executable lvtrun-exe other-modules: Errors WasmMod.Header + WasmMod.Leb128 WasmMod.Module WasmMod.Sections Paths_lvtrun diff --git a/lvtrun/test/HexFile b/lvtrun/test/HexFile index 34e4b4f854e41a08a6c12539bdad3d5a7304fb29..c64d4d71a8c8cafc9442c88c4e282e8cab147832 100644 GIT binary patch literal 5168 zcmd6rS&q~|5Jdfr#2w}cxTmqZNjxjLa-g^ZVC_ZHH3pu*&Zh`f_U{(3$I90B;(a_Yt z+IaBEizt`ndc5O{WIg<-4ZL%m=(gqaA^kXzr1~E>zu1B>x=(i<*!Phq-q;ay9~id5 z%|3FQ#e6U(mgj+x{Cb%O{M*2@jc%YbLn4Bq5ygP-WyHarF7mk!Bhi9XY7m@9W4>d%eK$^%m#R z7L3@Vf7BsIukPvKJ=IXjx8nttZOKO11J8EJZWCu8vqUvST*nEy-AfPY_qKG#$Y(d_ zxxvr=&oj0iGHclf+s2vg^fQM$NG5ZO(R@_5-R6`k^-BlApG^8X|6*}3j7GJcz;Z`~ zbKS(Aeeeu^kfIAm@-F_&Y__p{$-l^{lAKh&4sMh?b#yNqoOody4=2_-X1{W$H@cYv zncwb{7yRwQj+-@;rgPnRCD59meYSxaDu5bVJ(vUH!YbTaL+N;=?RPc zGS2v128tjLifYKGhcZ0)7P&X0Q9z^?kDj#8(}rAZrj}SWKJ`D3=TZ^PmY4fZHwr%K zbYp6Hj)Em9iV;7t!&v27VPB-6tQGR>oo8dv*D#o>h(TYBohO1Pw+0LS$w2o(eH|

wnd*c=b7riOsY}j1W z<7{g)&TF9$rlDbQI7+XYs0^9^}0RTIqOP|kbg^LC!`dIAyl?MY*Uq4m`pxjQP> zdCoGG;_k%Yt+m+_A#%3~)b2{~yPsm3(ID?Vapqa1?yXt!=u%sRuN-zTbZd)1{8SjOQZ1kI z!t_TPt7?egiOD_7n^AdY5=2B~x?iHxHuBgKJu>n8JH5ZwK_cp~q8;)_TmL@?ja>>) z^XBONWw>Jb4jN!+RL0R?V->q%! zpLVpppJ#g+_iyced)MX2e4i&T^L6hw=zJJ<+x3D+t&G#^Cj=1 zd>t=GkFmxfX7zUK@3(fpw0PNjrsN6gHRn%V@?oG23*UNyKeftvY&s zJk_t!y;J$rS}UVe|LVYedJ3JdqY7fE_igZIMrK#5e;+xjA1jf3={_GdlilxMw}I!& zh#s6@2WF5T?b|r*bno^6nVzG{mCt=htYfNaC)ihDwl`9JJyW&XSI59}UK6J6)Jb>Q zIlQJD^$*X-)m`LbM_(x>{BnO}1}uB}Ih1ZS<3KJgYjp9daw{vSN|LxR-Ad?#zf1W6JhtR{bP1! z>fWdov8&1VcYbC~SIxO|Rt}MGjm}*4-2kGZAKafu-@a7AbIzlk<8<%W>))-2(+Y-l zx6De_?UXMdv*|vsyUyGrfsNc@N%lTp-$dfZ+w26HQFCi}c)d2i9ys^0Py7u0RwR0hoQSvn9-Z80?V<^uyw*FmFTJYqu#K5|rU{Z&v-_(6{u2ZBtK$ zufKInT&&`aIf}eSVQ<76I=Lf1M?b^^n<1~Y7RX;3__NiWbiAc|#VLn1I+~BD(sgEy zj&YS&RQCTPD&@~mfn?QpbJkcO^w-@pY;y|K2{z;_w#m}ovxE(q0fWE2!w($3J&#}$ zNtmfjikvff@p_EePwnWUym}YU^t^6>nWOU+Tjc9h*1oC}G;?1)AYVN=J7(l`c&}4j z@grY7JjSZ??#$}5&bvOVrhe3{(_O8QTZQDMe$4_&n6EP~66=M$c8eQ3JlA~TWbI_C zkj}L3Vwh95_DP-V>KXabD(7_@c!!hphRBIhJ2~GVU-9pb3PP~m$8Y1hqr%}t9tqjy z4%^u(#>Ib?+(w^gj_~FjC3@xOEriIc{{7mebu-Ah-t;rA&g*xGOiqNi@pa1=3jJ_p z*3Z3}-Tlg|VvqsO@Z;uJJ~iuW?(}f(b+Sao>9@FYo8KqD;*1}yJjFni;3vno{tC|D z&e+QQqh|guVn Date: Sat, 6 Jan 2024 15:21:24 +0100 Subject: [PATCH 06/47] add better error handler --- lvtrun/app/Errors.hs | 21 ++++++---- lvtrun/app/Main.hs | 10 +++-- lvtrun/app/WasmMod/Module.hs | 10 +++-- lvtrun/app/WasmMod/Sections.hs | 60 +++++++++++++-------------- lvtrun/app/WasmMod/Sections/Types.hs | 43 +++++++++++++++++++ lvtrun/lvtrun.cabal | 1 + lvtrun/test/HexFile | Bin 5168 -> 5196 bytes 7 files changed, 101 insertions(+), 44 deletions(-) create mode 100644 lvtrun/app/WasmMod/Sections/Types.hs diff --git a/lvtrun/app/Errors.hs b/lvtrun/app/Errors.hs index f2bca94..4586488 100644 --- a/lvtrun/app/Errors.hs +++ b/lvtrun/app/Errors.hs @@ -4,16 +4,23 @@ -- File description: -- Errors -} - + module Errors ( - exitWithError + CustomException(..), + handleException ) where -import System.Exit (exitWith, ExitCode(..)) +import Control.Exception (Exception(..), SomeException, displayException) + +data CustomException = + ParseError String | + WasmError String | + RuntimeError String + deriving (Show, Eq) + +instance Exception CustomException -exitWithError :: String -> IO a -exitWithError msg = do - putStrLn msg - exitWith $ ExitFailure 84 +handleException :: SomeException -> IO () +handleException e = putStrLn $ "Error: " ++ displayException e diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index c52cbd3..c4e689a 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -5,11 +5,15 @@ -- Main -} + module Main (main) where +import Control.Exception (try) import WasmMod.Module +import Errors main :: IO () -main = do - wasmMod <- loadModule "./test/test.wasm" - print wasmMod +main = try (loadModule "./test/test.wasm") >>= \result -> + case result of + Left err -> handleException err + Right wasmMod -> print wasmMod diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index f9d905e..ab54758 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -13,12 +13,14 @@ module WasmMod.Module where import qualified Data.ByteString.Lazy as BS (ByteString, unpack, readFile) +import Control.Exception (throwIO) import Control.Monad (when) import Numeric (showHex) +import Errors import WasmMod.Header import WasmMod.Sections -import Errors +import WasmMod.Sections.Types data WasmModule = WasmModule { header :: ModHeader, @@ -40,7 +42,9 @@ loadModule :: String -> IO WasmModule loadModule filePath = do bytes <- getFileContent filePath let modHeader = getModHeader bytes - when (not $ isHeaderValid modHeader) $ exitWithError "Invalid header" + when (not $ isHeaderValid modHeader) $ throwIO (WasmError "Invalid header") let modSections = getModSections bytes - when (not $ areSectionsValid modSections) $ exitWithError "Invalid sections" + when (not $ areSectionsValid modSections) $ throwIO (WasmError "Invalid sections") + let funcType = parseTypes $ head modSections + print funcType return $ WasmModule modHeader modSections diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs index b13b0a1..5f66c3f 100644 --- a/lvtrun/app/WasmMod/Sections.hs +++ b/lvtrun/app/WasmMod/Sections.hs @@ -15,29 +15,26 @@ module WasmMod.Sections where import qualified Data.ByteString.Lazy as BS (ByteString, head, drop, take, null, unpack) -import Data.Binary.Get -import Data.Bits -import Data.Int (Int32, Int64) +import Data.Word (Word8) import Numeric (showHex) import WasmMod.Leb128 -import Data.Word -import WasmMod.Leb128 +import Data.Bits data SectionID = Custom - | Type - | Import - | Function - | Table - | Memory - | Global - | Export - | Start - | Element - | Code - | Data - | DataCount - | Invalid + | TypeID + | ImportID + | FunctionID + | TableID + | MemoryID + | GlobalID + | ExportID + | StartID + | ElementID + | CodeID + | DataID + | DataCountID + | InvalidID deriving (Show, Eq) data Section = Section { @@ -56,19 +53,19 @@ areSectionsValid :: [Section] -> Bool areSectionsValid sections = True getSectionID :: Word8 -> SectionID -getSectionID 1 = Type -getSectionID 2 = Import -getSectionID 3 = Function -getSectionID 4 = Table -getSectionID 5 = Memory -getSectionID 6 = Global -getSectionID 7 = Export -getSectionID 8 = Start -getSectionID 9 = Element -getSectionID 10 = Code -getSectionID 11 = Data -getSectionID 12 = DataCount -getSectionID _ = Invalid +getSectionID 1 = TypeID +getSectionID 2 = ImportID +getSectionID 3 = FunctionID +getSectionID 4 = TableID +getSectionID 5 = MemoryID +getSectionID 6 = GlobalID +getSectionID 7 = ExportID +getSectionID 8 = StartID +getSectionID 9 = ElementID +getSectionID 10 = CodeID +getSectionID 11 = DataID +getSectionID 12 = DataCountID +getSectionID _ = InvalidID getSection :: BS.ByteString -> (Section, BS.ByteString) getSection bytes = do @@ -90,5 +87,6 @@ getModSections' = do in section : getModSections'' rest getModSections'' +-- Todo: Check if sections are valid getModSections :: BS.ByteString -> [Section] getModSections bytes = getModSections' (removeHeader bytes) diff --git a/lvtrun/app/WasmMod/Sections/Types.hs b/lvtrun/app/WasmMod/Sections/Types.hs new file mode 100644 index 0000000..fa41c2a --- /dev/null +++ b/lvtrun/app/WasmMod/Sections/Types.hs @@ -0,0 +1,43 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Types +-} + +module WasmMod.Sections.Types + ( + parseTypes, + Type(..), + FuncType(..) + ) +where + +import WasmMod.Sections +import WasmMod.Leb128 +import Data.Int +import Errors +import Control.Exception +import qualified Data.ByteString.Lazy as Bs + +data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) + +data FuncType = FuncType { + typeId :: Int, + params :: [Type], + results :: [Type] +} + +instance Show FuncType where + show funcType = "(type " ++ (show $ typeId funcType) ++ " (func " ++ + (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")" + +parseFuncTypes :: Int64 -> Bs.ByteString -> [FuncType] +parseFuncTypes 0 _ = [] +parseFuncTypes idx content = throw $ (WasmError "Not implemented") + +parseTypes :: Section -> [FuncType] +parseTypes (Section TypeID _ content) = do + let (vecSize, rest) = extractLEB128 content + parseFuncTypes vecSize rest +parseTypes _ = [] diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 25d062b..f72ef50 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -47,6 +47,7 @@ executable lvtrun-exe WasmMod.Leb128 WasmMod.Module WasmMod.Sections + WasmMod.Sections.Types Paths_lvtrun autogen-modules: Paths_lvtrun diff --git a/lvtrun/test/HexFile b/lvtrun/test/HexFile index c64d4d71a8c8cafc9442c88c4e282e8cab147832..70f2ff6aea7bd81a4a37767645ccc874f5334da5 100644 GIT binary patch delta 94 zcmdm>aYkc<5fi@xg9(EIg8>6C0~ZKSc4Sgzhq2R{wBhW@Orl5feWz0~Z4bn=mLa7%(VIc4SgzRG6I0qzxqJGnFzbY?fwjWdm}SbLs*) Ow>cYGfK)W!M^*sD^$#Ne From 37eccd8dc111052c98a36e940d89127e6ec38ffa Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sat, 6 Jan 2024 17:20:01 +0100 Subject: [PATCH 07/47] add work in progress --- lvtrun/app/WasmMod/Leb128.hs | 9 +++--- lvtrun/app/WasmMod/Sections/Types.hs | 43 ++++++++++++++++++++++++++- lvtrun/test/HexFile | Bin 5196 -> 5188 bytes 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lvtrun/app/WasmMod/Leb128.hs b/lvtrun/app/WasmMod/Leb128.hs index c16d011..099cf99 100644 --- a/lvtrun/app/WasmMod/Leb128.hs +++ b/lvtrun/app/WasmMod/Leb128.hs @@ -14,7 +14,7 @@ where import Data.Binary.Get import Data.Bits -import Data.Int (Int32, Int64) +import Data.Int (Int64) import qualified Data.ByteString.Lazy as BS (ByteString, drop) getLEB128 :: Get Int @@ -32,12 +32,11 @@ extractLEB128' :: Get (Int64, Int64) extractLEB128' = do byte <- getWord8 let value = fromIntegral (byte .&. 0x7F) - if byte `testBit` 7 - then do + case byte `testBit` 7 of + True -> do (next, size) <- extractLEB128' return (value .|. (next `shiftL` 7), size + 1) - else - return (value, 1) + False -> return (value, 1) --function that returns the value and the rest of the bytestring extractLEB128 :: BS.ByteString -> (Int64, BS.ByteString) diff --git a/lvtrun/app/WasmMod/Sections/Types.hs b/lvtrun/app/WasmMod/Sections/Types.hs index fa41c2a..7ffb49a 100644 --- a/lvtrun/app/WasmMod/Sections/Types.hs +++ b/lvtrun/app/WasmMod/Sections/Types.hs @@ -19,6 +19,9 @@ import Data.Int import Errors import Control.Exception import qualified Data.ByteString.Lazy as Bs +import Data.Word + +import Debug.Trace data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) @@ -32,9 +35,47 @@ instance Show FuncType where show funcType = "(type " ++ (show $ typeId funcType) ++ " (func " ++ (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")" +--60 0 0 +--60 1 7f 0 +--60 2 7f 7f 1 7f +-- 7f = i32 7e = i64 7d = f32 7c = f64 + +getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) +getVectorSize content = extractLEB128 content + +getTypeFromByte :: Word8 -> Type +getTypeFromByte 0x7f = I32 +getTypeFromByte 0x7e = I64 +getTypeFromByte 0x7d = F32 +getTypeFromByte 0x7c = F64 +getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" + +extractParams :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) +extractParams (0, content) = ([], content) +extractParams (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : params, rest) + where (params, rest) = extractParams (idx - 1, Bs.drop 1 content) + +extractResults :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) +extractResults (0, content) = ([], content) +extractResults (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : results, rest) + where (results, rest) = extractResults (idx - 1, Bs.drop 1 content) + +extractTypes :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) +extractTypes (0, content) = ([], content) + +parseFuncType :: Bs.ByteString -> (FuncType, Bs.ByteString) +parseFuncType content = do + let (params, rest) = extractParams $ getVectorSize content + let (results, rest2) = extractResults $ getVectorSize rest + ((FuncType 0 params results), rest2) + parseFuncTypes :: Int64 -> Bs.ByteString -> [FuncType] parseFuncTypes 0 _ = [] -parseFuncTypes idx content = throw $ (WasmError "Not implemented") +parseFuncTypes idx content + | head (Bs.unpack content) == 0x60 = do + let (funcType, rest) = parseFuncType content + funcType : parseFuncTypes (idx - 1) rest + | otherwise = throw $ WasmError "ParseFuncTypes: 0x60 expected for function" parseTypes :: Section -> [FuncType] parseTypes (Section TypeID _ content) = do diff --git a/lvtrun/test/HexFile b/lvtrun/test/HexFile index 70f2ff6aea7bd81a4a37767645ccc874f5334da5..ec8a11540639775745ae7a995ca335177fcafdf5 100644 GIT binary patch delta 91 zcmX@3aYSQ+gD)=w7Xt{JF&Ho?fUzNvHfKmf6E#5M!&HFOBlH?EC`_EFzwt Date: Sun, 7 Jan 2024 01:28:16 +0100 Subject: [PATCH 08/47] add working typesFunc --- lvtrun/app/WasmMod/Sections/Types.hs | 52 ++++++++++------------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/lvtrun/app/WasmMod/Sections/Types.hs b/lvtrun/app/WasmMod/Sections/Types.hs index 7ffb49a..d0cf597 100644 --- a/lvtrun/app/WasmMod/Sections/Types.hs +++ b/lvtrun/app/WasmMod/Sections/Types.hs @@ -13,15 +13,14 @@ module WasmMod.Sections.Types ) where +import qualified Data.ByteString.Lazy as Bs +import Control.Exception (throw) +import Data.Int (Int64) +import Data.Word (Word8) + import WasmMod.Sections import WasmMod.Leb128 -import Data.Int import Errors -import Control.Exception -import qualified Data.ByteString.Lazy as Bs -import Data.Word - -import Debug.Trace data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) @@ -33,12 +32,7 @@ data FuncType = FuncType { instance Show FuncType where show funcType = "(type " ++ (show $ typeId funcType) ++ " (func " ++ - (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")" - ---60 0 0 ---60 1 7f 0 ---60 2 7f 7f 1 7f --- 7f = i32 7e = i64 7d = f32 7c = f64 + (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")\n" getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) getVectorSize content = extractLEB128 content @@ -50,35 +44,27 @@ getTypeFromByte 0x7d = F32 getTypeFromByte 0x7c = F64 getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" -extractParams :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) -extractParams (0, content) = ([], content) -extractParams (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : params, rest) - where (params, rest) = extractParams (idx - 1, Bs.drop 1 content) - -extractResults :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) -extractResults (0, content) = ([], content) -extractResults (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : results, rest) - where (results, rest) = extractResults (idx - 1, Bs.drop 1 content) - extractTypes :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) extractTypes (0, content) = ([], content) +extractTypes (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : types, rest) + where (types, rest) = extractTypes (idx - 1, Bs.drop 1 content) -parseFuncType :: Bs.ByteString -> (FuncType, Bs.ByteString) -parseFuncType content = do - let (params, rest) = extractParams $ getVectorSize content - let (results, rest2) = extractResults $ getVectorSize rest - ((FuncType 0 params results), rest2) +parseFuncType :: Int -> Bs.ByteString -> (FuncType, Bs.ByteString) +parseFuncType id content = do + let (params, rest) = extractTypes (getVectorSize content) + let (results, rest2) = extractTypes (getVectorSize rest) + ((FuncType id params results), rest2) -parseFuncTypes :: Int64 -> Bs.ByteString -> [FuncType] -parseFuncTypes 0 _ = [] -parseFuncTypes idx content +parseFuncTypes :: Int -> Int64 -> Bs.ByteString -> [FuncType] +parseFuncTypes idx maxIdx content + | idx >= (fromIntegral maxIdx) = [] | head (Bs.unpack content) == 0x60 = do - let (funcType, rest) = parseFuncType content - funcType : parseFuncTypes (idx - 1) rest + let (funcType, rest) = parseFuncType idx (Bs.drop 1 content) + funcType : parseFuncTypes (idx + 1) maxIdx rest | otherwise = throw $ WasmError "ParseFuncTypes: 0x60 expected for function" parseTypes :: Section -> [FuncType] parseTypes (Section TypeID _ content) = do let (vecSize, rest) = extractLEB128 content - parseFuncTypes vecSize rest + parseFuncTypes 0 vecSize rest parseTypes _ = [] From 7623136ccb8f6ebeadd45d53bddda8f23861f476 Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 7 Jan 2024 02:45:30 +0100 Subject: [PATCH 09/47] add type parsing --- lvtrun/README.md | 22 ++++---- lvtrun/app/Types.hs | 27 ++++++++++ lvtrun/app/WasmMod/Module.hs | 12 ++++- lvtrun/app/WasmMod/Sections/Global.hs | 72 ++++++++++++++++++++++++++ lvtrun/app/WasmMod/Sections/Types.hs | 12 +---- lvtrun/lvtrun.cabal | 2 + lvtrun/test/HexFile | Bin 5188 -> 5184 bytes 7 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 lvtrun/app/Types.hs create mode 100644 lvtrun/app/WasmMod/Sections/Global.hs diff --git a/lvtrun/README.md b/lvtrun/README.md index eb356f5..04bc844 100644 --- a/lvtrun/README.md +++ b/lvtrun/README.md @@ -48,8 +48,8 @@ # ----------------------- 3 ----------------------- # section 3 is the function section with 17 bytes of length -03 11 -10 01 00 01 +03 11 10 +01 00 01 01 01 02 02 00 01 00 00 00 00 02 03 00 @@ -80,14 +80,16 @@ # ----------------------- 6 ----------------------- the global section -06 -12 - -03 7f 01 -41 c7 c7 04 -0b 7f 01 41 -00 0b 7f 01 -41 00 0b +06 12 +# vector of 3 globals +03 +# i32 mut = i32.const 66560 +7f 01 41 c7 c7 04 0b +7f 01 41 00 0b +7f 01 41 00 0b +# (globalType, expr) +# globalType = (valtype, mut) with mut 0x00 = const, 0x01 = var +# expr = (instructions) 0xb # ----------------------- 7 ----------------------- diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs new file mode 100644 index 0000000..fe9cfba --- /dev/null +++ b/lvtrun/app/Types.hs @@ -0,0 +1,27 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Types +-} + +module Types + ( + Type(..), + getTypeFromByte + ) + where + +import Data.Word (Word8) +import Control.Exception (throw) + +import Errors + +data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) + +getTypeFromByte :: Word8 -> Type +getTypeFromByte 0x7f = I32 +getTypeFromByte 0x7e = I64 +getTypeFromByte 0x7d = F32 +getTypeFromByte 0x7c = F64 +getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index ab54758..8be0de2 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -13,7 +13,7 @@ module WasmMod.Module where import qualified Data.ByteString.Lazy as BS (ByteString, unpack, readFile) -import Control.Exception (throwIO) +import Control.Exception (throwIO, throw) import Control.Monad (when) import Numeric (showHex) @@ -21,6 +21,7 @@ import Errors import WasmMod.Header import WasmMod.Sections import WasmMod.Sections.Types +import WasmMod.Sections.Global data WasmModule = WasmModule { header :: ModHeader, @@ -38,6 +39,13 @@ instance Show WasmModule where getFileContent :: String -> IO BS.ByteString getFileContent path = BS.readFile path +--TEMP FUNC +getGlobalSection :: [Section] -> Section +getGlobalSection [] = throw (WasmError "No global section") +getGlobalSection (x:xs) + | identifier x == GlobalID = x + | otherwise = getGlobalSection xs + loadModule :: String -> IO WasmModule loadModule filePath = do bytes <- getFileContent filePath @@ -47,4 +55,6 @@ loadModule filePath = do when (not $ areSectionsValid modSections) $ throwIO (WasmError "Invalid sections") let funcType = parseTypes $ head modSections print funcType + let globals = parseGlobals $ getGlobalSection modSections + print globals return $ WasmModule modHeader modSections diff --git a/lvtrun/app/WasmMod/Sections/Global.hs b/lvtrun/app/WasmMod/Sections/Global.hs new file mode 100644 index 0000000..838cfb8 --- /dev/null +++ b/lvtrun/app/WasmMod/Sections/Global.hs @@ -0,0 +1,72 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Global +-} + +module WasmMod.Sections.Global + ( + parseGlobals, + Global(..) + ) +where + +import qualified Data.ByteString.Lazy as Bs +import Control.Exception (throw) +import Data.Word (Word8) +import Data.Int (Int64) + +import WasmMod.Leb128 +import WasmMod.Sections (Section(..), SectionID(..)) +import Types (Type(..), getTypeFromByte) +import Errors + +data Mutability = Const | Var deriving (Show) + +data Global = Global { + globalType :: Type, + mutability :: Mutability, + initExp :: [Word8] +} + +instance Show Global where + show global = "Global: " ++ (show $ globalType global) ++ " " ++ + (show $ mutability global) ++ " " ++ (show $ initExp global) ++ "\n" + +parseMutability :: Word8 -> Mutability +parseMutability 0x00 = Const +parseMutability 0x01 = Var +parseMutability _ = throw $ WasmError "ParseMutability: bad mutability" + +getHexaIndex :: Bs.ByteString -> Int64 -> Int64 +getHexaIndex content idx + | idx >= (fromIntegral $ Bs.length content) = throw $ WasmError "GetHexaIndex: no 0x0b found" + | (head $ Bs.unpack $ Bs.drop (fromIntegral idx) content) == 0x0b = idx + | otherwise = getHexaIndex content (idx + 1) + +extractExpression :: Bs.ByteString -> ([Word8], Bs.ByteString) +extractExpression content = do + let idx = getHexaIndex content 0 + let expression = Bs.take (fromIntegral (idx + 1)) content + (Bs.unpack expression, Bs.drop (fromIntegral (idx + 1)) content) + +parseGlobal :: Bs.ByteString -> (Global, Bs.ByteString) +parseGlobal content = do + let globalType = getTypeFromByte (head $ Bs.unpack content) + let mutability = parseMutability (head $ Bs.unpack $ Bs.drop 1 content) + let (expression, rest) = extractExpression (Bs.drop 2 content) + (Global globalType mutability expression, rest) + +parseGlobals' :: Int64 -> Int64 -> Bs.ByteString -> [Global] +parseGlobals' idx maxIdx content + | idx >= maxIdx = [] + | otherwise = do + let (global, rest) = parseGlobal content + global : parseGlobals' (idx + 1) maxIdx rest + +parseGlobals :: Section -> [Global] +parseGlobals (Section GlobalID _ content) = do + let (vecSize, rest) = extractLEB128 content + parseGlobals' 0 vecSize rest +parseGlobals _ = throw $ WasmError "ParseGlobals: bad section" diff --git a/lvtrun/app/WasmMod/Sections/Types.hs b/lvtrun/app/WasmMod/Sections/Types.hs index d0cf597..152964b 100644 --- a/lvtrun/app/WasmMod/Sections/Types.hs +++ b/lvtrun/app/WasmMod/Sections/Types.hs @@ -21,8 +21,7 @@ import Data.Word (Word8) import WasmMod.Sections import WasmMod.Leb128 import Errors - -data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) +import Types (Type(..), getTypeFromByte) data FuncType = FuncType { typeId :: Int, @@ -37,13 +36,6 @@ instance Show FuncType where getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) getVectorSize content = extractLEB128 content -getTypeFromByte :: Word8 -> Type -getTypeFromByte 0x7f = I32 -getTypeFromByte 0x7e = I64 -getTypeFromByte 0x7d = F32 -getTypeFromByte 0x7c = F64 -getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" - extractTypes :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) extractTypes (0, content) = ([], content) extractTypes (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : types, rest) @@ -67,4 +59,4 @@ parseTypes :: Section -> [FuncType] parseTypes (Section TypeID _ content) = do let (vecSize, rest) = extractLEB128 content parseFuncTypes 0 vecSize rest -parseTypes _ = [] +parseTypes _ = throw $ WasmError "ParseTypes: bad section" diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index f72ef50..e0b6842 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -43,10 +43,12 @@ executable lvtrun-exe main-is: Main.hs other-modules: Errors + Types WasmMod.Header WasmMod.Leb128 WasmMod.Module WasmMod.Sections + WasmMod.Sections.Global WasmMod.Sections.Types Paths_lvtrun autogen-modules: diff --git a/lvtrun/test/HexFile b/lvtrun/test/HexFile index ec8a11540639775745ae7a995ca335177fcafdf5..0e10ef8a4de317f9e272879ed9afa45f7363568a 100644 GIT binary patch delta 10 RcmX@2aX@2&%SMks!T=hL1hD`B delta 9 QcmX@0aYSQ+%R&bc02HhPo&W#< From 4ee064c1fdb4c46e84229d469f64df277f858c8d Mon Sep 17 00:00:00 2001 From: tenshi Date: Tue, 9 Jan 2024 15:25:00 +0100 Subject: [PATCH 10/47] add save --- lvtrun/README.md | 25 ++++++++++---- lvtrun/app/Main.hs | 1 - lvtrun/app/WasmMod/Module.hs | 9 +++++ lvtrun/app/WasmMod/Sections/Memory.hs | 47 +++++++++++++++++++++++++++ lvtrun/lvtrun.cabal | 1 + lvtrun/test/test.cpp | 11 +++++-- 6 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 lvtrun/app/WasmMod/Sections/Memory.hs diff --git a/lvtrun/README.md b/lvtrun/README.md index 04bc844..e641c57 100644 --- a/lvtrun/README.md +++ b/lvtrun/README.md @@ -48,11 +48,25 @@ # ----------------------- 3 ----------------------- # section 3 is the function section with 17 bytes of length +# index of the signature (type) of each internal function +# functionsec = section(vec(typeidx)) 03 11 10 -01 00 01 -01 01 02 02 -00 01 00 00 -00 00 02 03 00 +01 +00 +01 +01 +01 +02 +02 +00 +01 +00 +00 +00 +00 +02 +03 +00 # ----------------------- 4 ----------------------- the table section @@ -75,8 +89,7 @@ # 01 = number of memory (its a vector) 01 -01 c7 -02 c7 02 +01 c7 02 c7 02 # ----------------------- 6 ----------------------- the global section diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index c4e689a..7de385f 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -5,7 +5,6 @@ -- Main -} - module Main (main) where import Control.Exception (try) diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs index 8be0de2..3b01d24 100644 --- a/lvtrun/app/WasmMod/Module.hs +++ b/lvtrun/app/WasmMod/Module.hs @@ -22,6 +22,7 @@ import WasmMod.Header import WasmMod.Sections import WasmMod.Sections.Types import WasmMod.Sections.Global +import WasmMod.Sections.Memory data WasmModule = WasmModule { header :: ModHeader, @@ -46,6 +47,12 @@ getGlobalSection (x:xs) | identifier x == GlobalID = x | otherwise = getGlobalSection xs +getMemorySection :: [Section] -> Section +getMemorySection [] = throw (WasmError "No memory section") +getMemorySection (x:xs) + | identifier x == MemoryID = x + | otherwise = getMemorySection xs + loadModule :: String -> IO WasmModule loadModule filePath = do bytes <- getFileContent filePath @@ -57,4 +64,6 @@ loadModule filePath = do print funcType let globals = parseGlobals $ getGlobalSection modSections print globals + let memory = parseMemory $ getMemorySection modSections + print memory return $ WasmModule modHeader modSections diff --git a/lvtrun/app/WasmMod/Sections/Memory.hs b/lvtrun/app/WasmMod/Sections/Memory.hs new file mode 100644 index 0000000..92cbcaa --- /dev/null +++ b/lvtrun/app/WasmMod/Sections/Memory.hs @@ -0,0 +1,47 @@ + +module WasmMod.Sections.Memory + ( + Memory(..), + parseMemory + ) where + +import qualified Data.ByteString.Lazy as BS +import Control.Exception (throw) +import Control.Monad (when) + +import WasmMod.Leb128 +import WasmMod.Sections.Types +import WasmMod.Sections +import Errors + +data Memory = Memory { memMin :: Int, memMax :: Maybe Int } + deriving (Show, Eq) + +parseMinMax :: BS.ByteString -> Memory +parseMinMax content + | endBs /= BS.empty = throw $ WasmError "parseMinMax: bad memory section" + | otherwise = Memory {memMin = fromIntegral min, memMax = Just (fromIntegral max)} + where + (min, rest) = extractLEB128 content + (max, endBs) = extractLEB128 rest + +parseMin :: BS.ByteString -> Memory +parseMin content + | endBs /= BS.empty = throw $ WasmError "parseMin: bad memory section" + | otherwise = Memory {memMin = fromIntegral min, memMax = Nothing} + where + (min, endBs) = extractLEB128 content + +parseMemory' :: BS.ByteString -> Memory +parseMemory' content + | head (BS.unpack content) == 0x01 = parseMinMax (BS.drop 1 content) + | head (BS.unpack content) == 0x00 = parseMin (BS.drop 1 content) + | otherwise = throw $ WasmError "parseMemory': bad memory section" + +parseMemory :: Section -> Memory +parseMemory (Section MemoryID _ content) + | head (BS.unpack content) == 0x01 = parseMemory' (BS.drop 1 content) + | otherwise = throw $ WasmError "parseMemory: v1 allow 1 memory only" +parseMemory _ = throw $ WasmError "parseMemory: bad memory section" + +--https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index e0b6842..8edf32f 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -49,6 +49,7 @@ executable lvtrun-exe WasmMod.Module WasmMod.Sections WasmMod.Sections.Global + WasmMod.Sections.Memory WasmMod.Sections.Types Paths_lvtrun autogen-modules: diff --git a/lvtrun/test/test.cpp b/lvtrun/test/test.cpp index 7cc2cdb..6f20ba4 100644 --- a/lvtrun/test/test.cpp +++ b/lvtrun/test/test.cpp @@ -1,10 +1,14 @@ +#include + +int globa15 = 15; + int add(int a, int b) { return a + b; } int test() { - return 15; + return globa15; } bool compare(int a, int b) { @@ -15,8 +19,9 @@ bool compare(int a, int b) { } int main() { - int a = 5; - int b = 10; + int a = add(5, 10); + int b = add(10, 5); bool res = compare(a, b); + std::cout << "res: " << res << std::endl; return 0; } From 7b699d16c7a0648d789c47503473fb4f22c51fe9 Mon Sep 17 00:00:00 2001 From: tenshi Date: Wed, 10 Jan 2024 02:44:36 +0100 Subject: [PATCH 11/47] refactor + good global parsing --- lvtrun/app/IO.hs | 18 ++ lvtrun/app/{WasmMod => }/Leb128.hs | 23 +- lvtrun/app/Loader.hs | 20 ++ lvtrun/app/Main.hs | 4 +- .../Types.hs => Parsing/FuncTypes.hs} | 35 +-- lvtrun/app/Parsing/Global.hs | 151 ++++++++++++ lvtrun/app/Parsing/Header.hs | 25 ++ lvtrun/app/Parsing/Memory.hs | 48 ++++ lvtrun/app/Parsing/Parser.hs | 33 +++ lvtrun/app/Parsing/Sections.hs | 67 ++++++ lvtrun/app/Types.hs | 214 +++++++++++++++++- lvtrun/app/WasmMod/Header.hs | 29 --- lvtrun/app/WasmMod/Module.hs | 69 ------ lvtrun/app/WasmMod/Sections.hs | 92 -------- lvtrun/app/WasmMod/Sections/Global.hs | 72 ------ lvtrun/app/WasmMod/Sections/Memory.hs | 47 ---- lvtrun/lvtrun.cabal | 16 +- lvtrun/test/simple.wasm | Bin 0 -> 738 bytes lvtrun/test/test.cpp | 24 +- lvtrun/test/test.wasm | Bin 830 -> 170592 bytes 20 files changed, 616 insertions(+), 371 deletions(-) create mode 100644 lvtrun/app/IO.hs rename lvtrun/app/{WasmMod => }/Leb128.hs (63%) create mode 100644 lvtrun/app/Loader.hs rename lvtrun/app/{WasmMod/Sections/Types.hs => Parsing/FuncTypes.hs} (58%) create mode 100644 lvtrun/app/Parsing/Global.hs create mode 100644 lvtrun/app/Parsing/Header.hs create mode 100644 lvtrun/app/Parsing/Memory.hs create mode 100644 lvtrun/app/Parsing/Parser.hs create mode 100644 lvtrun/app/Parsing/Sections.hs delete mode 100644 lvtrun/app/WasmMod/Header.hs delete mode 100644 lvtrun/app/WasmMod/Module.hs delete mode 100644 lvtrun/app/WasmMod/Sections.hs delete mode 100644 lvtrun/app/WasmMod/Sections/Global.hs delete mode 100644 lvtrun/app/WasmMod/Sections/Memory.hs create mode 100644 lvtrun/test/simple.wasm diff --git a/lvtrun/app/IO.hs b/lvtrun/app/IO.hs new file mode 100644 index 0000000..be57a5c --- /dev/null +++ b/lvtrun/app/IO.hs @@ -0,0 +1,18 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- IO +-} + +module IO +( + getFileContent +) +where + +import qualified Data.ByteString.Lazy as BSL (readFile) +import Types + +getFileContent :: String -> IO FileContent +getFileContent path = BSL.readFile path diff --git a/lvtrun/app/WasmMod/Leb128.hs b/lvtrun/app/Leb128.hs similarity index 63% rename from lvtrun/app/WasmMod/Leb128.hs rename to lvtrun/app/Leb128.hs index 099cf99..da83419 100644 --- a/lvtrun/app/WasmMod/Leb128.hs +++ b/lvtrun/app/Leb128.hs @@ -5,16 +5,17 @@ -- Leb128 -} -module WasmMod.Leb128 +module Leb128 ( getLEB128, - extractLEB128 + extractLEB128, + extractLEB1282, ) where import Data.Binary.Get import Data.Bits -import Data.Int (Int64) +import Data.Int (Int64, Int32) import qualified Data.ByteString.Lazy as BS (ByteString, drop) getLEB128 :: Get Int @@ -43,3 +44,19 @@ extractLEB128 :: BS.ByteString -> (Int64, BS.ByteString) extractLEB128 bytes = do let (value, size) = runGet extractLEB128' bytes (value, BS.drop size bytes) + + +extractLEB1282' :: Get (Int32, Int64) +extractLEB1282' = do + byte <- getWord8 + let value = fromIntegral (byte .&. 0x7F) + case byte `testBit` 7 of + True -> do + (next, size) <- extractLEB1282' + return (value .|. (next `shiftL` 7), size + 1) + False -> return (value, 1) + +extractLEB1282 :: BS.ByteString -> (Int32, BS.ByteString) +extractLEB1282 bytes = do + let (value, size) = runGet extractLEB1282' bytes + (value, BS.drop size bytes) diff --git a/lvtrun/app/Loader.hs b/lvtrun/app/Loader.hs new file mode 100644 index 0000000..cff277a --- /dev/null +++ b/lvtrun/app/Loader.hs @@ -0,0 +1,20 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Loader +-} + +module Loader +( + loadModule +) +where + +import Parsing.Parser +import Types +import IO + +loadModule :: String -> IO WasmModule +loadModule path = getFileContent path >>= \bytes -> + return $ parseModule bytes diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 7de385f..0409807 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -8,11 +8,11 @@ module Main (main) where import Control.Exception (try) -import WasmMod.Module import Errors +import Loader main :: IO () -main = try (loadModule "./test/test.wasm") >>= \result -> +main = try (loadModule "./test/simple.wasm") >>= \result -> case result of Left err -> handleException err Right wasmMod -> print wasmMod diff --git a/lvtrun/app/WasmMod/Sections/Types.hs b/lvtrun/app/Parsing/FuncTypes.hs similarity index 58% rename from lvtrun/app/WasmMod/Sections/Types.hs rename to lvtrun/app/Parsing/FuncTypes.hs index 152964b..a50b1e3 100644 --- a/lvtrun/app/WasmMod/Sections/Types.hs +++ b/lvtrun/app/Parsing/FuncTypes.hs @@ -5,49 +5,36 @@ -- Types -} -module WasmMod.Sections.Types +module Parsing.FuncTypes ( - parseTypes, - Type(..), - FuncType(..) + getFuncTypes ) where import qualified Data.ByteString.Lazy as Bs import Control.Exception (throw) -import Data.Int (Int64) +import Data.Int (Int64, Int32) import Data.Word (Word8) -import WasmMod.Sections -import WasmMod.Leb128 +import Leb128 import Errors -import Types (Type(..), getTypeFromByte) - -data FuncType = FuncType { - typeId :: Int, - params :: [Type], - results :: [Type] -} - -instance Show FuncType where - show funcType = "(type " ++ (show $ typeId funcType) ++ " (func " ++ - (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")\n" +import Types getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) getVectorSize content = extractLEB128 content -extractTypes :: (Int64, Bs.ByteString) -> ([Type], Bs.ByteString) +extractTypes :: (Int64, Bs.ByteString) -> ([TypeName], Bs.ByteString) extractTypes (0, content) = ([], content) extractTypes (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : types, rest) where (types, rest) = extractTypes (idx - 1, Bs.drop 1 content) -parseFuncType :: Int -> Bs.ByteString -> (FuncType, Bs.ByteString) +parseFuncType :: Int32 -> Bs.ByteString -> (FuncType, Bs.ByteString) parseFuncType id content = do let (params, rest) = extractTypes (getVectorSize content) let (results, rest2) = extractTypes (getVectorSize rest) ((FuncType id params results), rest2) -parseFuncTypes :: Int -> Int64 -> Bs.ByteString -> [FuncType] +parseFuncTypes :: Int32 -> Int64 -> Bs.ByteString -> [FuncType] parseFuncTypes idx maxIdx content | idx >= (fromIntegral maxIdx) = [] | head (Bs.unpack content) == 0x60 = do @@ -55,8 +42,8 @@ parseFuncTypes idx maxIdx content funcType : parseFuncTypes (idx + 1) maxIdx rest | otherwise = throw $ WasmError "ParseFuncTypes: 0x60 expected for function" -parseTypes :: Section -> [FuncType] -parseTypes (Section TypeID _ content) = do +getFuncTypes :: Section -> [FuncType] +getFuncTypes (Section TypeID _ content) = do let (vecSize, rest) = extractLEB128 content parseFuncTypes 0 vecSize rest -parseTypes _ = throw $ WasmError "ParseTypes: bad section" +getFuncTypes _ = throw $ WasmError "getFuncTypes: bad section" diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs new file mode 100644 index 0000000..6dc6db2 --- /dev/null +++ b/lvtrun/app/Parsing/Global.hs @@ -0,0 +1,151 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Global +-} + +module Parsing.Global + ( + getGlobals, + ) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) +import Control.Monad (when) +import Data.Word (Word8) +import Data.Int (Int64) + +import Leb128 +import Types +import Errors + +extractOpCode :: BSL.ByteString -> ([Word8], Int64, BSL.ByteString) +extractOpCode bytes + | (head $ BSL.unpack bytes) == 0x00 = ([0x00], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x01 = ([0x01], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x10 = ([0x10], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x41 = ([0x41], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x42 = ([0x42], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x43 = ([0x43], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x44 = ([0x44], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x28 = ([0x28], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x29 = ([0x29], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x36 = ([0x36], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x20 = ([0x20], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) + | otherwise = throw $ WasmError "ExtractOpCode: bad opcode" + +createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) +createInstruction [0x01] bytes = (Unreachable, bytes) +createInstruction [0x01] bytes = (Nop, bytes) +createInstruction [0x0f] bytes = (Return, bytes) +createInstruction [0x10] bytes = do + let (value, rest) = extractLEB1282 bytes + (Call value, rest) +createInstruction [0x41] bytes = do + let (value, rest) = extractLEB1282 bytes + (I32Const value, rest) +createInstruction [0x42] bytes = do + let (value, rest) = extractLEB128 bytes + (I64Const value, rest) +createInstruction [0x43] bytes = do + let (value, rest) = extractLEB128 bytes + (F32Const (fromIntegral value), rest) +createInstruction [0x44] bytes = do + let (value, rest) = extractLEB128 bytes + (F64Const (fromIntegral value), rest) +createInstruction [0x28] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I32Load (MemArg align offset), rest2) +createInstruction [0x29] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I64Load (MemArg align offset), rest2) +createInstruction [0x36] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I32Store (MemArg align offset), rest2) +createInstruction [0x37] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I64Store (MemArg align offset), rest2) +createInstruction [0x20] bytes = do + let (value, rest) = extractLEB1282 bytes + (GetLocal value, rest) +createInstruction [0x24] bytes = do + let (value, rest) = extractLEB1282 bytes + (SetLocal value, rest) +createInstruction [0x23] bytes = do + let (value, rest) = extractLEB1282 bytes + (GetGlobal value, rest) +createInstruction [0x21] bytes = do + let (value, rest) = extractLEB1282 bytes + (SetGlobal value, rest) +createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) +createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) +createInstruction _ _ = throw $ WasmError "CreateInstruction: bad instruction" + +parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) +parseInstruction bytes + | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" + | otherwise = do + let (opCode, nbParams, rest) = extractOpCode bytes + let (instruction, rest2) = createInstruction opCode rest + (instruction, rest2) + +parseInstructions :: BSL.ByteString -> [Instruction] +parseInstructions bytes + | BSL.length bytes == 0 = [] + | head (BSL.unpack bytes) == 0x0b = [] + | otherwise = do + let (instruction, rest) = parseInstruction bytes + instruction : parseInstructions rest + +parseMutability :: Word8 -> Mutability +parseMutability 0x00 = Const +parseMutability 0x01 = Var +parseMutability _ = throw $ WasmError "ParseMutability: bad mutability" + +getHexaIndex :: BSL.ByteString -> Int64 -> Int64 +getHexaIndex content idx + | idx >= (fromIntegral $ BSL.length content) = throw $ WasmError "GetHexaIndex: no 0x0b found" + | (head $ BSL.unpack $ BSL.drop (fromIntegral idx) content) == 0x0b = idx + | otherwise = getHexaIndex content (idx + 1) + +extractExpression :: BSL.ByteString -> (BSL.ByteString, BSL.ByteString) +extractExpression content = do + let idx = getHexaIndex content 0 + let expression = BSL.take (fromIntegral (idx + 1)) content + let rest = BSL.drop (fromIntegral (idx + 1)) content + (expression, rest) + +parseGlobal :: BSL.ByteString -> (Global, BSL.ByteString) +parseGlobal content = do + let globalType = getTypeFromByte (head $ BSL.unpack content) + let mutability = parseMutability (head $ BSL.unpack $ BSL.drop 1 content) + let (expression, rest) = extractExpression (BSL.drop 2 content) + let instructions = parseInstructions expression + (Global globalType mutability instructions, rest) + +parseGlobals :: Int64 -> Int64 -> BSL.ByteString -> [Global] +parseGlobals idx maxIdx content + | idx >= maxIdx = [] + | otherwise = do + let (global, rest) = parseGlobal content + global : parseGlobals (idx + 1) maxIdx rest + +getGlobals :: Section -> [Global] +getGlobals (Section GlobalID _ content) = do + let (vecSize, rest) = extractLEB128 content + parseGlobals 0 vecSize rest +getGlobals _ = throw $ WasmError "getGlobals: bad section" diff --git a/lvtrun/app/Parsing/Header.hs b/lvtrun/app/Parsing/Header.hs new file mode 100644 index 0000000..d6aeecc --- /dev/null +++ b/lvtrun/app/Parsing/Header.hs @@ -0,0 +1,25 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Header +-} + +module Parsing.Header + ( + getModHeader, + isHeaderValid + ) +where + +import qualified Data.ByteString.Lazy as BSL (ByteString, take, drop, pack) + +import Types + +getModHeader :: Section -> ModHeader +getModHeader bytes = ModHeader (BSL.take 4 $ content bytes) (BSL.take 4 $ BSL.drop 4 $ content bytes) + +isHeaderValid :: ModHeader -> Bool +isHeaderValid header = + magicNumber header == BSL.pack [0x00, 0x61, 0x73, 0x6d] && + version header == BSL.pack [0x01, 0x00, 0x00, 0x00] diff --git a/lvtrun/app/Parsing/Memory.hs b/lvtrun/app/Parsing/Memory.hs new file mode 100644 index 0000000..e567f10 --- /dev/null +++ b/lvtrun/app/Parsing/Memory.hs @@ -0,0 +1,48 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Memory +-} + +module Parsing.Memory + ( + getMemories + ) where + +import qualified Data.ByteString.Lazy as BS +import Control.Exception (throw) +import Control.Monad (when) + +import Leb128 +import Types +import Errors + +parseMinMax :: BS.ByteString -> Memory +parseMinMax content + | endBs /= BS.empty = throw $ WasmError "parseMinMax: bad memory section" + | otherwise = Limit {lMin = fromIntegral min, lMax = Just (fromIntegral max)} + where + (min, rest) = extractLEB128 content + (max, endBs) = extractLEB128 rest + +parseMin :: BS.ByteString -> Memory +parseMin content + | endBs /= BS.empty = throw $ WasmError "parseMin: bad memory section" + | otherwise = Limit {lMin = fromIntegral min, lMax = Nothing} + where + (min, endBs) = extractLEB128 content + +parseMemory :: BS.ByteString -> Memory +parseMemory content + | head (BS.unpack content) == 0x01 = parseMinMax (BS.drop 1 content) + | head (BS.unpack content) == 0x00 = parseMin (BS.drop 1 content) + | otherwise = throw $ WasmError "parseMemory: bad memory section" + +getMemories :: Section -> Memory +getMemories (Section MemoryID _ content) + | head (BS.unpack content) == 0x01 = parseMemory (BS.drop 1 content) + | otherwise = throw $ WasmError "getMemories: v1 allow 1 memory only" +getMemories _ = throw $ WasmError "getMemories: bad memory section" + +--https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs new file mode 100644 index 0000000..e330b5b --- /dev/null +++ b/lvtrun/app/Parsing/Parser.hs @@ -0,0 +1,33 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Parser +-} + +module Parsing.Parser +( + parseModule, +) +where + +import Types +import Parsing.Sections +import Parsing.Header +import Parsing.Memory +import Parsing.FuncTypes +import Parsing.Global + +import Debug.Trace + +parseModule :: FileContent -> WasmModule +parseModule bytes = do + let sections = getSections bytes + WasmModule (getModHeader (getSectionWithId sections CustomID)) + (getFuncTypes (getSectionWithId sections TypeID)) + [] + [] + [] + (getMemories (getSectionWithId sections MemoryID)) + (getGlobals (getSectionWithId sections GlobalID)) + [] diff --git a/lvtrun/app/Parsing/Sections.hs b/lvtrun/app/Parsing/Sections.hs new file mode 100644 index 0000000..d4ed7bc --- /dev/null +++ b/lvtrun/app/Parsing/Sections.hs @@ -0,0 +1,67 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Parser +-} + +module Parsing.Sections +( + getSections, + getSectionWithId +) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) + +import Types +import Errors +import Leb128 + +extractHeader :: BSL.ByteString -> (Section, BSL.ByteString) +extractHeader bytes + | (BSL.length bytes) < 8 = throw (WasmError "Invalid header") + | otherwise = (Section CustomID 8 (BSL.take 8 bytes), BSL.drop 8 bytes) + +getSectionId :: BSL.ByteString -> SectionID +getSectionId bytes = case head (BSL.unpack $ BSL.take 1 bytes) of + 0 -> CustomID + 1 -> TypeID + 2 -> ImportID + 3 -> FunctionID + 4 -> TableID + 5 -> MemoryID + 6 -> GlobalID + 7 -> ExportID + 8 -> StartID + 9 -> ElementID + 10 -> CodeID + 11 -> DataID + _ -> throw (WasmError "Invalid section id") + +extractSection :: BSL.ByteString -> (Section, BSL.ByteString) +extractSection bytes = do + let sectionId = getSectionId bytes + let (size, rest) = extractLEB128 (BSL.drop 1 bytes) + let (content, rest2) = BSL.splitAt (fromIntegral size) rest + (Section sectionId (fromIntegral size) content, rest2) + +extractSections :: BSL.ByteString -> [Section] +extractSections bytes + | BSL.null bytes = [] + | otherwise = do + let (section, rest) = extractSection bytes + section : extractSections rest + +getSections :: FileContent -> [Section] +getSections bytes = do + let (header, rest) = extractHeader bytes + let sections = extractSections rest + header : sections + +getSectionWithId :: [Section] -> SectionID -> Section +getSectionWithId [] _ = throw (WasmError "No section with this id") +getSectionWithId (x:xs) id + | identifier x == id = x + | otherwise = getSectionWithId xs id diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index fe9cfba..52e1997 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -6,22 +6,222 @@ -} module Types - ( - Type(..), - getTypeFromByte - ) - where +( + TypeName(..), + Limit(..), + MemArg(..), + Instruction(..), + TypeIdx, + FuncIdx, + TableIdx, + MemIdx, + GlobalIdx, + ElemIdx, + DataIdx, + LocalIdx, + LabelIdx, + FuncType(..), + Import(..), + ImportDesc(..), + Function(..), + Mutability(..), + Global(..), + ExportDesc(..), + Export(..), + Table(..), + WasmModule(..), + getTypeFromByte, + ModHeader(..), + FileContent, + SectionID(..), + Section(..), + Memory(..), + OpCode +) where +import Data.Int (Int32, Int64) import Data.Word (Word8) +import Numeric (showHex) import Control.Exception (throw) +import qualified Data.ByteString.Lazy as BSL import Errors -data Type = I32 | I64 | F32 | F64 deriving (Show, Eq) +-- Indices +type TypeIdx = Int32 +type FuncIdx = Int32 +type TableIdx = Int32 +type MemIdx = Int32 +type GlobalIdx = Int32 +type ElemIdx = Int32 +type DataIdx = Int32 +type LocalIdx = Int32 +type LabelIdx = Int32 -getTypeFromByte :: Word8 -> Type +-- Common + +type FileContent = BSL.ByteString + +data TypeName = + I32 | + I64 | + F32 | + F64 + deriving (Show, Eq, Enum) + +getTypeFromByte :: Word8 -> TypeName getTypeFromByte 0x7f = I32 getTypeFromByte 0x7e = I64 getTypeFromByte 0x7d = F32 getTypeFromByte 0x7c = F64 getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" + +data Limit = Limit { + lMin :: Int32, + lMax :: Maybe Int32 +} + +instance Show Limit where + show limit = "[\n\tmin: " ++ (show $ lMin limit) ++ "\n\tmax: " ++ (show $ lMax limit) ++ "\n]" + +----------------------- + +data MemArg = MemArg { + offset :: Int32, + align :: Int32 +} + +instance Show MemArg where + show memArg = "[\n\toffset: " ++ (show $ offset memArg) ++ "\n\talign: " ++ (show $ align memArg) ++ "\n]" + +type OpCode = [Word8] + +data Instruction = + Unreachable + | Nop + | Return + | Call FuncIdx + | I32Const Int32 + | I64Const Int64 + | F32Const Float + | F64Const Double + | I32Load MemArg + | I64Load MemArg + | I32Store MemArg + | I64Store MemArg + | GetLocal LocalIdx + | SetLocal LocalIdx + | GetGlobal GlobalIdx + | SetGlobal GlobalIdx + | MemorySize + | MemoryGrow + deriving (Show) + +-- Module section + +data FuncType = FuncType { + typeId :: TypeIdx, + params :: [TypeName], + results :: [TypeName] +} + +instance Show FuncType where + show funcType = "\n\t(type " ++ (show $ typeId funcType) ++ " (func " ++ + (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")" + +data Import = Import { + mod :: String, + name :: String, + desc :: ImportDesc +} deriving (Show) + +data ImportDesc = + ImportFunc TypeIdx | + ImportMemory Limit + deriving (Show) + +data Function = Function { + funcType :: TypeIdx, + body :: [Instruction] +} deriving (Show) + +type Memory = Limit + +data Mutability = Const | Var deriving (Show) + +data Global = Global { + globalType :: TypeName, + mutability :: Mutability, + initExpr :: [Instruction] +} + +instance Show Global where + show global = "\n\t(global " ++ (show $ globalType global) ++ " " ++ + (show $ mutability global) ++ " " ++ (show $ initExpr global) ++ ")" + +data ExportDesc = ExportFunc FuncIdx deriving (Show) + +data Export = Export { + modName :: String + -- desc +} deriving (Show) + +data Table = Table { + notImpl :: String +} deriving (Show) + +data ModHeader = ModHeader { + magicNumber :: BSL.ByteString, + version :: BSL.ByteString +} deriving (Show) + +data SectionID = + CustomID + | TypeID + | ImportID + | FunctionID + | TableID + | MemoryID + | GlobalID + | ExportID + | StartID + | ElementID + | CodeID + | DataID + | DataCountID + | InvalidID + deriving (Show, Eq) + +data Section = Section { + identifier :: SectionID, + size :: Int, + content :: BSL.ByteString +} + +instance Show Section where + show section = + "\nSection " ++ (show $ identifier section) ++ + " Size: " ++ (show $ size section) ++ + " Content: " ++ (concat $ map (\x -> showHex x " ") (BSL.unpack $ content section)) + +data WasmModule = WasmModule { + header :: ModHeader, + types :: [FuncType], + imports :: [Import], + functions :: [Function], + tables :: [Table], + memory :: Memory, + globals :: [Global], + exports :: [Export] +} + +instance Show WasmModule where + show wasmMod = "\n[ Wasm Module Header ]\n" ++ + "- Header: " ++ (show $ header wasmMod) ++ "\n" ++ + "- Types: " ++ (show $ types wasmMod) ++ "\n" ++ + "- Imports: " ++ (show $ imports wasmMod) ++ "\n" ++ + "- Functions: " ++ (show $ functions wasmMod) ++ "\n" ++ + "- Tables: " ++ (show $ tables wasmMod) ++ "\n" ++ + "- Memory: " ++ (show $ memory wasmMod) ++ "\n" ++ + "- Globals: " ++ (show $ globals wasmMod) ++ "\n" ++ + "- Exports: " ++ (show $ exports wasmMod) ++ "\n" diff --git a/lvtrun/app/WasmMod/Header.hs b/lvtrun/app/WasmMod/Header.hs deleted file mode 100644 index 22d18fc..0000000 --- a/lvtrun/app/WasmMod/Header.hs +++ /dev/null @@ -1,29 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Header --} - -module WasmMod.Header - ( - ModHeader(..), - getModHeader, - isHeaderValid - ) -where - -import qualified Data.ByteString.Lazy as BS (ByteString, take, drop, pack) - -data ModHeader = ModHeader { - magicNumber :: BS.ByteString, - version :: BS.ByteString -} deriving (Show) - -getModHeader :: BS.ByteString -> ModHeader -getModHeader bytes = ModHeader (BS.take 4 bytes) (BS.take 4 $ BS.drop 4 bytes) - -isHeaderValid :: ModHeader -> Bool -isHeaderValid header = - magicNumber header == BS.pack [0x00, 0x61, 0x73, 0x6d] && - version header == BS.pack [0x01, 0x00, 0x00, 0x00] diff --git a/lvtrun/app/WasmMod/Module.hs b/lvtrun/app/WasmMod/Module.hs deleted file mode 100644 index 3b01d24..0000000 --- a/lvtrun/app/WasmMod/Module.hs +++ /dev/null @@ -1,69 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Module --} - -module WasmMod.Module - ( - WasmModule(..), - loadModule - ) -where - -import qualified Data.ByteString.Lazy as BS (ByteString, unpack, readFile) -import Control.Exception (throwIO, throw) -import Control.Monad (when) -import Numeric (showHex) - -import Errors -import WasmMod.Header -import WasmMod.Sections -import WasmMod.Sections.Types -import WasmMod.Sections.Global -import WasmMod.Sections.Memory - -data WasmModule = WasmModule { - header :: ModHeader, - sections :: [Section] -} - -instance Show WasmModule where - show wasmMod = "\n[ Wasm Module Header ]\n" ++ - "- Magic Number: " ++ (concat $ map (\x -> showHex x " ") $ - BS.unpack $ magicNumber $ header wasmMod) ++ "\n" ++ - "- Version: " ++ (concat $ map (\x -> showHex x " ") $ - BS.unpack $ version $ header wasmMod) ++ "\n" ++ - "- Sections: " ++ (show $ sections wasmMod) ++ "\n" - -getFileContent :: String -> IO BS.ByteString -getFileContent path = BS.readFile path - ---TEMP FUNC -getGlobalSection :: [Section] -> Section -getGlobalSection [] = throw (WasmError "No global section") -getGlobalSection (x:xs) - | identifier x == GlobalID = x - | otherwise = getGlobalSection xs - -getMemorySection :: [Section] -> Section -getMemorySection [] = throw (WasmError "No memory section") -getMemorySection (x:xs) - | identifier x == MemoryID = x - | otherwise = getMemorySection xs - -loadModule :: String -> IO WasmModule -loadModule filePath = do - bytes <- getFileContent filePath - let modHeader = getModHeader bytes - when (not $ isHeaderValid modHeader) $ throwIO (WasmError "Invalid header") - let modSections = getModSections bytes - when (not $ areSectionsValid modSections) $ throwIO (WasmError "Invalid sections") - let funcType = parseTypes $ head modSections - print funcType - let globals = parseGlobals $ getGlobalSection modSections - print globals - let memory = parseMemory $ getMemorySection modSections - print memory - return $ WasmModule modHeader modSections diff --git a/lvtrun/app/WasmMod/Sections.hs b/lvtrun/app/WasmMod/Sections.hs deleted file mode 100644 index 5f66c3f..0000000 --- a/lvtrun/app/WasmMod/Sections.hs +++ /dev/null @@ -1,92 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Sections --} - -module WasmMod.Sections - ( - SectionID(..), - Section(..), - areSectionsValid, - getModSections - ) -where - -import qualified Data.ByteString.Lazy as BS (ByteString, head, drop, take, null, unpack) -import Data.Word (Word8) -import Numeric (showHex) -import WasmMod.Leb128 -import Data.Bits - -data SectionID = - Custom - | TypeID - | ImportID - | FunctionID - | TableID - | MemoryID - | GlobalID - | ExportID - | StartID - | ElementID - | CodeID - | DataID - | DataCountID - | InvalidID - deriving (Show, Eq) - -data Section = Section { - identifier :: SectionID, - size :: Int, - content :: BS.ByteString -} - -instance Show Section where - show section = - "\nSection " ++ (show $ identifier section) ++ - " Size: " ++ (show $ size section) ++ - " Content: " ++ (concat $ map (\x -> showHex x " ") (BS.unpack $ content section)) - -areSectionsValid :: [Section] -> Bool -areSectionsValid sections = True - -getSectionID :: Word8 -> SectionID -getSectionID 1 = TypeID -getSectionID 2 = ImportID -getSectionID 3 = FunctionID -getSectionID 4 = TableID -getSectionID 5 = MemoryID -getSectionID 6 = GlobalID -getSectionID 7 = ExportID -getSectionID 8 = StartID -getSectionID 9 = ElementID -getSectionID 10 = CodeID -getSectionID 11 = DataID -getSectionID 12 = DataCountID -getSectionID _ = InvalidID - -getSection :: BS.ByteString -> (Section, BS.ByteString) -getSection bytes = do - let id = getSectionID (BS.head bytes) - let (size, rest) = extractLEB128 (BS.drop 1 bytes) - let content = BS.take (fromIntegral size) rest - (Section id (fromIntegral size) content, BS.drop (fromIntegral size) rest) - -removeHeader :: BS.ByteString -> BS.ByteString -removeHeader bytes = BS.drop 8 bytes - -getModSections' :: BS.ByteString -> [Section] -getModSections' = do - let getModSections'' bytes = - if BS.null bytes - then [] - else - let (section, rest) = getSection bytes - in section : getModSections'' rest - getModSections'' - --- Todo: Check if sections are valid -getModSections :: BS.ByteString -> [Section] -getModSections bytes = getModSections' (removeHeader bytes) diff --git a/lvtrun/app/WasmMod/Sections/Global.hs b/lvtrun/app/WasmMod/Sections/Global.hs deleted file mode 100644 index 838cfb8..0000000 --- a/lvtrun/app/WasmMod/Sections/Global.hs +++ /dev/null @@ -1,72 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Global --} - -module WasmMod.Sections.Global - ( - parseGlobals, - Global(..) - ) -where - -import qualified Data.ByteString.Lazy as Bs -import Control.Exception (throw) -import Data.Word (Word8) -import Data.Int (Int64) - -import WasmMod.Leb128 -import WasmMod.Sections (Section(..), SectionID(..)) -import Types (Type(..), getTypeFromByte) -import Errors - -data Mutability = Const | Var deriving (Show) - -data Global = Global { - globalType :: Type, - mutability :: Mutability, - initExp :: [Word8] -} - -instance Show Global where - show global = "Global: " ++ (show $ globalType global) ++ " " ++ - (show $ mutability global) ++ " " ++ (show $ initExp global) ++ "\n" - -parseMutability :: Word8 -> Mutability -parseMutability 0x00 = Const -parseMutability 0x01 = Var -parseMutability _ = throw $ WasmError "ParseMutability: bad mutability" - -getHexaIndex :: Bs.ByteString -> Int64 -> Int64 -getHexaIndex content idx - | idx >= (fromIntegral $ Bs.length content) = throw $ WasmError "GetHexaIndex: no 0x0b found" - | (head $ Bs.unpack $ Bs.drop (fromIntegral idx) content) == 0x0b = idx - | otherwise = getHexaIndex content (idx + 1) - -extractExpression :: Bs.ByteString -> ([Word8], Bs.ByteString) -extractExpression content = do - let idx = getHexaIndex content 0 - let expression = Bs.take (fromIntegral (idx + 1)) content - (Bs.unpack expression, Bs.drop (fromIntegral (idx + 1)) content) - -parseGlobal :: Bs.ByteString -> (Global, Bs.ByteString) -parseGlobal content = do - let globalType = getTypeFromByte (head $ Bs.unpack content) - let mutability = parseMutability (head $ Bs.unpack $ Bs.drop 1 content) - let (expression, rest) = extractExpression (Bs.drop 2 content) - (Global globalType mutability expression, rest) - -parseGlobals' :: Int64 -> Int64 -> Bs.ByteString -> [Global] -parseGlobals' idx maxIdx content - | idx >= maxIdx = [] - | otherwise = do - let (global, rest) = parseGlobal content - global : parseGlobals' (idx + 1) maxIdx rest - -parseGlobals :: Section -> [Global] -parseGlobals (Section GlobalID _ content) = do - let (vecSize, rest) = extractLEB128 content - parseGlobals' 0 vecSize rest -parseGlobals _ = throw $ WasmError "ParseGlobals: bad section" diff --git a/lvtrun/app/WasmMod/Sections/Memory.hs b/lvtrun/app/WasmMod/Sections/Memory.hs deleted file mode 100644 index 92cbcaa..0000000 --- a/lvtrun/app/WasmMod/Sections/Memory.hs +++ /dev/null @@ -1,47 +0,0 @@ - -module WasmMod.Sections.Memory - ( - Memory(..), - parseMemory - ) where - -import qualified Data.ByteString.Lazy as BS -import Control.Exception (throw) -import Control.Monad (when) - -import WasmMod.Leb128 -import WasmMod.Sections.Types -import WasmMod.Sections -import Errors - -data Memory = Memory { memMin :: Int, memMax :: Maybe Int } - deriving (Show, Eq) - -parseMinMax :: BS.ByteString -> Memory -parseMinMax content - | endBs /= BS.empty = throw $ WasmError "parseMinMax: bad memory section" - | otherwise = Memory {memMin = fromIntegral min, memMax = Just (fromIntegral max)} - where - (min, rest) = extractLEB128 content - (max, endBs) = extractLEB128 rest - -parseMin :: BS.ByteString -> Memory -parseMin content - | endBs /= BS.empty = throw $ WasmError "parseMin: bad memory section" - | otherwise = Memory {memMin = fromIntegral min, memMax = Nothing} - where - (min, endBs) = extractLEB128 content - -parseMemory' :: BS.ByteString -> Memory -parseMemory' content - | head (BS.unpack content) == 0x01 = parseMinMax (BS.drop 1 content) - | head (BS.unpack content) == 0x00 = parseMin (BS.drop 1 content) - | otherwise = throw $ WasmError "parseMemory': bad memory section" - -parseMemory :: Section -> Memory -parseMemory (Section MemoryID _ content) - | head (BS.unpack content) == 0x01 = parseMemory' (BS.drop 1 content) - | otherwise = throw $ WasmError "parseMemory: v1 allow 1 memory only" -parseMemory _ = throw $ WasmError "parseMemory: bad memory section" - ---https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 8edf32f..a70c8c9 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -43,14 +43,16 @@ executable lvtrun-exe main-is: Main.hs other-modules: Errors + IO + Leb128 + Loader + Parsing.FuncTypes + Parsing.Global + Parsing.Header + Parsing.Memory + Parsing.Parser + Parsing.Sections Types - WasmMod.Header - WasmMod.Leb128 - WasmMod.Module - WasmMod.Sections - WasmMod.Sections.Global - WasmMod.Sections.Memory - WasmMod.Sections.Types Paths_lvtrun autogen-modules: Paths_lvtrun diff --git a/lvtrun/test/simple.wasm b/lvtrun/test/simple.wasm new file mode 100644 index 0000000000000000000000000000000000000000..8c85cc089000a9c84fad85b86afe1e339b3a6393 GIT binary patch literal 738 zcmZvay^hmR5QWc-{}Nw^+(i7XL~*@I5os%$)awFKAR2f8$Lqa-LoeJ_S&@RD9WWo?nRRd#5<)m?gM>|wvy@9n;Belo7o zw5aZj#^zo6<)O;EqOQ^|yW3fUpSE4rbZ~W=+NP=MbXVuu(7^bG-M4vD96DPKt-KVB z+!S8@_x`IDa$_yj3o6x%nG%4-q0E(e*hO@V$29) z@L>X_w5Md#)4ui$^av`DOasct=_i?=WXXb=Zj;0JB}rp}1(nLhf`>uF7#0kcP;+AW Obc0gL*CZ$Yi~j;RqLdW? literal 0 HcmV?d00001 diff --git a/lvtrun/test/test.cpp b/lvtrun/test/test.cpp index 6f20ba4..e03428a 100644 --- a/lvtrun/test/test.cpp +++ b/lvtrun/test/test.cpp @@ -1,27 +1,13 @@ -#include - int globa15 = 15; int add(int a, int b) { - return a + b; -} - -int test() -{ - return globa15; -} - -bool compare(int a, int b) { - if (a != b) { - return false; - } - return true; + int c = a + b; + return c; } int main() { - int a = add(5, 10); - int b = add(10, 5); - bool res = compare(a, b); - std::cout << "res: " << res << std::endl; + int a = 10; + int b = 20; + int res = add(a, b); return 0; } diff --git a/lvtrun/test/test.wasm b/lvtrun/test/test.wasm index e36427c28f0d2484bba49d7d89589d8cab7c22d6..30ee53f9366761bfe51ce0b10de69dd2de9b12a0 100644 GIT binary patch literal 170592 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

nEzt=oklm`!wG|c~*AZiKVid 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 B?5qF) literal 830 zcmZva&2H0B5QS&Pf3dF}U#I1#5{tPci%N(En=ZPF7l;L70}tT1?gi2`c4E6NNJ!3G z@HD&?55PFEqYKa6bI;7tXUkA_TLb`n=B)sS6~GDySkW+uKq<7qrw?VfF1othbzhsl z*tPb4Z6Dr*ySAwc`)%C=IkOBMKtv)KLdvJA8*tV^k;eE3keZn|Cp_bmy*cI6RNdor(U%pvg=mJmd+7d%-1+F2Ru?CE2ma_(oup1+Yq02DHpk-a@0&jt)Q(s8@)C z%#W4;d`Q4qGUPvQUcxy+h9CmoC!mz}lx%w1*B*nOfC|LZ0Lq8s4>&uv1xZYLlkfHm il!-aaaid(A(^zmU;{wA1)C@_!0>egzU!oxXv;P1dp`N|~ From a2a1cc035700b991b6a3b295d006a2020d3c3eb6 Mon Sep 17 00:00:00 2001 From: tenshi Date: Wed, 10 Jan 2024 21:51:57 +0100 Subject: [PATCH 12/47] add exports --- lvtrun/README.md | 2 -- lvtrun/app/Parsing/Exports.hs | 68 +++++++++++++++++++++++++++++++++++ lvtrun/app/Parsing/Parser.hs | 3 +- lvtrun/app/Types.hs | 17 ++++++--- lvtrun/lvtrun.cabal | 1 + 5 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 lvtrun/app/Parsing/Exports.hs diff --git a/lvtrun/README.md b/lvtrun/README.md index e641c57..66f147e 100644 --- a/lvtrun/README.md +++ b/lvtrun/README.md @@ -135,7 +135,6 @@ b6 # 01 is export type "table" 01 00 - # size 6 06 # name "start" @@ -143,7 +142,6 @@ b6 # its a type function with id 03 00 03 - # size = 16 name = __errno_location " 10 5f 5f 65 72 72 6e 6f 5f 6c 6f 63 61 74 69 6f 6e diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/app/Parsing/Exports.hs new file mode 100644 index 0000000..2fddcf8 --- /dev/null +++ b/lvtrun/app/Parsing/Exports.hs @@ -0,0 +1,68 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Exports +-} + +module Parsing.Exports + ( + getExports + ) +where + +import qualified Data.ByteString.Lazy as Bs +import Control.Exception (throw) +import Data.Int (Int64, Int32) +import Data.Word (Word8) +import Numeric (showHex) +import Data.Char (chr) + +import Leb128 +import Errors +import Types + +import Debug.Trace + +isExportValid :: Word8 -> Bool +isExportValid 0x00 = True +isExportValid 0x01 = True +isExportValid 0x02 = True +isExportValid 0x03 = True +isExportValid _ = False + +getExportNb :: Bs.ByteString -> (Int64, Bs.ByteString) +getExportNb content = extractLEB128 content + +word8ToString :: [Word8] -> String +word8ToString = map (chr . fromIntegral) + +createExport :: [Word8] -> Word8 -> FuncIdx -> Export +createExport name 0x00 idx = Export (word8ToString name) (ExportFunc idx) +createExport name 0x01 idx = Export (word8ToString name) (ExportTable idx) +createExport name 0x02 idx = Export (word8ToString name) (ExportMemory idx) +createExport name 0x03 idx = Export (word8ToString name) (ExportGlobal idx) +createExport _ _ _ = throw $ WasmError "createExport: bad export" + +parseExports :: Int32 -> Int64 -> Bs.ByteString -> [Export] +parseExports idx maxIdx content + | idx >= (fromIntegral maxIdx) = [] + | Bs.length content == 0 = [] + | otherwise = do + let (nameLen, rest) = extractLEB128 content + let (name, rest2) = Bs.splitAt (fromIntegral nameLen) rest + let exportType = head (Bs.unpack rest2) + let (exportValue, rest3) = extractLEB128 (Bs.drop 1 rest2) + let export = createExport (Bs.unpack name) exportType (fromIntegral exportValue) + export : parseExports (idx + 1) maxIdx rest3 + +printHex :: [Word8] -> String +printHex [] = [] +printHex (x:xs) = showHex x " " ++ printHex xs + +getExports :: Section -> [Export] +getExports (Section ExportID _ content) = do + let (exprtsNb, rest) = getExportNb content + trace ("content" ++ printHex (Bs.unpack content)) + parseExports 0 exprtsNb rest +getExports _ = throw $ WasmError "getExports: bad section" diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index e330b5b..3de0c37 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -17,6 +17,7 @@ import Parsing.Header import Parsing.Memory import Parsing.FuncTypes import Parsing.Global +import Parsing.Exports import Debug.Trace @@ -30,4 +31,4 @@ parseModule bytes = do [] (getMemories (getSectionWithId sections MemoryID)) (getGlobals (getSectionWithId sections GlobalID)) - [] + (getExports (getSectionWithId sections ExportID)) diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index 52e1997..61818e3 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -159,12 +159,21 @@ instance Show Global where show global = "\n\t(global " ++ (show $ globalType global) ++ " " ++ (show $ mutability global) ++ " " ++ (show $ initExpr global) ++ ")" -data ExportDesc = ExportFunc FuncIdx deriving (Show) + +data ExportDesc = + ExportFunc FuncIdx + | ExportTable TableIdx + | ExportMemory MemIdx + | ExportGlobal GlobalIdx + deriving (Show) data Export = Export { - modName :: String - -- desc -} deriving (Show) + expName :: String, + expDesc :: ExportDesc +} + +instance Show Export where + show export = "\n\t(export \"" ++ (expName export) ++ "\" " ++ (show $ expDesc export) ++ ")" data Table = Table { notImpl :: String diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index a70c8c9..00bef13 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -46,6 +46,7 @@ executable lvtrun-exe IO Leb128 Loader + Parsing.Exports Parsing.FuncTypes Parsing.Global Parsing.Header From 6fe88c3624706235df53a9c0981d530afdf0d8b1 Mon Sep 17 00:00:00 2001 From: tenshi Date: Wed, 10 Jan 2024 22:31:41 +0100 Subject: [PATCH 13/47] add getFuncTypeIdx --- lvtrun/app/Main.hs | 3 ++- lvtrun/app/Parsing/Exports.hs | 23 +++++++++----------- lvtrun/app/Parsing/Functions.hs | 37 +++++++++++++++++++++++++++++++++ lvtrun/app/Parsing/Parser.hs | 3 ++- lvtrun/app/Run/Start.hs | 21 +++++++++++++++++++ lvtrun/app/Types.hs | 7 ++++++- lvtrun/lvtrun.cabal | 2 ++ 7 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 lvtrun/app/Parsing/Functions.hs create mode 100644 lvtrun/app/Run/Start.hs diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 0409807..8fc7fc3 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -10,9 +10,10 @@ module Main (main) where import Control.Exception (try) import Errors import Loader +import Run.Start main :: IO () main = try (loadModule "./test/simple.wasm") >>= \result -> case result of Left err -> handleException err - Right wasmMod -> print wasmMod + Right wasmMod -> start wasmMod diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/app/Parsing/Exports.hs index 2fddcf8..be89d01 100644 --- a/lvtrun/app/Parsing/Exports.hs +++ b/lvtrun/app/Parsing/Exports.hs @@ -22,8 +22,6 @@ import Leb128 import Errors import Types -import Debug.Trace - isExportValid :: Word8 -> Bool isExportValid 0x00 = True isExportValid 0x01 = True @@ -46,15 +44,15 @@ createExport _ _ _ = throw $ WasmError "createExport: bad export" parseExports :: Int32 -> Int64 -> Bs.ByteString -> [Export] parseExports idx maxIdx content - | idx >= (fromIntegral maxIdx) = [] - | Bs.length content == 0 = [] - | otherwise = do - let (nameLen, rest) = extractLEB128 content - let (name, rest2) = Bs.splitAt (fromIntegral nameLen) rest - let exportType = head (Bs.unpack rest2) - let (exportValue, rest3) = extractLEB128 (Bs.drop 1 rest2) - let export = createExport (Bs.unpack name) exportType (fromIntegral exportValue) - export : parseExports (idx + 1) maxIdx rest3 + | idx >= (fromIntegral maxIdx) = [] + | Bs.length content == 0 = [] + | otherwise = do + let (nameLen, rest) = extractLEB128 content + let (name, rest2) = Bs.splitAt (fromIntegral nameLen) rest + let exportType = head (Bs.unpack rest2) + let (exportValue, rest3) = extractLEB128 (Bs.drop 1 rest2) + let export = createExport (Bs.unpack name) exportType (fromIntegral exportValue) + export : parseExports (idx + 1) maxIdx rest3 printHex :: [Word8] -> String printHex [] = [] @@ -63,6 +61,5 @@ printHex (x:xs) = showHex x " " ++ printHex xs getExports :: Section -> [Export] getExports (Section ExportID _ content) = do let (exprtsNb, rest) = getExportNb content - trace ("content" ++ printHex (Bs.unpack content)) - parseExports 0 exprtsNb rest + parseExports 0 exprtsNb rest getExports _ = throw $ WasmError "getExports: bad section" diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs new file mode 100644 index 0000000..2f2f67b --- /dev/null +++ b/lvtrun/app/Parsing/Functions.hs @@ -0,0 +1,37 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Functions +-} + +module Parsing.Functions +( + getFunctions +) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) +import Data.Int (Int64, Int32) +import Data.Word (Word8) + +import Types +import Errors +import Leb128 + +import Debug.Trace + +parseFunctionsIndex :: Int32 -> Int64 -> BSL.ByteString -> [Function] +parseFunctionsIndex idx maxIdx content + | idx >= (fromIntegral maxIdx) = [] + | BSL.length content == 0 = [] + | otherwise = do + let (typeIdx, rest) = extractLEB1282 content + Function {funcType = fromIntegral typeIdx, funcIdx = idx, body = []} : parseFunctionsIndex (idx + 1) maxIdx rest + +getFunctions :: Section -> [Function] +getFunctions (Section FunctionID _ content) = do + let (vecSize, rest) = extractLEB128 content + parseFunctionsIndex 0 vecSize rest +getFunctions _ = throw $ WasmError "getFunctions: bad section" diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index 3de0c37..63db434 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -18,6 +18,7 @@ import Parsing.Memory import Parsing.FuncTypes import Parsing.Global import Parsing.Exports +import Parsing.Functions import Debug.Trace @@ -27,7 +28,7 @@ parseModule bytes = do WasmModule (getModHeader (getSectionWithId sections CustomID)) (getFuncTypes (getSectionWithId sections TypeID)) [] - [] + (getFunctions (getSectionWithId sections FunctionID)) [] (getMemories (getSectionWithId sections MemoryID)) (getGlobals (getSectionWithId sections GlobalID)) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs new file mode 100644 index 0000000..eca7a80 --- /dev/null +++ b/lvtrun/app/Run/Start.hs @@ -0,0 +1,21 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Start +-} + +module Run.Start + ( + start + ) +where + +import Types +import Errors + +start :: WasmModule -> IO () +start wasmMod = do + putStrLn "Start" + print wasmMod + putStrLn "End" diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index 61818e3..4fe4c48 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -142,8 +142,13 @@ data ImportDesc = data Function = Function { funcType :: TypeIdx, + funcIdx :: FuncIdx, body :: [Instruction] -} deriving (Show) +} + +instance Show Function where + show func = "\n\t(func idx:" ++ (show $ funcIdx func) ++ " typeId:" ++ (show $ funcType func) ++ " " ++ + (show $ body func) ++ ")" type Memory = Limit diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 00bef13..078ab73 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -47,12 +47,14 @@ executable lvtrun-exe Leb128 Loader Parsing.Exports + Parsing.Functions Parsing.FuncTypes Parsing.Global Parsing.Header Parsing.Memory Parsing.Parser Parsing.Sections + Run.Start Types Paths_lvtrun autogen-modules: From bade394b910f2b826baa31c882571d5407a15fa4 Mon Sep 17 00:00:00 2001 From: tenshi Date: Wed, 10 Jan 2024 22:58:31 +0100 Subject: [PATCH 14/47] getStartFunc --- lvtrun/app/Run/Start.hs | 32 +++++++++++++++++++++++++++++--- lvtrun/app/Run/Types.hs | 18 ++++++++++++++++++ lvtrun/lvtrun.cabal | 1 + 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 lvtrun/app/Run/Types.hs diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index eca7a80..067446a 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -11,11 +11,37 @@ module Run.Start ) where +import Data.Int (Int32) +import Control.Exception (throw) + import Types import Errors +-------------------------------------- + +getStartFunctionId :: [Export] -> Int32 +getStartFunctionId [] = throw $ WasmError "No start function" +getStartFunctionId (x:xs) + | expName x == "_start" = + case expDesc x of + ExportFunc idx -> idx + _ -> throw $ WasmError "getStartFunctionId: bad export" + | otherwise = getStartFunctionId xs +getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" + +getFunctionFromId :: Int32 -> [Function] -> Function +getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" +getFunctionFromId id (x:xs) + | funcIdx x == id = x + | otherwise = getFunctionFromId id xs + +getStartFunction :: [Export] -> [Function] -> Function +getStartFunction exports functions = + getFunctionFromId (getStartFunctionId exports) functions + + ------------------------- + start :: WasmModule -> IO () start wasmMod = do - putStrLn "Start" - print wasmMod - putStrLn "End" + let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) + print startFunc diff --git a/lvtrun/app/Run/Types.hs b/lvtrun/app/Run/Types.hs new file mode 100644 index 0000000..3e82a9f --- /dev/null +++ b/lvtrun/app/Run/Types.hs @@ -0,0 +1,18 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Types +-} + +module Run.Types + ( + VMConfig(..), + ) +where + +import Types + +data VMConfig = VMConfig { + start :: Bool +} deriving (Show) diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 078ab73..6fcc8d2 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -55,6 +55,7 @@ executable lvtrun-exe Parsing.Parser Parsing.Sections Run.Start + Run.Types Types Paths_lvtrun autogen-modules: From 54f6db5f33572fead7a85422e157fcb817d53cda Mon Sep 17 00:00:00 2001 From: tenshi Date: Thu, 11 Jan 2024 02:55:56 +0100 Subject: [PATCH 15/47] good instruciton parsing --- lvtrun/app/Parsing/Code.hs | 211 ++++++++++++++++++++++++++++++++ lvtrun/app/Parsing/Functions.hs | 4 +- lvtrun/app/Parsing/Global.hs | 4 + lvtrun/app/Parsing/Parser.hs | 3 +- lvtrun/app/Run/Start.hs | 8 +- lvtrun/app/Types.hs | 64 +++++++++- lvtrun/lvtrun.cabal | 1 + 7 files changed, 287 insertions(+), 8 deletions(-) create mode 100644 lvtrun/app/Parsing/Code.hs diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs new file mode 100644 index 0000000..084de79 --- /dev/null +++ b/lvtrun/app/Parsing/Code.hs @@ -0,0 +1,211 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Code +-} + +module Parsing.Code + ( + getFuncCode, + ) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) +import Control.Monad (when) +import Data.Word (Word8) +import Data.Int (Int64, Int32) +import Numeric (showHex) + +import Leb128 +import Types +import Errors + +import Debug.Trace + +-- GET LOCALS + +diviseBytes :: BSL.ByteString -> [BSL.ByteString] +diviseBytes bytes + | BSL.length bytes == 0 = [] + | otherwise = do + let (size, rest) = extractLEB128 bytes + let (code, rest2) = BSL.splitAt (fromIntegral size) rest + code : diviseBytes rest2 + +createLocal :: LocalIdx -> TypeName -> Local +createLocal idx typee = Local {lcIdx = idx, lcType = typee} + +extractLocal :: Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) +extractLocal id bytes + | BSL.length bytes == 0 = throw $ WasmError "extractLocal: bad section" + | otherwise = do + let (nbOfThisType, rest) = extractLEB1282 bytes + let typee = getTypeFromByte (head (BSL.unpack (BSL.take 1 rest))) + let locals = map (\x -> createLocal (fromIntegral id) typee) [0..(fromIntegral nbOfThisType - 1)] + (locals, BSL.drop 1 rest) + +extractLocals :: Int64 -> Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) +extractLocals id idMax bytes + | id >= idMax = ([], bytes) + | BSL.length bytes == 0 = ([], bytes) + | otherwise = do + let (local, rest) = extractLocal id bytes + let (locals, rest2) = extractLocals (id + 1) idMax rest + (local ++ locals, rest2) + +------------------------- + +extractOpCode :: BSL.ByteString -> ([Word8], Int64, BSL.ByteString) +extractOpCode bytes + | (head $ BSL.unpack bytes) == 0x03 = ([0x00], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x11 = ([0x00], 1, BSL.drop 3 bytes) + + | (head $ BSL.unpack bytes) == 0x00 = ([0x00], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0b = ([0x0b], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0d = ([0x0d], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0c = ([0x0c], 1, BSL.drop 2 bytes) + | (head $ BSL.unpack bytes) == 0x02 = ([0x02], 1, BSL.drop 2 bytes) + + | (head $ BSL.unpack bytes) == 0x01 = ([0x01], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x10 = ([0x10], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x41 = ([0x41], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x42 = ([0x42], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x43 = ([0x43], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x44 = ([0x44], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x28 = ([0x28], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x29 = ([0x29], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x22 = ([0x22], 1, BSL.drop 2 bytes) + | (head $ BSL.unpack bytes) == 0x36 = ([0x36], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4b = ([0x4b], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x20 = ([0x20], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4d = ([0x4d], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x45 = ([0x45], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x71 = ([0x00], 1, BSL.drop 1 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) + | otherwise = throw $ WasmError "extractOpCode: bad instruction" + +createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) +createInstruction [0x03] bytes = (Nop, bytes) +createInstruction [0x11] bytes = (Nop, bytes) + +createInstruction [0x00] bytes = (Unreachable, bytes) +createInstruction [0x01] bytes = (Nop, bytes) +createInstruction [0x02] bytes = (Block EmptyType, bytes) +createInstruction [0x0b] bytes = (End, bytes) +createInstruction [0x0f] bytes = (Return, bytes) +createInstruction [0x4b] bytes = (I32Gtu, bytes) +createInstruction [0x6a] bytes = (I32Add, bytes) +createInstruction [0x6b] bytes = (I32Sub, bytes) +createInstruction [0x45] bytes = (I32Eqz, bytes) +createInstruction [0x4d] bytes = (I32Leu, bytes) +createInstruction [0x71] bytes = (I32And, bytes) +createInstruction [0x0d] bytes = do + let (value, rest) = extractLEB1282 bytes + (BrIf value, rest) +createInstruction [0x0c] bytes = do + let (value, rest) = extractLEB1282 bytes + (Br value, rest) +createInstruction [0x22] bytes = do + let (value, rest) = extractLEB1282 bytes + (LocalTee value, rest) +createInstruction [0x10] bytes = do + let (value, rest) = extractLEB1282 bytes + (Call value, rest) +createInstruction [0x41] bytes = do + let (value, rest) = extractLEB1282 bytes + (I32Const value, rest) +createInstruction [0x42] bytes = do + let (value, rest) = extractLEB128 bytes + (I64Const value, rest) +createInstruction [0x43] bytes = do + let (value, rest) = extractLEB128 bytes + (F32Const (fromIntegral value), rest) +createInstruction [0x44] bytes = do + let (value, rest) = extractLEB128 bytes + (F64Const (fromIntegral value), rest) +createInstruction [0x28] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I32Load (MemArg offset align), rest2) +createInstruction [0x29] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I64Load (MemArg offset align), rest2) +createInstruction [0x36] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I32Store (MemArg offset align), rest2) +createInstruction [0x37] bytes = do + let (align, rest) = extractLEB1282 bytes + let (offset, rest2) = extractLEB1282 rest + (I64Store (MemArg offset align), rest2) +createInstruction [0x20] bytes = do + let (value, rest) = extractLEB1282 bytes + (GetLocal value, rest) +createInstruction [0x24] bytes = do + let (value, rest) = extractLEB1282 bytes + (SetLocal value, rest) +createInstruction [0x23] bytes = do + let (value, rest) = extractLEB1282 bytes + (GetGlobal value, rest) +createInstruction [0x21] bytes = do + let (value, rest) = extractLEB1282 bytes + (SetGlobal value, rest) +createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) +createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) +createInstruction opCode _ = trace ("createInstruction: " ++ show opCode) throw $ WasmError "createInstruction: bad instruction" + +parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) +parseInstruction bytes + | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" + | otherwise = do + let (opCode, nbParams, rest) = extractOpCode bytes + let (instruction, rest2) = createInstruction opCode rest + (instruction, rest2) + +extractCode :: BSL.ByteString -> [Instruction] +extractCode bytes + | BSL.length bytes == 0 = [] + | otherwise = do + let (instruction, rest) = parseInstruction bytes + instruction : extractCode rest + +------------------------ + +showBytes :: BSL.ByteString -> String +showBytes bytes = do + let bytesList = BSL.unpack bytes + let hexList = map (\x -> showHex x " ") bytesList + foldl (\acc x -> acc ++ x) " " hexList + +parseFunction :: BSL.ByteString -> Function -> Function +parseFunction bytes func = do + let (nbLocalsTypes, rest) = extractLEB128 bytes + let (locals, rest2) = extractLocals 0 nbLocalsTypes rest + func {locals = locals, body = extractCode rest2} + +parseFunctions :: [BSL.ByteString] -> [Function] -> [Function] +parseFunctions [] [] = [] +parseFunctions [] _ = throw $ WasmError "parseFunctions: bad section" +parseFunctions _ [] = throw $ WasmError "parseFunctions: bad section" +parseFunctions (x:xs) (y:ys) = parseFunction x y : parseFunctions xs ys + +getFuncCode :: Section -> [Function] -> [Function] +getFuncCode (Section CodeID _ content) functions = do + let (nbFunc, rest) = extractLEB128 content + let funcCodes = diviseBytes rest + if (fromIntegral nbFunc) /= length functions + then throw $ WasmError "getFuncCode: bad section" + else parseFunctions funcCodes functions +getFuncCode _ _ = throw $ WasmError "getFuncCode: bad section" diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs index 2f2f67b..b2bf129 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/app/Parsing/Functions.hs @@ -24,7 +24,7 @@ import Debug.Trace parseFunctionsIndex :: Int32 -> Int64 -> BSL.ByteString -> [Function] parseFunctionsIndex idx maxIdx content - | idx >= (fromIntegral maxIdx) = [] + | idx > (fromIntegral maxIdx) = [] | BSL.length content == 0 = [] | otherwise = do let (typeIdx, rest) = extractLEB1282 content @@ -33,5 +33,5 @@ parseFunctionsIndex idx maxIdx content getFunctions :: Section -> [Function] getFunctions (Section FunctionID _ content) = do let (vecSize, rest) = extractLEB128 content - parseFunctionsIndex 0 vecSize rest + parseFunctionsIndex 1 vecSize rest getFunctions _ = throw $ WasmError "getFunctions: bad section" diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs index 6dc6db2..e09c09e 100644 --- a/lvtrun/app/Parsing/Global.hs +++ b/lvtrun/app/Parsing/Global.hs @@ -40,6 +40,8 @@ extractOpCode bytes | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) | otherwise = throw $ WasmError "ExtractOpCode: bad opcode" @@ -48,6 +50,8 @@ createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) createInstruction [0x01] bytes = (Unreachable, bytes) createInstruction [0x01] bytes = (Nop, bytes) createInstruction [0x0f] bytes = (Return, bytes) +createInstruction [0x6a] bytes = (I32Add, bytes) +createInstruction [0x6b] bytes = (I32Sub, bytes) createInstruction [0x10] bytes = do let (value, rest) = extractLEB1282 bytes (Call value, rest) diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index 63db434..93f548e 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -19,6 +19,7 @@ import Parsing.FuncTypes import Parsing.Global import Parsing.Exports import Parsing.Functions +import Parsing.Code import Debug.Trace @@ -28,7 +29,7 @@ parseModule bytes = do WasmModule (getModHeader (getSectionWithId sections CustomID)) (getFuncTypes (getSectionWithId sections TypeID)) [] - (getFunctions (getSectionWithId sections FunctionID)) + ((getFuncCode (getSectionWithId sections CodeID) (getFunctions (getSectionWithId sections FunctionID)))) [] (getMemories (getSectionWithId sections MemoryID)) (getGlobals (getSectionWithId sections GlobalID)) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index 067446a..a7810f6 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -2,7 +2,7 @@ -- EPITECH PROJECT, 2023 -- Leviator Run -- File description: --- Start +-- Code -} module Run.Start @@ -41,7 +41,13 @@ getStartFunction exports functions = ------------------------- +execStartFunction :: Function -> IO () +execStartFunction (Function typeIdx funcIdx locals body) = do + print "----execStartFunction---" + start :: WasmModule -> IO () start wasmMod = do let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) print startFunc + execStartFunction startFunc + return () diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index 4fe4c48..d588e68 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -36,7 +36,8 @@ module Types SectionID(..), Section(..), Memory(..), - OpCode + OpCode, + Local(..) ) where import Data.Int (Int32, Int64) @@ -96,6 +97,12 @@ instance Show MemArg where type OpCode = [Word8] +data BlockType = + EmptyType + | ValType TypeName + | TypeIdx TypeIdx + deriving (Show) + data Instruction = Unreachable | Nop @@ -113,12 +120,61 @@ data Instruction = | SetLocal LocalIdx | GetGlobal GlobalIdx | SetGlobal GlobalIdx + | I32Add + | I32Sub + | I32And + | I32Eqz + | I32Gtu + | I32Leu + | LocalTee LocalIdx + | BrIf LabelIdx + | Br LabelIdx + | Block BlockType + | End | MemorySize | MemoryGrow - deriving (Show) + +instance Show Instruction where + show Unreachable = "\n\t\t\t\tunreachable" + show Nop = "\n\t\t\t\tnop" + show Return = "\n\t\t\t\treturn" + show (Call idx) = "\n\t\t\t\tcall " ++ (show idx) + show (I32Const value) = "\n\t\t\t\ti32.const " ++ (show value) + show (I64Const value) = "\n\t\t\t\ti64.const " ++ (show value) + show (F32Const value) = "\n\t\t\t\tf32.const " ++ (show value) + show (F64Const value) = "\n\t\t\t\tf64.const " ++ (show value) + show (I32Load memArg) = "\n\t\t\t\ti32.load " ++ (show memArg) + show (I64Load memArg) = "\n\t\t\t\ti64.load " ++ (show memArg) + show (I32Store memArg) = "\n\t\t\t\ti32.store " ++ (show memArg) + show (I64Store memArg) = "\n\t\t\t\ti64.store " ++ (show memArg) + show (GetLocal idx) = "\n\t\t\t\tget_local " ++ (show idx) + show (SetLocal idx) = "\n\t\t\t\tset_local " ++ (show idx) + show (GetGlobal idx) = "\n\t\t\t\tget_global " ++ (show idx) + show (SetGlobal idx) = "\n\t\t\t\tset_global " ++ (show idx) + show I32Add = "\n\t\t\t\ti32.add" + show I32Sub = "\n\t\t\t\ti32.sub" + show MemorySize = "\n\t\t\t\tmemory.size" + show MemoryGrow = "\n\t\t\t\tmemory.grow" + show I32And = "\n\t\t\t\ti32.and" + show I32Eqz = "\n\t\t\t\ti32.eqz" + show I32Gtu = "\n\t\t\t\ti32.gt_u" + show I32Leu = "\n\t\t\t\ti32.le_u" + show (LocalTee idx) = "\n\t\t\t\tlocal.tee " ++ (show idx) + show (BrIf idx) = "\n\t\t\t\tbr_if " ++ (show idx) + show (Br idx) = "\n\t\t\t\tbr " ++ (show idx) + show End = "\n\t\t\t\tend" + show (Block blockType) = "\n\t\t\t\tblock " ++ (show blockType) -- Module section +data Local = Local { + lcIdx :: LocalIdx, + lcType :: TypeName +} + +instance Show Local where + show local = "\n\t\t(local idx:" ++ (show $ lcIdx local) ++ " type:" ++ (show $ lcType local) ++ ")" + data FuncType = FuncType { typeId :: TypeIdx, params :: [TypeName], @@ -143,12 +199,12 @@ data ImportDesc = data Function = Function { funcType :: TypeIdx, funcIdx :: FuncIdx, + locals :: [Local], body :: [Instruction] } instance Show Function where - show func = "\n\t(func idx:" ++ (show $ funcIdx func) ++ " typeId:" ++ (show $ funcType func) ++ " " ++ - (show $ body func) ++ ")" + show func = "\n\t(func idx:" ++ (show $ funcIdx func) ++ " typeId:" ++ (show $ funcType func) ++ " " ++ (show $ locals func) ++ "\nIntructions:\n" ++ (show $ body func) ++ ")" type Memory = Limit diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 6fcc8d2..3bb58bf 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -46,6 +46,7 @@ executable lvtrun-exe IO Leb128 Loader + Parsing.Code Parsing.Exports Parsing.Functions Parsing.FuncTypes From 2bb85cf891f90575b1ec5836a000cd7f4e3f508e Mon Sep 17 00:00:00 2001 From: tenshi Date: Thu, 11 Jan 2024 04:13:57 +0100 Subject: [PATCH 16/47] add execution --- lvtrun/app/Run/Start.hs | 37 ++++++++++++++++++++++++++++++------- lvtrun/app/Run/Types.hs | 32 +++++++++++++++++++++++++++++++- lvtrun/app/Types.hs | 3 ++- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index a7810f6..683905f 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -11,11 +11,14 @@ module Run.Start ) where -import Data.Int (Int32) +import Data.Int (Int32, Int64) import Control.Exception (throw) import Types import Errors +import Run.Types + +import Debug.Trace -------------------------------------- @@ -39,15 +42,35 @@ getStartFunction :: [Export] -> [Function] -> Function getStartFunction exports functions = getFunctionFromId (getStartFunctionId exports) functions - ------------------------- +----------------------------- + +executeInstruction :: VMConfig -> Instruction -> VMConfig +executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (Block _) = VMConfig stack (instructionIdx + 1) fnIdx wasmMod +executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (End) = VMConfig stack (instructionIdx + 1) fnIdx wasmMod +executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (I32Const value) = VMConfig (addStackValue stack (I_32 value)) (instructionIdx + 1) fnIdx wasmMod +executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (I32Eqz) = do + case stackDrop stack of + I_32 0 -> trace "true" (VMConfig (addStackValue stack (I_32 1)) (instructionIdx + 1) fnIdx wasmMod) + _ -> trace "false" (VMConfig (addStackValue stack (I_32 0)) (instructionIdx + 1) fnIdx wasmMod) +executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (BrIf labelIdx) + | stackDrop stack == I_32 0 = executeInstruction (VMConfig stack (instructionIdx + 1) fnIdx wasmMod) (Br labelIdx) + | otherwise = executeInstruction (VMConfig stack (instructionIdx + 1) fnIdx wasmMod) (Br labelIdx) +executeInstruction vmCOnf (Call funcIdx) = trace ("call") execFunction (vmCOnf { instructionIdx = 0 }) (getFunctionFromId funcIdx (functions (wasmModule vmCOnf))) +executeInstruction vmConf Unreachable = throw $ WasmError "Unreachable" +executeInstruction vmConf _ = vmConf + +------------------------------ -execStartFunction :: Function -> IO () -execStartFunction (Function typeIdx funcIdx locals body) = do - print "----execStartFunction---" +execFunction :: VMConfig -> Function -> VMConfig +execFunction config (Function typeIdx funcIdx locals body) = do + let vmConf = trace ("execFunction: " ++ show body) config { currentFunctionIdx = funcIdx } + let finalVMConf = foldl executeInstruction vmConf body + finalVMConf start :: WasmModule -> IO () start wasmMod = do + let configVm = VMConfig (Stack []) 0 0 wasmMod let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) - print startFunc - execStartFunction startFunc + let finalVMConf = execFunction configVm startFunc + print (vmStack finalVMConf) return () diff --git a/lvtrun/app/Run/Types.hs b/lvtrun/app/Run/Types.hs index 3e82a9f..def3d74 100644 --- a/lvtrun/app/Run/Types.hs +++ b/lvtrun/app/Run/Types.hs @@ -8,11 +8,41 @@ module Run.Types ( VMConfig(..), + Stack(..), + StackValue(..), + addStackValue, + stackPop, + stackDrop ) where +import Data.Int (Int32, Int64) +import Control.Exception (throw) + import Types +import Errors + +------------------------- stack +data StackValue = I_32 Int32 | I_64 Int64 | F_32 Float | F_64 Double | Null deriving (Show, Eq) + +data Stack = Stack [StackValue] deriving (Show) + +addStackValue :: Stack -> StackValue -> Stack +addStackValue (Stack stack) value = Stack (value : stack) + +stackPop :: Stack -> (StackValue, Stack) +stackPop (Stack []) = throw $ WasmError "stackPop: empty stack" +stackPop (Stack (x:xs)) = (x, Stack xs) + +stackDrop :: Stack -> StackValue +stackDrop (Stack []) = throw $ WasmError "stackDrop: empty stack" +stackDrop (Stack (x:xs)) = x + +------------------------- config data VMConfig = VMConfig { - start :: Bool + vmStack :: Stack, + instructionIdx :: Int32, + currentFunctionIdx :: Int32, + wasmModule :: WasmModule } deriving (Show) diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index d588e68..1b89cff 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -37,7 +37,8 @@ module Types Section(..), Memory(..), OpCode, - Local(..) + Local(..), + BlockType(..) ) where import Data.Int (Int32, Int64) From 5ee052c4538c12bc8ee191b0bf27abbd147bc337 Mon Sep 17 00:00:00 2001 From: tenshi Date: Thu, 11 Jan 2024 17:57:48 +0100 Subject: [PATCH 17/47] add work in progress --- lvtrun/app/Run/Functions.hs | 49 ++++++++++++++++++ lvtrun/app/Run/{Types.hs => Stack.hs} | 23 ++++----- lvtrun/app/Run/Start.hs | 73 ++++++++++++--------------- lvtrun/app/Run/Vm.hs | 70 +++++++++++++++++++++++++ lvtrun/lvtrun.cabal | 4 +- lvtrun/test/test.cpp | 15 +++--- 6 files changed, 170 insertions(+), 64 deletions(-) create mode 100644 lvtrun/app/Run/Functions.hs rename lvtrun/app/Run/{Types.hs => Stack.hs} (65%) create mode 100644 lvtrun/app/Run/Vm.hs diff --git a/lvtrun/app/Run/Functions.hs b/lvtrun/app/Run/Functions.hs new file mode 100644 index 0000000..19cb079 --- /dev/null +++ b/lvtrun/app/Run/Functions.hs @@ -0,0 +1,49 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Functions +-} + +module Run.Functions + ( + getStartFunctionId, + getFunctionFromId, + getStartFunction, + getFuncTypeFromId + ) +where + +import Data.Int (Int32) +import Control.Exception (throw) + +import Types +import Errors + +-------------------------------------- + +getStartFunctionId :: [Export] -> Int32 +getStartFunctionId [] = throw $ WasmError "No start function" +getStartFunctionId (x:xs) + | expName x == "_start" = + case expDesc x of + ExportFunc idx -> idx + _ -> throw $ WasmError "getStartFunctionId: bad export" + | otherwise = getStartFunctionId xs +getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" + +getFunctionFromId :: Int32 -> [Function] -> Function +getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" +getFunctionFromId id (x:xs) + | funcIdx x == id = x + | otherwise = getFunctionFromId id xs + +getStartFunction :: [Export] -> [Function] -> Function +getStartFunction exports functions = + getFunctionFromId (getStartFunctionId exports) functions + +getFuncTypeFromId :: Int32 -> [FuncType] -> FuncType +getFuncTypeFromId id [] = throw $ WasmError "getFuncTypeFromId: bad id" +getFuncTypeFromId id (x:xs) + | typeId x == id = x + | otherwise = getFuncTypeFromId id xs diff --git a/lvtrun/app/Run/Types.hs b/lvtrun/app/Run/Stack.hs similarity index 65% rename from lvtrun/app/Run/Types.hs rename to lvtrun/app/Run/Stack.hs index def3d74..a40505e 100644 --- a/lvtrun/app/Run/Types.hs +++ b/lvtrun/app/Run/Stack.hs @@ -2,12 +2,11 @@ -- EPITECH PROJECT, 2023 -- Leviator Run -- File description: --- Types +-- stack -} -module Run.Types +module Run.Stack ( - VMConfig(..), Stack(..), StackValue(..), addStackValue, @@ -22,8 +21,13 @@ import Control.Exception (throw) import Types import Errors -------------------------- stack -data StackValue = I_32 Int32 | I_64 Int64 | F_32 Float | F_64 Double | Null deriving (Show, Eq) +data StackValue = + I_32 Int32 + | I_64 Int64 + | F_32 Float + | F_64 Double + | Null + deriving (Show, Eq) data Stack = Stack [StackValue] deriving (Show) @@ -37,12 +41,3 @@ stackPop (Stack (x:xs)) = (x, Stack xs) stackDrop :: Stack -> StackValue stackDrop (Stack []) = throw $ WasmError "stackDrop: empty stack" stackDrop (Stack (x:xs)) = x - -------------------------- config - -data VMConfig = VMConfig { - vmStack :: Stack, - instructionIdx :: Int32, - currentFunctionIdx :: Int32, - wasmModule :: WasmModule -} deriving (Show) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index 683905f..a176f9b 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -16,60 +16,53 @@ import Control.Exception (throw) import Types import Errors -import Run.Types +import Run.Vm +import Run.Stack +import Run.Functions import Debug.Trace --------------------------------------- - -getStartFunctionId :: [Export] -> Int32 -getStartFunctionId [] = throw $ WasmError "No start function" -getStartFunctionId (x:xs) - | expName x == "_start" = - case expDesc x of - ExportFunc idx -> idx - _ -> throw $ WasmError "getStartFunctionId: bad export" - | otherwise = getStartFunctionId xs -getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" - -getFunctionFromId :: Int32 -> [Function] -> Function -getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" -getFunctionFromId id (x:xs) - | funcIdx x == id = x - | otherwise = getFunctionFromId id xs - -getStartFunction :: [Export] -> [Function] -> Function -getStartFunction exports functions = - getFunctionFromId (getStartFunctionId exports) functions ----------------------------- -executeInstruction :: VMConfig -> Instruction -> VMConfig -executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (Block _) = VMConfig stack (instructionIdx + 1) fnIdx wasmMod -executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (End) = VMConfig stack (instructionIdx + 1) fnIdx wasmMod -executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (I32Const value) = VMConfig (addStackValue stack (I_32 value)) (instructionIdx + 1) fnIdx wasmMod -executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (I32Eqz) = do - case stackDrop stack of - I_32 0 -> trace "true" (VMConfig (addStackValue stack (I_32 1)) (instructionIdx + 1) fnIdx wasmMod) - _ -> trace "false" (VMConfig (addStackValue stack (I_32 0)) (instructionIdx + 1) fnIdx wasmMod) -executeInstruction (VMConfig stack instructionIdx fnIdx wasmMod) (BrIf labelIdx) - | stackDrop stack == I_32 0 = executeInstruction (VMConfig stack (instructionIdx + 1) fnIdx wasmMod) (Br labelIdx) - | otherwise = executeInstruction (VMConfig stack (instructionIdx + 1) fnIdx wasmMod) (Br labelIdx) -executeInstruction vmCOnf (Call funcIdx) = trace ("call") execFunction (vmCOnf { instructionIdx = 0 }) (getFunctionFromId funcIdx (functions (wasmModule vmCOnf))) -executeInstruction vmConf Unreachable = throw $ WasmError "Unreachable" +executeInstruction :: VM -> Instruction -> VM +executeInstruction vm (Block _) = addLabelIdx vm (instructionIdx vm) +executeInstruction vm (Nop) = vm +executeInstruction vm (End) = vm +executeInstruction vm (I32Const value) = vm { vmStack = addStackValue (vmStack vm) (I_32 value) } +executeInstruction vm (I32Eqz) = case stackDrop (vmStack vm) of + I_32 0 -> vm { vmStack = addStackValue (vmStack vm) (I_32 1) } + _ -> vm { vmStack = addStackValue (vmStack vm) (I_32 0) } +executeInstruction vm (Call idx) = execFunction vm (getFunctionFromId idx (functions (wasmModule vm))) +executeInstruction vm (BrIf lIdx) = case stackDrop (vmStack vm) of + I_32 0 -> vm + _ -> executeInstruction (vm { instructionIdx = getInstructionIdxFromLabel vm lIdx }) (Nop) executeInstruction vmConf _ = vmConf ------------------------------ -execFunction :: VMConfig -> Function -> VMConfig +execFunction' :: VM -> Function -> VM +execFunction' vm (Function typeIdx funcIdx locals body) + | instructionIdx vm >= fromIntegral (length body) = vm + | otherwise = do + let instruction = body !! (fromIntegral (instructionIdx vm)) + let newVm = executeInstruction vm instruction + execFunction' (incrementInstructionIdx newVm) (Function typeIdx funcIdx locals body) + +execFunction :: VM -> Function -> VM execFunction config (Function typeIdx funcIdx locals body) = do - let vmConf = trace ("execFunction: " ++ show body) config { currentFunctionIdx = funcIdx } - let finalVMConf = foldl executeInstruction vmConf body - finalVMConf + let vmConf = trace ("execFunction: " ++ show funcIdx) config { currentFunctionIdx = funcIdx } + execFunction' vmConf (Function typeIdx funcIdx locals body) + +getTestAddFunction :: Function +getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] + +getMainTestFunction :: Function +getMainTestFunction = Function 0 1 [] [I32Const 10, I32Const 5, Call 2, End] start :: WasmModule -> IO () start wasmMod = do - let configVm = VMConfig (Stack []) 0 0 wasmMod + let configVm = VM (Stack []) 0 0 [] [] wasmMod let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) let finalVMConf = execFunction configVm startFunc print (vmStack finalVMConf) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs new file mode 100644 index 0000000..8b8f31c --- /dev/null +++ b/lvtrun/app/Run/Vm.hs @@ -0,0 +1,70 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Vm +-} + +module Run.Vm + ( + VM(..), + incrementInstructionIdx, + getInstructionIdxFromLabel, + addLabelIdx + ) +where + +import Data.Int (Int32, Int64) +import Control.Exception (throw) + +import Types +import Errors +import Run.Stack +import Run.Functions + +data VM = VM { + vmStack :: Stack, + instructionIdx :: Int32, + currentFunctionIdx :: Int32, + labels :: [Int32], + vmLocals :: [StackValue], + wasmModule :: WasmModule +} deriving (Show) + +endInstruction :: VM -> VM +endInstruction (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod + +incrementInstructionIdx :: VM -> VM +incrementInstructionIdx (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod + +getInstructionIdxFromLabel :: VM -> Int32 -> Int32 +getInstructionIdxFromLabel vm labelIdx + | labelIdx < 0 = throw $ WasmError "getInstructionIdxFromLabel: bad label" + | labelIdx >= fromIntegral (length (labels vm)) = throw $ WasmError "getInstructionIdxFromLabel: bad label" + | otherwise = (labels vm) !! (fromIntegral labelIdx) + +addLabelIdx :: VM -> Int32 -> VM +addLabelIdx vm labelIdx = vm { labels = labelIdx:(labels vm) } + +--------------------------- + +initFuncVars :: Function -> [StackValue] +initFuncVars (Function _ _ [] _) = [] +initFuncVars (Function _ _ ((Local _ I32):xs) _) = (I_32 0):(initFuncVars (Function 0 0 xs [])) +initFuncVars (Function _ _ ((Local _ I64):xs) _) = (I_64 0):(initFuncVars (Function 0 0 xs [])) +initFuncVars (Function _ _ ((Local _ F32):xs) _) = (F_32 0.0):(initFuncVars (Function 0 0 xs [])) +initFuncVars (Function _ _ ((Local _ F64):xs) _) = (F_64 0.0):(initFuncVars (Function 0 0 xs [])) +initFuncVars _ = throw $ WasmError "initFuncVars: bad local" + +initLocalVars :: Function -> FuncType -> [StackValue] +initLocalVars func funcType = (initFuncVars func) + +--------------------------- + +initLocals :: VM -> VM +initLocals vm = do + let fnTypes = types (wasmModule vm) + let currentFunc = getFunctionFromId (currentFunctionIdx vm) (functions (wasmModule vm)) + let fnType = getFuncTypeFromId (funcType currentFunc) fnTypes + let localVars = initLocalVars currentFunc fnType + vm { vmLocals = localVars } diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 3bb58bf..1c6f906 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -55,8 +55,10 @@ executable lvtrun-exe Parsing.Memory Parsing.Parser Parsing.Sections + Run.Functions + Run.Stack Run.Start - Run.Types + Run.Vm Types Paths_lvtrun autogen-modules: diff --git a/lvtrun/test/test.cpp b/lvtrun/test/test.cpp index e03428a..87eb861 100644 --- a/lvtrun/test/test.cpp +++ b/lvtrun/test/test.cpp @@ -1,13 +1,10 @@ -int globa15 = 15; - int add(int a, int b) { - int c = a + b; - return c; + int one = a; + int two = b; + return one + two; } -int main() { - int a = 10; - int b = 20; - int res = add(a, b); - return 0; +int main() +{ + return add(10, 5); } From b0e63f0645b3aaf99a32b8c5220a6a61ceec0f44 Mon Sep 17 00:00:00 2001 From: tenshi Date: Thu, 11 Jan 2024 23:00:12 +0100 Subject: [PATCH 18/47] add work in progress --- lvtrun/app/Run/Start.hs | 23 +++++++--- lvtrun/app/Run/Vm.hs | 40 +++++++++++++----- lvtrun/save/Functions.hs | 49 ++++++++++++++++++++++ lvtrun/save/Stack.hs | 43 +++++++++++++++++++ lvtrun/save/Start.hs | 82 ++++++++++++++++++++++++++++++++++++ lvtrun/save/Vm.hs | 90 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 312 insertions(+), 15 deletions(-) create mode 100644 lvtrun/save/Functions.hs create mode 100644 lvtrun/save/Stack.hs create mode 100644 lvtrun/save/Start.hs create mode 100644 lvtrun/save/Vm.hs diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index a176f9b..08f9b8b 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -37,6 +37,15 @@ executeInstruction vm (Call idx) = execFunction vm (getFunctionFromId idx (funct executeInstruction vm (BrIf lIdx) = case stackDrop (vmStack vm) of I_32 0 -> vm _ -> executeInstruction (vm { instructionIdx = getInstructionIdxFromLabel vm lIdx }) (Nop) +executeInstruction vm (I32Add) = do + let (var1, vmStack') = stackPop (vmStack vm) + let (var2, vmStack'') = stackPop vmStack' + case (var1, var2) of + (I_32 x, I_32 y) -> vm { vmStack = addStackValue vmStack'' (I_32 (x + y)) } + _ -> throw $ WasmError "I32Add: bad parameters" +executeInstruction vm (GetLocal idx) = + let local = (vmLocals vm) !! (fromIntegral idx) + in vm { vmStack = addStackValue (vmStack vm) local } executeInstruction vmConf _ = vmConf ------------------------------ @@ -46,13 +55,13 @@ execFunction' vm (Function typeIdx funcIdx locals body) | instructionIdx vm >= fromIntegral (length body) = vm | otherwise = do let instruction = body !! (fromIntegral (instructionIdx vm)) - let newVm = executeInstruction vm instruction + let newVm = trace ("Exe Instruciton: " ++ show instruction ++ " locals: " ++ show (vmLocals vm)) executeInstruction vm instruction execFunction' (incrementInstructionIdx newVm) (Function typeIdx funcIdx locals body) execFunction :: VM -> Function -> VM execFunction config (Function typeIdx funcIdx locals body) = do - let vmConf = trace ("execFunction: " ++ show funcIdx) config { currentFunctionIdx = funcIdx } - execFunction' vmConf (Function typeIdx funcIdx locals body) + let vmConf = config { currentFunctionIdx = funcIdx, instructionIdx = 0} + execFunction' (initLocals vmConf) (Function typeIdx funcIdx locals body) getTestAddFunction :: Function getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] @@ -60,10 +69,14 @@ getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLoc getMainTestFunction :: Function getMainTestFunction = Function 0 1 [] [I32Const 10, I32Const 5, Call 2, End] +getFunctionsForWasmMod :: [Function] +getFunctionsForWasmMod = [getTestAddFunction, getMainTestFunction] + start :: WasmModule -> IO () start wasmMod = do - let configVm = VM (Stack []) 0 0 [] [] wasmMod - let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) + let configVm = VM (Stack []) 0 0 [] [] (wasmMod { functions = getFunctionsForWasmMod }) + --let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) + let startFunc = getMainTestFunction let finalVMConf = execFunction configVm startFunc print (vmStack finalVMConf) return () diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index 8b8f31c..a292e77 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -10,7 +10,8 @@ module Run.Vm VM(..), incrementInstructionIdx, getInstructionIdxFromLabel, - addLabelIdx + addLabelIdx, + initLocals ) where @@ -22,12 +23,16 @@ import Errors import Run.Stack import Run.Functions +import Debug.Trace + +type VmLocals = [StackValue] + data VM = VM { vmStack :: Stack, instructionIdx :: Int32, currentFunctionIdx :: Int32, labels :: [Int32], - vmLocals :: [StackValue], + vmLocals :: VmLocals, wasmModule :: WasmModule } deriving (Show) @@ -48,16 +53,26 @@ addLabelIdx vm labelIdx = vm { labels = labelIdx:(labels vm) } --------------------------- -initFuncVars :: Function -> [StackValue] +setLocalWithParameters :: FuncType -> Stack -> VmLocals -> (VmLocals, Stack) +setLocalWithParameters _ stack [] = ([], stack) +setLocalWithParameters (FuncType id [] res) stack locals = trace ("here3") (locals, stack) +setLocalWithParameters (FuncType id (I32:xs) res) (Stack (I_32 x:xsStack)) (I_32 y:xsLocals) + = trace ("here") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_32 x:xsLocals) +setLocalWithParameters (FuncType id (I64:xs) res) (Stack (I_64 x:xsStack)) (I_64 y:xsLocals) + = trace ("here2") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_64 x:xsLocals) +setLocalWithParameters _ _ _ = throw $ WasmError "setLocalWithParameters: bad parameters" + +initFuncVars :: Function -> VmLocals initFuncVars (Function _ _ [] _) = [] initFuncVars (Function _ _ ((Local _ I32):xs) _) = (I_32 0):(initFuncVars (Function 0 0 xs [])) -initFuncVars (Function _ _ ((Local _ I64):xs) _) = (I_64 0):(initFuncVars (Function 0 0 xs [])) -initFuncVars (Function _ _ ((Local _ F32):xs) _) = (F_32 0.0):(initFuncVars (Function 0 0 xs [])) -initFuncVars (Function _ _ ((Local _ F64):xs) _) = (F_64 0.0):(initFuncVars (Function 0 0 xs [])) initFuncVars _ = throw $ WasmError "initFuncVars: bad local" -initLocalVars :: Function -> FuncType -> [StackValue] -initLocalVars func funcType = (initFuncVars func) +initLocalVars :: Function -> Stack -> FuncType -> (VmLocals, Stack) +initLocalVars func stack funcType = do + let localsDefaultInit = initFuncVars func + let nbOfLocals = length localsDefaultInit + let values = stackPopN stack nbOfLocals + --------------------------- @@ -66,5 +81,10 @@ initLocals vm = do let fnTypes = types (wasmModule vm) let currentFunc = getFunctionFromId (currentFunctionIdx vm) (functions (wasmModule vm)) let fnType = getFuncTypeFromId (funcType currentFunc) fnTypes - let localVars = initLocalVars currentFunc fnType - vm { vmLocals = localVars } + let (localVars, newStack) = initLocalVars currentFunc (vmStack vm) fnType + vm { vmLocals = localVars, vmStack = newStack } + + +-- initFuncVars (Function _ _ ((Local _ I64):xs) _) = trace ("hh2") (I_64 0):(initFuncVars (Function 0 0 xs [])) +-- initFuncVars (Function _ _ ((Local _ F32):xs) _) = trace ("hh3") (F_32 0.0):(initFuncVars (Function 0 0 xs [])) +-- initFuncVars (Function _ _ ((Local _ F64):xs) _) = trace ("hh4") (F_64 0.0):(initFuncVars (Function 0 0 xs [])) \ No newline at end of file diff --git a/lvtrun/save/Functions.hs b/lvtrun/save/Functions.hs new file mode 100644 index 0000000..19cb079 --- /dev/null +++ b/lvtrun/save/Functions.hs @@ -0,0 +1,49 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Functions +-} + +module Run.Functions + ( + getStartFunctionId, + getFunctionFromId, + getStartFunction, + getFuncTypeFromId + ) +where + +import Data.Int (Int32) +import Control.Exception (throw) + +import Types +import Errors + +-------------------------------------- + +getStartFunctionId :: [Export] -> Int32 +getStartFunctionId [] = throw $ WasmError "No start function" +getStartFunctionId (x:xs) + | expName x == "_start" = + case expDesc x of + ExportFunc idx -> idx + _ -> throw $ WasmError "getStartFunctionId: bad export" + | otherwise = getStartFunctionId xs +getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" + +getFunctionFromId :: Int32 -> [Function] -> Function +getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" +getFunctionFromId id (x:xs) + | funcIdx x == id = x + | otherwise = getFunctionFromId id xs + +getStartFunction :: [Export] -> [Function] -> Function +getStartFunction exports functions = + getFunctionFromId (getStartFunctionId exports) functions + +getFuncTypeFromId :: Int32 -> [FuncType] -> FuncType +getFuncTypeFromId id [] = throw $ WasmError "getFuncTypeFromId: bad id" +getFuncTypeFromId id (x:xs) + | typeId x == id = x + | otherwise = getFuncTypeFromId id xs diff --git a/lvtrun/save/Stack.hs b/lvtrun/save/Stack.hs new file mode 100644 index 0000000..a40505e --- /dev/null +++ b/lvtrun/save/Stack.hs @@ -0,0 +1,43 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- stack +-} + +module Run.Stack + ( + Stack(..), + StackValue(..), + addStackValue, + stackPop, + stackDrop + ) +where + +import Data.Int (Int32, Int64) +import Control.Exception (throw) + +import Types +import Errors + +data StackValue = + I_32 Int32 + | I_64 Int64 + | F_32 Float + | F_64 Double + | Null + deriving (Show, Eq) + +data Stack = Stack [StackValue] deriving (Show) + +addStackValue :: Stack -> StackValue -> Stack +addStackValue (Stack stack) value = Stack (value : stack) + +stackPop :: Stack -> (StackValue, Stack) +stackPop (Stack []) = throw $ WasmError "stackPop: empty stack" +stackPop (Stack (x:xs)) = (x, Stack xs) + +stackDrop :: Stack -> StackValue +stackDrop (Stack []) = throw $ WasmError "stackDrop: empty stack" +stackDrop (Stack (x:xs)) = x diff --git a/lvtrun/save/Start.hs b/lvtrun/save/Start.hs new file mode 100644 index 0000000..08f9b8b --- /dev/null +++ b/lvtrun/save/Start.hs @@ -0,0 +1,82 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Code +-} + +module Run.Start + ( + start + ) +where + +import Data.Int (Int32, Int64) +import Control.Exception (throw) + +import Types +import Errors +import Run.Vm +import Run.Stack +import Run.Functions + +import Debug.Trace + + +----------------------------- + +executeInstruction :: VM -> Instruction -> VM +executeInstruction vm (Block _) = addLabelIdx vm (instructionIdx vm) +executeInstruction vm (Nop) = vm +executeInstruction vm (End) = vm +executeInstruction vm (I32Const value) = vm { vmStack = addStackValue (vmStack vm) (I_32 value) } +executeInstruction vm (I32Eqz) = case stackDrop (vmStack vm) of + I_32 0 -> vm { vmStack = addStackValue (vmStack vm) (I_32 1) } + _ -> vm { vmStack = addStackValue (vmStack vm) (I_32 0) } +executeInstruction vm (Call idx) = execFunction vm (getFunctionFromId idx (functions (wasmModule vm))) +executeInstruction vm (BrIf lIdx) = case stackDrop (vmStack vm) of + I_32 0 -> vm + _ -> executeInstruction (vm { instructionIdx = getInstructionIdxFromLabel vm lIdx }) (Nop) +executeInstruction vm (I32Add) = do + let (var1, vmStack') = stackPop (vmStack vm) + let (var2, vmStack'') = stackPop vmStack' + case (var1, var2) of + (I_32 x, I_32 y) -> vm { vmStack = addStackValue vmStack'' (I_32 (x + y)) } + _ -> throw $ WasmError "I32Add: bad parameters" +executeInstruction vm (GetLocal idx) = + let local = (vmLocals vm) !! (fromIntegral idx) + in vm { vmStack = addStackValue (vmStack vm) local } +executeInstruction vmConf _ = vmConf + +------------------------------ + +execFunction' :: VM -> Function -> VM +execFunction' vm (Function typeIdx funcIdx locals body) + | instructionIdx vm >= fromIntegral (length body) = vm + | otherwise = do + let instruction = body !! (fromIntegral (instructionIdx vm)) + let newVm = trace ("Exe Instruciton: " ++ show instruction ++ " locals: " ++ show (vmLocals vm)) executeInstruction vm instruction + execFunction' (incrementInstructionIdx newVm) (Function typeIdx funcIdx locals body) + +execFunction :: VM -> Function -> VM +execFunction config (Function typeIdx funcIdx locals body) = do + let vmConf = config { currentFunctionIdx = funcIdx, instructionIdx = 0} + execFunction' (initLocals vmConf) (Function typeIdx funcIdx locals body) + +getTestAddFunction :: Function +getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] + +getMainTestFunction :: Function +getMainTestFunction = Function 0 1 [] [I32Const 10, I32Const 5, Call 2, End] + +getFunctionsForWasmMod :: [Function] +getFunctionsForWasmMod = [getTestAddFunction, getMainTestFunction] + +start :: WasmModule -> IO () +start wasmMod = do + let configVm = VM (Stack []) 0 0 [] [] (wasmMod { functions = getFunctionsForWasmMod }) + --let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) + let startFunc = getMainTestFunction + let finalVMConf = execFunction configVm startFunc + print (vmStack finalVMConf) + return () diff --git a/lvtrun/save/Vm.hs b/lvtrun/save/Vm.hs new file mode 100644 index 0000000..a292e77 --- /dev/null +++ b/lvtrun/save/Vm.hs @@ -0,0 +1,90 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Vm +-} + +module Run.Vm + ( + VM(..), + incrementInstructionIdx, + getInstructionIdxFromLabel, + addLabelIdx, + initLocals + ) +where + +import Data.Int (Int32, Int64) +import Control.Exception (throw) + +import Types +import Errors +import Run.Stack +import Run.Functions + +import Debug.Trace + +type VmLocals = [StackValue] + +data VM = VM { + vmStack :: Stack, + instructionIdx :: Int32, + currentFunctionIdx :: Int32, + labels :: [Int32], + vmLocals :: VmLocals, + wasmModule :: WasmModule +} deriving (Show) + +endInstruction :: VM -> VM +endInstruction (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod + +incrementInstructionIdx :: VM -> VM +incrementInstructionIdx (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod + +getInstructionIdxFromLabel :: VM -> Int32 -> Int32 +getInstructionIdxFromLabel vm labelIdx + | labelIdx < 0 = throw $ WasmError "getInstructionIdxFromLabel: bad label" + | labelIdx >= fromIntegral (length (labels vm)) = throw $ WasmError "getInstructionIdxFromLabel: bad label" + | otherwise = (labels vm) !! (fromIntegral labelIdx) + +addLabelIdx :: VM -> Int32 -> VM +addLabelIdx vm labelIdx = vm { labels = labelIdx:(labels vm) } + +--------------------------- + +setLocalWithParameters :: FuncType -> Stack -> VmLocals -> (VmLocals, Stack) +setLocalWithParameters _ stack [] = ([], stack) +setLocalWithParameters (FuncType id [] res) stack locals = trace ("here3") (locals, stack) +setLocalWithParameters (FuncType id (I32:xs) res) (Stack (I_32 x:xsStack)) (I_32 y:xsLocals) + = trace ("here") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_32 x:xsLocals) +setLocalWithParameters (FuncType id (I64:xs) res) (Stack (I_64 x:xsStack)) (I_64 y:xsLocals) + = trace ("here2") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_64 x:xsLocals) +setLocalWithParameters _ _ _ = throw $ WasmError "setLocalWithParameters: bad parameters" + +initFuncVars :: Function -> VmLocals +initFuncVars (Function _ _ [] _) = [] +initFuncVars (Function _ _ ((Local _ I32):xs) _) = (I_32 0):(initFuncVars (Function 0 0 xs [])) +initFuncVars _ = throw $ WasmError "initFuncVars: bad local" + +initLocalVars :: Function -> Stack -> FuncType -> (VmLocals, Stack) +initLocalVars func stack funcType = do + let localsDefaultInit = initFuncVars func + let nbOfLocals = length localsDefaultInit + let values = stackPopN stack nbOfLocals + + +--------------------------- + +initLocals :: VM -> VM +initLocals vm = do + let fnTypes = types (wasmModule vm) + let currentFunc = getFunctionFromId (currentFunctionIdx vm) (functions (wasmModule vm)) + let fnType = getFuncTypeFromId (funcType currentFunc) fnTypes + let (localVars, newStack) = initLocalVars currentFunc (vmStack vm) fnType + vm { vmLocals = localVars, vmStack = newStack } + + +-- initFuncVars (Function _ _ ((Local _ I64):xs) _) = trace ("hh2") (I_64 0):(initFuncVars (Function 0 0 xs [])) +-- initFuncVars (Function _ _ ((Local _ F32):xs) _) = trace ("hh3") (F_32 0.0):(initFuncVars (Function 0 0 xs [])) +-- initFuncVars (Function _ _ ((Local _ F64):xs) _) = trace ("hh4") (F_64 0.0):(initFuncVars (Function 0 0 xs [])) \ No newline at end of file From f6359bf14b49e630a795b138d94a43d90e93331b Mon Sep 17 00:00:00 2001 From: tenshi Date: Fri, 12 Jan 2024 19:48:36 +0100 Subject: [PATCH 19/47] add new execution version --- lvtrun/app/Run/Stack.hs | 43 ------ lvtrun/app/Run/Start.hs | 56 ++------ lvtrun/app/Run/Vm.hs | 273 +++++++++++++++++++++++++++++++-------- lvtrun/app/Types.hs | 6 +- lvtrun/lvtrun.cabal | 1 - lvtrun/save/Functions.hs | 49 ------- lvtrun/save/Stack.hs | 43 ------ lvtrun/save/Start.hs | 82 ------------ lvtrun/save/Vm.hs | 90 ------------- 9 files changed, 231 insertions(+), 412 deletions(-) delete mode 100644 lvtrun/app/Run/Stack.hs delete mode 100644 lvtrun/save/Functions.hs delete mode 100644 lvtrun/save/Stack.hs delete mode 100644 lvtrun/save/Start.hs delete mode 100644 lvtrun/save/Vm.hs diff --git a/lvtrun/app/Run/Stack.hs b/lvtrun/app/Run/Stack.hs deleted file mode 100644 index a40505e..0000000 --- a/lvtrun/app/Run/Stack.hs +++ /dev/null @@ -1,43 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- stack --} - -module Run.Stack - ( - Stack(..), - StackValue(..), - addStackValue, - stackPop, - stackDrop - ) -where - -import Data.Int (Int32, Int64) -import Control.Exception (throw) - -import Types -import Errors - -data StackValue = - I_32 Int32 - | I_64 Int64 - | F_32 Float - | F_64 Double - | Null - deriving (Show, Eq) - -data Stack = Stack [StackValue] deriving (Show) - -addStackValue :: Stack -> StackValue -> Stack -addStackValue (Stack stack) value = Stack (value : stack) - -stackPop :: Stack -> (StackValue, Stack) -stackPop (Stack []) = throw $ WasmError "stackPop: empty stack" -stackPop (Stack (x:xs)) = (x, Stack xs) - -stackDrop :: Stack -> StackValue -stackDrop (Stack []) = throw $ WasmError "stackDrop: empty stack" -stackDrop (Stack (x:xs)) = x diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index 08f9b8b..fb6daf8 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -17,66 +17,26 @@ import Control.Exception (throw) import Types import Errors import Run.Vm -import Run.Stack import Run.Functions import Debug.Trace +getTypeFunc1 :: FuncType +getTypeFunc1 = FuncType 0 [I32, I32] [I32] ------------------------------ - -executeInstruction :: VM -> Instruction -> VM -executeInstruction vm (Block _) = addLabelIdx vm (instructionIdx vm) -executeInstruction vm (Nop) = vm -executeInstruction vm (End) = vm -executeInstruction vm (I32Const value) = vm { vmStack = addStackValue (vmStack vm) (I_32 value) } -executeInstruction vm (I32Eqz) = case stackDrop (vmStack vm) of - I_32 0 -> vm { vmStack = addStackValue (vmStack vm) (I_32 1) } - _ -> vm { vmStack = addStackValue (vmStack vm) (I_32 0) } -executeInstruction vm (Call idx) = execFunction vm (getFunctionFromId idx (functions (wasmModule vm))) -executeInstruction vm (BrIf lIdx) = case stackDrop (vmStack vm) of - I_32 0 -> vm - _ -> executeInstruction (vm { instructionIdx = getInstructionIdxFromLabel vm lIdx }) (Nop) -executeInstruction vm (I32Add) = do - let (var1, vmStack') = stackPop (vmStack vm) - let (var2, vmStack'') = stackPop vmStack' - case (var1, var2) of - (I_32 x, I_32 y) -> vm { vmStack = addStackValue vmStack'' (I_32 (x + y)) } - _ -> throw $ WasmError "I32Add: bad parameters" -executeInstruction vm (GetLocal idx) = - let local = (vmLocals vm) !! (fromIntegral idx) - in vm { vmStack = addStackValue (vmStack vm) local } -executeInstruction vmConf _ = vmConf - ------------------------------- - -execFunction' :: VM -> Function -> VM -execFunction' vm (Function typeIdx funcIdx locals body) - | instructionIdx vm >= fromIntegral (length body) = vm - | otherwise = do - let instruction = body !! (fromIntegral (instructionIdx vm)) - let newVm = trace ("Exe Instruciton: " ++ show instruction ++ " locals: " ++ show (vmLocals vm)) executeInstruction vm instruction - execFunction' (incrementInstructionIdx newVm) (Function typeIdx funcIdx locals body) - -execFunction :: VM -> Function -> VM -execFunction config (Function typeIdx funcIdx locals body) = do - let vmConf = config { currentFunctionIdx = funcIdx, instructionIdx = 0} - execFunction' (initLocals vmConf) (Function typeIdx funcIdx locals body) +getTypeFunc2 :: FuncType +getTypeFunc2 = FuncType 1 [] [I32] getTestAddFunction :: Function -getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] +getTestAddFunction = Function 0 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] getMainTestFunction :: Function -getMainTestFunction = Function 0 1 [] [I32Const 10, I32Const 5, Call 2, End] +getMainTestFunction = Function 1 1 [] [I32Const 10, I32Const 5, Call 2, End] getFunctionsForWasmMod :: [Function] getFunctionsForWasmMod = [getTestAddFunction, getMainTestFunction] start :: WasmModule -> IO () start wasmMod = do - let configVm = VM (Stack []) 0 0 [] [] (wasmMod { functions = getFunctionsForWasmMod }) - --let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) - let startFunc = getMainTestFunction - let finalVMConf = execFunction configVm startFunc - print (vmStack finalVMConf) - return () + let newWasmMod = wasmMod { functions = getFunctionsForWasmMod, types = [getTypeFunc1, getTypeFunc2] } + startExecution (createVm newWasmMod) 1 diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index a292e77..c13928a 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -8,83 +8,246 @@ module Run.Vm ( VM(..), - incrementInstructionIdx, - getInstructionIdxFromLabel, - addLabelIdx, - initLocals + startExecution, + createVm ) where import Data.Int (Int32, Int64) +import Data.Word (Word8) import Control.Exception (throw) +import System.Exit import Types import Errors -import Run.Stack import Run.Functions import Debug.Trace -type VmLocals = [StackValue] +data Value = + I_32 Int32 + | I_64 Int64 + | F_32 Float + | F_64 Double + deriving (Show) + +type Stack = [Value] +type Locals = [Value] + +data CurrentExec = CurrentExec { + ceLocals :: Locals, + ceStack :: Stack, + ceInstructions :: [Instruction], + ceInstIdx :: Int, + ceLabels :: [Int], + ceParams :: [TypeName], + ceResults :: [TypeName] +} deriving (Show) + +data InstMemory = Memory { + memRange :: Limit, + memData :: [Word8] +} deriving (Show) data VM = VM { vmStack :: Stack, - instructionIdx :: Int32, - currentFunctionIdx :: Int32, - labels :: [Int32], - vmLocals :: VmLocals, + currentExec :: CurrentExec, + vmMemory :: InstMemory, wasmModule :: WasmModule -} deriving (Show) - -endInstruction :: VM -> VM -endInstruction (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod - -incrementInstructionIdx :: VM -> VM -incrementInstructionIdx (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod - -getInstructionIdxFromLabel :: VM -> Int32 -> Int32 -getInstructionIdxFromLabel vm labelIdx - | labelIdx < 0 = throw $ WasmError "getInstructionIdxFromLabel: bad label" - | labelIdx >= fromIntegral (length (labels vm)) = throw $ WasmError "getInstructionIdxFromLabel: bad label" - | otherwise = (labels vm) !! (fromIntegral labelIdx) - -addLabelIdx :: VM -> Int32 -> VM -addLabelIdx vm labelIdx = vm { labels = labelIdx:(labels vm) } +} + +instance Show VM where + show vm = "VM { vmStack = " ++ show (vmStack vm) ++ ", currentExec = " ++ show (currentExec vm) ++ " }" + +createVm :: WasmModule -> VM +createVm wasmMod = VM { + vmStack = [], + currentExec = CurrentExec { + ceLocals = [], + ceStack = [], + ceInstructions = [], + ceParams = [], + ceResults = [] + }, + vmMemory = Memory { + memRange = Limit 0 Nothing, + memData = [] + }, + wasmModule = wasmMod +} + + +---------------------------- + +getLocalFromId' :: Int32 -> LocalIdx -> Locals -> Value +getLocalFromId' _ _ [] = throw $ WasmError "getLocalFromId: bad id" +getLocalFromId' idx id (x:xs) + | idx > id = throw $ WasmError "getLocalFromId: bad id" + | idx == id = x + | otherwise = getLocalFromId' (idx + 1) id xs + +getLocalFromId :: CurrentExec -> LocalIdx -> Value +getLocalFromId cEx id = getLocalFromId' 0 id (ceLocals cEx) + +-- pushResults StackToPushTo StackToPopFrom ResultTypes +pushResults :: Stack -> Stack -> [TypeName] -> Stack +pushResults stack1 stack2 [] = stack1 +pushResults stack1 stack2 ((I32):xs) = case stackTop stack2 of + I_32 val -> pushResults (stackPush stack1 (I_32 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((I64):xs) = case stackTop stack2 of + I_64 val -> pushResults (stackPush stack1 (I_64 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((F32):xs) = case stackTop stack2 of + F_32 val -> pushResults (stackPush stack1 (F_32 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((F64):xs) = case stackTop stack2 of + F_64 val -> pushResults (stackPush stack1 (F_64 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 _ = throw $ WasmError "pushResults: bad type" + +stackPush :: Stack -> Value -> Stack +stackPush stack value = value:stack + +stackPop :: Stack -> (Value, Stack) +stackPop [] = throw $ WasmError "stackPop: empty stack" +stackPop (x:xs) = (x, xs) + +stackTop :: Stack -> Value +stackTop [] = throw $ WasmError "stackTop: empty stack" +stackTop (x:xs) = x + +stackPopN :: Stack -> Int -> ([Value], Stack) +stackPopN stack 0 = ([], stack) +stackPopN stack n + | n > 0 = do + let (value, newStack) = stackPop stack + let (values, finalStack) = stackPopN newStack (n - 1) + (value : values, finalStack) + | otherwise = error "stackPopN: bad n" --------------------------- -setLocalWithParameters :: FuncType -> Stack -> VmLocals -> (VmLocals, Stack) -setLocalWithParameters _ stack [] = ([], stack) -setLocalWithParameters (FuncType id [] res) stack locals = trace ("here3") (locals, stack) -setLocalWithParameters (FuncType id (I32:xs) res) (Stack (I_32 x:xsStack)) (I_32 y:xsLocals) - = trace ("here") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_32 x:xsLocals) -setLocalWithParameters (FuncType id (I64:xs) res) (Stack (I_64 x:xsStack)) (I_64 y:xsLocals) - = trace ("here2") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_64 x:xsLocals) -setLocalWithParameters _ _ _ = throw $ WasmError "setLocalWithParameters: bad parameters" +createEmptyLocals :: Locals -> [Local] -> Locals +createEmptyLocals newLocals [] = newLocals +createEmptyLocals newLocals ((Local _ I32):xs) = createEmptyLocals (I_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ I64):xs) = createEmptyLocals (I_64 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F32):xs) = createEmptyLocals (F_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F64):xs) = createEmptyLocals (F_64 0 : newLocals) xs + +fillLocals :: [TypeName] -> [Value] -> Locals -> Locals -> Locals +fillLocals [] [] _ acc = reverse acc +fillLocals (I32:xs) (I_32 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (I_32 val : acc) +fillLocals (I64:xs) (I_64 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (I_64 val : acc) +fillLocals (F32:xs) (F_32 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (F_32 val : acc) +fillLocals (F64:xs) (F_64 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (F_64 val : acc) +fillLocals _ _ _ _ = throw $ WasmError "fillLocals: bad type" + +initLocals :: Int -> [TypeName] -> Stack -> Locals -> (Locals, Stack) +initLocals nb types stack locals + | nb /= length types = throw $ WasmError "initLocals: bad nb" + | nb > length stack = throw $ WasmError "initLocals: bad nb" + | otherwise = do + let (values, newStack) = stackPopN stack nb + let reversedValues = reverse values + let newLocals = fillLocals types reversedValues locals [] + (newLocals, newStack) -initFuncVars :: Function -> VmLocals -initFuncVars (Function _ _ [] _) = [] -initFuncVars (Function _ _ ((Local _ I32):xs) _) = (I_32 0):(initFuncVars (Function 0 0 xs [])) -initFuncVars _ = throw $ WasmError "initFuncVars: bad local" +--------------------------- -initLocalVars :: Function -> Stack -> FuncType -> (VmLocals, Stack) -initLocalVars func stack funcType = do - let localsDefaultInit = initFuncVars func - let nbOfLocals = length localsDefaultInit - let values = stackPopN stack nbOfLocals +addLabel :: CurrentExec -> CurrentExec +addLabel cEx = cEx { ceLabels = (ceLabels cEx) ++ [ceInstIdx cEx] } +incrementInstIdx :: CurrentExec -> CurrentExec +incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } --------------------------- -initLocals :: VM -> VM -initLocals vm = do - let fnTypes = types (wasmModule vm) - let currentFunc = getFunctionFromId (currentFunctionIdx vm) (functions (wasmModule vm)) - let fnType = getFuncTypeFromId (funcType currentFunc) fnTypes - let (localVars, newStack) = initLocalVars currentFunc (vmStack vm) fnType - vm { vmLocals = localVars, vmStack = newStack } - - --- initFuncVars (Function _ _ ((Local _ I64):xs) _) = trace ("hh2") (I_64 0):(initFuncVars (Function 0 0 xs [])) --- initFuncVars (Function _ _ ((Local _ F32):xs) _) = trace ("hh3") (F_32 0.0):(initFuncVars (Function 0 0 xs [])) --- initFuncVars (Function _ _ ((Local _ F64):xs) _) = trace ("hh4") (F_64 0.0):(initFuncVars (Function 0 0 xs [])) \ No newline at end of file +execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec +execOpCode vm cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } +execOpCode vm cEx (Block _) = addLabel cEx +execOpCode vm cEx (I32Eqz) = do + let value = stackTop (ceStack cEx) + case value of + I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } + I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } + _ -> throw $ WasmError "exec I32eqz: bad type" +execOpCode vm cEx (I32Add) = do + let (value2, newStack1) = stackPop (ceStack cEx) + let (value1, newStack2) = stackPop newStack1 + case (value1, value2) of + (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } + _ -> throw $ WasmError "exec I32Add: bad type" +execOpCode vm cEx (BrIf labelIdx) = do + let (value, newStack) = stackPop (ceStack cEx) + case value of + I_32 0 -> cEx + I_32 _ -> cEx { ceStack = newStack, ceInstIdx = (fromIntegral labelIdx) } + _ -> throw $ WasmError "exec brIf: bad type" +execOpCode vm cEx (Call funcIdx) = do + let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) + let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) + cEx { ceStack = newStack } +execOpCode vm cEx (GetLocal localIdx) = do + let value = getLocalFromId cEx localIdx + cEx { ceStack = stackPush (ceStack cEx) value } +execOpCode vm cEx _ = cEx + +execOpCodes :: VM -> [Instruction] -> CurrentExec +execOpCodes vm [] = currentExec vm +execOpCodes vm instructions + | ceInstIdx cEx >= length instructions = cEx + | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" + | (instructions !! ceInstIdx cEx) == End = cEx + | otherwise = do + let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) + let newVm = vm { currentExec = (incrementInstIdx newCEx) } + execOpCodes newVm instructions + where cEx = currentExec vm + +execFunction :: VM -> VM +execFunction vm = do + let newCEx = trace ("opcodes=" ++ show (ceInstructions (currentExec vm))) execOpCodes vm (ceInstructions (currentExec vm)) + vm { currentExec = newCEx, vmStack = (pushResults (vmStack vm) (ceStack newCEx) (ceResults newCEx)) } + +execFunctionWithIdx :: VM -> FuncIdx -> Stack -> VM +execFunctionWithIdx vm funcIdx currentStack = do + let function = getFunctionFromId funcIdx (functions (wasmModule vm)) + let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) + let emptyLocals = createEmptyLocals [] (locals function) + let stack = currentStack + let (newLocals, newStack) = initLocals (length (params funcTypee)) (params funcTypee) stack emptyLocals + let cexec = CurrentExec { + ceLocals = newLocals, + ceStack = newStack, + ceInstructions = body function, + ceInstIdx = 0, + ceLabels = [], + ceParams = params funcTypee, + ceResults = results funcTypee + } + execFunction vm { currentExec = cexec } + +startExecution :: VM -> FuncIdx -> IO () +startExecution vm funcIdx = do + let function = getFunctionFromId funcIdx (functions (wasmModule vm)) + let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) + let cexec = CurrentExec { + ceLocals = [], + ceStack = [], + ceInstructions = body function, + ceInstIdx = 0, + ceLabels = [], + ceParams = params funcTypee, + ceResults = results funcTypee + } + let newVm = execFunction vm { currentExec = cexec } + let resStack = [] + let res = pushResults resStack (vmStack newVm) (ceResults (currentExec newVm)) + let exitCode = case res of + [] -> 0 + (x:xs) -> case x of + I_32 val -> val + _ -> 0 + putStrLn $ "Exit correctly with code: " ++ show exitCode + exitWith $ ExitSuccess diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index 1b89cff..3297993 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -93,6 +93,9 @@ data MemArg = MemArg { align :: Int32 } +instance Eq MemArg where + (==) memArg1 memArg2 = (offset memArg1) == (offset memArg2) && (align memArg1) == (align memArg2) + instance Show MemArg where show memArg = "[\n\toffset: " ++ (show $ offset memArg) ++ "\n\talign: " ++ (show $ align memArg) ++ "\n]" @@ -102,7 +105,7 @@ data BlockType = EmptyType | ValType TypeName | TypeIdx TypeIdx - deriving (Show) + deriving (Show, Eq) data Instruction = Unreachable @@ -134,6 +137,7 @@ data Instruction = | End | MemorySize | MemoryGrow + deriving (Eq) instance Show Instruction where show Unreachable = "\n\t\t\t\tunreachable" diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 1c6f906..27e4c2b 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -56,7 +56,6 @@ executable lvtrun-exe Parsing.Parser Parsing.Sections Run.Functions - Run.Stack Run.Start Run.Vm Types diff --git a/lvtrun/save/Functions.hs b/lvtrun/save/Functions.hs deleted file mode 100644 index 19cb079..0000000 --- a/lvtrun/save/Functions.hs +++ /dev/null @@ -1,49 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Functions --} - -module Run.Functions - ( - getStartFunctionId, - getFunctionFromId, - getStartFunction, - getFuncTypeFromId - ) -where - -import Data.Int (Int32) -import Control.Exception (throw) - -import Types -import Errors - --------------------------------------- - -getStartFunctionId :: [Export] -> Int32 -getStartFunctionId [] = throw $ WasmError "No start function" -getStartFunctionId (x:xs) - | expName x == "_start" = - case expDesc x of - ExportFunc idx -> idx - _ -> throw $ WasmError "getStartFunctionId: bad export" - | otherwise = getStartFunctionId xs -getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" - -getFunctionFromId :: Int32 -> [Function] -> Function -getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" -getFunctionFromId id (x:xs) - | funcIdx x == id = x - | otherwise = getFunctionFromId id xs - -getStartFunction :: [Export] -> [Function] -> Function -getStartFunction exports functions = - getFunctionFromId (getStartFunctionId exports) functions - -getFuncTypeFromId :: Int32 -> [FuncType] -> FuncType -getFuncTypeFromId id [] = throw $ WasmError "getFuncTypeFromId: bad id" -getFuncTypeFromId id (x:xs) - | typeId x == id = x - | otherwise = getFuncTypeFromId id xs diff --git a/lvtrun/save/Stack.hs b/lvtrun/save/Stack.hs deleted file mode 100644 index a40505e..0000000 --- a/lvtrun/save/Stack.hs +++ /dev/null @@ -1,43 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- stack --} - -module Run.Stack - ( - Stack(..), - StackValue(..), - addStackValue, - stackPop, - stackDrop - ) -where - -import Data.Int (Int32, Int64) -import Control.Exception (throw) - -import Types -import Errors - -data StackValue = - I_32 Int32 - | I_64 Int64 - | F_32 Float - | F_64 Double - | Null - deriving (Show, Eq) - -data Stack = Stack [StackValue] deriving (Show) - -addStackValue :: Stack -> StackValue -> Stack -addStackValue (Stack stack) value = Stack (value : stack) - -stackPop :: Stack -> (StackValue, Stack) -stackPop (Stack []) = throw $ WasmError "stackPop: empty stack" -stackPop (Stack (x:xs)) = (x, Stack xs) - -stackDrop :: Stack -> StackValue -stackDrop (Stack []) = throw $ WasmError "stackDrop: empty stack" -stackDrop (Stack (x:xs)) = x diff --git a/lvtrun/save/Start.hs b/lvtrun/save/Start.hs deleted file mode 100644 index 08f9b8b..0000000 --- a/lvtrun/save/Start.hs +++ /dev/null @@ -1,82 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Code --} - -module Run.Start - ( - start - ) -where - -import Data.Int (Int32, Int64) -import Control.Exception (throw) - -import Types -import Errors -import Run.Vm -import Run.Stack -import Run.Functions - -import Debug.Trace - - ------------------------------ - -executeInstruction :: VM -> Instruction -> VM -executeInstruction vm (Block _) = addLabelIdx vm (instructionIdx vm) -executeInstruction vm (Nop) = vm -executeInstruction vm (End) = vm -executeInstruction vm (I32Const value) = vm { vmStack = addStackValue (vmStack vm) (I_32 value) } -executeInstruction vm (I32Eqz) = case stackDrop (vmStack vm) of - I_32 0 -> vm { vmStack = addStackValue (vmStack vm) (I_32 1) } - _ -> vm { vmStack = addStackValue (vmStack vm) (I_32 0) } -executeInstruction vm (Call idx) = execFunction vm (getFunctionFromId idx (functions (wasmModule vm))) -executeInstruction vm (BrIf lIdx) = case stackDrop (vmStack vm) of - I_32 0 -> vm - _ -> executeInstruction (vm { instructionIdx = getInstructionIdxFromLabel vm lIdx }) (Nop) -executeInstruction vm (I32Add) = do - let (var1, vmStack') = stackPop (vmStack vm) - let (var2, vmStack'') = stackPop vmStack' - case (var1, var2) of - (I_32 x, I_32 y) -> vm { vmStack = addStackValue vmStack'' (I_32 (x + y)) } - _ -> throw $ WasmError "I32Add: bad parameters" -executeInstruction vm (GetLocal idx) = - let local = (vmLocals vm) !! (fromIntegral idx) - in vm { vmStack = addStackValue (vmStack vm) local } -executeInstruction vmConf _ = vmConf - ------------------------------- - -execFunction' :: VM -> Function -> VM -execFunction' vm (Function typeIdx funcIdx locals body) - | instructionIdx vm >= fromIntegral (length body) = vm - | otherwise = do - let instruction = body !! (fromIntegral (instructionIdx vm)) - let newVm = trace ("Exe Instruciton: " ++ show instruction ++ " locals: " ++ show (vmLocals vm)) executeInstruction vm instruction - execFunction' (incrementInstructionIdx newVm) (Function typeIdx funcIdx locals body) - -execFunction :: VM -> Function -> VM -execFunction config (Function typeIdx funcIdx locals body) = do - let vmConf = config { currentFunctionIdx = funcIdx, instructionIdx = 0} - execFunction' (initLocals vmConf) (Function typeIdx funcIdx locals body) - -getTestAddFunction :: Function -getTestAddFunction = Function 3 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] - -getMainTestFunction :: Function -getMainTestFunction = Function 0 1 [] [I32Const 10, I32Const 5, Call 2, End] - -getFunctionsForWasmMod :: [Function] -getFunctionsForWasmMod = [getTestAddFunction, getMainTestFunction] - -start :: WasmModule -> IO () -start wasmMod = do - let configVm = VM (Stack []) 0 0 [] [] (wasmMod { functions = getFunctionsForWasmMod }) - --let startFunc = getStartFunction (exports wasmMod) (functions wasmMod) - let startFunc = getMainTestFunction - let finalVMConf = execFunction configVm startFunc - print (vmStack finalVMConf) - return () diff --git a/lvtrun/save/Vm.hs b/lvtrun/save/Vm.hs deleted file mode 100644 index a292e77..0000000 --- a/lvtrun/save/Vm.hs +++ /dev/null @@ -1,90 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Vm --} - -module Run.Vm - ( - VM(..), - incrementInstructionIdx, - getInstructionIdxFromLabel, - addLabelIdx, - initLocals - ) -where - -import Data.Int (Int32, Int64) -import Control.Exception (throw) - -import Types -import Errors -import Run.Stack -import Run.Functions - -import Debug.Trace - -type VmLocals = [StackValue] - -data VM = VM { - vmStack :: Stack, - instructionIdx :: Int32, - currentFunctionIdx :: Int32, - labels :: [Int32], - vmLocals :: VmLocals, - wasmModule :: WasmModule -} deriving (Show) - -endInstruction :: VM -> VM -endInstruction (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod - -incrementInstructionIdx :: VM -> VM -incrementInstructionIdx (VM stack instructionIdx fnIdx lb lc wasmMod) = VM stack (instructionIdx + 1) fnIdx lb lc wasmMod - -getInstructionIdxFromLabel :: VM -> Int32 -> Int32 -getInstructionIdxFromLabel vm labelIdx - | labelIdx < 0 = throw $ WasmError "getInstructionIdxFromLabel: bad label" - | labelIdx >= fromIntegral (length (labels vm)) = throw $ WasmError "getInstructionIdxFromLabel: bad label" - | otherwise = (labels vm) !! (fromIntegral labelIdx) - -addLabelIdx :: VM -> Int32 -> VM -addLabelIdx vm labelIdx = vm { labels = labelIdx:(labels vm) } - ---------------------------- - -setLocalWithParameters :: FuncType -> Stack -> VmLocals -> (VmLocals, Stack) -setLocalWithParameters _ stack [] = ([], stack) -setLocalWithParameters (FuncType id [] res) stack locals = trace ("here3") (locals, stack) -setLocalWithParameters (FuncType id (I32:xs) res) (Stack (I_32 x:xsStack)) (I_32 y:xsLocals) - = trace ("here") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_32 x:xsLocals) -setLocalWithParameters (FuncType id (I64:xs) res) (Stack (I_64 x:xsStack)) (I_64 y:xsLocals) - = trace ("here2") setLocalWithParameters (FuncType id xs res) (Stack xsStack) (I_64 x:xsLocals) -setLocalWithParameters _ _ _ = throw $ WasmError "setLocalWithParameters: bad parameters" - -initFuncVars :: Function -> VmLocals -initFuncVars (Function _ _ [] _) = [] -initFuncVars (Function _ _ ((Local _ I32):xs) _) = (I_32 0):(initFuncVars (Function 0 0 xs [])) -initFuncVars _ = throw $ WasmError "initFuncVars: bad local" - -initLocalVars :: Function -> Stack -> FuncType -> (VmLocals, Stack) -initLocalVars func stack funcType = do - let localsDefaultInit = initFuncVars func - let nbOfLocals = length localsDefaultInit - let values = stackPopN stack nbOfLocals - - ---------------------------- - -initLocals :: VM -> VM -initLocals vm = do - let fnTypes = types (wasmModule vm) - let currentFunc = getFunctionFromId (currentFunctionIdx vm) (functions (wasmModule vm)) - let fnType = getFuncTypeFromId (funcType currentFunc) fnTypes - let (localVars, newStack) = initLocalVars currentFunc (vmStack vm) fnType - vm { vmLocals = localVars, vmStack = newStack } - - --- initFuncVars (Function _ _ ((Local _ I64):xs) _) = trace ("hh2") (I_64 0):(initFuncVars (Function 0 0 xs [])) --- initFuncVars (Function _ _ ((Local _ F32):xs) _) = trace ("hh3") (F_32 0.0):(initFuncVars (Function 0 0 xs [])) --- initFuncVars (Function _ _ ((Local _ F64):xs) _) = trace ("hh4") (F_64 0.0):(initFuncVars (Function 0 0 xs [])) \ No newline at end of file From 185c58ebdc36c657051ecd6a769b6d6eac5e820c Mon Sep 17 00:00:00 2001 From: tenshi Date: Sat, 13 Jan 2024 17:15:20 +0100 Subject: [PATCH 20/47] add new operations --- lvtrun/app/Main.hs | 2 +- lvtrun/app/Parsing/Code.hs | 18 +++++++++++++++++- lvtrun/app/Parsing/Global.hs | 18 +++++++++++++++++- lvtrun/app/Run/Functions.hs | 2 +- lvtrun/app/Run/Start.hs | 20 +++----------------- lvtrun/app/Run/Vm.hs | 23 +++++++++++++++++------ lvtrun/app/Types.hs | 18 ++++++++++++++++++ lvtrun/test/out.wasm | Bin 0 -> 172 bytes lvtrun/test/test.wasm | Bin 170592 -> 0 bytes 9 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 lvtrun/test/out.wasm delete mode 100644 lvtrun/test/test.wasm diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 8fc7fc3..8eeca28 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -13,7 +13,7 @@ import Loader import Run.Start main :: IO () -main = try (loadModule "./test/simple.wasm") >>= \result -> +main = try (loadModule "./test/out.wasm") >>= \result -> case result of Left err -> handleException err Right wasmMod -> start wasmMod diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs index 084de79..8f40df8 100644 --- a/lvtrun/app/Parsing/Code.hs +++ b/lvtrun/app/Parsing/Code.hs @@ -73,6 +73,8 @@ extractOpCode bytes | (head $ BSL.unpack bytes) == 0x10 = ([0x10], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x41 = ([0x41], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x42 = ([0x42], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x43 = ([0x43], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x44 = ([0x44], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x28 = ([0x28], 1, BSL.drop 1 bytes) @@ -90,10 +92,16 @@ extractOpCode bytes | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x45 = ([0x45], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x46 = ([0x46], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x71 = ([0x00], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x48 = ([0x48], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x47 = ([0x47], 1, BSL.drop 1 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) - | otherwise = throw $ WasmError "extractOpCode: bad instruction" + | otherwise = trace ("extractOpCode: " ++ showBytes bytes) throw $ WasmError "ExtractOpCode2: bad opcode" createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) createInstruction [0x03] bytes = (Nop, bytes) @@ -103,12 +111,20 @@ createInstruction [0x00] bytes = (Unreachable, bytes) createInstruction [0x01] bytes = (Nop, bytes) createInstruction [0x02] bytes = (Block EmptyType, bytes) createInstruction [0x0b] bytes = (End, bytes) +createInstruction [0x48] bytes = (I32Lts, bytes) createInstruction [0x0f] bytes = (Return, bytes) createInstruction [0x4b] bytes = (I32Gtu, bytes) createInstruction [0x6a] bytes = (I32Add, bytes) +createInstruction [0x6c] bytes = (I32Mul, bytes) +createInstruction [0x6d] bytes = (I32Divs, bytes) +createInstruction [0x47] bytes = (I32Ne, bytes) createInstruction [0x6b] bytes = (I32Sub, bytes) +createInstruction [0x4a] bytes = (I32Gts, bytes) +createInstruction [0x46] bytes = (I32Eqz, bytes) createInstruction [0x45] bytes = (I32Eqz, bytes) createInstruction [0x4d] bytes = (I32Leu, bytes) +createInstruction [0x4e] bytes = (I32Ges, bytes) +createInstruction [0x4c] bytes = (I32Les, bytes) createInstruction [0x71] bytes = (I32And, bytes) createInstruction [0x0d] bytes = do let (value, rest) = extractLEB1282 bytes diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs index e09c09e..ae6d7dd 100644 --- a/lvtrun/app/Parsing/Global.hs +++ b/lvtrun/app/Parsing/Global.hs @@ -37,14 +37,22 @@ extractOpCode bytes | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x20 = ([0x20], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x46 = ([0x46], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x48 = ([0x48], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], 1, BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x47 = ([0x47], 1, BSL.drop 1 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) - | otherwise = throw $ WasmError "ExtractOpCode: bad opcode" + | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) createInstruction [0x01] bytes = (Unreachable, bytes) @@ -52,6 +60,14 @@ createInstruction [0x01] bytes = (Nop, bytes) createInstruction [0x0f] bytes = (Return, bytes) createInstruction [0x6a] bytes = (I32Add, bytes) createInstruction [0x6b] bytes = (I32Sub, bytes) +createInstruction [0x48] bytes = (I32Lts, bytes) +createInstruction [0x6c] bytes = (I32Mul, bytes) +createInstruction [0x6d] bytes = (I32Divs, bytes) +createInstruction [0x4e] bytes = (I32Ges, bytes) +createInstruction [0x4c] bytes = (I32Les, bytes) +createInstruction [0x4a] bytes = (I32Gts, bytes) +createInstruction [0x46] bytes = (I32Eqz, bytes) +createInstruction [0x47] bytes = (I32Ne, bytes) createInstruction [0x10] bytes = do let (value, rest) = extractLEB1282 bytes (Call value, rest) diff --git a/lvtrun/app/Run/Functions.hs b/lvtrun/app/Run/Functions.hs index 19cb079..ac52546 100644 --- a/lvtrun/app/Run/Functions.hs +++ b/lvtrun/app/Run/Functions.hs @@ -25,7 +25,7 @@ import Errors getStartFunctionId :: [Export] -> Int32 getStartFunctionId [] = throw $ WasmError "No start function" getStartFunctionId (x:xs) - | expName x == "_start" = + | expName x == "start" = case expDesc x of ExportFunc idx -> idx _ -> throw $ WasmError "getStartFunctionId: bad export" diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index fb6daf8..c1e52d5 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -21,22 +21,8 @@ import Run.Functions import Debug.Trace -getTypeFunc1 :: FuncType -getTypeFunc1 = FuncType 0 [I32, I32] [I32] - -getTypeFunc2 :: FuncType -getTypeFunc2 = FuncType 1 [] [I32] - -getTestAddFunction :: Function -getTestAddFunction = Function 0 2 [Local 0 I32, Local 1 I32] [GetLocal 0, GetLocal 1, I32Add, End] - -getMainTestFunction :: Function -getMainTestFunction = Function 1 1 [] [I32Const 10, I32Const 5, Call 2, End] - -getFunctionsForWasmMod :: [Function] -getFunctionsForWasmMod = [getTestAddFunction, getMainTestFunction] - start :: WasmModule -> IO () start wasmMod = do - let newWasmMod = wasmMod { functions = getFunctionsForWasmMod, types = [getTypeFunc1, getTypeFunc2] } - startExecution (createVm newWasmMod) 1 + print wasmMod + let res = startExecution (createVm wasmMod) (getStartFunctionId (exports wasmMod)) + return () diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index c13928a..b9203bc 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -41,7 +41,8 @@ data CurrentExec = CurrentExec { ceInstIdx :: Int, ceLabels :: [Int], ceParams :: [TypeName], - ceResults :: [TypeName] + ceResults :: [TypeName], + crBlockIndents :: Int } deriving (Show) data InstMemory = Memory { @@ -165,7 +166,7 @@ incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec execOpCode vm cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } -execOpCode vm cEx (Block _) = addLabel cEx +execOpCode vm cEx (Block _) = addLabel cEx { crBlockIndents = (crBlockIndents cEx) + 1 } execOpCode vm cEx (I32Eqz) = do let value = stackTop (ceStack cEx) case value of @@ -178,6 +179,12 @@ execOpCode vm cEx (I32Add) = do case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } _ -> throw $ WasmError "exec I32Add: bad type" +execOpCode vm cEx (I32Sub) = do + let (value2, newStack1) = stackPop (ceStack cEx) + let (value1, newStack2) = stackPop newStack1 + case (value1, value2) of + (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } + _ -> throw $ WasmError "exec I32Sub: bad type" execOpCode vm cEx (BrIf labelIdx) = do let (value, newStack) = stackPop (ceStack cEx) case value of @@ -188,6 +195,8 @@ execOpCode vm cEx (Call funcIdx) = do let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } +execOpCode vm cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" execOpCode vm cEx (GetLocal localIdx) = do let value = getLocalFromId cEx localIdx cEx { ceStack = stackPush (ceStack cEx) value } @@ -198,7 +207,7 @@ execOpCodes vm [] = currentExec vm execOpCodes vm instructions | ceInstIdx cEx >= length instructions = cEx | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" - | (instructions !! ceInstIdx cEx) == End = cEx + | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx | otherwise = do let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) let newVm = vm { currentExec = (incrementInstIdx newCEx) } @@ -215,7 +224,7 @@ execFunctionWithIdx vm funcIdx currentStack = do let function = getFunctionFromId funcIdx (functions (wasmModule vm)) let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) let emptyLocals = createEmptyLocals [] (locals function) - let stack = currentStack + let stack = trace ("newFunc") currentStack let (newLocals, newStack) = initLocals (length (params funcTypee)) (params funcTypee) stack emptyLocals let cexec = CurrentExec { ceLocals = newLocals, @@ -224,7 +233,8 @@ execFunctionWithIdx vm funcIdx currentStack = do ceInstIdx = 0, ceLabels = [], ceParams = params funcTypee, - ceResults = results funcTypee + ceResults = results funcTypee, + crBlockIndents = 0 } execFunction vm { currentExec = cexec } @@ -239,7 +249,8 @@ startExecution vm funcIdx = do ceInstIdx = 0, ceLabels = [], ceParams = params funcTypee, - ceResults = results funcTypee + ceResults = results funcTypee, + crBlockIndents = 0 } let newVm = execFunction vm { currentExec = cexec } let resStack = [] diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index 3297993..c953268 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -113,6 +113,7 @@ data Instruction = | Return | Call FuncIdx | I32Const Int32 + | I64Const Int64 | F32Const Float | F64Const Double @@ -127,9 +128,17 @@ data Instruction = | I32Add | I32Sub | I32And + | I32Mul + | I32Divs | I32Eqz | I32Gtu | I32Leu + | I32Eq + | I32Lts + | I32Gts + | I32Les + | I32Ges + | I32Ne | LocalTee LocalIdx | BrIf LabelIdx | Br LabelIdx @@ -158,10 +167,18 @@ instance Show Instruction where show (SetGlobal idx) = "\n\t\t\t\tset_global " ++ (show idx) show I32Add = "\n\t\t\t\ti32.add" show I32Sub = "\n\t\t\t\ti32.sub" + show I32Mul = "\n\t\t\t\ti32.mul" + show I32Divs = "\n\t\t\t\ti32.div_s" show MemorySize = "\n\t\t\t\tmemory.size" show MemoryGrow = "\n\t\t\t\tmemory.grow" show I32And = "\n\t\t\t\ti32.and" show I32Eqz = "\n\t\t\t\ti32.eqz" + show I32Gts = "\n\t\t\t\ti32.gt_s" + show I32Les = "\n\t\t\t\ti32.le_s" + show I32Eq = "\n\t\t\t\ti32.eq" + show I32Ne = "\n\t\t\t\ti32.ne" + show I32Ges = "\n\t\t\t\ti32.ge_s" + show I32Lts = "\n\t\t\t\ti32.lt_s" show I32Gtu = "\n\t\t\t\ti32.gt_u" show I32Leu = "\n\t\t\t\ti32.le_u" show (LocalTee idx) = "\n\t\t\t\tlocal.tee " ++ (show idx) @@ -169,6 +186,7 @@ instance Show Instruction where show (Br idx) = "\n\t\t\t\tbr " ++ (show idx) show End = "\n\t\t\t\tend" show (Block blockType) = "\n\t\t\t\tblock " ++ (show blockType) + show _ = throw $ WasmError "Show Instruction: bad instruction" -- Module section diff --git a/lvtrun/test/out.wasm b/lvtrun/test/out.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0beed09cba54e336d16151986f463d1c25d5c573 GIT binary patch literal 172 zcmXxcK@Ng26a>(jl1B7n3O6W6af8Mf&(LlaHkAFpIyLfVF?pHAAeV&z*f+_kQWJj# zYIE)CeuU0D@N}jxTV6NFrF1PKB0oX?_!?%zVwemM!_)9GybbptzCH&T==8?1x*r)C FWCz&)5q$sv literal 0 HcmV?d00001 diff --git a/lvtrun/test/test.wasm b/lvtrun/test/test.wasm deleted file mode 100644 index 30ee53f9366761bfe51ce0b10de69dd2de9b12a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170592 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

nEzt=oklm`!wG|c~*AZiKVid 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 B?5qF) From d3a18ba3a7d4f667dee84828dcfac6790738e5fc Mon Sep 17 00:00:00 2001 From: tenshi Date: Sat, 13 Jan 2024 23:11:15 +0100 Subject: [PATCH 21/47] add some fixes --- lvtrun/app/Parsing/Code.hs | 4 ++-- lvtrun/app/Parsing/Exports.hs | 4 ++++ lvtrun/app/Parsing/Functions.hs | 12 ++++++------ lvtrun/app/Parsing/Global.hs | 4 ++-- lvtrun/app/Parsing/Parser.hs | 2 +- lvtrun/app/Run/Start.hs | 5 +---- lvtrun/app/Run/Vm.hs | 12 +++++++++++- lvtrun/test/out.wasm | Bin 172 -> 194 bytes 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs index 8f40df8..793d406 100644 --- a/lvtrun/app/Parsing/Code.hs +++ b/lvtrun/app/Parsing/Code.hs @@ -171,13 +171,13 @@ createInstruction [0x20] bytes = do (GetLocal value, rest) createInstruction [0x24] bytes = do let (value, rest) = extractLEB1282 bytes - (SetLocal value, rest) + (SetGlobal value, rest) createInstruction [0x23] bytes = do let (value, rest) = extractLEB1282 bytes (GetGlobal value, rest) createInstruction [0x21] bytes = do let (value, rest) = extractLEB1282 bytes - (SetGlobal value, rest) + (SetLocal value, rest) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) createInstruction opCode _ = trace ("createInstruction: " ++ show opCode) throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/app/Parsing/Exports.hs index be89d01..85890fa 100644 --- a/lvtrun/app/Parsing/Exports.hs +++ b/lvtrun/app/Parsing/Exports.hs @@ -17,6 +17,7 @@ import Data.Int (Int64, Int32) import Data.Word (Word8) import Numeric (showHex) import Data.Char (chr) +import Control.Monad (when) import Leb128 import Errors @@ -48,7 +49,10 @@ parseExports idx maxIdx content | Bs.length content == 0 = [] | otherwise = do let (nameLen, rest) = extractLEB128 content + when (nameLen == 0) (throw $ WasmError "parseExports: bad export") + when (Bs.length rest == 0) (throw $ WasmError "parseExports: bad export") let (name, rest2) = Bs.splitAt (fromIntegral nameLen) rest + when (Bs.length rest2 == 0) (throw $ WasmError "parseExports: bad export") let exportType = head (Bs.unpack rest2) let (exportValue, rest3) = extractLEB128 (Bs.drop 1 rest2) let export = createExport (Bs.unpack name) exportType (fromIntegral exportValue) diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs index b2bf129..a118865 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/app/Parsing/Functions.hs @@ -24,14 +24,14 @@ import Debug.Trace parseFunctionsIndex :: Int32 -> Int64 -> BSL.ByteString -> [Function] parseFunctionsIndex idx maxIdx content - | idx > (fromIntegral maxIdx) = [] - | BSL.length content == 0 = [] - | otherwise = do - let (typeIdx, rest) = extractLEB1282 content - Function {funcType = fromIntegral typeIdx, funcIdx = idx, body = []} : parseFunctionsIndex (idx + 1) maxIdx rest + | idx > (fromIntegral maxIdx) = [] + | BSL.length content == 0 = [] + | otherwise = do + let (typeIdx, rest) = extractLEB1282 content + Function {funcType = fromIntegral typeIdx, funcIdx = idx, body = []} : parseFunctionsIndex (idx + 1) maxIdx rest getFunctions :: Section -> [Function] getFunctions (Section FunctionID _ content) = do let (vecSize, rest) = extractLEB128 content - parseFunctionsIndex 1 vecSize rest + parseFunctionsIndex 0 vecSize rest getFunctions _ = throw $ WasmError "getFunctions: bad section" diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs index ae6d7dd..0a3fc7f 100644 --- a/lvtrun/app/Parsing/Global.hs +++ b/lvtrun/app/Parsing/Global.hs @@ -104,13 +104,13 @@ createInstruction [0x20] bytes = do (GetLocal value, rest) createInstruction [0x24] bytes = do let (value, rest) = extractLEB1282 bytes - (SetLocal value, rest) + (SetGlobal value, rest) createInstruction [0x23] bytes = do let (value, rest) = extractLEB1282 bytes (GetGlobal value, rest) createInstruction [0x21] bytes = do let (value, rest) = extractLEB1282 bytes - (SetGlobal value, rest) + (SetLocal value, rest) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) createInstruction _ _ = throw $ WasmError "CreateInstruction: bad instruction" diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index 93f548e..41021ba 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -32,5 +32,5 @@ parseModule bytes = do ((getFuncCode (getSectionWithId sections CodeID) (getFunctions (getSectionWithId sections FunctionID)))) [] (getMemories (getSectionWithId sections MemoryID)) - (getGlobals (getSectionWithId sections GlobalID)) + [] (getExports (getSectionWithId sections ExportID)) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index c1e52d5..eb29556 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -22,7 +22,4 @@ import Run.Functions import Debug.Trace start :: WasmModule -> IO () -start wasmMod = do - print wasmMod - let res = startExecution (createVm wasmMod) (getStartFunctionId (exports wasmMod)) - return () +start wasmMod = startExecution (createVm wasmMod) (getStartFunctionId (exports wasmMod)) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index b9203bc..1478781 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -90,6 +90,12 @@ getLocalFromId' idx id (x:xs) getLocalFromId :: CurrentExec -> LocalIdx -> Value getLocalFromId cEx id = getLocalFromId' 0 id (ceLocals cEx) +setLocalWithId :: Locals -> Value -> LocalIdx -> Locals +setLocalWithId [] _ _ = throw $ WasmError "setLocalWithId: bad id" +setLocalWithId (x:xs) value id + | id == 0 = value : xs + | otherwise = x : setLocalWithId xs value (id - 1) + -- pushResults StackToPushTo StackToPopFrom ResultTypes pushResults :: Stack -> Stack -> [TypeName] -> Stack pushResults stack1 stack2 [] = stack1 @@ -200,6 +206,10 @@ execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" execOpCode vm cEx (GetLocal localIdx) = do let value = getLocalFromId cEx localIdx cEx { ceStack = stackPush (ceStack cEx) value } +execOpCode vm cEx (SetLocal localIdx) = do + let (value, newStack) = stackPop (ceStack cEx) + let newLocals = setLocalWithId (ceLocals cEx) value localIdx + cEx { ceStack = newStack, ceLocals = newLocals } execOpCode vm cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec @@ -243,7 +253,7 @@ startExecution vm funcIdx = do let function = getFunctionFromId funcIdx (functions (wasmModule vm)) let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) let cexec = CurrentExec { - ceLocals = [], + ceLocals = createEmptyLocals [] (locals function), ceStack = [], ceInstructions = body function, ceInstIdx = 0, diff --git a/lvtrun/test/out.wasm b/lvtrun/test/out.wasm index 0beed09cba54e336d16151986f463d1c25d5c573..550ce5a9a0b9bbeecb4775813b197d281db70247 100644 GIT binary patch delta 112 zcmXxau?>Sj5Jb_LT~0`Mwjgu5+krDnWLF0X6yU^=Zw>B)DyR$y_~FyvxbuT~`4Rw& zOcKGEz|iO8q&p@h*e0fBoqsDz&ioJK`$Y|di_z7@w1+p=bmj7z9W^-(#y+2qbmahp C3JsS4 delta 90 zcmX@axQ5Y+A+b1@k%57MQGhvtslJ}EK7kQLFfi6L^YQ@2p@5Nzm5G6oos*HZxFoTt jgn^r@c%pf Date: Sun, 14 Jan 2024 03:26:22 +0100 Subject: [PATCH 22/47] fix locals index --- lvtrun/app/Run/Vm.hs | 111 ++++++++++++++++++++++++++++--------------- lvtrun/test/out.wasm | Bin 194 -> 194 bytes 2 files changed, 72 insertions(+), 39 deletions(-) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index 1478781..69c049c 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -90,11 +90,12 @@ getLocalFromId' idx id (x:xs) getLocalFromId :: CurrentExec -> LocalIdx -> Value getLocalFromId cEx id = getLocalFromId' 0 id (ceLocals cEx) -setLocalWithId :: Locals -> Value -> LocalIdx -> Locals -setLocalWithId [] _ _ = throw $ WasmError "setLocalWithId: bad id" -setLocalWithId (x:xs) value id - | id == 0 = value : xs - | otherwise = x : setLocalWithId xs value (id - 1) +setLocalWithId :: Int32 -> Locals -> Value -> LocalIdx -> Locals +setLocalWithId _ [] _ _ = throw $ WasmError "setLocalWithId: bad id" +setLocalWithId idx (x:xs) value id + | idx > id = throw $ WasmError "setLocalWithId: bad id" + | idx == id = value : xs + | otherwise = x : setLocalWithId (idx + 1) xs value id -- pushResults StackToPushTo StackToPopFrom ResultTypes pushResults :: Stack -> Stack -> [TypeName] -> Stack @@ -135,6 +136,39 @@ stackPopN stack n --------------------------- +initLocalsVar :: Locals -> [Local] -> Locals +initLocalsVar newLocals [] = newLocals +initLocalsVar newLocals ((Local _ I32):xs) = initLocalsVar (I_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ I64):xs) = initLocalsVar (I_64 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F32):xs) = initLocalsVar (F_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F64):xs) = initLocalsVar (F_64 0 : newLocals) xs + +createLocalsParams :: [TypeName] -> [Value] -> Locals +createLocalsParams [] [] = [] +createLocalsParams (I32:xs) (I_32 val:xs2) = (I_32 val : createLocalsParams xs xs2) +createLocalsParams (I64:xs) (I_64 val:xs2) = (I_64 val : createLocalsParams xs xs2) +createLocalsParams (F32:xs) (F_32 val:xs2) = (F_32 val : createLocalsParams xs xs2) +createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) +createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" + +initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) +initLocalsParams [] stack = ([], stack) +initLocalsParams params stack + | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" + | otherwise = do + let (values, newStack) = stackPopN stack (length params) + let reversedValues = reverse values + let newLocals = createLocalsParams params reversedValues + (newLocals, newStack) + +initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) +initLocals localVarTypes paramTypes stack = do + let (newLocals, newStack) = initLocalsParams paramTypes stack + let localsVar = initLocalsVar newLocals localVarTypes + (newLocals ++ localsVar, newStack) + +--------------------------- + createEmptyLocals :: Locals -> [Local] -> Locals createEmptyLocals newLocals [] = newLocals createEmptyLocals newLocals ((Local _ I32):xs) = createEmptyLocals (I_32 0 : newLocals) xs @@ -142,23 +176,23 @@ createEmptyLocals newLocals ((Local _ I64):xs) = createEmptyLocals (I_64 0 : new createEmptyLocals newLocals ((Local _ F32):xs) = createEmptyLocals (F_32 0 : newLocals) xs createEmptyLocals newLocals ((Local _ F64):xs) = createEmptyLocals (F_64 0 : newLocals) xs -fillLocals :: [TypeName] -> [Value] -> Locals -> Locals -> Locals -fillLocals [] [] _ acc = reverse acc -fillLocals (I32:xs) (I_32 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (I_32 val : acc) -fillLocals (I64:xs) (I_64 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (I_64 val : acc) -fillLocals (F32:xs) (F_32 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (F_32 val : acc) -fillLocals (F64:xs) (F_64 val:xs2) (_:locals) acc = fillLocals xs xs2 locals (F_64 val : acc) -fillLocals _ _ _ _ = throw $ WasmError "fillLocals: bad type" - -initLocals :: Int -> [TypeName] -> Stack -> Locals -> (Locals, Stack) -initLocals nb types stack locals - | nb /= length types = throw $ WasmError "initLocals: bad nb" - | nb > length stack = throw $ WasmError "initLocals: bad nb" - | otherwise = do - let (values, newStack) = stackPopN stack nb - let reversedValues = reverse values - let newLocals = fillLocals types reversedValues locals [] - (newLocals, newStack) +-- fillLocals :: [TypeName] -> [Value] -> Locals -> Locals +-- fillLocals [] [] locals = locals +-- fillLocals (I32:xs) (I_32 val:xs2) (_:locals) = (I_32 val : fillLocals xs xs2 locals) +-- fillLocals (I64:xs) (I_64 val:xs2) (_:locals) = (I_64 val : fillLocals xs xs2 locals) +-- fillLocals (F32:xs) (F_32 val:xs2) (_:locals) = (F_32 val : fillLocals xs xs2 locals) +-- fillLocals (F64:xs) (F_64 val:xs2) (_:locals) = (F_64 val : fillLocals xs xs2 locals) +-- fillLocals _ _ _ = throw $ WasmError "fillLocals: bad type" + +-- initLocals :: Int -> [TypeName] -> Stack -> Locals -> (Locals, Stack) +-- initLocals nb types stack locals +-- | nb /= length types = throw $ WasmError "initLocals: bad nb" +-- | nb > length stack = throw $ WasmError "initLocals: bad nb" +-- | otherwise = do +-- let (values, newStack) = stackPopN stack nb +-- let reversedValues = reverse values +-- let newLocals = fillLocals types reversedValues locals +-- (newLocals, newStack) --------------------------- @@ -171,22 +205,22 @@ incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } --------------------------- execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec -execOpCode vm cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } -execOpCode vm cEx (Block _) = addLabel cEx { crBlockIndents = (crBlockIndents cEx) + 1 } +execOpCode vm cEx (I32Const val) = trace ("--i32const--" ++ show val) cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } +execOpCode vm cEx (Block _) = trace "--block--" addLabel cEx { crBlockIndents = (crBlockIndents cEx) + 1 } execOpCode vm cEx (I32Eqz) = do - let value = stackTop (ceStack cEx) + let value = trace "--i32eqz--" stackTop (ceStack cEx) case value of I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } _ -> throw $ WasmError "exec I32eqz: bad type" execOpCode vm cEx (I32Add) = do - let (value2, newStack1) = stackPop (ceStack cEx) + let (value2, newStack1) = trace "--i32add--" stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } _ -> throw $ WasmError "exec I32Add: bad type" execOpCode vm cEx (I32Sub) = do - let (value2, newStack1) = stackPop (ceStack cEx) + let (value2, newStack1) = trace "--i32sub--" stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } @@ -198,19 +232,20 @@ execOpCode vm cEx (BrIf labelIdx) = do I_32 _ -> cEx { ceStack = newStack, ceInstIdx = (fromIntegral labelIdx) } _ -> throw $ WasmError "exec brIf: bad type" execOpCode vm cEx (Call funcIdx) = do - let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) + let newVm = trace ("--call--" ++ show funcIdx) execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } -execOpCode vm cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode vm cEx (End) = trace "--end--" cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode vm cEx (Return) = trace "--end--" cEx { crBlockIndents = (crBlockIndents cEx) - 1 } execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" execOpCode vm cEx (GetLocal localIdx) = do - let value = getLocalFromId cEx localIdx + let value = trace ("--getLocal--" ++ show localIdx) getLocalFromId cEx localIdx cEx { ceStack = stackPush (ceStack cEx) value } execOpCode vm cEx (SetLocal localIdx) = do - let (value, newStack) = stackPop (ceStack cEx) - let newLocals = setLocalWithId (ceLocals cEx) value localIdx + let (value, newStack) = trace ("--setLocal--" ++ show localIdx) stackPop (ceStack cEx) + let newLocals = setLocalWithId 0 (ceLocals cEx) value localIdx cEx { ceStack = newStack, ceLocals = newLocals } -execOpCode vm cEx _ = cEx +execOpCode vm cEx _ = trace ("not for: " ++ show (ceInstIdx cEx)) cEx execOpCodes :: VM -> [Instruction] -> CurrentExec execOpCodes vm [] = currentExec vm @@ -219,25 +254,23 @@ execOpCodes vm instructions | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx | otherwise = do - let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) + let newCEx = trace ("currentOpCode: " ++ show (instructions !! ceInstIdx cEx)) execOpCode vm cEx (instructions !! ceInstIdx cEx) let newVm = vm { currentExec = (incrementInstIdx newCEx) } execOpCodes newVm instructions where cEx = currentExec vm execFunction :: VM -> VM execFunction vm = do - let newCEx = trace ("opcodes=" ++ show (ceInstructions (currentExec vm))) execOpCodes vm (ceInstructions (currentExec vm)) + let newCEx = trace ("exex: " ++ show (ceInstructions (currentExec vm))) execOpCodes vm (ceInstructions (currentExec vm)) vm { currentExec = newCEx, vmStack = (pushResults (vmStack vm) (ceStack newCEx) (ceResults newCEx)) } execFunctionWithIdx :: VM -> FuncIdx -> Stack -> VM execFunctionWithIdx vm funcIdx currentStack = do let function = getFunctionFromId funcIdx (functions (wasmModule vm)) let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) - let emptyLocals = createEmptyLocals [] (locals function) - let stack = trace ("newFunc") currentStack - let (newLocals, newStack) = initLocals (length (params funcTypee)) (params funcTypee) stack emptyLocals + let (newLocals, newStack) = initLocals (locals function) (params funcTypee) currentStack let cexec = CurrentExec { - ceLocals = newLocals, + ceLocals = trace ("ij" ++ show newLocals) newLocals, ceStack = newStack, ceInstructions = body function, ceInstIdx = 0, diff --git a/lvtrun/test/out.wasm b/lvtrun/test/out.wasm index 550ce5a9a0b9bbeecb4775813b197d281db70247..c9736277e4fded223ed4b1c211b72bc64f6002e3 100644 GIT binary patch delta 22 dcmX@ac!+U=77r&QYjH_pQ3(S#SNBA-9so(H27Lek delta 22 dcmX@ac!+U=77r^UYjH_pQ3(S#7uQ6y9soya1^55} From cafae16cf501f7b206d3802d16ae5bed3422826a Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 03:28:06 +0100 Subject: [PATCH 23/47] remove trace --- lvtrun/app/Parsing/Code.hs | 6 ++---- lvtrun/app/Parsing/Functions.hs | 2 -- lvtrun/app/Parsing/Parser.hs | 2 -- lvtrun/app/Run/Start.hs | 2 -- lvtrun/app/Run/Vm.hs | 30 ++++++++++++++---------------- 5 files changed, 16 insertions(+), 26 deletions(-) diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs index 793d406..b91a7a7 100644 --- a/lvtrun/app/Parsing/Code.hs +++ b/lvtrun/app/Parsing/Code.hs @@ -22,8 +22,6 @@ import Leb128 import Types import Errors -import Debug.Trace - -- GET LOCALS diviseBytes :: BSL.ByteString -> [BSL.ByteString] @@ -101,7 +99,7 @@ extractOpCode bytes | (head $ BSL.unpack bytes) == 0x47 = ([0x47], 1, BSL.drop 1 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) - | otherwise = trace ("extractOpCode: " ++ showBytes bytes) throw $ WasmError "ExtractOpCode2: bad opcode" + | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) createInstruction [0x03] bytes = (Nop, bytes) @@ -180,7 +178,7 @@ createInstruction [0x21] bytes = do (SetLocal value, rest) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) -createInstruction opCode _ = trace ("createInstruction: " ++ show opCode) throw $ WasmError "createInstruction: bad instruction" +createInstruction opCode _ = throw $ WasmError "createInstruction: bad instruction" parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs index a118865..efd0429 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/app/Parsing/Functions.hs @@ -20,8 +20,6 @@ import Types import Errors import Leb128 -import Debug.Trace - parseFunctionsIndex :: Int32 -> Int64 -> BSL.ByteString -> [Function] parseFunctionsIndex idx maxIdx content | idx > (fromIntegral maxIdx) = [] diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index 41021ba..b5e067b 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -21,8 +21,6 @@ import Parsing.Exports import Parsing.Functions import Parsing.Code -import Debug.Trace - parseModule :: FileContent -> WasmModule parseModule bytes = do let sections = getSections bytes diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index eb29556..91fb24a 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -19,7 +19,5 @@ import Errors import Run.Vm import Run.Functions -import Debug.Trace - start :: WasmModule -> IO () start wasmMod = startExecution (createVm wasmMod) (getStartFunctionId (exports wasmMod)) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index 69c049c..50dcb6f 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -22,8 +22,6 @@ import Types import Errors import Run.Functions -import Debug.Trace - data Value = I_32 Int32 | I_64 Int64 @@ -205,22 +203,22 @@ incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } --------------------------- execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec -execOpCode vm cEx (I32Const val) = trace ("--i32const--" ++ show val) cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } -execOpCode vm cEx (Block _) = trace "--block--" addLabel cEx { crBlockIndents = (crBlockIndents cEx) + 1 } +execOpCode vm cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } +execOpCode vm cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } execOpCode vm cEx (I32Eqz) = do - let value = trace "--i32eqz--" stackTop (ceStack cEx) + let value = stackTop (ceStack cEx) case value of I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } _ -> throw $ WasmError "exec I32eqz: bad type" execOpCode vm cEx (I32Add) = do - let (value2, newStack1) = trace "--i32add--" stackPop (ceStack cEx) + let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } _ -> throw $ WasmError "exec I32Add: bad type" execOpCode vm cEx (I32Sub) = do - let (value2, newStack1) = trace "--i32sub--" stackPop (ceStack cEx) + let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } @@ -232,20 +230,20 @@ execOpCode vm cEx (BrIf labelIdx) = do I_32 _ -> cEx { ceStack = newStack, ceInstIdx = (fromIntegral labelIdx) } _ -> throw $ WasmError "exec brIf: bad type" execOpCode vm cEx (Call funcIdx) = do - let newVm = trace ("--call--" ++ show funcIdx) execFunctionWithIdx vm funcIdx (ceStack cEx) + let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } -execOpCode vm cEx (End) = trace "--end--" cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode vm cEx (Return) = trace "--end--" cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode vm cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode vm cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" execOpCode vm cEx (GetLocal localIdx) = do - let value = trace ("--getLocal--" ++ show localIdx) getLocalFromId cEx localIdx + let value = getLocalFromId cEx localIdx cEx { ceStack = stackPush (ceStack cEx) value } execOpCode vm cEx (SetLocal localIdx) = do - let (value, newStack) = trace ("--setLocal--" ++ show localIdx) stackPop (ceStack cEx) + let (value, newStack) = stackPop (ceStack cEx) let newLocals = setLocalWithId 0 (ceLocals cEx) value localIdx cEx { ceStack = newStack, ceLocals = newLocals } -execOpCode vm cEx _ = trace ("not for: " ++ show (ceInstIdx cEx)) cEx +execOpCode vm cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec execOpCodes vm [] = currentExec vm @@ -254,14 +252,14 @@ execOpCodes vm instructions | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx | otherwise = do - let newCEx = trace ("currentOpCode: " ++ show (instructions !! ceInstIdx cEx)) execOpCode vm cEx (instructions !! ceInstIdx cEx) + let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) let newVm = vm { currentExec = (incrementInstIdx newCEx) } execOpCodes newVm instructions where cEx = currentExec vm execFunction :: VM -> VM execFunction vm = do - let newCEx = trace ("exex: " ++ show (ceInstructions (currentExec vm))) execOpCodes vm (ceInstructions (currentExec vm)) + let newCEx = execOpCodes vm (ceInstructions (currentExec vm)) vm { currentExec = newCEx, vmStack = (pushResults (vmStack vm) (ceStack newCEx) (ceResults newCEx)) } execFunctionWithIdx :: VM -> FuncIdx -> Stack -> VM @@ -270,7 +268,7 @@ execFunctionWithIdx vm funcIdx currentStack = do let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) let (newLocals, newStack) = initLocals (locals function) (params funcTypee) currentStack let cexec = CurrentExec { - ceLocals = trace ("ij" ++ show newLocals) newLocals, + ceLocals = newLocals, ceStack = newStack, ceInstructions = body function, ceInstIdx = 0, From 00db589fc6731be1bc7b873015668041b46a4b1a Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 04:03:42 +0100 Subject: [PATCH 24/47] add better main --- lvtrun/app/Errors.hs | 15 ++-- lvtrun/app/Leb128.hs | 19 +++-- lvtrun/app/Loader.hs | 19 ++++- lvtrun/app/Main.hs | 8 +-- lvtrun/app/Run/Locals.hs | 83 ++++++++++++++++++++++ lvtrun/app/Run/Stack.hs | 60 ++++++++++++++++ lvtrun/app/Run/Start.hs | 10 +-- lvtrun/app/Run/Vm.hs | 148 +++------------------------------------ lvtrun/app/Types.hs | 10 ++- lvtrun/lvtrun.cabal | 2 + 10 files changed, 206 insertions(+), 168 deletions(-) create mode 100644 lvtrun/app/Run/Locals.hs create mode 100644 lvtrun/app/Run/Stack.hs diff --git a/lvtrun/app/Errors.hs b/lvtrun/app/Errors.hs index 4586488..b86a40a 100644 --- a/lvtrun/app/Errors.hs +++ b/lvtrun/app/Errors.hs @@ -6,18 +6,19 @@ -} module Errors - ( - CustomException(..), - handleException - ) +( + CustomException(..), + handleException +) where import Control.Exception (Exception(..), SomeException, displayException) data CustomException = - ParseError String | - WasmError String | - RuntimeError String + ParseError String + | WasmError String + | RuntimeError String + | UsageError String deriving (Show, Eq) instance Exception CustomException diff --git a/lvtrun/app/Leb128.hs b/lvtrun/app/Leb128.hs index da83419..69e4400 100644 --- a/lvtrun/app/Leb128.hs +++ b/lvtrun/app/Leb128.hs @@ -6,11 +6,11 @@ -} module Leb128 - ( - getLEB128, - extractLEB128, - extractLEB1282, - ) +( + getLEB128, + extractLEB128, + extractLEB1282, +) where import Data.Binary.Get @@ -42,9 +42,8 @@ extractLEB128' = do --function that returns the value and the rest of the bytestring extractLEB128 :: BS.ByteString -> (Int64, BS.ByteString) extractLEB128 bytes = do - let (value, size) = runGet extractLEB128' bytes - (value, BS.drop size bytes) - + let (value, size) = runGet extractLEB128' bytes + (value, BS.drop size bytes) extractLEB1282' :: Get (Int32, Int64) extractLEB1282' = do @@ -58,5 +57,5 @@ extractLEB1282' = do extractLEB1282 :: BS.ByteString -> (Int32, BS.ByteString) extractLEB1282 bytes = do - let (value, size) = runGet extractLEB1282' bytes - (value, BS.drop size bytes) + let (value, size) = runGet extractLEB1282' bytes + (value, BS.drop size bytes) diff --git a/lvtrun/app/Loader.hs b/lvtrun/app/Loader.hs index cff277a..fe25ddd 100644 --- a/lvtrun/app/Loader.hs +++ b/lvtrun/app/Loader.hs @@ -11,10 +11,23 @@ module Loader ) where +import System.Environment (getArgs) +import Control.Exception (throw) + import Parsing.Parser import Types import IO +import Errors + +getFilePath :: IO String +getFilePath = do + args <- getArgs + case args of + [path] -> return path + _ -> throw $ UsageError "Usage: ./run " -loadModule :: String -> IO WasmModule -loadModule path = getFileContent path >>= \bytes -> - return $ parseModule bytes +loadModule :: IO WasmModule +loadModule = do + filePath <- getFilePath + getFileContent filePath >>= \bytes -> + return $ parseModule bytes diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index 8eeca28..a21b372 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -13,7 +13,7 @@ import Loader import Run.Start main :: IO () -main = try (loadModule "./test/out.wasm") >>= \result -> - case result of - Left err -> handleException err - Right wasmMod -> start wasmMod +main = try (startExecution =<< loadModule) >>= \result -> + case result of + Left err -> handleException err + Right _ -> return () diff --git a/lvtrun/app/Run/Locals.hs b/lvtrun/app/Run/Locals.hs new file mode 100644 index 0000000..930f1a3 --- /dev/null +++ b/lvtrun/app/Run/Locals.hs @@ -0,0 +1,83 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Locals +-} + +module Run.Locals +( + Locals, + getLocalFromId, + setLocalWithId, + initLocals, + createEmptyLocals +) +where + +import Data.Int (Int32, Int64) +import Data.Word (Word8) +import Control.Exception (throw) + +import Types +import Errors +import Run.Stack + +type Locals = [Value] + +getLocalFromId' :: Int32 -> LocalIdx -> Locals -> Value +getLocalFromId' _ _ [] = throw $ WasmError "getLocalFromId: bad id" +getLocalFromId' idx id (x:xs) + | idx > id = throw $ WasmError "getLocalFromId: bad id" + | idx == id = x + | otherwise = getLocalFromId' (idx + 1) id xs + +getLocalFromId :: Locals -> LocalIdx -> Value +getLocalFromId locals id = getLocalFromId' 0 id locals + +setLocalWithId :: Int32 -> Locals -> Value -> LocalIdx -> Locals +setLocalWithId _ [] _ _ = throw $ WasmError "setLocalWithId: bad id" +setLocalWithId idx (x:xs) value id + | idx > id = throw $ WasmError "setLocalWithId: bad id" + | idx == id = value : xs + | otherwise = x : setLocalWithId (idx + 1) xs value id + +--------------------------- + +initLocalsVar :: Locals -> [Local] -> Locals +initLocalsVar newLocals [] = newLocals +initLocalsVar newLocals ((Local _ I32):xs) = initLocalsVar (I_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ I64):xs) = initLocalsVar (I_64 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F32):xs) = initLocalsVar (F_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F64):xs) = initLocalsVar (F_64 0 : newLocals) xs + +createLocalsParams :: [TypeName] -> [Value] -> Locals +createLocalsParams [] [] = [] +createLocalsParams (I32:xs) (I_32 val:xs2) = (I_32 val : createLocalsParams xs xs2) +createLocalsParams (I64:xs) (I_64 val:xs2) = (I_64 val : createLocalsParams xs xs2) +createLocalsParams (F32:xs) (F_32 val:xs2) = (F_32 val : createLocalsParams xs xs2) +createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) +createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" + +initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) +initLocalsParams [] stack = ([], stack) +initLocalsParams params stack + | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" + | otherwise = do + let (values, newStack) = stackPopN stack (length params) + let reversedValues = reverse values + let newLocals = createLocalsParams params reversedValues + (newLocals, newStack) + +initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) +initLocals localVarTypes paramTypes stack = do + let (newLocals, newStack) = initLocalsParams paramTypes stack + let localsVar = initLocalsVar newLocals localVarTypes + (newLocals ++ localsVar, newStack) + +createEmptyLocals :: Locals -> [Local] -> Locals +createEmptyLocals newLocals [] = newLocals +createEmptyLocals newLocals ((Local _ I32):xs) = createEmptyLocals (I_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ I64):xs) = createEmptyLocals (I_64 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F32):xs) = createEmptyLocals (F_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F64):xs) = createEmptyLocals (F_64 0 : newLocals) xs diff --git a/lvtrun/app/Run/Stack.hs b/lvtrun/app/Run/Stack.hs new file mode 100644 index 0000000..325809f --- /dev/null +++ b/lvtrun/app/Run/Stack.hs @@ -0,0 +1,60 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Stack +-} + +module Run.Stack +( + Stack, + pushResults, + stackPush, + stackPop, + stackTop, + stackPopN +) +where + +import Control.Exception (throw) + +import Types +import Errors + +type Stack = [Value] + +pushResults :: Stack -> Stack -> [TypeName] -> Stack +pushResults stack1 stack2 [] = stack1 +pushResults stack1 stack2 ((I32):xs) = case stackTop stack2 of + I_32 val -> pushResults (stackPush stack1 (I_32 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((I64):xs) = case stackTop stack2 of + I_64 val -> pushResults (stackPush stack1 (I_64 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((F32):xs) = case stackTop stack2 of + F_32 val -> pushResults (stackPush stack1 (F_32 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 ((F64):xs) = case stackTop stack2 of + F_64 val -> pushResults (stackPush stack1 (F_64 val)) (tail stack2) xs + _ -> throw $ WasmError "pushResults: bad type" +pushResults stack1 stack2 _ = throw $ WasmError "pushResults: bad type" + +stackPush :: Stack -> Value -> Stack +stackPush stack value = value:stack + +stackPop :: Stack -> (Value, Stack) +stackPop [] = throw $ WasmError "stackPop: empty stack" +stackPop (x:xs) = (x, xs) + +stackTop :: Stack -> Value +stackTop [] = throw $ WasmError "stackTop: empty stack" +stackTop (x:xs) = x + +stackPopN :: Stack -> Int -> ([Value], Stack) +stackPopN stack 0 = ([], stack) +stackPopN stack n + | n > 0 = do + let (value, newStack) = stackPop stack + let (values, finalStack) = stackPopN newStack (n - 1) + (value : values, finalStack) + | otherwise = error "stackPopN: bad n" diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index 91fb24a..353a886 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -6,9 +6,9 @@ -} module Run.Start - ( - start - ) +( + startExecution +) where import Data.Int (Int32, Int64) @@ -19,5 +19,5 @@ import Errors import Run.Vm import Run.Functions -start :: WasmModule -> IO () -start wasmMod = startExecution (createVm wasmMod) (getStartFunctionId (exports wasmMod)) +startExecution :: WasmModule -> IO () +startExecution wasmMod = startExecution2 (createVm wasmMod) (getStartFunctionId (exports wasmMod)) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index 50dcb6f..668e053 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -6,11 +6,11 @@ -} module Run.Vm - ( - VM(..), - startExecution, - createVm - ) +( + VM(..), + startExecution2, + createVm +) where import Data.Int (Int32, Int64) @@ -21,16 +21,8 @@ import System.Exit import Types import Errors import Run.Functions - -data Value = - I_32 Int32 - | I_64 Int64 - | F_32 Float - | F_64 Double - deriving (Show) - -type Stack = [Value] -type Locals = [Value] +import Run.Stack +import Run.Locals data CurrentExec = CurrentExec { ceLocals :: Locals, @@ -75,125 +67,6 @@ createVm wasmMod = VM { wasmModule = wasmMod } - ----------------------------- - -getLocalFromId' :: Int32 -> LocalIdx -> Locals -> Value -getLocalFromId' _ _ [] = throw $ WasmError "getLocalFromId: bad id" -getLocalFromId' idx id (x:xs) - | idx > id = throw $ WasmError "getLocalFromId: bad id" - | idx == id = x - | otherwise = getLocalFromId' (idx + 1) id xs - -getLocalFromId :: CurrentExec -> LocalIdx -> Value -getLocalFromId cEx id = getLocalFromId' 0 id (ceLocals cEx) - -setLocalWithId :: Int32 -> Locals -> Value -> LocalIdx -> Locals -setLocalWithId _ [] _ _ = throw $ WasmError "setLocalWithId: bad id" -setLocalWithId idx (x:xs) value id - | idx > id = throw $ WasmError "setLocalWithId: bad id" - | idx == id = value : xs - | otherwise = x : setLocalWithId (idx + 1) xs value id - --- pushResults StackToPushTo StackToPopFrom ResultTypes -pushResults :: Stack -> Stack -> [TypeName] -> Stack -pushResults stack1 stack2 [] = stack1 -pushResults stack1 stack2 ((I32):xs) = case stackTop stack2 of - I_32 val -> pushResults (stackPush stack1 (I_32 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((I64):xs) = case stackTop stack2 of - I_64 val -> pushResults (stackPush stack1 (I_64 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((F32):xs) = case stackTop stack2 of - F_32 val -> pushResults (stackPush stack1 (F_32 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((F64):xs) = case stackTop stack2 of - F_64 val -> pushResults (stackPush stack1 (F_64 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 _ = throw $ WasmError "pushResults: bad type" - -stackPush :: Stack -> Value -> Stack -stackPush stack value = value:stack - -stackPop :: Stack -> (Value, Stack) -stackPop [] = throw $ WasmError "stackPop: empty stack" -stackPop (x:xs) = (x, xs) - -stackTop :: Stack -> Value -stackTop [] = throw $ WasmError "stackTop: empty stack" -stackTop (x:xs) = x - -stackPopN :: Stack -> Int -> ([Value], Stack) -stackPopN stack 0 = ([], stack) -stackPopN stack n - | n > 0 = do - let (value, newStack) = stackPop stack - let (values, finalStack) = stackPopN newStack (n - 1) - (value : values, finalStack) - | otherwise = error "stackPopN: bad n" - ---------------------------- - -initLocalsVar :: Locals -> [Local] -> Locals -initLocalsVar newLocals [] = newLocals -initLocalsVar newLocals ((Local _ I32):xs) = initLocalsVar (I_32 0 : newLocals) xs -initLocalsVar newLocals ((Local _ I64):xs) = initLocalsVar (I_64 0 : newLocals) xs -initLocalsVar newLocals ((Local _ F32):xs) = initLocalsVar (F_32 0 : newLocals) xs -initLocalsVar newLocals ((Local _ F64):xs) = initLocalsVar (F_64 0 : newLocals) xs - -createLocalsParams :: [TypeName] -> [Value] -> Locals -createLocalsParams [] [] = [] -createLocalsParams (I32:xs) (I_32 val:xs2) = (I_32 val : createLocalsParams xs xs2) -createLocalsParams (I64:xs) (I_64 val:xs2) = (I_64 val : createLocalsParams xs xs2) -createLocalsParams (F32:xs) (F_32 val:xs2) = (F_32 val : createLocalsParams xs xs2) -createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) -createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" - -initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) -initLocalsParams [] stack = ([], stack) -initLocalsParams params stack - | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" - | otherwise = do - let (values, newStack) = stackPopN stack (length params) - let reversedValues = reverse values - let newLocals = createLocalsParams params reversedValues - (newLocals, newStack) - -initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) -initLocals localVarTypes paramTypes stack = do - let (newLocals, newStack) = initLocalsParams paramTypes stack - let localsVar = initLocalsVar newLocals localVarTypes - (newLocals ++ localsVar, newStack) - ---------------------------- - -createEmptyLocals :: Locals -> [Local] -> Locals -createEmptyLocals newLocals [] = newLocals -createEmptyLocals newLocals ((Local _ I32):xs) = createEmptyLocals (I_32 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ I64):xs) = createEmptyLocals (I_64 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ F32):xs) = createEmptyLocals (F_32 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ F64):xs) = createEmptyLocals (F_64 0 : newLocals) xs - --- fillLocals :: [TypeName] -> [Value] -> Locals -> Locals --- fillLocals [] [] locals = locals --- fillLocals (I32:xs) (I_32 val:xs2) (_:locals) = (I_32 val : fillLocals xs xs2 locals) --- fillLocals (I64:xs) (I_64 val:xs2) (_:locals) = (I_64 val : fillLocals xs xs2 locals) --- fillLocals (F32:xs) (F_32 val:xs2) (_:locals) = (F_32 val : fillLocals xs xs2 locals) --- fillLocals (F64:xs) (F_64 val:xs2) (_:locals) = (F_64 val : fillLocals xs xs2 locals) --- fillLocals _ _ _ = throw $ WasmError "fillLocals: bad type" - --- initLocals :: Int -> [TypeName] -> Stack -> Locals -> (Locals, Stack) --- initLocals nb types stack locals --- | nb /= length types = throw $ WasmError "initLocals: bad nb" --- | nb > length stack = throw $ WasmError "initLocals: bad nb" --- | otherwise = do --- let (values, newStack) = stackPopN stack nb --- let reversedValues = reverse values --- let newLocals = fillLocals types reversedValues locals --- (newLocals, newStack) - ---------------------------- - addLabel :: CurrentExec -> CurrentExec addLabel cEx = cEx { ceLabels = (ceLabels cEx) ++ [ceInstIdx cEx] } @@ -237,7 +110,7 @@ execOpCode vm cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } execOpCode vm cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" execOpCode vm cEx (GetLocal localIdx) = do - let value = getLocalFromId cEx localIdx + let value = getLocalFromId (ceLocals cEx) localIdx cEx { ceStack = stackPush (ceStack cEx) value } execOpCode vm cEx (SetLocal localIdx) = do let (value, newStack) = stackPop (ceStack cEx) @@ -279,8 +152,8 @@ execFunctionWithIdx vm funcIdx currentStack = do } execFunction vm { currentExec = cexec } -startExecution :: VM -> FuncIdx -> IO () -startExecution vm funcIdx = do +startExecution2 :: VM -> FuncIdx -> IO () +startExecution2 vm funcIdx = do let function = getFunctionFromId funcIdx (functions (wasmModule vm)) let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) let cexec = CurrentExec { @@ -302,4 +175,3 @@ startExecution vm funcIdx = do I_32 val -> val _ -> 0 putStrLn $ "Exit correctly with code: " ++ show exitCode - exitWith $ ExitSuccess diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index c953268..e82eb49 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -38,7 +38,8 @@ module Types Memory(..), OpCode, Local(..), - BlockType(..) + BlockType(..), + Value(..) ) where import Data.Int (Int32, Int64) @@ -190,6 +191,13 @@ instance Show Instruction where -- Module section +data Value = + I_32 Int32 + | I_64 Int64 + | F_32 Float + | F_64 Double + deriving (Show) + data Local = Local { lcIdx :: LocalIdx, lcType :: TypeName diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 27e4c2b..883926e 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -56,6 +56,8 @@ executable lvtrun-exe Parsing.Parser Parsing.Sections Run.Functions + Run.Locals + Run.Stack Run.Start Run.Vm Types From e330c9159c114546f1f5caf6e1f1dab59b9cdbf4 Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 04:14:34 +0100 Subject: [PATCH 25/47] rename leb128 func --- lvtrun/app/Leb128.hs | 45 ++++++++++++---------------- lvtrun/app/Parsing/Code.hs | 52 ++++++++++++++++----------------- lvtrun/app/Parsing/Exports.hs | 10 +++---- lvtrun/app/Parsing/FuncTypes.hs | 4 +-- lvtrun/app/Parsing/Functions.hs | 4 +-- lvtrun/app/Parsing/Global.hs | 36 +++++++++++------------ lvtrun/app/Parsing/Memory.hs | 10 +++---- lvtrun/app/Parsing/Sections.hs | 4 +-- 8 files changed, 78 insertions(+), 87 deletions(-) diff --git a/lvtrun/app/Leb128.hs b/lvtrun/app/Leb128.hs index 69e4400..fdbb51e 100644 --- a/lvtrun/app/Leb128.hs +++ b/lvtrun/app/Leb128.hs @@ -7,9 +7,8 @@ module Leb128 ( - getLEB128, - extractLEB128, - extractLEB1282, + getLEB128ToI64, + getLEB128ToI32, ) where @@ -18,44 +17,36 @@ import Data.Bits import Data.Int (Int64, Int32) import qualified Data.ByteString.Lazy as BS (ByteString, drop) -getLEB128 :: Get Int -getLEB128 = do - byte <- getWord8 - let value = fromIntegral (byte .&. 0x7F) - if byte `testBit` 7 - then do - next <- getLEB128 - return $ value .|. (next `shiftL` 7) - else - return value - -extractLEB128' :: Get (Int64, Int64) -extractLEB128' = do +--------------------- TO INT64 --------------------- + +getLEB128ToI64' :: Get (Int64, Int64) +getLEB128ToI64' = do byte <- getWord8 let value = fromIntegral (byte .&. 0x7F) case byte `testBit` 7 of True -> do - (next, size) <- extractLEB128' + (next, size) <- getLEB128ToI64' return (value .|. (next `shiftL` 7), size + 1) False -> return (value, 1) ---function that returns the value and the rest of the bytestring -extractLEB128 :: BS.ByteString -> (Int64, BS.ByteString) -extractLEB128 bytes = do - let (value, size) = runGet extractLEB128' bytes +getLEB128ToI64 :: BS.ByteString -> (Int64, BS.ByteString) +getLEB128ToI64 bytes = do + let (value, size) = runGet getLEB128ToI64' bytes (value, BS.drop size bytes) -extractLEB1282' :: Get (Int32, Int64) -extractLEB1282' = do +--------------------- TO INT32 --------------------- + +getLEB128ToI32' :: Get (Int32, Int64) +getLEB128ToI32' = do byte <- getWord8 let value = fromIntegral (byte .&. 0x7F) case byte `testBit` 7 of True -> do - (next, size) <- extractLEB1282' + (next, size) <- getLEB128ToI32' return (value .|. (next `shiftL` 7), size + 1) False -> return (value, 1) -extractLEB1282 :: BS.ByteString -> (Int32, BS.ByteString) -extractLEB1282 bytes = do - let (value, size) = runGet extractLEB1282' bytes +getLEB128ToI32 :: BS.ByteString -> (Int32, BS.ByteString) +getLEB128ToI32 bytes = do + let (value, size) = runGet getLEB128ToI32' bytes (value, BS.drop size bytes) diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs index b91a7a7..df63896 100644 --- a/lvtrun/app/Parsing/Code.hs +++ b/lvtrun/app/Parsing/Code.hs @@ -28,8 +28,8 @@ diviseBytes :: BSL.ByteString -> [BSL.ByteString] diviseBytes bytes | BSL.length bytes == 0 = [] | otherwise = do - let (size, rest) = extractLEB128 bytes - let (code, rest2) = BSL.splitAt (fromIntegral size) rest + let (size, rest) = getLEB128ToI64 bytes + let (code, rest2) = BSL.splitAt size rest code : diviseBytes rest2 createLocal :: LocalIdx -> TypeName -> Local @@ -39,9 +39,9 @@ extractLocal :: Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) extractLocal id bytes | BSL.length bytes == 0 = throw $ WasmError "extractLocal: bad section" | otherwise = do - let (nbOfThisType, rest) = extractLEB1282 bytes + let (nbOfThisType, rest) = getLEB128ToI64 bytes let typee = getTypeFromByte (head (BSL.unpack (BSL.take 1 rest))) - let locals = map (\x -> createLocal (fromIntegral id) typee) [0..(fromIntegral nbOfThisType - 1)] + let locals = map (\x -> createLocal (fromIntegral id) typee) [0..nbOfThisType - 1] (locals, BSL.drop 1 rest) extractLocals :: Int64 -> Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) @@ -125,56 +125,56 @@ createInstruction [0x4e] bytes = (I32Ges, bytes) createInstruction [0x4c] bytes = (I32Les, bytes) createInstruction [0x71] bytes = (I32And, bytes) createInstruction [0x0d] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (BrIf value, rest) createInstruction [0x0c] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (Br value, rest) createInstruction [0x22] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (LocalTee value, rest) createInstruction [0x10] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (Call value, rest) createInstruction [0x41] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (I32Const value, rest) createInstruction [0x42] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (I64Const value, rest) createInstruction [0x43] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (F32Const (fromIntegral value), rest) createInstruction [0x44] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (F64Const (fromIntegral value), rest) createInstruction [0x28] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I32Load (MemArg offset align), rest2) createInstruction [0x29] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I64Load (MemArg offset align), rest2) createInstruction [0x36] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I32Store (MemArg offset align), rest2) createInstruction [0x37] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I64Store (MemArg offset align), rest2) createInstruction [0x20] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (GetLocal value, rest) createInstruction [0x24] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (SetGlobal value, rest) createInstruction [0x23] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (GetGlobal value, rest) createInstruction [0x21] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (SetLocal value, rest) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) @@ -205,7 +205,7 @@ showBytes bytes = do parseFunction :: BSL.ByteString -> Function -> Function parseFunction bytes func = do - let (nbLocalsTypes, rest) = extractLEB128 bytes + let (nbLocalsTypes, rest) = getLEB128ToI64 bytes let (locals, rest2) = extractLocals 0 nbLocalsTypes rest func {locals = locals, body = extractCode rest2} @@ -217,7 +217,7 @@ parseFunctions (x:xs) (y:ys) = parseFunction x y : parseFunctions xs ys getFuncCode :: Section -> [Function] -> [Function] getFuncCode (Section CodeID _ content) functions = do - let (nbFunc, rest) = extractLEB128 content + let (nbFunc, rest) = getLEB128ToI64 content let funcCodes = diviseBytes rest if (fromIntegral nbFunc) /= length functions then throw $ WasmError "getFuncCode: bad section" diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/app/Parsing/Exports.hs index 85890fa..640aa2b 100644 --- a/lvtrun/app/Parsing/Exports.hs +++ b/lvtrun/app/Parsing/Exports.hs @@ -31,7 +31,7 @@ isExportValid 0x03 = True isExportValid _ = False getExportNb :: Bs.ByteString -> (Int64, Bs.ByteString) -getExportNb content = extractLEB128 content +getExportNb content = getLEB128ToI64 content word8ToString :: [Word8] -> String word8ToString = map (chr . fromIntegral) @@ -48,14 +48,14 @@ parseExports idx maxIdx content | idx >= (fromIntegral maxIdx) = [] | Bs.length content == 0 = [] | otherwise = do - let (nameLen, rest) = extractLEB128 content + let (nameLen, rest) = getLEB128ToI64 content when (nameLen == 0) (throw $ WasmError "parseExports: bad export") when (Bs.length rest == 0) (throw $ WasmError "parseExports: bad export") - let (name, rest2) = Bs.splitAt (fromIntegral nameLen) rest + let (name, rest2) = Bs.splitAt nameLen rest when (Bs.length rest2 == 0) (throw $ WasmError "parseExports: bad export") let exportType = head (Bs.unpack rest2) - let (exportValue, rest3) = extractLEB128 (Bs.drop 1 rest2) - let export = createExport (Bs.unpack name) exportType (fromIntegral exportValue) + let (exportValue, rest3) = getLEB128ToI32 (Bs.drop 1 rest2) + let export = createExport (Bs.unpack name) exportType exportValue export : parseExports (idx + 1) maxIdx rest3 printHex :: [Word8] -> String diff --git a/lvtrun/app/Parsing/FuncTypes.hs b/lvtrun/app/Parsing/FuncTypes.hs index a50b1e3..0ddf465 100644 --- a/lvtrun/app/Parsing/FuncTypes.hs +++ b/lvtrun/app/Parsing/FuncTypes.hs @@ -21,7 +21,7 @@ import Errors import Types getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) -getVectorSize content = extractLEB128 content +getVectorSize content = getLEB128ToI64 content extractTypes :: (Int64, Bs.ByteString) -> ([TypeName], Bs.ByteString) extractTypes (0, content) = ([], content) @@ -44,6 +44,6 @@ parseFuncTypes idx maxIdx content getFuncTypes :: Section -> [FuncType] getFuncTypes (Section TypeID _ content) = do - let (vecSize, rest) = extractLEB128 content + let (vecSize, rest) = getLEB128ToI64 content parseFuncTypes 0 vecSize rest getFuncTypes _ = throw $ WasmError "getFuncTypes: bad section" diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs index efd0429..184c130 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/app/Parsing/Functions.hs @@ -25,11 +25,11 @@ parseFunctionsIndex idx maxIdx content | idx > (fromIntegral maxIdx) = [] | BSL.length content == 0 = [] | otherwise = do - let (typeIdx, rest) = extractLEB1282 content + let (typeIdx, rest) = getLEB128ToI32 content Function {funcType = fromIntegral typeIdx, funcIdx = idx, body = []} : parseFunctionsIndex (idx + 1) maxIdx rest getFunctions :: Section -> [Function] getFunctions (Section FunctionID _ content) = do - let (vecSize, rest) = extractLEB128 content + let (vecSize, rest) = getLEB128ToI64 content parseFunctionsIndex 0 vecSize rest getFunctions _ = throw $ WasmError "getFunctions: bad section" diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs index 0a3fc7f..b28b820 100644 --- a/lvtrun/app/Parsing/Global.hs +++ b/lvtrun/app/Parsing/Global.hs @@ -69,47 +69,47 @@ createInstruction [0x4a] bytes = (I32Gts, bytes) createInstruction [0x46] bytes = (I32Eqz, bytes) createInstruction [0x47] bytes = (I32Ne, bytes) createInstruction [0x10] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (Call value, rest) createInstruction [0x41] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (I32Const value, rest) createInstruction [0x42] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (I64Const value, rest) createInstruction [0x43] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (F32Const (fromIntegral value), rest) createInstruction [0x44] bytes = do - let (value, rest) = extractLEB128 bytes + let (value, rest) = getLEB128ToI64 bytes (F64Const (fromIntegral value), rest) createInstruction [0x28] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I32Load (MemArg align offset), rest2) createInstruction [0x29] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I64Load (MemArg align offset), rest2) createInstruction [0x36] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I32Store (MemArg align offset), rest2) createInstruction [0x37] bytes = do - let (align, rest) = extractLEB1282 bytes - let (offset, rest2) = extractLEB1282 rest + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest (I64Store (MemArg align offset), rest2) createInstruction [0x20] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (GetLocal value, rest) createInstruction [0x24] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (SetGlobal value, rest) createInstruction [0x23] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (GetGlobal value, rest) createInstruction [0x21] bytes = do - let (value, rest) = extractLEB1282 bytes + let (value, rest) = getLEB128ToI32 bytes (SetLocal value, rest) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) @@ -166,6 +166,6 @@ parseGlobals idx maxIdx content getGlobals :: Section -> [Global] getGlobals (Section GlobalID _ content) = do - let (vecSize, rest) = extractLEB128 content + let (vecSize, rest) = getLEB128ToI64 content parseGlobals 0 vecSize rest getGlobals _ = throw $ WasmError "getGlobals: bad section" diff --git a/lvtrun/app/Parsing/Memory.hs b/lvtrun/app/Parsing/Memory.hs index e567f10..5a42cc6 100644 --- a/lvtrun/app/Parsing/Memory.hs +++ b/lvtrun/app/Parsing/Memory.hs @@ -21,17 +21,17 @@ import Errors parseMinMax :: BS.ByteString -> Memory parseMinMax content | endBs /= BS.empty = throw $ WasmError "parseMinMax: bad memory section" - | otherwise = Limit {lMin = fromIntegral min, lMax = Just (fromIntegral max)} + | otherwise = Limit {lMin = min, lMax = Just max} where - (min, rest) = extractLEB128 content - (max, endBs) = extractLEB128 rest + (min, rest) = getLEB128ToI32 content + (max, endBs) = getLEB128ToI32 rest parseMin :: BS.ByteString -> Memory parseMin content | endBs /= BS.empty = throw $ WasmError "parseMin: bad memory section" - | otherwise = Limit {lMin = fromIntegral min, lMax = Nothing} + | otherwise = Limit {lMin = min, lMax = Nothing} where - (min, endBs) = extractLEB128 content + (min, endBs) = getLEB128ToI32 content parseMemory :: BS.ByteString -> Memory parseMemory content diff --git a/lvtrun/app/Parsing/Sections.hs b/lvtrun/app/Parsing/Sections.hs index d4ed7bc..b02daa5 100644 --- a/lvtrun/app/Parsing/Sections.hs +++ b/lvtrun/app/Parsing/Sections.hs @@ -43,8 +43,8 @@ getSectionId bytes = case head (BSL.unpack $ BSL.take 1 bytes) of extractSection :: BSL.ByteString -> (Section, BSL.ByteString) extractSection bytes = do let sectionId = getSectionId bytes - let (size, rest) = extractLEB128 (BSL.drop 1 bytes) - let (content, rest2) = BSL.splitAt (fromIntegral size) rest + let (size, rest) = getLEB128ToI64 (BSL.drop 1 bytes) + let (content, rest2) = BSL.splitAt size rest (Section sectionId (fromIntegral size) content, rest2) extractSections :: BSL.ByteString -> [Section] From 34cb9bdcd07580299fd034ce9adf428cbcaca743 Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 05:27:25 +0100 Subject: [PATCH 26/47] clean code --- lvtrun/app/OpCodes.hs | 143 ++++++++++++++++++++++++++++++ lvtrun/app/Parsing/Code.hs | 149 ++------------------------------ lvtrun/app/Parsing/Exports.hs | 10 +-- lvtrun/app/Parsing/FuncTypes.hs | 19 ++-- lvtrun/app/Parsing/Functions.hs | 7 +- lvtrun/app/Parsing/Global.hs | 98 +-------------------- lvtrun/app/Parsing/Header.hs | 16 ++-- lvtrun/app/Parsing/Memory.hs | 15 ++-- lvtrun/app/Parsing/Parser.hs | 41 +++++---- lvtrun/app/Parsing/Sections.hs | 60 ++++++------- lvtrun/app/Run/Functions.hs | 19 ++-- lvtrun/app/Run/Locals.hs | 41 +++++---- lvtrun/app/Run/Stack.hs | 41 +++++---- lvtrun/app/Run/Start.hs | 14 +-- lvtrun/app/Run/Vm.hs | 44 ++++------ lvtrun/app/Types.hs | 46 +++------- lvtrun/lvtrun.cabal | 1 + 17 files changed, 331 insertions(+), 433 deletions(-) create mode 100644 lvtrun/app/OpCodes.hs diff --git a/lvtrun/app/OpCodes.hs b/lvtrun/app/OpCodes.hs new file mode 100644 index 0000000..8f35c18 --- /dev/null +++ b/lvtrun/app/OpCodes.hs @@ -0,0 +1,143 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- OpCodes +-} + +module OpCodes +( + extractOpCode, + createInstruction +) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) +import Data.Word (Word8) + +import Leb128 +import Types +import Errors + +extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) +extractOpCode bytes + | (head $ BSL.unpack bytes) == 0x03 = ([0x00], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x11 = ([0x00], BSL.drop 3 bytes) + | (head $ BSL.unpack bytes) == 0x00 = ([0x00], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0b = ([0x0b], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0d = ([0x0d], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0c = ([0x0c], BSL.drop 2 bytes) + | (head $ BSL.unpack bytes) == 0x02 = ([0x02], BSL.drop 2 bytes) + | (head $ BSL.unpack bytes) == 0x01 = ([0x01], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x10 = ([0x10], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x41 = ([0x41], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x42 = ([0x42], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x43 = ([0x43], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x44 = ([0x44], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x28 = ([0x28], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x29 = ([0x29], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x22 = ([0x22], BSL.drop 2 bytes) + | (head $ BSL.unpack bytes) == 0x36 = ([0x36], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4b = ([0x4b], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x37 = ([0x37], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x20 = ([0x20], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4d = ([0x4d], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x21 = ([0x21], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x23 = ([0x23], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x24 = ([0x24], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x45 = ([0x45], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x46 = ([0x46], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x71 = ([0x00], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x48 = ([0x48], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], BSL.drop 1 bytes) + | (head $ BSL.unpack bytes) == 0x47 = ([0x47], BSL.drop 1 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], BSL.drop 2 bytes) + | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], BSL.drop 2 bytes) + | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" + +createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) +createInstruction [0x03] bytes = (Nop, bytes) +createInstruction [0x11] bytes = (Nop, bytes) +createInstruction [0x00] bytes = (Unreachable, bytes) +createInstruction [0x01] bytes = (Nop, bytes) +createInstruction [0x02] bytes = (Block EmptyType, bytes) +createInstruction [0x0b] bytes = (End, bytes) +createInstruction [0x48] bytes = (I32Lts, bytes) +createInstruction [0x0f] bytes = (Return, bytes) +createInstruction [0x4b] bytes = (I32Gtu, bytes) +createInstruction [0x6a] bytes = (I32Add, bytes) +createInstruction [0x6c] bytes = (I32Mul, bytes) +createInstruction [0x6d] bytes = (I32Divs, bytes) +createInstruction [0x47] bytes = (I32Ne, bytes) +createInstruction [0x6b] bytes = (I32Sub, bytes) +createInstruction [0x4a] bytes = (I32Gts, bytes) +createInstruction [0x46] bytes = (I32Eqz, bytes) +createInstruction [0x45] bytes = (I32Eqz, bytes) +createInstruction [0x4d] bytes = (I32Leu, bytes) +createInstruction [0x4e] bytes = (I32Ges, bytes) +createInstruction [0x4c] bytes = (I32Les, bytes) +createInstruction [0x71] bytes = (I32And, bytes) +createInstruction [0x0d] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (BrIf value, rest) +createInstruction [0x0c] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (Br value, rest) +createInstruction [0x22] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (LocalTee value, rest) +createInstruction [0x10] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (Call value, rest) +createInstruction [0x41] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (I32Const value, rest) +createInstruction [0x42] bytes = do + let (value, rest) = getLEB128ToI64 bytes + (I64Const value, rest) +createInstruction [0x43] bytes = do + let (value, rest) = getLEB128ToI64 bytes + (F32Const (fromIntegral value), rest) +createInstruction [0x44] bytes = do + let (value, rest) = getLEB128ToI64 bytes + (F64Const (fromIntegral value), rest) +createInstruction [0x28] bytes = do + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest + (I32Load (MemArg offset align), rest2) +createInstruction [0x29] bytes = do + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest + (I64Load (MemArg offset align), rest2) +createInstruction [0x36] bytes = do + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest + (I32Store (MemArg offset align), rest2) +createInstruction [0x37] bytes = do + let (align, rest) = getLEB128ToI32 bytes + let (offset, rest2) = getLEB128ToI32 rest + (I64Store (MemArg offset align), rest2) +createInstruction [0x20] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (GetLocal value, rest) +createInstruction [0x24] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (SetGlobal value, rest) +createInstruction [0x23] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (GetGlobal value, rest) +createInstruction [0x21] bytes = do + let (value, rest) = getLEB128ToI32 bytes + (SetLocal value, rest) +createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) +createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) +createInstruction _ _ = throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/app/Parsing/Code.hs index df63896..d79411f 100644 --- a/lvtrun/app/Parsing/Code.hs +++ b/lvtrun/app/Parsing/Code.hs @@ -6,23 +6,19 @@ -} module Parsing.Code - ( - getFuncCode, - ) +( + getFuncCode, +) where import qualified Data.ByteString.Lazy as BSL import Control.Exception (throw) -import Control.Monad (when) -import Data.Word (Word8) -import Data.Int (Int64, Int32) -import Numeric (showHex) +import Data.Int (Int64) import Leb128 import Types import Errors - --- GET LOCALS +import OpCodes diviseBytes :: BSL.ByteString -> [BSL.ByteString] diviseBytes bytes @@ -55,136 +51,11 @@ extractLocals id idMax bytes ------------------------- -extractOpCode :: BSL.ByteString -> ([Word8], Int64, BSL.ByteString) -extractOpCode bytes - | (head $ BSL.unpack bytes) == 0x03 = ([0x00], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x11 = ([0x00], 1, BSL.drop 3 bytes) - - | (head $ BSL.unpack bytes) == 0x00 = ([0x00], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0b = ([0x0b], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0d = ([0x0d], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0c = ([0x0c], 1, BSL.drop 2 bytes) - | (head $ BSL.unpack bytes) == 0x02 = ([0x02], 1, BSL.drop 2 bytes) - - | (head $ BSL.unpack bytes) == 0x01 = ([0x01], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x10 = ([0x10], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x41 = ([0x41], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x42 = ([0x42], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x43 = ([0x43], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x44 = ([0x44], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x28 = ([0x28], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x29 = ([0x29], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x22 = ([0x22], 1, BSL.drop 2 bytes) - | (head $ BSL.unpack bytes) == 0x36 = ([0x36], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4b = ([0x4b], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x20 = ([0x20], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4d = ([0x4d], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x45 = ([0x45], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x46 = ([0x46], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x71 = ([0x00], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x48 = ([0x48], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x47 = ([0x47], 1, BSL.drop 1 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) - | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" - -createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) -createInstruction [0x03] bytes = (Nop, bytes) -createInstruction [0x11] bytes = (Nop, bytes) - -createInstruction [0x00] bytes = (Unreachable, bytes) -createInstruction [0x01] bytes = (Nop, bytes) -createInstruction [0x02] bytes = (Block EmptyType, bytes) -createInstruction [0x0b] bytes = (End, bytes) -createInstruction [0x48] bytes = (I32Lts, bytes) -createInstruction [0x0f] bytes = (Return, bytes) -createInstruction [0x4b] bytes = (I32Gtu, bytes) -createInstruction [0x6a] bytes = (I32Add, bytes) -createInstruction [0x6c] bytes = (I32Mul, bytes) -createInstruction [0x6d] bytes = (I32Divs, bytes) -createInstruction [0x47] bytes = (I32Ne, bytes) -createInstruction [0x6b] bytes = (I32Sub, bytes) -createInstruction [0x4a] bytes = (I32Gts, bytes) -createInstruction [0x46] bytes = (I32Eqz, bytes) -createInstruction [0x45] bytes = (I32Eqz, bytes) -createInstruction [0x4d] bytes = (I32Leu, bytes) -createInstruction [0x4e] bytes = (I32Ges, bytes) -createInstruction [0x4c] bytes = (I32Les, bytes) -createInstruction [0x71] bytes = (I32And, bytes) -createInstruction [0x0d] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (BrIf value, rest) -createInstruction [0x0c] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (Br value, rest) -createInstruction [0x22] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (LocalTee value, rest) -createInstruction [0x10] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (Call value, rest) -createInstruction [0x41] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (I32Const value, rest) -createInstruction [0x42] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (I64Const value, rest) -createInstruction [0x43] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F32Const (fromIntegral value), rest) -createInstruction [0x44] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F64Const (fromIntegral value), rest) -createInstruction [0x28] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Load (MemArg offset align), rest2) -createInstruction [0x29] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Load (MemArg offset align), rest2) -createInstruction [0x36] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Store (MemArg offset align), rest2) -createInstruction [0x37] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Store (MemArg offset align), rest2) -createInstruction [0x20] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetLocal value, rest) -createInstruction [0x24] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetGlobal value, rest) -createInstruction [0x23] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetGlobal value, rest) -createInstruction [0x21] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetLocal value, rest) -createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) -createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) -createInstruction opCode _ = throw $ WasmError "createInstruction: bad instruction" - parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" | otherwise = do - let (opCode, nbParams, rest) = extractOpCode bytes + let (opCode, rest) = extractOpCode bytes let (instruction, rest2) = createInstruction opCode rest (instruction, rest2) @@ -195,14 +66,6 @@ extractCode bytes let (instruction, rest) = parseInstruction bytes instruction : extractCode rest ------------------------- - -showBytes :: BSL.ByteString -> String -showBytes bytes = do - let bytesList = BSL.unpack bytes - let hexList = map (\x -> showHex x " ") bytesList - foldl (\acc x -> acc ++ x) " " hexList - parseFunction :: BSL.ByteString -> Function -> Function parseFunction bytes func = do let (nbLocalsTypes, rest) = getLEB128ToI64 bytes diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/app/Parsing/Exports.hs index 640aa2b..ea4c375 100644 --- a/lvtrun/app/Parsing/Exports.hs +++ b/lvtrun/app/Parsing/Exports.hs @@ -6,9 +6,9 @@ -} module Parsing.Exports - ( - getExports - ) +( + getExports +) where import qualified Data.ByteString.Lazy as Bs @@ -58,10 +58,6 @@ parseExports idx maxIdx content let export = createExport (Bs.unpack name) exportType exportValue export : parseExports (idx + 1) maxIdx rest3 -printHex :: [Word8] -> String -printHex [] = [] -printHex (x:xs) = showHex x " " ++ printHex xs - getExports :: Section -> [Export] getExports (Section ExportID _ content) = do let (exprtsNb, rest) = getExportNb content diff --git a/lvtrun/app/Parsing/FuncTypes.hs b/lvtrun/app/Parsing/FuncTypes.hs index 0ddf465..18c69eb 100644 --- a/lvtrun/app/Parsing/FuncTypes.hs +++ b/lvtrun/app/Parsing/FuncTypes.hs @@ -6,19 +6,18 @@ -} module Parsing.FuncTypes - ( - getFuncTypes - ) +( + getFuncTypes +) where import qualified Data.ByteString.Lazy as Bs import Control.Exception (throw) import Data.Int (Int64, Int32) -import Data.Word (Word8) -import Leb128 -import Errors -import Types +import Leb128 (getLEB128ToI64) +import Errors (CustomException(..)) +import Types (TypeName(..), FuncType(..), Section(..), SectionID(..), getTypeFromByte) getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) getVectorSize content = getLEB128ToI64 content @@ -29,10 +28,10 @@ extractTypes (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : type where (types, rest) = extractTypes (idx - 1, Bs.drop 1 content) parseFuncType :: Int32 -> Bs.ByteString -> (FuncType, Bs.ByteString) -parseFuncType id content = do +parseFuncType id content = let (params, rest) = extractTypes (getVectorSize content) - let (results, rest2) = extractTypes (getVectorSize rest) - ((FuncType id params results), rest2) + (results, rest2) = extractTypes (getVectorSize rest) + in (FuncType id params results, rest2) parseFuncTypes :: Int32 -> Int64 -> Bs.ByteString -> [FuncType] parseFuncTypes idx maxIdx content diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/app/Parsing/Functions.hs index 184c130..beb429f 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/app/Parsing/Functions.hs @@ -14,7 +14,6 @@ where import qualified Data.ByteString.Lazy as BSL import Control.Exception (throw) import Data.Int (Int64, Int32) -import Data.Word (Word8) import Types import Errors @@ -26,7 +25,11 @@ parseFunctionsIndex idx maxIdx content | BSL.length content == 0 = [] | otherwise = do let (typeIdx, rest) = getLEB128ToI32 content - Function {funcType = fromIntegral typeIdx, funcIdx = idx, body = []} : parseFunctionsIndex (idx + 1) maxIdx rest + Function { + funcType = fromIntegral typeIdx, + funcIdx = idx, + body = [] + } : parseFunctionsIndex (idx + 1) maxIdx rest getFunctions :: Section -> [Function] getFunctions (Section FunctionID _ content) = do diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/app/Parsing/Global.hs index b28b820..0d9cfe0 100644 --- a/lvtrun/app/Parsing/Global.hs +++ b/lvtrun/app/Parsing/Global.hs @@ -13,113 +13,19 @@ where import qualified Data.ByteString.Lazy as BSL import Control.Exception (throw) -import Control.Monad (when) import Data.Word (Word8) import Data.Int (Int64) import Leb128 import Types import Errors - -extractOpCode :: BSL.ByteString -> ([Word8], Int64, BSL.ByteString) -extractOpCode bytes - | (head $ BSL.unpack bytes) == 0x00 = ([0x00], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x01 = ([0x01], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x10 = ([0x10], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x41 = ([0x41], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x42 = ([0x42], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x43 = ([0x43], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x44 = ([0x44], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x28 = ([0x28], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x29 = ([0x29], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x36 = ([0x36], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x20 = ([0x20], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x21 = ([0x21], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x23 = ([0x23], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x24 = ([0x24], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x46 = ([0x46], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x48 = ([0x48], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], 1, BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x47 = ([0x47], 1, BSL.drop 1 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], 2, BSL.drop 2 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], 2, BSL.drop 2 bytes) - | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" - -createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) -createInstruction [0x01] bytes = (Unreachable, bytes) -createInstruction [0x01] bytes = (Nop, bytes) -createInstruction [0x0f] bytes = (Return, bytes) -createInstruction [0x6a] bytes = (I32Add, bytes) -createInstruction [0x6b] bytes = (I32Sub, bytes) -createInstruction [0x48] bytes = (I32Lts, bytes) -createInstruction [0x6c] bytes = (I32Mul, bytes) -createInstruction [0x6d] bytes = (I32Divs, bytes) -createInstruction [0x4e] bytes = (I32Ges, bytes) -createInstruction [0x4c] bytes = (I32Les, bytes) -createInstruction [0x4a] bytes = (I32Gts, bytes) -createInstruction [0x46] bytes = (I32Eqz, bytes) -createInstruction [0x47] bytes = (I32Ne, bytes) -createInstruction [0x10] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (Call value, rest) -createInstruction [0x41] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (I32Const value, rest) -createInstruction [0x42] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (I64Const value, rest) -createInstruction [0x43] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F32Const (fromIntegral value), rest) -createInstruction [0x44] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F64Const (fromIntegral value), rest) -createInstruction [0x28] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Load (MemArg align offset), rest2) -createInstruction [0x29] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Load (MemArg align offset), rest2) -createInstruction [0x36] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Store (MemArg align offset), rest2) -createInstruction [0x37] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Store (MemArg align offset), rest2) -createInstruction [0x20] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetLocal value, rest) -createInstruction [0x24] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetGlobal value, rest) -createInstruction [0x23] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetGlobal value, rest) -createInstruction [0x21] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetLocal value, rest) -createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) -createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) -createInstruction _ _ = throw $ WasmError "CreateInstruction: bad instruction" +import OpCodes parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" | otherwise = do - let (opCode, nbParams, rest) = extractOpCode bytes + let (opCode, rest) = extractOpCode bytes let (instruction, rest2) = createInstruction opCode rest (instruction, rest2) diff --git a/lvtrun/app/Parsing/Header.hs b/lvtrun/app/Parsing/Header.hs index d6aeecc..15f1a63 100644 --- a/lvtrun/app/Parsing/Header.hs +++ b/lvtrun/app/Parsing/Header.hs @@ -6,18 +6,20 @@ -} module Parsing.Header - ( - getModHeader, - isHeaderValid - ) +( + getModHeader, + isHeaderValid +) where -import qualified Data.ByteString.Lazy as BSL (ByteString, take, drop, pack) +import qualified Data.ByteString.Lazy as BSL (take, drop, pack) -import Types +import Types (ModHeader(..), Section(..)) getModHeader :: Section -> ModHeader -getModHeader bytes = ModHeader (BSL.take 4 $ content bytes) (BSL.take 4 $ BSL.drop 4 $ content bytes) +getModHeader bytes = ModHeader + (BSL.take 4 $ content bytes) + (BSL.take 4 $ BSL.drop 4 $ content bytes) isHeaderValid :: ModHeader -> Bool isHeaderValid header = diff --git a/lvtrun/app/Parsing/Memory.hs b/lvtrun/app/Parsing/Memory.hs index 5a42cc6..455681f 100644 --- a/lvtrun/app/Parsing/Memory.hs +++ b/lvtrun/app/Parsing/Memory.hs @@ -6,17 +6,16 @@ -} module Parsing.Memory - ( - getMemories - ) where +( + getMemories +) where -import qualified Data.ByteString.Lazy as BS import Control.Exception (throw) -import Control.Monad (when) +import qualified Data.ByteString.Lazy as BS (ByteString, drop, unpack, empty) -import Leb128 import Types -import Errors +import Leb128 (getLEB128ToI32) +import Errors (CustomException(..)) parseMinMax :: BS.ByteString -> Memory parseMinMax content @@ -44,5 +43,3 @@ getMemories (Section MemoryID _ content) | head (BS.unpack content) == 0x01 = parseMemory (BS.drop 1 content) | otherwise = throw $ WasmError "getMemories: v1 allow 1 memory only" getMemories _ = throw $ WasmError "getMemories: bad memory section" - ---https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/app/Parsing/Parser.hs index b5e067b..bf0b226 100644 --- a/lvtrun/app/Parsing/Parser.hs +++ b/lvtrun/app/Parsing/Parser.hs @@ -12,23 +12,28 @@ module Parsing.Parser where import Types -import Parsing.Sections -import Parsing.Header -import Parsing.Memory -import Parsing.FuncTypes -import Parsing.Global -import Parsing.Exports -import Parsing.Functions -import Parsing.Code +import qualified Parsing.Header as PH +import qualified Parsing.FuncTypes as FT +import qualified Parsing.Functions as FN +import qualified Parsing.Memory as M +import qualified Parsing.Exports as E +import qualified Parsing.Sections as S +import qualified Parsing.Global as G +import qualified Parsing.Code as C parseModule :: FileContent -> WasmModule -parseModule bytes = do - let sections = getSections bytes - WasmModule (getModHeader (getSectionWithId sections CustomID)) - (getFuncTypes (getSectionWithId sections TypeID)) - [] - ((getFuncCode (getSectionWithId sections CodeID) (getFunctions (getSectionWithId sections FunctionID)))) - [] - (getMemories (getSectionWithId sections MemoryID)) - [] - (getExports (getSectionWithId sections ExportID)) +parseModule bytes = + WasmModule { + header = PH.getModHeader (S.getSectionWithId sections CustomID), + types = FT.getFuncTypes (S.getSectionWithId sections TypeID), + imports = [], + functions = C.getFuncCode codeSection funcs, + tables = [], + globals = G.getGlobals (S.getSectionWithId sections GlobalID), + memory = M.getMemories (S.getSectionWithId sections MemoryID), + exports = E.getExports (S.getSectionWithId sections ExportID) + } + where + sections = S.getSections bytes + codeSection = S.getSectionWithId sections CodeID + funcs = FN.getFunctions (S.getSectionWithId sections FunctionID) diff --git a/lvtrun/app/Parsing/Sections.hs b/lvtrun/app/Parsing/Sections.hs index b02daa5..9c333d4 100644 --- a/lvtrun/app/Parsing/Sections.hs +++ b/lvtrun/app/Parsing/Sections.hs @@ -12,12 +12,12 @@ module Parsing.Sections ) where -import qualified Data.ByteString.Lazy as BSL +import qualified Data.ByteString.Lazy as BSL (ByteString, length, unpack, take, drop, splitAt) import Control.Exception (throw) -import Types -import Errors -import Leb128 +import Types (FileContent, Section(..), SectionID(..)) +import Errors (CustomException(..)) +import Leb128 (getLEB128ToI64) extractHeader :: BSL.ByteString -> (Section, BSL.ByteString) extractHeader bytes @@ -26,39 +26,39 @@ extractHeader bytes getSectionId :: BSL.ByteString -> SectionID getSectionId bytes = case head (BSL.unpack $ BSL.take 1 bytes) of - 0 -> CustomID - 1 -> TypeID - 2 -> ImportID - 3 -> FunctionID - 4 -> TableID - 5 -> MemoryID - 6 -> GlobalID - 7 -> ExportID - 8 -> StartID - 9 -> ElementID - 10 -> CodeID - 11 -> DataID - _ -> throw (WasmError "Invalid section id") + 0 -> CustomID + 1 -> TypeID + 2 -> ImportID + 3 -> FunctionID + 4 -> TableID + 5 -> MemoryID + 6 -> GlobalID + 7 -> ExportID + 8 -> StartID + 9 -> ElementID + 10 -> CodeID + 11 -> DataID + _ -> throw (WasmError "Invalid section id") extractSection :: BSL.ByteString -> (Section, BSL.ByteString) -extractSection bytes = do - let sectionId = getSectionId bytes - let (size, rest) = getLEB128ToI64 (BSL.drop 1 bytes) - let (content, rest2) = BSL.splitAt size rest - (Section sectionId (fromIntegral size) content, rest2) +extractSection bytes = (Section sectionId (fromIntegral size) content, rest2) + where + sectionId = getSectionId bytes + (size, rest) = getLEB128ToI64 (BSL.drop 1 bytes) + (content, rest2) = BSL.splitAt size rest extractSections :: BSL.ByteString -> [Section] extractSections bytes - | BSL.null bytes = [] - | otherwise = do - let (section, rest) = extractSection bytes - section : extractSections rest + | BSL.length bytes == 0 = [] + | otherwise = section : extractSections rest + where + (section, rest) = extractSection bytes getSections :: FileContent -> [Section] -getSections bytes = do - let (header, rest) = extractHeader bytes - let sections = extractSections rest - header : sections +getSections bytes = header : sections + where + (header, rest) = extractHeader bytes + sections = extractSections rest getSectionWithId :: [Section] -> SectionID -> Section getSectionWithId [] _ = throw (WasmError "No section with this id") diff --git a/lvtrun/app/Run/Functions.hs b/lvtrun/app/Run/Functions.hs index ac52546..53631f4 100644 --- a/lvtrun/app/Run/Functions.hs +++ b/lvtrun/app/Run/Functions.hs @@ -6,12 +6,12 @@ -} module Run.Functions - ( - getStartFunctionId, - getFunctionFromId, - getStartFunction, - getFuncTypeFromId - ) +( + getStartFunctionId, + getFunctionFromId, + getStartFunction, + getFuncTypeFromId +) where import Data.Int (Int32) @@ -20,8 +20,6 @@ import Control.Exception (throw) import Types import Errors --------------------------------------- - getStartFunctionId :: [Export] -> Int32 getStartFunctionId [] = throw $ WasmError "No start function" getStartFunctionId (x:xs) @@ -30,10 +28,9 @@ getStartFunctionId (x:xs) ExportFunc idx -> idx _ -> throw $ WasmError "getStartFunctionId: bad export" | otherwise = getStartFunctionId xs -getStartFunctionId _ = throw $ WasmError "getStartFunctionId: bad export" getFunctionFromId :: Int32 -> [Function] -> Function -getFunctionFromId id [] = throw $ WasmError "getFunctionFromId: bad id" +getFunctionFromId _ [] = throw $ WasmError "getFunctionFromId: bad id" getFunctionFromId id (x:xs) | funcIdx x == id = x | otherwise = getFunctionFromId id xs @@ -43,7 +40,7 @@ getStartFunction exports functions = getFunctionFromId (getStartFunctionId exports) functions getFuncTypeFromId :: Int32 -> [FuncType] -> FuncType -getFuncTypeFromId id [] = throw $ WasmError "getFuncTypeFromId: bad id" +getFuncTypeFromId _ [] = throw $ WasmError "getFuncTypeFromId: bad id" getFuncTypeFromId id (x:xs) | typeId x == id = x | otherwise = getFuncTypeFromId id xs diff --git a/lvtrun/app/Run/Locals.hs b/lvtrun/app/Run/Locals.hs index 930f1a3..6b36957 100644 --- a/lvtrun/app/Run/Locals.hs +++ b/lvtrun/app/Run/Locals.hs @@ -15,8 +15,7 @@ module Run.Locals ) where -import Data.Int (Int32, Int64) -import Data.Word (Word8) +import Data.Int (Int32) import Control.Exception (throw) import Types @@ -42,21 +41,29 @@ setLocalWithId idx (x:xs) value id | idx == id = value : xs | otherwise = x : setLocalWithId (idx + 1) xs value id ---------------------------- +----------- INITIALISATION ---------------- initLocalsVar :: Locals -> [Local] -> Locals initLocalsVar newLocals [] = newLocals -initLocalsVar newLocals ((Local _ I32):xs) = initLocalsVar (I_32 0 : newLocals) xs -initLocalsVar newLocals ((Local _ I64):xs) = initLocalsVar (I_64 0 : newLocals) xs -initLocalsVar newLocals ((Local _ F32):xs) = initLocalsVar (F_32 0 : newLocals) xs -initLocalsVar newLocals ((Local _ F64):xs) = initLocalsVar (F_64 0 : newLocals) xs +initLocalsVar newLocals ((Local _ I32):xs) = + initLocalsVar (I_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ I64):xs) = + initLocalsVar (I_64 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F32):xs) = + initLocalsVar (F_32 0 : newLocals) xs +initLocalsVar newLocals ((Local _ F64):xs) = + initLocalsVar (F_64 0 : newLocals) xs createLocalsParams :: [TypeName] -> [Value] -> Locals createLocalsParams [] [] = [] -createLocalsParams (I32:xs) (I_32 val:xs2) = (I_32 val : createLocalsParams xs xs2) -createLocalsParams (I64:xs) (I_64 val:xs2) = (I_64 val : createLocalsParams xs xs2) -createLocalsParams (F32:xs) (F_32 val:xs2) = (F_32 val : createLocalsParams xs xs2) -createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) +createLocalsParams (I32:xs) (I_32 val:xs2) = + (I_32 val : createLocalsParams xs xs2) +createLocalsParams (I64:xs) (I_64 val:xs2) = + (I_64 val : createLocalsParams xs xs2) +createLocalsParams (F32:xs) (F_32 val:xs2) = + (F_32 val : createLocalsParams xs xs2) +createLocalsParams (F64:xs) (F_64 val:xs2) = + (F_64 val : createLocalsParams xs xs2) createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) @@ -77,7 +84,11 @@ initLocals localVarTypes paramTypes stack = do createEmptyLocals :: Locals -> [Local] -> Locals createEmptyLocals newLocals [] = newLocals -createEmptyLocals newLocals ((Local _ I32):xs) = createEmptyLocals (I_32 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ I64):xs) = createEmptyLocals (I_64 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ F32):xs) = createEmptyLocals (F_32 0 : newLocals) xs -createEmptyLocals newLocals ((Local _ F64):xs) = createEmptyLocals (F_64 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ I32):xs) = + createEmptyLocals (I_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ I64):xs) = + createEmptyLocals (I_64 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F32):xs) = + createEmptyLocals (F_32 0 : newLocals) xs +createEmptyLocals newLocals ((Local _ F64):xs) = + createEmptyLocals (F_64 0 : newLocals) xs diff --git a/lvtrun/app/Run/Stack.hs b/lvtrun/app/Run/Stack.hs index 325809f..f1a24c7 100644 --- a/lvtrun/app/Run/Stack.hs +++ b/lvtrun/app/Run/Stack.hs @@ -18,37 +18,40 @@ where import Control.Exception (throw) -import Types -import Errors +import Types (Value(..), TypeName(..)) +import Errors (CustomException(..)) type Stack = [Value] pushResults :: Stack -> Stack -> [TypeName] -> Stack -pushResults stack1 stack2 [] = stack1 -pushResults stack1 stack2 ((I32):xs) = case stackTop stack2 of - I_32 val -> pushResults (stackPush stack1 (I_32 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((I64):xs) = case stackTop stack2 of - I_64 val -> pushResults (stackPush stack1 (I_64 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((F32):xs) = case stackTop stack2 of - F_32 val -> pushResults (stackPush stack1 (F_32 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 ((F64):xs) = case stackTop stack2 of - F_64 val -> pushResults (stackPush stack1 (F_64 val)) (tail stack2) xs - _ -> throw $ WasmError "pushResults: bad type" -pushResults stack1 stack2 _ = throw $ WasmError "pushResults: bad type" +pushResults toStack _ [] = toStack +pushResults toStack fromStack ((I32):xs) = + case stackTop fromStack of + I_32 val -> pushResults (stackPush toStack (I_32 val)) (tail fromStack) xs + _ -> throw $ RuntimeError "pushResults: bad type" +pushResults toStack fromStack ((I64):xs) = + case stackTop fromStack of + I_64 val -> pushResults (stackPush toStack (I_64 val)) (tail fromStack) xs + _ -> throw $ RuntimeError "pushResults: bad type" +pushResults toStack fromStack ((F32):xs) = + case stackTop fromStack of + F_32 val -> pushResults (stackPush toStack (F_32 val)) (tail fromStack) xs + _ -> throw $ RuntimeError "pushResults: bad type" +pushResults toStack fromStack ((F64):xs) = + case stackTop fromStack of + F_64 val -> pushResults (stackPush toStack (F_64 val)) (tail fromStack) xs + _ -> throw $ RuntimeError "pushResults: bad type" stackPush :: Stack -> Value -> Stack stackPush stack value = value:stack stackPop :: Stack -> (Value, Stack) -stackPop [] = throw $ WasmError "stackPop: empty stack" +stackPop [] = throw $ RuntimeError "stackPop: empty stack" stackPop (x:xs) = (x, xs) stackTop :: Stack -> Value -stackTop [] = throw $ WasmError "stackTop: empty stack" -stackTop (x:xs) = x +stackTop [] = throw $ RuntimeError "stackTop: empty stack" +stackTop (x:_) = x stackPopN :: Stack -> Int -> ([Value], Stack) stackPopN stack 0 = ([], stack) diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/app/Run/Start.hs index 353a886..c8b3271 100644 --- a/lvtrun/app/Run/Start.hs +++ b/lvtrun/app/Run/Start.hs @@ -11,13 +11,17 @@ module Run.Start ) where -import Data.Int (Int32, Int64) -import Control.Exception (throw) - import Types -import Errors import Run.Vm import Run.Functions +import Run.Stack + +exitCorrectly :: Stack -> IO () +exitCorrectly [] = putStrLn "Exit correctly with code: 0" +exitCorrectly (x:_) = putStrLn $ "Exit correctly with code: " ++ show x startExecution :: WasmModule -> IO () -startExecution wasmMod = startExecution2 (createVm wasmMod) (getStartFunctionId (exports wasmMod)) +startExecution wasmMod = exitCorrectly $ vmAtEnd + where + vmAtEnd = runMain (createVm wasmMod) startFuncId + startFuncId = getStartFunctionId (exports wasmMod) diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/app/Run/Vm.hs index 668e053..fec8863 100644 --- a/lvtrun/app/Run/Vm.hs +++ b/lvtrun/app/Run/Vm.hs @@ -8,15 +8,13 @@ module Run.Vm ( VM(..), - startExecution2, + runMain, createVm ) where -import Data.Int (Int32, Int64) import Data.Word (Word8) import Control.Exception (throw) -import System.Exit import Types import Errors @@ -67,36 +65,33 @@ createVm wasmMod = VM { wasmModule = wasmMod } -addLabel :: CurrentExec -> CurrentExec -addLabel cEx = cEx { ceLabels = (ceLabels cEx) ++ [ceInstIdx cEx] } - incrementInstIdx :: CurrentExec -> CurrentExec incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } --------------------------- execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec -execOpCode vm cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } -execOpCode vm cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } -execOpCode vm cEx (I32Eqz) = do +execOpCode _ cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } +execOpCode _ cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } +execOpCode _ cEx (I32Eqz) = do let value = stackTop (ceStack cEx) case value of I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } _ -> throw $ WasmError "exec I32eqz: bad type" -execOpCode vm cEx (I32Add) = do +execOpCode _ cEx (I32Add) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } _ -> throw $ WasmError "exec I32Add: bad type" -execOpCode vm cEx (I32Sub) = do +execOpCode _ cEx (I32Sub) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } _ -> throw $ WasmError "exec I32Sub: bad type" -execOpCode vm cEx (BrIf labelIdx) = do +execOpCode _ cEx (BrIf labelIdx) = do let (value, newStack) = stackPop (ceStack cEx) case value of I_32 0 -> cEx @@ -106,17 +101,17 @@ execOpCode vm cEx (Call funcIdx) = do let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } -execOpCode vm cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode vm cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode vm cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" -execOpCode vm cEx (GetLocal localIdx) = do +execOpCode _ cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode _ cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } +execOpCode _ cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" +execOpCode _ cEx (GetLocal localIdx) = do let value = getLocalFromId (ceLocals cEx) localIdx cEx { ceStack = stackPush (ceStack cEx) value } -execOpCode vm cEx (SetLocal localIdx) = do +execOpCode _ cEx (SetLocal localIdx) = do let (value, newStack) = stackPop (ceStack cEx) let newLocals = setLocalWithId 0 (ceLocals cEx) value localIdx cEx { ceStack = newStack, ceLocals = newLocals } -execOpCode vm cEx _ = cEx +execOpCode _ cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec execOpCodes vm [] = currentExec vm @@ -152,8 +147,8 @@ execFunctionWithIdx vm funcIdx currentStack = do } execFunction vm { currentExec = cexec } -startExecution2 :: VM -> FuncIdx -> IO () -startExecution2 vm funcIdx = do +runMain :: VM -> FuncIdx -> Stack +runMain vm funcIdx = do let function = getFunctionFromId funcIdx (functions (wasmModule vm)) let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) let cexec = CurrentExec { @@ -167,11 +162,4 @@ startExecution2 vm funcIdx = do crBlockIndents = 0 } let newVm = execFunction vm { currentExec = cexec } - let resStack = [] - let res = pushResults resStack (vmStack newVm) (ceResults (currentExec newVm)) - let exitCode = case res of - [] -> 0 - (x:xs) -> case x of - I_32 val -> val - _ -> 0 - putStrLn $ "Exit correctly with code: " ++ show exitCode + pushResults [] (vmStack newVm) (ceResults (currentExec newVm)) diff --git a/lvtrun/app/Types.hs b/lvtrun/app/Types.hs index e82eb49..f1daba1 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/app/Types.hs @@ -44,7 +44,6 @@ module Types import Data.Int (Int32, Int64) import Data.Word (Word8) -import Numeric (showHex) import Control.Exception (throw) import qualified Data.ByteString.Lazy as BSL @@ -187,7 +186,6 @@ instance Show Instruction where show (Br idx) = "\n\t\t\t\tbr " ++ (show idx) show End = "\n\t\t\t\tend" show (Block blockType) = "\n\t\t\t\tblock " ++ (show blockType) - show _ = throw $ WasmError "Show Instruction: bad instruction" -- Module section @@ -196,25 +194,24 @@ data Value = | I_64 Int64 | F_32 Float | F_64 Double - deriving (Show) + deriving (Eq) + +instance Show Value where + show (I_32 val) = show val + show (I_64 val) = show val + show (F_32 val) = show val + show (F_64 val) = show val data Local = Local { lcIdx :: LocalIdx, lcType :: TypeName -} - -instance Show Local where - show local = "\n\t\t(local idx:" ++ (show $ lcIdx local) ++ " type:" ++ (show $ lcType local) ++ ")" +} deriving (Show) data FuncType = FuncType { typeId :: TypeIdx, params :: [TypeName], results :: [TypeName] -} - -instance Show FuncType where - show funcType = "\n\t(type " ++ (show $ typeId funcType) ++ " (func " ++ - (show $ params funcType) ++ ") " ++ (show $ results funcType) ++ ")" +} deriving (Show) data Import = Import { mod :: String, @@ -232,10 +229,7 @@ data Function = Function { funcIdx :: FuncIdx, locals :: [Local], body :: [Instruction] -} - -instance Show Function where - show func = "\n\t(func idx:" ++ (show $ funcIdx func) ++ " typeId:" ++ (show $ funcType func) ++ " " ++ (show $ locals func) ++ "\nIntructions:\n" ++ (show $ body func) ++ ")" +} deriving (Show) type Memory = Limit @@ -245,12 +239,7 @@ data Global = Global { globalType :: TypeName, mutability :: Mutability, initExpr :: [Instruction] -} - -instance Show Global where - show global = "\n\t(global " ++ (show $ globalType global) ++ " " ++ - (show $ mutability global) ++ " " ++ (show $ initExpr global) ++ ")" - +} deriving (Show) data ExportDesc = ExportFunc FuncIdx @@ -262,10 +251,7 @@ data ExportDesc = data Export = Export { expName :: String, expDesc :: ExportDesc -} - -instance Show Export where - show export = "\n\t(export \"" ++ (expName export) ++ "\" " ++ (show $ expDesc export) ++ ")" +} deriving (Show) data Table = Table { notImpl :: String @@ -297,13 +283,7 @@ data Section = Section { identifier :: SectionID, size :: Int, content :: BSL.ByteString -} - -instance Show Section where - show section = - "\nSection " ++ (show $ identifier section) ++ - " Size: " ++ (show $ size section) ++ - " Content: " ++ (concat $ map (\x -> showHex x " ") (BSL.unpack $ content section)) +} deriving (Show) data WasmModule = WasmModule { header :: ModHeader, diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index 883926e..d6f248e 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -46,6 +46,7 @@ executable lvtrun-exe IO Leb128 Loader + OpCodes Parsing.Code Parsing.Exports Parsing.Functions From c08082d9f630bad5b12eef62d1377c1b04c98f02 Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 13:35:33 +0100 Subject: [PATCH 27/47] move to lib --- lvtrun/app/Loader.hs | 6 +- lvtrun/app/OpCodes.hs | 143 ----------------------- lvtrun/lvtrun.cabal | 35 +++--- lvtrun/{app => src}/Errors.hs | 0 lvtrun/{app => src}/IO.hs | 0 lvtrun/{app => src}/Leb128.hs | 0 lvtrun/src/Lib.hs | 13 --- lvtrun/src/OpCodes.hs | 106 +++++++++++++++++ lvtrun/{app => src}/Parsing/Code.hs | 0 lvtrun/{app => src}/Parsing/Exports.hs | 0 lvtrun/{app => src}/Parsing/FuncTypes.hs | 0 lvtrun/{app => src}/Parsing/Functions.hs | 4 +- lvtrun/{app => src}/Parsing/Global.hs | 0 lvtrun/{app => src}/Parsing/Header.hs | 0 lvtrun/{app => src}/Parsing/Memory.hs | 0 lvtrun/{app => src}/Parsing/Parser.hs | 0 lvtrun/{app => src}/Parsing/Sections.hs | 0 lvtrun/{app => src}/Run/Functions.hs | 0 lvtrun/{app => src}/Run/Locals.hs | 0 lvtrun/{app => src}/Run/Stack.hs | 0 lvtrun/{app => src}/Run/Start.hs | 0 lvtrun/{app => src}/Run/Vm.hs | 0 lvtrun/{app => src}/Types.hs | 80 ++++++------- lvtrun/test/facotial.wasm | Bin 0 -> 197 bytes 24 files changed, 161 insertions(+), 226 deletions(-) delete mode 100644 lvtrun/app/OpCodes.hs rename lvtrun/{app => src}/Errors.hs (100%) rename lvtrun/{app => src}/IO.hs (100%) rename lvtrun/{app => src}/Leb128.hs (100%) delete mode 100644 lvtrun/src/Lib.hs create mode 100644 lvtrun/src/OpCodes.hs rename lvtrun/{app => src}/Parsing/Code.hs (100%) rename lvtrun/{app => src}/Parsing/Exports.hs (100%) rename lvtrun/{app => src}/Parsing/FuncTypes.hs (100%) rename lvtrun/{app => src}/Parsing/Functions.hs (92%) rename lvtrun/{app => src}/Parsing/Global.hs (100%) rename lvtrun/{app => src}/Parsing/Header.hs (100%) rename lvtrun/{app => src}/Parsing/Memory.hs (100%) rename lvtrun/{app => src}/Parsing/Parser.hs (100%) rename lvtrun/{app => src}/Parsing/Sections.hs (100%) rename lvtrun/{app => src}/Run/Functions.hs (100%) rename lvtrun/{app => src}/Run/Locals.hs (100%) rename lvtrun/{app => src}/Run/Stack.hs (100%) rename lvtrun/{app => src}/Run/Start.hs (100%) rename lvtrun/{app => src}/Run/Vm.hs (100%) rename lvtrun/{app => src}/Types.hs (93%) create mode 100644 lvtrun/test/facotial.wasm diff --git a/lvtrun/app/Loader.hs b/lvtrun/app/Loader.hs index fe25ddd..f3ae24d 100644 --- a/lvtrun/app/Loader.hs +++ b/lvtrun/app/Loader.hs @@ -20,14 +20,12 @@ import IO import Errors getFilePath :: IO String -getFilePath = do - args <- getArgs +getFilePath = getArgs >>= \args -> case args of [path] -> return path _ -> throw $ UsageError "Usage: ./run " loadModule :: IO WasmModule -loadModule = do - filePath <- getFilePath +loadModule = getFilePath >>= \filePath -> getFileContent filePath >>= \bytes -> return $ parseModule bytes diff --git a/lvtrun/app/OpCodes.hs b/lvtrun/app/OpCodes.hs deleted file mode 100644 index 8f35c18..0000000 --- a/lvtrun/app/OpCodes.hs +++ /dev/null @@ -1,143 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- OpCodes --} - -module OpCodes -( - extractOpCode, - createInstruction -) -where - -import qualified Data.ByteString.Lazy as BSL -import Control.Exception (throw) -import Data.Word (Word8) - -import Leb128 -import Types -import Errors - -extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) -extractOpCode bytes - | (head $ BSL.unpack bytes) == 0x03 = ([0x00], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x11 = ([0x00], BSL.drop 3 bytes) - | (head $ BSL.unpack bytes) == 0x00 = ([0x00], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0b = ([0x0b], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0d = ([0x0d], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0c = ([0x0c], BSL.drop 2 bytes) - | (head $ BSL.unpack bytes) == 0x02 = ([0x02], BSL.drop 2 bytes) - | (head $ BSL.unpack bytes) == 0x01 = ([0x01], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x0f = ([0x0f], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x10 = ([0x10], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x41 = ([0x41], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x42 = ([0x42], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6c = ([0x6c], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6d = ([0x6d], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x43 = ([0x43], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x44 = ([0x44], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x28 = ([0x28], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x29 = ([0x29], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x22 = ([0x22], BSL.drop 2 bytes) - | (head $ BSL.unpack bytes) == 0x36 = ([0x36], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4b = ([0x4b], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x37 = ([0x37], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x20 = ([0x20], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4d = ([0x4d], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x21 = ([0x21], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x23 = ([0x23], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x24 = ([0x24], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6a = ([0x6a], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x6b = ([0x6b], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x45 = ([0x45], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x46 = ([0x46], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x71 = ([0x00], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x48 = ([0x48], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4a = ([0x4a], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4c = ([0x4c], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x4e = ([0x4e], BSL.drop 1 bytes) - | (head $ BSL.unpack bytes) == 0x47 = ([0x47], BSL.drop 1 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x3f, 0x00] = ([0x3f, 0x00], BSL.drop 2 bytes) - | (BSL.unpack $ BSL.take 2 bytes) == [0x40, 0x00] = ([0x40, 0x00], BSL.drop 2 bytes) - | otherwise = throw $ WasmError "ExtractOpCode2: bad opcode" - -createInstruction :: OpCode -> BSL.ByteString -> (Instruction, BSL.ByteString) -createInstruction [0x03] bytes = (Nop, bytes) -createInstruction [0x11] bytes = (Nop, bytes) -createInstruction [0x00] bytes = (Unreachable, bytes) -createInstruction [0x01] bytes = (Nop, bytes) -createInstruction [0x02] bytes = (Block EmptyType, bytes) -createInstruction [0x0b] bytes = (End, bytes) -createInstruction [0x48] bytes = (I32Lts, bytes) -createInstruction [0x0f] bytes = (Return, bytes) -createInstruction [0x4b] bytes = (I32Gtu, bytes) -createInstruction [0x6a] bytes = (I32Add, bytes) -createInstruction [0x6c] bytes = (I32Mul, bytes) -createInstruction [0x6d] bytes = (I32Divs, bytes) -createInstruction [0x47] bytes = (I32Ne, bytes) -createInstruction [0x6b] bytes = (I32Sub, bytes) -createInstruction [0x4a] bytes = (I32Gts, bytes) -createInstruction [0x46] bytes = (I32Eqz, bytes) -createInstruction [0x45] bytes = (I32Eqz, bytes) -createInstruction [0x4d] bytes = (I32Leu, bytes) -createInstruction [0x4e] bytes = (I32Ges, bytes) -createInstruction [0x4c] bytes = (I32Les, bytes) -createInstruction [0x71] bytes = (I32And, bytes) -createInstruction [0x0d] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (BrIf value, rest) -createInstruction [0x0c] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (Br value, rest) -createInstruction [0x22] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (LocalTee value, rest) -createInstruction [0x10] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (Call value, rest) -createInstruction [0x41] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (I32Const value, rest) -createInstruction [0x42] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (I64Const value, rest) -createInstruction [0x43] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F32Const (fromIntegral value), rest) -createInstruction [0x44] bytes = do - let (value, rest) = getLEB128ToI64 bytes - (F64Const (fromIntegral value), rest) -createInstruction [0x28] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Load (MemArg offset align), rest2) -createInstruction [0x29] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Load (MemArg offset align), rest2) -createInstruction [0x36] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I32Store (MemArg offset align), rest2) -createInstruction [0x37] bytes = do - let (align, rest) = getLEB128ToI32 bytes - let (offset, rest2) = getLEB128ToI32 rest - (I64Store (MemArg offset align), rest2) -createInstruction [0x20] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetLocal value, rest) -createInstruction [0x24] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetGlobal value, rest) -createInstruction [0x23] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (GetGlobal value, rest) -createInstruction [0x21] bytes = do - let (value, rest) = getLEB128ToI32 bytes - (SetLocal value, rest) -createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) -createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) -createInstruction _ _ = throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index d6f248e..f1377ba 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -25,27 +25,9 @@ source-repository head library exposed-modules: - Lib - other-modules: - Paths_lvtrun - autogen-modules: - Paths_lvtrun - hs-source-dirs: - src - ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints - build-depends: - base >=4.7 && <5 - , binary - , bytestring - default-language: Haskell2010 - -executable lvtrun-exe - main-is: Main.hs - other-modules: Errors IO Leb128 - Loader OpCodes Parsing.Code Parsing.Exports @@ -62,6 +44,23 @@ executable lvtrun-exe Run.Start Run.Vm Types + other-modules: + Paths_lvtrun + autogen-modules: + Paths_lvtrun + hs-source-dirs: + src + ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints + build-depends: + base >=4.7 && <5 + , binary + , bytestring + default-language: Haskell2010 + +executable lvtrun-exe + main-is: Main.hs + other-modules: + Loader Paths_lvtrun autogen-modules: Paths_lvtrun diff --git a/lvtrun/app/Errors.hs b/lvtrun/src/Errors.hs similarity index 100% rename from lvtrun/app/Errors.hs rename to lvtrun/src/Errors.hs diff --git a/lvtrun/app/IO.hs b/lvtrun/src/IO.hs similarity index 100% rename from lvtrun/app/IO.hs rename to lvtrun/src/IO.hs diff --git a/lvtrun/app/Leb128.hs b/lvtrun/src/Leb128.hs similarity index 100% rename from lvtrun/app/Leb128.hs rename to lvtrun/src/Leb128.hs diff --git a/lvtrun/src/Lib.hs b/lvtrun/src/Lib.hs deleted file mode 100644 index 3f12ee2..0000000 --- a/lvtrun/src/Lib.hs +++ /dev/null @@ -1,13 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Leviator Run --- File description: --- Lib --} - -module Lib - ( someFunc - ) where - -someFunc :: IO () -someFunc = putStrLn "someFunc" diff --git a/lvtrun/src/OpCodes.hs b/lvtrun/src/OpCodes.hs new file mode 100644 index 0000000..ca85328 --- /dev/null +++ b/lvtrun/src/OpCodes.hs @@ -0,0 +1,106 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- OpCodes +-} + +module OpCodes +( + extractOpCode, + createInstruction +) +where + +import qualified Data.ByteString.Lazy as BSL +import Control.Exception (throw) +import Data.Word (Word8) + +import Leb128 +import Types +import Errors + +extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) +extractOpCode bytes = case BSL.unpack bytes of + 0x03 : rest -> ([0x03], BSL.pack rest) + 0x11 : rest -> ([0x11], BSL.pack rest) + 0x00 : rest -> ([0x00], BSL.pack rest) + 0x0b : rest -> ([0x0b], BSL.pack rest) + 0x0d : rest -> ([0x0d], BSL.pack rest) + 0x0c : rest -> ([0x0c], BSL.pack rest) + 0x02 : rest -> ([0x02], BSL.pack rest) + 0x01 : rest -> ([0x01], BSL.pack rest) + 0x0f : rest -> ([0x0f], BSL.pack rest) + 0x10 : rest -> ([0x10], BSL.pack rest) + 0x41 : rest -> ([0x41], BSL.pack rest) + 0x42 : rest -> ([0x42], BSL.pack rest) + 0x6c : rest -> ([0x6c], BSL.pack rest) + 0x6d : rest -> ([0x6d], BSL.pack rest) + 0x43 : rest -> ([0x43], BSL.pack rest) + 0x44 : rest -> ([0x44], BSL.pack rest) + 0x28 : rest -> ([0x28], BSL.pack rest) + 0x29 : rest -> ([0x29], BSL.pack rest) + 0x22 : rest -> ([0x22], BSL.pack rest) + 0x36 : rest -> ([0x36], BSL.pack rest) + 0x37 : rest -> ([0x37], BSL.pack rest) + 0x4b : rest -> ([0x4b], BSL.pack rest) + 0x20 : rest -> ([0x20], BSL.pack rest) + 0x4d : rest -> ([0x4d], BSL.pack rest) + 0x21 : rest -> ([0x21], BSL.pack rest) + 0x23 : rest -> ([0x23], BSL.pack rest) + 0x24 : rest -> ([0x24], BSL.pack rest) + 0x6a : rest -> ([0x6a], BSL.pack rest) + 0x6b : rest -> ([0x6b], BSL.pack rest) + 0x45 : rest -> ([0x45], BSL.pack rest) + 0x46 : rest -> ([0x46], BSL.pack rest) + 0x71 : rest -> ([0x00], BSL.pack rest) + 0x48 : rest -> ([0x48], BSL.pack rest) + 0x4a : rest -> ([0x4a], BSL.pack rest) + 0x4c : rest -> ([0x4c], BSL.pack rest) + 0x4e : rest -> ([0x4e], BSL.pack rest) + 0x47 : rest -> ([0x47], BSL.pack rest) + 0x3f : 0x00 : rest -> ([0x3f, 0x00], BSL.pack rest) + 0x40 : 0x00 : rest -> ([0x40, 0x00], BSL.pack rest) + _ -> throw $ WasmError "ExtractOpCode: bad opcode" + +createInstruction :: [Word8] -> BSL.ByteString -> (Instruction, BSL.ByteString) +createInstruction [0x03] bytes = (Nop, bytes) +createInstruction [0x11] bytes = (Nop, bytes) +createInstruction [0x00] bytes = (Unreachable, bytes) +createInstruction [0x01] bytes = (Nop, bytes) +createInstruction [0x02] bytes = (Block EmptyType, bytes) +createInstruction [0x0b] bytes = (End, bytes) +createInstruction [0x48] bytes = (I32Lts, bytes) +createInstruction [0x0f] bytes = (Return, bytes) +createInstruction [0x4b] bytes = (I32Gtu, bytes) +createInstruction [0x6a] bytes = (I32Add, bytes) +createInstruction [0x6c] bytes = (I32Mul, bytes) +createInstruction [0x6d] bytes = (I32Divs, bytes) +createInstruction [0x47] bytes = (I32Ne, bytes) +createInstruction [0x6b] bytes = (I32Sub, bytes) +createInstruction [0x4a] bytes = (I32Gts, bytes) +createInstruction [0x46] bytes = (I32Eqz, bytes) +createInstruction [0x45] bytes = (I32Eqz, bytes) +createInstruction [0x4d] bytes = (I32Leu, bytes) +createInstruction [0x4e] bytes = (I32Ges, bytes) +createInstruction [0x4c] bytes = (I32Les, bytes) +createInstruction [0x71] bytes = (I32And, bytes) +createInstruction [0x0d] bytes = (\(value, rest) -> (BrIf value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x0c] bytes = (\(value, rest) -> (Br value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x22] bytes = (\(value, rest) -> (LocalTee value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x10] bytes = (\(value, rest) -> (Call value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x41] bytes = (\(value, rest) -> (I32Const value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x42] bytes = (\(value, rest) -> (I64Const value, rest)) (getLEB128ToI64 bytes) +createInstruction [0x43] bytes = (\(value, rest) -> (F32Const (fromIntegral value), rest)) (getLEB128ToI32 bytes) +createInstruction [0x20] bytes = (\(value, rest) -> (GetLocal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x24] bytes = (\(value, rest) -> (SetGlobal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x23] bytes = (\(value, rest) -> (GetGlobal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x21] bytes = (\(value, rest) -> (SetLocal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x44] bytes = (\(value, rest) -> (F64Const (fromIntegral value), rest)) (getLEB128ToI64 bytes) +createInstruction [0x28] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Load (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x29] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Load (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x36] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x37] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) +createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) +createInstruction _ _ = throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/app/Parsing/Code.hs b/lvtrun/src/Parsing/Code.hs similarity index 100% rename from lvtrun/app/Parsing/Code.hs rename to lvtrun/src/Parsing/Code.hs diff --git a/lvtrun/app/Parsing/Exports.hs b/lvtrun/src/Parsing/Exports.hs similarity index 100% rename from lvtrun/app/Parsing/Exports.hs rename to lvtrun/src/Parsing/Exports.hs diff --git a/lvtrun/app/Parsing/FuncTypes.hs b/lvtrun/src/Parsing/FuncTypes.hs similarity index 100% rename from lvtrun/app/Parsing/FuncTypes.hs rename to lvtrun/src/Parsing/FuncTypes.hs diff --git a/lvtrun/app/Parsing/Functions.hs b/lvtrun/src/Parsing/Functions.hs similarity index 92% rename from lvtrun/app/Parsing/Functions.hs rename to lvtrun/src/Parsing/Functions.hs index beb429f..15fa49f 100644 --- a/lvtrun/app/Parsing/Functions.hs +++ b/lvtrun/src/Parsing/Functions.hs @@ -23,13 +23,13 @@ parseFunctionsIndex :: Int32 -> Int64 -> BSL.ByteString -> [Function] parseFunctionsIndex idx maxIdx content | idx > (fromIntegral maxIdx) = [] | BSL.length content == 0 = [] - | otherwise = do - let (typeIdx, rest) = getLEB128ToI32 content + | otherwise = Function { funcType = fromIntegral typeIdx, funcIdx = idx, body = [] } : parseFunctionsIndex (idx + 1) maxIdx rest + where (typeIdx, rest) = getLEB128ToI32 content getFunctions :: Section -> [Function] getFunctions (Section FunctionID _ content) = do diff --git a/lvtrun/app/Parsing/Global.hs b/lvtrun/src/Parsing/Global.hs similarity index 100% rename from lvtrun/app/Parsing/Global.hs rename to lvtrun/src/Parsing/Global.hs diff --git a/lvtrun/app/Parsing/Header.hs b/lvtrun/src/Parsing/Header.hs similarity index 100% rename from lvtrun/app/Parsing/Header.hs rename to lvtrun/src/Parsing/Header.hs diff --git a/lvtrun/app/Parsing/Memory.hs b/lvtrun/src/Parsing/Memory.hs similarity index 100% rename from lvtrun/app/Parsing/Memory.hs rename to lvtrun/src/Parsing/Memory.hs diff --git a/lvtrun/app/Parsing/Parser.hs b/lvtrun/src/Parsing/Parser.hs similarity index 100% rename from lvtrun/app/Parsing/Parser.hs rename to lvtrun/src/Parsing/Parser.hs diff --git a/lvtrun/app/Parsing/Sections.hs b/lvtrun/src/Parsing/Sections.hs similarity index 100% rename from lvtrun/app/Parsing/Sections.hs rename to lvtrun/src/Parsing/Sections.hs diff --git a/lvtrun/app/Run/Functions.hs b/lvtrun/src/Run/Functions.hs similarity index 100% rename from lvtrun/app/Run/Functions.hs rename to lvtrun/src/Run/Functions.hs diff --git a/lvtrun/app/Run/Locals.hs b/lvtrun/src/Run/Locals.hs similarity index 100% rename from lvtrun/app/Run/Locals.hs rename to lvtrun/src/Run/Locals.hs diff --git a/lvtrun/app/Run/Stack.hs b/lvtrun/src/Run/Stack.hs similarity index 100% rename from lvtrun/app/Run/Stack.hs rename to lvtrun/src/Run/Stack.hs diff --git a/lvtrun/app/Run/Start.hs b/lvtrun/src/Run/Start.hs similarity index 100% rename from lvtrun/app/Run/Start.hs rename to lvtrun/src/Run/Start.hs diff --git a/lvtrun/app/Run/Vm.hs b/lvtrun/src/Run/Vm.hs similarity index 100% rename from lvtrun/app/Run/Vm.hs rename to lvtrun/src/Run/Vm.hs diff --git a/lvtrun/app/Types.hs b/lvtrun/src/Types.hs similarity index 93% rename from lvtrun/app/Types.hs rename to lvtrun/src/Types.hs index f1daba1..501dc21 100644 --- a/lvtrun/app/Types.hs +++ b/lvtrun/src/Types.hs @@ -36,7 +36,6 @@ module Types SectionID(..), Section(..), Memory(..), - OpCode, Local(..), BlockType(..), Value(..) @@ -60,46 +59,30 @@ type DataIdx = Int32 type LocalIdx = Int32 type LabelIdx = Int32 --- Common - type FileContent = BSL.ByteString +type Memory = Limit + data TypeName = - I32 | - I64 | - F32 | - F64 + I32 + | I64 + | F32 + | F64 deriving (Show, Eq, Enum) -getTypeFromByte :: Word8 -> TypeName -getTypeFromByte 0x7f = I32 -getTypeFromByte 0x7e = I64 -getTypeFromByte 0x7d = F32 -getTypeFromByte 0x7c = F64 -getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" - data Limit = Limit { lMin :: Int32, lMax :: Maybe Int32 -} - -instance Show Limit where - show limit = "[\n\tmin: " ++ (show $ lMin limit) ++ "\n\tmax: " ++ (show $ lMax limit) ++ "\n]" - ------------------------ +} deriving (Show, Eq) data MemArg = MemArg { offset :: Int32, align :: Int32 -} +} deriving (Show) instance Eq MemArg where - (==) memArg1 memArg2 = (offset memArg1) == (offset memArg2) && (align memArg1) == (align memArg2) - -instance Show MemArg where - show memArg = "[\n\toffset: " ++ (show $ offset memArg) ++ "\n\talign: " ++ (show $ align memArg) ++ "\n]" - -type OpCode = [Word8] + (==) memArg1 memArg2 = (offset memArg1) == (offset memArg2) + && (align memArg1) == (align memArg2) data BlockType = EmptyType @@ -196,6 +179,23 @@ data Value = | F_64 Double deriving (Eq) +data SectionID = + CustomID + | TypeID + | ImportID + | FunctionID + | TableID + | MemoryID + | GlobalID + | ExportID + | StartID + | ElementID + | CodeID + | DataID + | DataCountID + | InvalidID + deriving (Show, Eq) + instance Show Value where show (I_32 val) = show val show (I_64 val) = show val @@ -231,8 +231,6 @@ data Function = Function { body :: [Instruction] } deriving (Show) -type Memory = Limit - data Mutability = Const | Var deriving (Show) data Global = Global { @@ -262,23 +260,6 @@ data ModHeader = ModHeader { version :: BSL.ByteString } deriving (Show) -data SectionID = - CustomID - | TypeID - | ImportID - | FunctionID - | TableID - | MemoryID - | GlobalID - | ExportID - | StartID - | ElementID - | CodeID - | DataID - | DataCountID - | InvalidID - deriving (Show, Eq) - data Section = Section { identifier :: SectionID, size :: Int, @@ -306,3 +287,10 @@ instance Show WasmModule where "- Memory: " ++ (show $ memory wasmMod) ++ "\n" ++ "- Globals: " ++ (show $ globals wasmMod) ++ "\n" ++ "- Exports: " ++ (show $ exports wasmMod) ++ "\n" + +getTypeFromByte :: Word8 -> TypeName +getTypeFromByte 0x7f = I32 +getTypeFromByte 0x7e = I64 +getTypeFromByte 0x7d = F32 +getTypeFromByte 0x7c = F64 +getTypeFromByte _ = throw $ WasmError "GetTypeFromByte: bad type" diff --git a/lvtrun/test/facotial.wasm b/lvtrun/test/facotial.wasm new file mode 100644 index 0000000000000000000000000000000000000000..86c3dc67e1160d1054de1e25046bc8269950f50f GIT binary patch literal 197 zcmXxdF%H5o3Y+(53C}nNve5um}6emi%d$M-UN`^lS#} zu{MIqzODOzfrEgs*&)o!FfF9b8|{`dWjvz(@N~Q!qvPbbIIfPH|JwmMe6s-SK WQ@Bn}TqH}>1K7GyYIeOKBkC8?$P}0W literal 0 HcmV?d00001 From bb5fc933a9144193059a18275a6e93e8c694158c Mon Sep 17 00:00:00 2001 From: tenshi Date: Sun, 14 Jan 2024 13:52:38 +0100 Subject: [PATCH 28/47] add some operation --- lvtrun/src/Run/Vm.hs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index fec8863..f058a98 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -91,6 +91,19 @@ execOpCode _ cEx (I32Sub) = do case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } _ -> throw $ WasmError "exec I32Sub: bad type" +execOpCode _ cEx (I32Mul) = do + let (value2, newStack1) = stackPop (ceStack cEx) + let (value1, newStack2) = stackPop newStack1 + case (value1, value2) of + (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 * val2)) } + _ -> throw $ WasmError "exec I32Mul: bad type" +execOpCode _ cEx (I32Divs) = do + let (value2, newStack1) = stackPop (ceStack cEx) + let (value1, newStack2) = stackPop newStack1 + case (value1, value2) of + (I_32 _, I_32 0) -> throw $ WasmError "exec I32Divs: division by zero" + (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 `div` val2)) } + _ -> throw $ WasmError "exec I32Divs: bad type" execOpCode _ cEx (BrIf labelIdx) = do let (value, newStack) = stackPop (ceStack cEx) case value of From fabc45fc4c2fa278fed598db2bd4e8f551f0b302 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 15:18:46 +0100 Subject: [PATCH 29/47] clean code --- lvtrun/lvtrun.cabal | 1 + lvtrun/src/Run/Functions.hs | 4 +- lvtrun/src/Run/Locals.hs | 15 +++--- lvtrun/src/Run/Stack.hs | 2 +- lvtrun/src/Run/Start.hs | 7 +-- lvtrun/src/Run/Types.hs | 68 ++++++++++++++++++++++++ lvtrun/src/Run/Vm.hs | 55 ++----------------- lvtrun/test/{out.wasm => addition.wasm} | Bin lvtrun/test/facotial.wasm | Bin 197 -> 0 bytes 9 files changed, 88 insertions(+), 64 deletions(-) create mode 100644 lvtrun/src/Run/Types.hs rename lvtrun/test/{out.wasm => addition.wasm} (100%) delete mode 100644 lvtrun/test/facotial.wasm diff --git a/lvtrun/lvtrun.cabal b/lvtrun/lvtrun.cabal index f1377ba..20bc188 100644 --- a/lvtrun/lvtrun.cabal +++ b/lvtrun/lvtrun.cabal @@ -42,6 +42,7 @@ library Run.Locals Run.Stack Run.Start + Run.Types Run.Vm Types other-modules: diff --git a/lvtrun/src/Run/Functions.hs b/lvtrun/src/Run/Functions.hs index 53631f4..293a968 100644 --- a/lvtrun/src/Run/Functions.hs +++ b/lvtrun/src/Run/Functions.hs @@ -17,8 +17,8 @@ where import Data.Int (Int32) import Control.Exception (throw) -import Types -import Errors +import Errors (CustomException(..)) +import Types (Export(..), ExportDesc(..), Function(..), FuncType(..)) getStartFunctionId :: [Export] -> Int32 getStartFunctionId [] = throw $ WasmError "No start function" diff --git a/lvtrun/src/Run/Locals.hs b/lvtrun/src/Run/Locals.hs index 6b36957..f03da07 100644 --- a/lvtrun/src/Run/Locals.hs +++ b/lvtrun/src/Run/Locals.hs @@ -19,8 +19,8 @@ import Data.Int (Int32) import Control.Exception (throw) import Types -import Errors -import Run.Stack +import Errors (CustomException(..)) +import Run.Stack (Stack, stackPop, stackPopN) type Locals = [Value] @@ -66,15 +66,16 @@ createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" +initLocalsParams' :: (Locals, Stack) -> [TypeName] -> (Locals, Stack) +initLocalsParams' ([], newStack) _ = ([], newStack) +initLocalsParams' (values, newStack) params = + (createLocalsParams params (reverse values), newStack) + initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) initLocalsParams [] stack = ([], stack) initLocalsParams params stack | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" - | otherwise = do - let (values, newStack) = stackPopN stack (length params) - let reversedValues = reverse values - let newLocals = createLocalsParams params reversedValues - (newLocals, newStack) + | otherwise = initLocalsParams' (stackPopN stack (length params)) params initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) initLocals localVarTypes paramTypes stack = do diff --git a/lvtrun/src/Run/Stack.hs b/lvtrun/src/Run/Stack.hs index f1a24c7..ec1953b 100644 --- a/lvtrun/src/Run/Stack.hs +++ b/lvtrun/src/Run/Stack.hs @@ -18,8 +18,8 @@ where import Control.Exception (throw) -import Types (Value(..), TypeName(..)) import Errors (CustomException(..)) +import Types (Value(..), TypeName(..)) type Stack = [Value] diff --git a/lvtrun/src/Run/Start.hs b/lvtrun/src/Run/Start.hs index c8b3271..1204f72 100644 --- a/lvtrun/src/Run/Start.hs +++ b/lvtrun/src/Run/Start.hs @@ -12,9 +12,10 @@ module Run.Start where import Types -import Run.Vm -import Run.Functions -import Run.Stack +import Run.Vm (runMain) +import Run.Stack (Stack) +import Run.Types (createVm) +import Run.Functions (getStartFunctionId) exitCorrectly :: Stack -> IO () exitCorrectly [] = putStrLn "Exit correctly with code: 0" diff --git a/lvtrun/src/Run/Types.hs b/lvtrun/src/Run/Types.hs new file mode 100644 index 0000000..e444d2d --- /dev/null +++ b/lvtrun/src/Run/Types.hs @@ -0,0 +1,68 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator Run +-- File description: +-- Types +-} + +module Run.Types +( + CurrentExec(..), + InstMemory(..), + VM(..), + createVm, + incrementInstIdx +) +where + +import Data.Word (Word8) + +import Types +import Run.Stack (Stack) +import Run.Locals (Locals) + +data CurrentExec = CurrentExec { + ceLocals :: Locals, + ceStack :: Stack, + ceInstructions :: [Instruction], + ceInstIdx :: Int, + ceLabels :: [Int], + ceParams :: [TypeName], + ceResults :: [TypeName], + crBlockIndents :: Int +} deriving (Show) + +data InstMemory = Memory { + memRange :: Limit, + memData :: [Word8] +} deriving (Show) + +data VM = VM { + vmStack :: Stack, + currentExec :: CurrentExec, + vmMemory :: InstMemory, + wasmModule :: WasmModule +} deriving (Show) + +createVm :: WasmModule -> VM +createVm wasmMod = VM { + vmStack = [], + currentExec = CurrentExec { + ceLocals = [], + ceStack = [], + ceInstructions = [], + ceParams = [], + ceResults = [], + ceInstIdx = 0, + ceLabels = [], + crBlockIndents = 0 + }, + vmMemory = Memory { + memRange = Limit 0 Nothing, + memData = [] + }, + wasmModule = wasmMod +} + +incrementInstIdx :: CurrentExec -> CurrentExec +incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index f058a98..4ba4cb2 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -17,58 +17,11 @@ import Data.Word (Word8) import Control.Exception (throw) import Types -import Errors -import Run.Functions -import Run.Stack +import Run.Types import Run.Locals - -data CurrentExec = CurrentExec { - ceLocals :: Locals, - ceStack :: Stack, - ceInstructions :: [Instruction], - ceInstIdx :: Int, - ceLabels :: [Int], - ceParams :: [TypeName], - ceResults :: [TypeName], - crBlockIndents :: Int -} deriving (Show) - -data InstMemory = Memory { - memRange :: Limit, - memData :: [Word8] -} deriving (Show) - -data VM = VM { - vmStack :: Stack, - currentExec :: CurrentExec, - vmMemory :: InstMemory, - wasmModule :: WasmModule -} - -instance Show VM where - show vm = "VM { vmStack = " ++ show (vmStack vm) ++ ", currentExec = " ++ show (currentExec vm) ++ " }" - -createVm :: WasmModule -> VM -createVm wasmMod = VM { - vmStack = [], - currentExec = CurrentExec { - ceLocals = [], - ceStack = [], - ceInstructions = [], - ceParams = [], - ceResults = [] - }, - vmMemory = Memory { - memRange = Limit 0 Nothing, - memData = [] - }, - wasmModule = wasmMod -} - -incrementInstIdx :: CurrentExec -> CurrentExec -incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } - ---------------------------- +import Errors (CustomException(..)) +import Run.Functions (getFunctionFromId, getFuncTypeFromId) +import Run.Stack (Stack, stackPush, stackPop, stackTop, pushResults) execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec execOpCode _ cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } diff --git a/lvtrun/test/out.wasm b/lvtrun/test/addition.wasm similarity index 100% rename from lvtrun/test/out.wasm rename to lvtrun/test/addition.wasm diff --git a/lvtrun/test/facotial.wasm b/lvtrun/test/facotial.wasm deleted file mode 100644 index 86c3dc67e1160d1054de1e25046bc8269950f50f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmXxdF%H5o3Y+(53C}nNve5um}6emi%d$M-UN`^lS#} zu{MIqzODOzfrEgs*&)o!FfF9b8|{`dWjvz(@N~Q!qvPbbIIfPH|JwmMe6s-SK WQ@Bn}TqH}>1K7GyYIeOKBkC8?$P}0W From 8efe9bf6bb72d2890f4bffd8d65aef3691762548 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 15:27:52 +0100 Subject: [PATCH 30/47] fix some warning --- lvtrun/src/Run/Locals.hs | 28 ++++++++++++++-------------- lvtrun/src/Types.hs | 3 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lvtrun/src/Run/Locals.hs b/lvtrun/src/Run/Locals.hs index f03da07..b9c367c 100644 --- a/lvtrun/src/Run/Locals.hs +++ b/lvtrun/src/Run/Locals.hs @@ -20,26 +20,26 @@ import Control.Exception (throw) import Types import Errors (CustomException(..)) -import Run.Stack (Stack, stackPop, stackPopN) +import Run.Stack (Stack, stackPopN) type Locals = [Value] getLocalFromId' :: Int32 -> LocalIdx -> Locals -> Value getLocalFromId' _ _ [] = throw $ WasmError "getLocalFromId: bad id" -getLocalFromId' idx id (x:xs) - | idx > id = throw $ WasmError "getLocalFromId: bad id" - | idx == id = x - | otherwise = getLocalFromId' (idx + 1) id xs +getLocalFromId' idx idntifier (x:xs) + | idx > idntifier = throw $ WasmError "getLocalFromId: bad id" + | idx == idntifier = x + | otherwise = getLocalFromId' (idx + 1) idntifier xs getLocalFromId :: Locals -> LocalIdx -> Value -getLocalFromId locals id = getLocalFromId' 0 id locals +getLocalFromId lcals idntifier = getLocalFromId' 0 idntifier lcals setLocalWithId :: Int32 -> Locals -> Value -> LocalIdx -> Locals setLocalWithId _ [] _ _ = throw $ WasmError "setLocalWithId: bad id" -setLocalWithId idx (x:xs) value id - | idx > id = throw $ WasmError "setLocalWithId: bad id" - | idx == id = value : xs - | otherwise = x : setLocalWithId (idx + 1) xs value id +setLocalWithId idx (x:xs) value idntifier + | idx > idntifier = throw $ WasmError "setLocalWithId: bad id" + | idx == idntifier = value : xs + | otherwise = x : setLocalWithId (idx + 1) xs value idntifier ----------- INITIALISATION ---------------- @@ -68,14 +68,14 @@ createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" initLocalsParams' :: (Locals, Stack) -> [TypeName] -> (Locals, Stack) initLocalsParams' ([], newStack) _ = ([], newStack) -initLocalsParams' (values, newStack) params = - (createLocalsParams params (reverse values), newStack) +initLocalsParams' (values, newStack) prms = + (createLocalsParams prms (reverse values), newStack) initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) initLocalsParams [] stack = ([], stack) -initLocalsParams params stack +initLocalsParams prms stack | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" - | otherwise = initLocalsParams' (stackPopN stack (length params)) params + | otherwise = initLocalsParams' (stackPopN stack (length params)) prms initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) initLocals localVarTypes paramTypes stack = do diff --git a/lvtrun/src/Types.hs b/lvtrun/src/Types.hs index 501dc21..d50db7a 100644 --- a/lvtrun/src/Types.hs +++ b/lvtrun/src/Types.hs @@ -35,7 +35,7 @@ module Types FileContent, SectionID(..), Section(..), - Memory(..), + Memory, Local(..), BlockType(..), Value(..) @@ -96,7 +96,6 @@ data Instruction = | Return | Call FuncIdx | I32Const Int32 - | I64Const Int64 | F32Const Float | F64Const Double From 6b75cd5d73223b1c5f9c6c53365c8039a2879e60 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 15:28:27 +0100 Subject: [PATCH 31/47] fix compilation --- lvtrun/src/Run/Locals.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lvtrun/src/Run/Locals.hs b/lvtrun/src/Run/Locals.hs index b9c367c..9ab8a52 100644 --- a/lvtrun/src/Run/Locals.hs +++ b/lvtrun/src/Run/Locals.hs @@ -74,8 +74,8 @@ initLocalsParams' (values, newStack) prms = initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) initLocalsParams [] stack = ([], stack) initLocalsParams prms stack - | length params > length stack = throw $ WasmError "initLocalsParam: bad nb" - | otherwise = initLocalsParams' (stackPopN stack (length params)) prms + | length prms > length stack = throw $ WasmError "initLocalsParam: bad nb" + | otherwise = initLocalsParams' (stackPopN stack (length prms)) prms initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) initLocals localVarTypes paramTypes stack = do From 1b63e16b3be769f78bddfc11cd3765f6517e5273 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 16:58:06 +0100 Subject: [PATCH 32/47] fix if and eq --- lvtrun/src/OpCodes.hs | 8 +++++--- lvtrun/src/Run/Vm.hs | 27 +++++++++++++++++++++++---- lvtrun/src/Types.hs | 4 +++- lvtrun/test/conditionReturn5.wasm | Bin 0 -> 227 bytes lvtrun/test/factorial.wasm | Bin 0 -> 235 bytes 5 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 lvtrun/test/conditionReturn5.wasm create mode 100644 lvtrun/test/factorial.wasm diff --git a/lvtrun/src/OpCodes.hs b/lvtrun/src/OpCodes.hs index ca85328..4ccd4ae 100644 --- a/lvtrun/src/OpCodes.hs +++ b/lvtrun/src/OpCodes.hs @@ -61,6 +61,7 @@ extractOpCode bytes = case BSL.unpack bytes of 0x47 : rest -> ([0x47], BSL.pack rest) 0x3f : 0x00 : rest -> ([0x3f, 0x00], BSL.pack rest) 0x40 : 0x00 : rest -> ([0x40, 0x00], BSL.pack rest) + 0x04 : 0x40 : rest -> ([0x04, 0x40], BSL.pack rest) _ -> throw $ WasmError "ExtractOpCode: bad opcode" createInstruction :: [Word8] -> BSL.ByteString -> (Instruction, BSL.ByteString) @@ -79,12 +80,15 @@ createInstruction [0x6d] bytes = (I32Divs, bytes) createInstruction [0x47] bytes = (I32Ne, bytes) createInstruction [0x6b] bytes = (I32Sub, bytes) createInstruction [0x4a] bytes = (I32Gts, bytes) -createInstruction [0x46] bytes = (I32Eqz, bytes) +createInstruction [0x46] bytes = (I32Eq, bytes) createInstruction [0x45] bytes = (I32Eqz, bytes) createInstruction [0x4d] bytes = (I32Leu, bytes) createInstruction [0x4e] bytes = (I32Ges, bytes) createInstruction [0x4c] bytes = (I32Les, bytes) createInstruction [0x71] bytes = (I32And, bytes) +createInstruction [0x04, 0x40] bytes = (If, bytes) +createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) +createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) createInstruction [0x0d] bytes = (\(value, rest) -> (BrIf value, rest)) (getLEB128ToI32 bytes) createInstruction [0x0c] bytes = (\(value, rest) -> (Br value, rest)) (getLEB128ToI32 bytes) createInstruction [0x22] bytes = (\(value, rest) -> (LocalTee value, rest)) (getLEB128ToI32 bytes) @@ -101,6 +105,4 @@ createInstruction [0x28] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Loa createInstruction [0x29] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Load (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) createInstruction [0x36] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) createInstruction [0x37] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) -createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) -createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) createInstruction _ _ = throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index 4ba4cb2..75461af 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -23,6 +23,15 @@ import Errors (CustomException(..)) import Run.Functions (getFunctionFromId, getFuncTypeFromId) import Run.Stack (Stack, stackPush, stackPop, stackTop, pushResults) +goToEndInstruction :: CurrentExec -> CurrentExec +goToEndInstruction cexec + | ceInstIdx cexec >= length (ceInstructions cexec) = + throw $ WasmError "goToEndInstruction: missing end instruction" + | currentOpCode == End = cexec { ceInstIdx = (ceInstIdx cexec) } + | otherwise = goToEndInstruction (incrementInstIdx cexec) + where + currentOpCode = (ceInstructions cexec) !! (ceInstIdx cexec) + execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec execOpCode _ cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } execOpCode _ cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } @@ -32,6 +41,12 @@ execOpCode _ cEx (I32Eqz) = do I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } _ -> throw $ WasmError "exec I32eqz: bad type" +execOpCode _ cEx (I32Eq) = do + let (value2, newStack1) = stackPop (ceStack cEx) + let (value1, newStack2) = stackPop newStack1 + case (value1, value2) of + (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (if val1 == val2 then 1 else 0)) } + _ -> throw $ WasmError "exec I32Eq: bad type" execOpCode _ cEx (I32Add) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 @@ -57,11 +72,9 @@ execOpCode _ cEx (I32Divs) = do (I_32 _, I_32 0) -> throw $ WasmError "exec I32Divs: division by zero" (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 `div` val2)) } _ -> throw $ WasmError "exec I32Divs: bad type" -execOpCode _ cEx (BrIf labelIdx) = do - let (value, newStack) = stackPop (ceStack cEx) - case value of +execOpCode _ cEx (BrIf labelIdx) = case stackTop (ceStack cEx) of I_32 0 -> cEx - I_32 _ -> cEx { ceStack = newStack, ceInstIdx = (fromIntegral labelIdx) } + I_32 _ -> cEx { ceInstIdx = (fromIntegral labelIdx) } _ -> throw $ WasmError "exec brIf: bad type" execOpCode vm cEx (Call funcIdx) = do let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) @@ -77,6 +90,11 @@ execOpCode _ cEx (SetLocal localIdx) = do let (value, newStack) = stackPop (ceStack cEx) let newLocals = setLocalWithId 0 (ceLocals cEx) value localIdx cEx { ceStack = newStack, ceLocals = newLocals } +execOpCode _ cEx (If) = case stackTop (ceStack cEx) of + I_32 0 -> goToEndInstruction cEx + I_32 1 -> cEx { crBlockIndents = (crBlockIndents cEx) + 1 } + I_32 _ -> throw $ WasmError "execOpCode: bad if statement" + _ -> throw $ WasmError "execOpCode: bad type" execOpCode _ cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec @@ -85,6 +103,7 @@ execOpCodes vm instructions | ceInstIdx cEx >= length instructions = cEx | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx + | (instructions !! ceInstIdx cEx) == Return = cEx { ceInstIdx = (length instructions) } | otherwise = do let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) let newVm = vm { currentExec = (incrementInstIdx newCEx) } diff --git a/lvtrun/src/Types.hs b/lvtrun/src/Types.hs index d50db7a..6807b19 100644 --- a/lvtrun/src/Types.hs +++ b/lvtrun/src/Types.hs @@ -123,6 +123,7 @@ data Instruction = | I32Ne | LocalTee LocalIdx | BrIf LabelIdx + | If | Br LabelIdx | Block BlockType | End @@ -157,12 +158,13 @@ instance Show Instruction where show I32Eqz = "\n\t\t\t\ti32.eqz" show I32Gts = "\n\t\t\t\ti32.gt_s" show I32Les = "\n\t\t\t\ti32.le_s" - show I32Eq = "\n\t\t\t\ti32.eq" show I32Ne = "\n\t\t\t\ti32.ne" show I32Ges = "\n\t\t\t\ti32.ge_s" show I32Lts = "\n\t\t\t\ti32.lt_s" show I32Gtu = "\n\t\t\t\ti32.gt_u" show I32Leu = "\n\t\t\t\ti32.le_u" + show If = "\n\t\t\t\tif" + show I32Eq = "\n\t\t\t\ti32.eq" show (LocalTee idx) = "\n\t\t\t\tlocal.tee " ++ (show idx) show (BrIf idx) = "\n\t\t\t\tbr_if " ++ (show idx) show (Br idx) = "\n\t\t\t\tbr " ++ (show idx) diff --git a/lvtrun/test/conditionReturn5.wasm b/lvtrun/test/conditionReturn5.wasm new file mode 100644 index 0000000000000000000000000000000000000000..fc7866df77a5fd9c8b2495d4e555bd2dd8520991 GIT binary patch literal 227 zcmXxdI}XAy41i%fkEV$$g(KKEp$i~{khnp+0$tkfIT3fnL!Iffe@lURTMz({+~iur zHUTz2kgkr0(Ia5W7`J_1cPQnD)B^+KyB_!$zJ|qcG29Gy!^7}2Tr0hBXk!)6USNz8 oI0%j)k)t@BJN>si1xcL3S?_x8#gT^0$XP?-q9F<~X<%3B53T|h7ytkO literal 0 HcmV?d00001 diff --git a/lvtrun/test/factorial.wasm b/lvtrun/test/factorial.wasm new file mode 100644 index 0000000000000000000000000000000000000000..013c6a16d95150dcd1c8ec6f86427c49a687a86c GIT binary patch literal 235 zcmXxeNe;p=3AG_Q^@ZjTkoWP%|yJF)-2~T}^k%NY8x?dzg~F)DP Date: Sun, 14 Jan 2024 17:25:45 +0100 Subject: [PATCH 33/47] clean code --- lvtrun/app/Loader.hs | 8 ++--- lvtrun/app/Main.hs | 7 +++-- lvtrun/src/IO.hs | 1 + lvtrun/src/Leb128.hs | 16 +++++----- lvtrun/src/OpCodes.hs | 10 +++--- lvtrun/src/Parsing/Code.hs | 64 +++++++++++++++++++------------------- lvtrun/src/Run/Vm.hs | 26 ++++++++-------- 7 files changed, 67 insertions(+), 65 deletions(-) diff --git a/lvtrun/app/Loader.hs b/lvtrun/app/Loader.hs index f3ae24d..728c60c 100644 --- a/lvtrun/app/Loader.hs +++ b/lvtrun/app/Loader.hs @@ -14,10 +14,10 @@ where import System.Environment (getArgs) import Control.Exception (throw) -import Parsing.Parser -import Types -import IO -import Errors +import Types (WasmModule) +import IO (getFileContent) +import Parsing.Parser (parseModule) +import Errors (CustomException(..)) getFilePath :: IO String getFilePath = getArgs >>= \args -> diff --git a/lvtrun/app/Main.hs b/lvtrun/app/Main.hs index a21b372..aff4e51 100644 --- a/lvtrun/app/Main.hs +++ b/lvtrun/app/Main.hs @@ -8,9 +8,10 @@ module Main (main) where import Control.Exception (try) -import Errors -import Loader -import Run.Start + +import Loader (loadModule) +import Errors (handleException) +import Run.Start (startExecution) main :: IO () main = try (startExecution =<< loadModule) >>= \result -> diff --git a/lvtrun/src/IO.hs b/lvtrun/src/IO.hs index be57a5c..e3edcd5 100644 --- a/lvtrun/src/IO.hs +++ b/lvtrun/src/IO.hs @@ -12,6 +12,7 @@ module IO where import qualified Data.ByteString.Lazy as BSL (readFile) + import Types getFileContent :: String -> IO FileContent diff --git a/lvtrun/src/Leb128.hs b/lvtrun/src/Leb128.hs index fdbb51e..a31bb45 100644 --- a/lvtrun/src/Leb128.hs +++ b/lvtrun/src/Leb128.hs @@ -12,9 +12,9 @@ module Leb128 ) where -import Data.Binary.Get -import Data.Bits import Data.Int (Int64, Int32) +import Data.Binary.Get (Get, getWord8, runGet) +import Data.Bits ((.&.), (.|.), shiftL, testBit) import qualified Data.ByteString.Lazy as BS (ByteString, drop) --------------------- TO INT64 --------------------- @@ -30,9 +30,9 @@ getLEB128ToI64' = do False -> return (value, 1) getLEB128ToI64 :: BS.ByteString -> (Int64, BS.ByteString) -getLEB128ToI64 bytes = do - let (value, size) = runGet getLEB128ToI64' bytes - (value, BS.drop size bytes) +getLEB128ToI64 bytes = (value, BS.drop size bytes) + where + (value, size) = runGet getLEB128ToI64' bytes --------------------- TO INT32 --------------------- @@ -47,6 +47,6 @@ getLEB128ToI32' = do False -> return (value, 1) getLEB128ToI32 :: BS.ByteString -> (Int32, BS.ByteString) -getLEB128ToI32 bytes = do - let (value, size) = runGet getLEB128ToI32' bytes - (value, BS.drop size bytes) +getLEB128ToI32 bytes = (value, BS.drop size bytes) + where + (value, size) = runGet getLEB128ToI32' bytes diff --git a/lvtrun/src/OpCodes.hs b/lvtrun/src/OpCodes.hs index 4ccd4ae..35a6428 100644 --- a/lvtrun/src/OpCodes.hs +++ b/lvtrun/src/OpCodes.hs @@ -12,13 +12,13 @@ module OpCodes ) where -import qualified Data.ByteString.Lazy as BSL -import Control.Exception (throw) import Data.Word (Word8) +import Control.Exception (throw) +import qualified Data.ByteString.Lazy as BSL -import Leb128 -import Types -import Errors +import Errors (CustomException(..)) +import Leb128 (getLEB128ToI32, getLEB128ToI64) +import Types (Instruction(..), MemArg(..), BlockType(..)) extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) extractOpCode bytes = case BSL.unpack bytes of diff --git a/lvtrun/src/Parsing/Code.hs b/lvtrun/src/Parsing/Code.hs index d79411f..21b1d5b 100644 --- a/lvtrun/src/Parsing/Code.hs +++ b/lvtrun/src/Parsing/Code.hs @@ -11,22 +11,23 @@ module Parsing.Code ) where -import qualified Data.ByteString.Lazy as BSL -import Control.Exception (throw) import Data.Int (Int64) +import Control.Exception (throw) +import Control.Monad (when) +import qualified Data.ByteString.Lazy as BSL -import Leb128 import Types -import Errors -import OpCodes +import Leb128 (getLEB128ToI64) +import Errors (CustomException(..)) +import OpCodes (extractOpCode, createInstruction) diviseBytes :: BSL.ByteString -> [BSL.ByteString] diviseBytes bytes | BSL.length bytes == 0 = [] - | otherwise = do - let (size, rest) = getLEB128ToI64 bytes - let (code, rest2) = BSL.splitAt size rest - code : diviseBytes rest2 + | otherwise = code : diviseBytes rest2 + where + (size, rest) = getLEB128ToI64 bytes + (code, rest2) = BSL.splitAt size rest createLocal :: LocalIdx -> TypeName -> Local createLocal idx typee = Local {lcIdx = idx, lcType = typee} @@ -34,43 +35,42 @@ createLocal idx typee = Local {lcIdx = idx, lcType = typee} extractLocal :: Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) extractLocal id bytes | BSL.length bytes == 0 = throw $ WasmError "extractLocal: bad section" - | otherwise = do - let (nbOfThisType, rest) = getLEB128ToI64 bytes - let typee = getTypeFromByte (head (BSL.unpack (BSL.take 1 rest))) - let locals = map (\x -> createLocal (fromIntegral id) typee) [0..nbOfThisType - 1] - (locals, BSL.drop 1 rest) + | otherwise = (locals, BSL.drop 1 rest) + where + (nbOfThisType, rest) = getLEB128ToI64 bytes + typee = getTypeFromByte (head (BSL.unpack (BSL.take 1 rest))) + locals = map (\x -> createLocal (fromIntegral id) typee) [0..nbOfThisType - 1] extractLocals :: Int64 -> Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) extractLocals id idMax bytes | id >= idMax = ([], bytes) | BSL.length bytes == 0 = ([], bytes) - | otherwise = do - let (local, rest) = extractLocal id bytes - let (locals, rest2) = extractLocals (id + 1) idMax rest - (local ++ locals, rest2) + | otherwise = (local ++ locals, rest2) + where + (local, rest) = extractLocal id bytes + (locals, rest2) = extractLocals (id + 1) idMax rest ------------------------- parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" - | otherwise = do - let (opCode, rest) = extractOpCode bytes - let (instruction, rest2) = createInstruction opCode rest - (instruction, rest2) + | otherwise = createInstruction opCode rest + where + (opCode, rest) = extractOpCode bytes extractCode :: BSL.ByteString -> [Instruction] extractCode bytes | BSL.length bytes == 0 = [] - | otherwise = do - let (instruction, rest) = parseInstruction bytes - instruction : extractCode rest + | otherwise = instruction : extractCode rest + where + (instruction, rest) = parseInstruction bytes parseFunction :: BSL.ByteString -> Function -> Function -parseFunction bytes func = do - let (nbLocalsTypes, rest) = getLEB128ToI64 bytes - let (locals, rest2) = extractLocals 0 nbLocalsTypes rest - func {locals = locals, body = extractCode rest2} +parseFunction bytes func = func {locals = lcals, body = extractCode rest2} + where + (nbLocalsTypes, rest) = getLEB128ToI64 bytes + (lcals, rest2) = extractLocals 0 nbLocalsTypes rest parseFunctions :: [BSL.ByteString] -> [Function] -> [Function] parseFunctions [] [] = [] @@ -82,7 +82,7 @@ getFuncCode :: Section -> [Function] -> [Function] getFuncCode (Section CodeID _ content) functions = do let (nbFunc, rest) = getLEB128ToI64 content let funcCodes = diviseBytes rest - if (fromIntegral nbFunc) /= length functions - then throw $ WasmError "getFuncCode: bad section" - else parseFunctions funcCodes functions + when (nbFunc /= fromIntegral (length funcCodes)) $ + throw $ WasmError "getFuncCode: bad section" + parseFunctions funcCodes functions getFuncCode _ _ = throw $ WasmError "getFuncCode: bad section" diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index 75461af..c7d1720 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -26,7 +26,7 @@ import Run.Stack (Stack, stackPush, stackPop, stackTop, pushResults) goToEndInstruction :: CurrentExec -> CurrentExec goToEndInstruction cexec | ceInstIdx cexec >= length (ceInstructions cexec) = - throw $ WasmError "goToEndInstruction: missing end instruction" + throw $ RuntimeError "goToEndInstruction: missing end instruction" | currentOpCode == End = cexec { ceInstIdx = (ceInstIdx cexec) } | otherwise = goToEndInstruction (incrementInstIdx cexec) where @@ -40,49 +40,49 @@ execOpCode _ cEx (I32Eqz) = do case value of I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } - _ -> throw $ WasmError "exec I32eqz: bad type" + _ -> throw $ RuntimeError "exec I32eqz: bad type" execOpCode _ cEx (I32Eq) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (if val1 == val2 then 1 else 0)) } - _ -> throw $ WasmError "exec I32Eq: bad type" + _ -> throw $ RuntimeError "exec I32Eq: bad type" execOpCode _ cEx (I32Add) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } - _ -> throw $ WasmError "exec I32Add: bad type" + _ -> throw $ RuntimeError "exec I32Add: bad type" execOpCode _ cEx (I32Sub) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } - _ -> throw $ WasmError "exec I32Sub: bad type" + _ -> throw $ RuntimeError "exec I32Sub: bad type" execOpCode _ cEx (I32Mul) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 * val2)) } - _ -> throw $ WasmError "exec I32Mul: bad type" + _ -> throw $ RuntimeError "exec I32Mul: bad type" execOpCode _ cEx (I32Divs) = do let (value2, newStack1) = stackPop (ceStack cEx) let (value1, newStack2) = stackPop newStack1 case (value1, value2) of - (I_32 _, I_32 0) -> throw $ WasmError "exec I32Divs: division by zero" + (I_32 _, I_32 0) -> throw $ RuntimeError "exec I32Divs: division by zero" (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 `div` val2)) } - _ -> throw $ WasmError "exec I32Divs: bad type" + _ -> throw $ RuntimeError "exec I32Divs: bad type" execOpCode _ cEx (BrIf labelIdx) = case stackTop (ceStack cEx) of I_32 0 -> cEx I_32 _ -> cEx { ceInstIdx = (fromIntegral labelIdx) } - _ -> throw $ WasmError "exec brIf: bad type" + _ -> throw $ RuntimeError "exec brIf: bad type" execOpCode vm cEx (Call funcIdx) = do let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } execOpCode _ cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } execOpCode _ cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode _ cEx (Unreachable) = throw $ WasmError "execOpCode: unreachable" +execOpCode _ cEx (Unreachable) = throw $ RuntimeError "execOpCode: unreachable" execOpCode _ cEx (GetLocal localIdx) = do let value = getLocalFromId (ceLocals cEx) localIdx cEx { ceStack = stackPush (ceStack cEx) value } @@ -93,15 +93,15 @@ execOpCode _ cEx (SetLocal localIdx) = do execOpCode _ cEx (If) = case stackTop (ceStack cEx) of I_32 0 -> goToEndInstruction cEx I_32 1 -> cEx { crBlockIndents = (crBlockIndents cEx) + 1 } - I_32 _ -> throw $ WasmError "execOpCode: bad if statement" - _ -> throw $ WasmError "execOpCode: bad type" + I_32 _ -> throw $ RuntimeError "execOpCode: bad if statement" + _ -> throw $ RuntimeError "execOpCode: bad type" execOpCode _ cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec execOpCodes vm [] = currentExec vm execOpCodes vm instructions | ceInstIdx cEx >= length instructions = cEx - | ceInstIdx cEx < 0 = throw $ WasmError "execOpCodes: bad index" + | ceInstIdx cEx < 0 = throw $ RuntimeError "execOpCodes: bad index" | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx | (instructions !! ceInstIdx cEx) == Return = cEx { ceInstIdx = (length instructions) } | otherwise = do From 139245b8b2d94de06a95a0210b2d84699a94e80f Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 17:41:33 +0100 Subject: [PATCH 34/47] split opcode --- lvtrun/src/OpCodes.hs | 138 ++++++++++++++++++++-------------- lvtrun/src/Parsing/Exports.hs | 19 +++-- 2 files changed, 89 insertions(+), 68 deletions(-) diff --git a/lvtrun/src/OpCodes.hs b/lvtrun/src/OpCodes.hs index 35a6428..a8788da 100644 --- a/lvtrun/src/OpCodes.hs +++ b/lvtrun/src/OpCodes.hs @@ -20,49 +20,51 @@ import Errors (CustomException(..)) import Leb128 (getLEB128ToI32, getLEB128ToI64) import Types (Instruction(..), MemArg(..), BlockType(..)) +extractOpCode' :: [Word8] -> ([Word8], BSL.ByteString) +extractOpCode' (0x03:rest) = ([0x03], BSL.pack rest) +extractOpCode' (0x11:rest) = ([0x11], BSL.pack rest) +extractOpCode' (0x00:rest) = ([0x00], BSL.pack rest) +extractOpCode' (0x0b:rest) = ([0x0b], BSL.pack rest) +extractOpCode' (0x0d:rest) = ([0x0d], BSL.pack rest) +extractOpCode' (0x0c:rest) = ([0x0c], BSL.pack rest) +extractOpCode' (0x02:rest) = ([0x02], BSL.pack rest) +extractOpCode' (0x01:rest) = ([0x01], BSL.pack rest) +extractOpCode' (0x0f:rest) = ([0x0f], BSL.pack rest) +extractOpCode' (0x10:rest) = ([0x10], BSL.pack rest) +extractOpCode' (0x41:rest) = ([0x41], BSL.pack rest) +extractOpCode' (0x42:rest) = ([0x42], BSL.pack rest) +extractOpCode' (0x6c:rest) = ([0x6c], BSL.pack rest) +extractOpCode' (0x6d:rest) = ([0x6d], BSL.pack rest) +extractOpCode' (0x43:rest) = ([0x43], BSL.pack rest) +extractOpCode' (0x44:rest) = ([0x44], BSL.pack rest) +extractOpCode' (0x28:rest) = ([0x28], BSL.pack rest) +extractOpCode' (0x29:rest) = ([0x29], BSL.pack rest) +extractOpCode' (0x22:rest) = ([0x22], BSL.pack rest) +extractOpCode' (0x36:rest) = ([0x36], BSL.pack rest) +extractOpCode' (0x37:rest) = ([0x37], BSL.pack rest) +extractOpCode' (0x4b:rest) = ([0x4b], BSL.pack rest) +extractOpCode' (0x20:rest) = ([0x20], BSL.pack rest) +extractOpCode' (0x4d:rest) = ([0x4d], BSL.pack rest) +extractOpCode' (0x21:rest) = ([0x21], BSL.pack rest) +extractOpCode' (0x23:rest) = ([0x23], BSL.pack rest) +extractOpCode' (0x24:rest) = ([0x24], BSL.pack rest) +extractOpCode' (0x6a:rest) = ([0x6a], BSL.pack rest) +extractOpCode' (0x6b:rest) = ([0x6b], BSL.pack rest) +extractOpCode' (0x45:rest) = ([0x45], BSL.pack rest) +extractOpCode' (0x46:rest) = ([0x46], BSL.pack rest) +extractOpCode' (0x71:rest) = ([0x71], BSL.pack rest) +extractOpCode' (0x48:rest) = ([0x48], BSL.pack rest) +extractOpCode' (0x4a:rest) = ([0x4a], BSL.pack rest) +extractOpCode' (0x4c:rest) = ([0x4c], BSL.pack rest) +extractOpCode' (0x4e:rest) = ([0x4e], BSL.pack rest) +extractOpCode' (0x47:rest) = ([0x47], BSL.pack rest) +extractOpCode' (0x3f:0x00:rest) = ([0x3f, 0x00], BSL.pack rest) +extractOpCode' (0x40:0x00:rest) = ([0x40, 0x00], BSL.pack rest) +extractOpCode' (0x04:0x40:rest) = ([0x04, 0x40], BSL.pack rest) +extractOpCode' _ = throw $ WasmError "ExtractOpCode: bad opcode" + extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) -extractOpCode bytes = case BSL.unpack bytes of - 0x03 : rest -> ([0x03], BSL.pack rest) - 0x11 : rest -> ([0x11], BSL.pack rest) - 0x00 : rest -> ([0x00], BSL.pack rest) - 0x0b : rest -> ([0x0b], BSL.pack rest) - 0x0d : rest -> ([0x0d], BSL.pack rest) - 0x0c : rest -> ([0x0c], BSL.pack rest) - 0x02 : rest -> ([0x02], BSL.pack rest) - 0x01 : rest -> ([0x01], BSL.pack rest) - 0x0f : rest -> ([0x0f], BSL.pack rest) - 0x10 : rest -> ([0x10], BSL.pack rest) - 0x41 : rest -> ([0x41], BSL.pack rest) - 0x42 : rest -> ([0x42], BSL.pack rest) - 0x6c : rest -> ([0x6c], BSL.pack rest) - 0x6d : rest -> ([0x6d], BSL.pack rest) - 0x43 : rest -> ([0x43], BSL.pack rest) - 0x44 : rest -> ([0x44], BSL.pack rest) - 0x28 : rest -> ([0x28], BSL.pack rest) - 0x29 : rest -> ([0x29], BSL.pack rest) - 0x22 : rest -> ([0x22], BSL.pack rest) - 0x36 : rest -> ([0x36], BSL.pack rest) - 0x37 : rest -> ([0x37], BSL.pack rest) - 0x4b : rest -> ([0x4b], BSL.pack rest) - 0x20 : rest -> ([0x20], BSL.pack rest) - 0x4d : rest -> ([0x4d], BSL.pack rest) - 0x21 : rest -> ([0x21], BSL.pack rest) - 0x23 : rest -> ([0x23], BSL.pack rest) - 0x24 : rest -> ([0x24], BSL.pack rest) - 0x6a : rest -> ([0x6a], BSL.pack rest) - 0x6b : rest -> ([0x6b], BSL.pack rest) - 0x45 : rest -> ([0x45], BSL.pack rest) - 0x46 : rest -> ([0x46], BSL.pack rest) - 0x71 : rest -> ([0x00], BSL.pack rest) - 0x48 : rest -> ([0x48], BSL.pack rest) - 0x4a : rest -> ([0x4a], BSL.pack rest) - 0x4c : rest -> ([0x4c], BSL.pack rest) - 0x4e : rest -> ([0x4e], BSL.pack rest) - 0x47 : rest -> ([0x47], BSL.pack rest) - 0x3f : 0x00 : rest -> ([0x3f, 0x00], BSL.pack rest) - 0x40 : 0x00 : rest -> ([0x40, 0x00], BSL.pack rest) - 0x04 : 0x40 : rest -> ([0x04, 0x40], BSL.pack rest) - _ -> throw $ WasmError "ExtractOpCode: bad opcode" +extractOpCode bytes = extractOpCode' (BSL.unpack bytes) createInstruction :: [Word8] -> BSL.ByteString -> (Instruction, BSL.ByteString) createInstruction [0x03] bytes = (Nop, bytes) @@ -89,20 +91,40 @@ createInstruction [0x71] bytes = (I32And, bytes) createInstruction [0x04, 0x40] bytes = (If, bytes) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) -createInstruction [0x0d] bytes = (\(value, rest) -> (BrIf value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x0c] bytes = (\(value, rest) -> (Br value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x22] bytes = (\(value, rest) -> (LocalTee value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x10] bytes = (\(value, rest) -> (Call value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x41] bytes = (\(value, rest) -> (I32Const value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x42] bytes = (\(value, rest) -> (I64Const value, rest)) (getLEB128ToI64 bytes) -createInstruction [0x43] bytes = (\(value, rest) -> (F32Const (fromIntegral value), rest)) (getLEB128ToI32 bytes) -createInstruction [0x20] bytes = (\(value, rest) -> (GetLocal value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x24] bytes = (\(value, rest) -> (SetGlobal value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x23] bytes = (\(value, rest) -> (GetGlobal value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x21] bytes = (\(value, rest) -> (SetLocal value, rest)) (getLEB128ToI32 bytes) -createInstruction [0x44] bytes = (\(value, rest) -> (F64Const (fromIntegral value), rest)) (getLEB128ToI64 bytes) -createInstruction [0x28] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Load (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) -createInstruction [0x29] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Load (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) -createInstruction [0x36] bytes = (\(align, rest) -> (\(offset, rest2) -> (I32Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) -createInstruction [0x37] bytes = (\(align, rest) -> (\(offset, rest2) -> (I64Store (MemArg offset align), rest2)) (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x0d] bytes = (\(value, rest) -> + (BrIf value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x0c] bytes = (\(value, rest) -> + (Br value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x22] bytes = (\(value, rest) -> + (LocalTee value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x10] bytes = (\(value, rest) -> + (Call value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x41] bytes = (\(value, rest) -> + (I32Const value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x42] bytes = (\(value, rest) -> + (I64Const value, rest)) (getLEB128ToI64 bytes) +createInstruction [0x43] bytes = (\(value, rest) -> + (F32Const (fromIntegral value), rest)) (getLEB128ToI32 bytes) +createInstruction [0x20] bytes = (\(value, rest) -> + (GetLocal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x24] bytes = (\(value, rest) -> + (SetGlobal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x23] bytes = (\(value, rest) -> + (GetGlobal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x21] bytes = (\(value, rest) -> + (SetLocal value, rest)) (getLEB128ToI32 bytes) +createInstruction [0x44] bytes = (\(value, rest) -> + (F64Const (fromIntegral value), rest)) (getLEB128ToI64 bytes) +createInstruction [0x28] bytes = (\(align, rest) -> + (\(offset, rest2) -> (I32Load (MemArg offset align), rest2)) + (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x29] bytes = (\(align, rest) -> + (\(offset, rest2) -> (I64Load (MemArg offset align), rest2)) + (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x36] bytes = (\(align, rest) -> + (\(offset, rest2) -> (I32Store (MemArg offset align), rest2)) + (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) +createInstruction [0x37] bytes = (\(align, rest) -> + (\(offset, rest2) -> (I64Store (MemArg offset align), rest2)) + (getLEB128ToI32 rest)) (getLEB128ToI32 bytes) createInstruction _ _ = throw $ WasmError "createInstruction: bad instruction" diff --git a/lvtrun/src/Parsing/Exports.hs b/lvtrun/src/Parsing/Exports.hs index ea4c375..a0d28df 100644 --- a/lvtrun/src/Parsing/Exports.hs +++ b/lvtrun/src/Parsing/Exports.hs @@ -11,17 +11,16 @@ module Parsing.Exports ) where -import qualified Data.ByteString.Lazy as Bs -import Control.Exception (throw) -import Data.Int (Int64, Int32) -import Data.Word (Word8) -import Numeric (showHex) import Data.Char (chr) +import Data.Word (Word8) import Control.Monad (when) +import Data.Int (Int64, Int32) +import Control.Exception (throw) +import qualified Data.ByteString.Lazy as Bs -import Leb128 -import Errors import Types +import Leb128 (getLEB128ToI64, getLEB128ToI32) +import Errors (CustomException(WasmError)) isExportValid :: Word8 -> Bool isExportValid 0x00 = True @@ -59,7 +58,7 @@ parseExports idx maxIdx content export : parseExports (idx + 1) maxIdx rest3 getExports :: Section -> [Export] -getExports (Section ExportID _ content) = do - let (exprtsNb, rest) = getExportNb content - parseExports 0 exprtsNb rest +getExports (Section ExportID _ content) = parseExports 0 exprtsNb rest + where + (exprtsNb, rest) = getExportNb content getExports _ = throw $ WasmError "getExports: bad section" From bbe24206cc9b58acdab1ce9ff0ab7c01cc56687c Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:03:18 +0100 Subject: [PATCH 35/47] clean code --- lvtrun/src/Parsing/Code.hs | 4 ++-- lvtrun/src/Parsing/FuncTypes.hs | 28 ++++++++++++------------- lvtrun/src/Parsing/Parser.hs | 12 ++++------- lvtrun/src/Parsing/Sections.hs | 37 ++++++++++++++++++--------------- lvtrun/src/Run/Locals.hs | 8 +++---- lvtrun/src/Run/Stack.hs | 8 +++---- lvtrun/src/Run/Types.hs | 20 ++++++------------ 7 files changed, 54 insertions(+), 63 deletions(-) diff --git a/lvtrun/src/Parsing/Code.hs b/lvtrun/src/Parsing/Code.hs index 21b1d5b..16b9e40 100644 --- a/lvtrun/src/Parsing/Code.hs +++ b/lvtrun/src/Parsing/Code.hs @@ -37,9 +37,9 @@ extractLocal id bytes | BSL.length bytes == 0 = throw $ WasmError "extractLocal: bad section" | otherwise = (locals, BSL.drop 1 rest) where - (nbOfThisType, rest) = getLEB128ToI64 bytes + (nb, rest) = getLEB128ToI64 bytes typee = getTypeFromByte (head (BSL.unpack (BSL.take 1 rest))) - locals = map (\x -> createLocal (fromIntegral id) typee) [0..nbOfThisType - 1] + locals = map (\x -> createLocal (fromIntegral id) typee) [0..nb - 1] extractLocals :: Int64 -> Int64 -> BSL.ByteString -> ([Local], BSL.ByteString) extractLocals id idMax bytes diff --git a/lvtrun/src/Parsing/FuncTypes.hs b/lvtrun/src/Parsing/FuncTypes.hs index 18c69eb..29110b5 100644 --- a/lvtrun/src/Parsing/FuncTypes.hs +++ b/lvtrun/src/Parsing/FuncTypes.hs @@ -11,38 +11,38 @@ module Parsing.FuncTypes ) where -import qualified Data.ByteString.Lazy as Bs -import Control.Exception (throw) import Data.Int (Int64, Int32) +import Control.Exception (throw) +import qualified Data.ByteString.Lazy as Bs +import Types import Leb128 (getLEB128ToI64) import Errors (CustomException(..)) -import Types (TypeName(..), FuncType(..), Section(..), SectionID(..), getTypeFromByte) getVectorSize :: Bs.ByteString -> (Int64, Bs.ByteString) getVectorSize content = getLEB128ToI64 content extractTypes :: (Int64, Bs.ByteString) -> ([TypeName], Bs.ByteString) extractTypes (0, content) = ([], content) -extractTypes (idx, content) = (getTypeFromByte (head $ Bs.unpack content) : types, rest) - where (types, rest) = extractTypes (idx - 1, Bs.drop 1 content) +extractTypes (idx, content) = + (getTypeFromByte (head $ Bs.unpack content) : types, rest) + where (types, rest) = extractTypes (idx - 1, Bs.drop 1 content) parseFuncType :: Int32 -> Bs.ByteString -> (FuncType, Bs.ByteString) -parseFuncType id content = - let (params, rest) = extractTypes (getVectorSize content) - (results, rest2) = extractTypes (getVectorSize rest) - in (FuncType id params results, rest2) +parseFuncType id content = (FuncType id params results, rest2) + where + (params, rest) = extractTypes (getVectorSize content) + (results, rest2) = extractTypes (getVectorSize rest) parseFuncTypes :: Int32 -> Int64 -> Bs.ByteString -> [FuncType] parseFuncTypes idx maxIdx content | idx >= (fromIntegral maxIdx) = [] - | head (Bs.unpack content) == 0x60 = do - let (funcType, rest) = parseFuncType idx (Bs.drop 1 content) + | head (Bs.unpack content) == 0x60 = funcType : parseFuncTypes (idx + 1) maxIdx rest | otherwise = throw $ WasmError "ParseFuncTypes: 0x60 expected for function" + where (funcType, rest) = parseFuncType idx (Bs.drop 1 content) getFuncTypes :: Section -> [FuncType] -getFuncTypes (Section TypeID _ content) = do - let (vecSize, rest) = getLEB128ToI64 content - parseFuncTypes 0 vecSize rest +getFuncTypes (Section TypeID _ content) = parseFuncTypes 0 vecSize rest + where (vecSize, rest) = getLEB128ToI64 content getFuncTypes _ = throw $ WasmError "getFuncTypes: bad section" diff --git a/lvtrun/src/Parsing/Parser.hs b/lvtrun/src/Parsing/Parser.hs index bf0b226..b379fd1 100644 --- a/lvtrun/src/Parsing/Parser.hs +++ b/lvtrun/src/Parsing/Parser.hs @@ -22,17 +22,13 @@ import qualified Parsing.Global as G import qualified Parsing.Code as C parseModule :: FileContent -> WasmModule -parseModule bytes = - WasmModule { +parseModule bytes = WasmModule { header = PH.getModHeader (S.getSectionWithId sections CustomID), types = FT.getFuncTypes (S.getSectionWithId sections TypeID), - imports = [], - functions = C.getFuncCode codeSection funcs, - tables = [], - globals = G.getGlobals (S.getSectionWithId sections GlobalID), + imports = [], functions = C.getFuncCode codeSection funcs, + tables = [], globals = G.getGlobals (S.getSectionWithId sections GlobalID), memory = M.getMemories (S.getSectionWithId sections MemoryID), - exports = E.getExports (S.getSectionWithId sections ExportID) - } + exports = E.getExports (S.getSectionWithId sections ExportID)} where sections = S.getSections bytes codeSection = S.getSectionWithId sections CodeID diff --git a/lvtrun/src/Parsing/Sections.hs b/lvtrun/src/Parsing/Sections.hs index 9c333d4..8fa167e 100644 --- a/lvtrun/src/Parsing/Sections.hs +++ b/lvtrun/src/Parsing/Sections.hs @@ -12,33 +12,36 @@ module Parsing.Sections ) where -import qualified Data.ByteString.Lazy as BSL (ByteString, length, unpack, take, drop, splitAt) +import Data.Word (Word8) import Control.Exception (throw) +import qualified Data.ByteString.Lazy as BSL -import Types (FileContent, Section(..), SectionID(..)) -import Errors (CustomException(..)) import Leb128 (getLEB128ToI64) +import Errors (CustomException(..)) +import Types (FileContent, Section(..), SectionID(..)) extractHeader :: BSL.ByteString -> (Section, BSL.ByteString) extractHeader bytes | (BSL.length bytes) < 8 = throw (WasmError "Invalid header") | otherwise = (Section CustomID 8 (BSL.take 8 bytes), BSL.drop 8 bytes) +getSectionId' :: Word8 -> SectionID +getSectionId' 0 = CustomID +getSectionId' 1 = TypeID +getSectionId' 2 = ImportID +getSectionId' 3 = FunctionID +getSectionId' 4 = TableID +getSectionId' 5 = MemoryID +getSectionId' 6 = GlobalID +getSectionId' 7 = ExportID +getSectionId' 8 = StartID +getSectionId' 9 = ElementID +getSectionId' 10 = CodeID +getSectionId' 11 = DataID +getSectionId' _ = throw (WasmError "Invalid section id") + getSectionId :: BSL.ByteString -> SectionID -getSectionId bytes = case head (BSL.unpack $ BSL.take 1 bytes) of - 0 -> CustomID - 1 -> TypeID - 2 -> ImportID - 3 -> FunctionID - 4 -> TableID - 5 -> MemoryID - 6 -> GlobalID - 7 -> ExportID - 8 -> StartID - 9 -> ElementID - 10 -> CodeID - 11 -> DataID - _ -> throw (WasmError "Invalid section id") +getSectionId bytes = getSectionId' (head (BSL.unpack bytes)) extractSection :: BSL.ByteString -> (Section, BSL.ByteString) extractSection bytes = (Section sectionId (fromIntegral size) content, rest2) diff --git a/lvtrun/src/Run/Locals.hs b/lvtrun/src/Run/Locals.hs index 9ab8a52..4c9c30f 100644 --- a/lvtrun/src/Run/Locals.hs +++ b/lvtrun/src/Run/Locals.hs @@ -78,10 +78,10 @@ initLocalsParams prms stack | otherwise = initLocalsParams' (stackPopN stack (length prms)) prms initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) -initLocals localVarTypes paramTypes stack = do - let (newLocals, newStack) = initLocalsParams paramTypes stack - let localsVar = initLocalsVar newLocals localVarTypes - (newLocals ++ localsVar, newStack) +initLocals localVarTypes paramTypes stack = (newLocals ++ localsVar, newStack) + where + (newLocals, newStack) = initLocalsParams paramTypes stack + localsVar = initLocalsVar newLocals localVarTypes createEmptyLocals :: Locals -> [Local] -> Locals createEmptyLocals newLocals [] = newLocals diff --git a/lvtrun/src/Run/Stack.hs b/lvtrun/src/Run/Stack.hs index ec1953b..399b209 100644 --- a/lvtrun/src/Run/Stack.hs +++ b/lvtrun/src/Run/Stack.hs @@ -56,8 +56,8 @@ stackTop (x:_) = x stackPopN :: Stack -> Int -> ([Value], Stack) stackPopN stack 0 = ([], stack) stackPopN stack n - | n > 0 = do - let (value, newStack) = stackPop stack - let (values, finalStack) = stackPopN newStack (n - 1) - (value : values, finalStack) + | n > 0 = (value : values, finalStack) | otherwise = error "stackPopN: bad n" + where + (value, newStack) = stackPop stack + (values, finalStack) = stackPopN newStack (n - 1) diff --git a/lvtrun/src/Run/Types.hs b/lvtrun/src/Run/Types.hs index e444d2d..ddbb8da 100644 --- a/lvtrun/src/Run/Types.hs +++ b/lvtrun/src/Run/Types.hs @@ -45,21 +45,13 @@ data VM = VM { } deriving (Show) createVm :: WasmModule -> VM -createVm wasmMod = VM { - vmStack = [], - currentExec = CurrentExec { - ceLocals = [], - ceStack = [], - ceInstructions = [], - ceParams = [], - ceResults = [], - ceInstIdx = 0, - ceLabels = [], - crBlockIndents = 0 - }, +createVm wasmMod = VM { vmStack = [], + currentExec = CurrentExec { ceLocals = [], + ceStack = [], ceInstructions = [], + ceParams = [], ceResults = [], ceInstIdx = 0, + ceLabels = [], crBlockIndents = 0}, vmMemory = Memory { - memRange = Limit 0 Nothing, - memData = [] + memRange = Limit 0 Nothing, memData = [] }, wasmModule = wasmMod } From 2fa4b3af73bd527315126ab70834e78318384bfb Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:10:42 +0100 Subject: [PATCH 36/47] fix some norm error --- lvtrun/src/Parsing/Global.hs | 53 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/lvtrun/src/Parsing/Global.hs b/lvtrun/src/Parsing/Global.hs index 0d9cfe0..5842091 100644 --- a/lvtrun/src/Parsing/Global.hs +++ b/lvtrun/src/Parsing/Global.hs @@ -11,31 +11,32 @@ module Parsing.Global ) where -import qualified Data.ByteString.Lazy as BSL -import Control.Exception (throw) -import Data.Word (Word8) import Data.Int (Int64) +import Data.Word (Word8) +import Control.Exception (throw) +import qualified Data.ByteString.Lazy as BSL -import Leb128 import Types -import Errors -import OpCodes +import Errors (CustomException(..)) +import Leb128 (getLEB128ToI64) +import OpCodes (extractOpCode, createInstruction) parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes - | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" - | otherwise = do - let (opCode, rest) = extractOpCode bytes - let (instruction, rest2) = createInstruction opCode rest - (instruction, rest2) + | BSL.length bytes == 0 = + throw $ WasmError "ParseInstruction: no instruction" + | otherwise = (instruction, rest2) + where + (opCode, rest) = extractOpCode bytes + (instruction, rest2) = createInstruction opCode rest parseInstructions :: BSL.ByteString -> [Instruction] parseInstructions bytes | BSL.length bytes == 0 = [] | head (BSL.unpack bytes) == 0x0b = [] - | otherwise = do - let (instruction, rest) = parseInstruction bytes - instruction : parseInstructions rest + | otherwise = instruction : parseInstructions rest + where + (instruction, rest) = parseInstruction bytes parseMutability :: Word8 -> Mutability parseMutability 0x00 = Const @@ -44,16 +45,17 @@ parseMutability _ = throw $ WasmError "ParseMutability: bad mutability" getHexaIndex :: BSL.ByteString -> Int64 -> Int64 getHexaIndex content idx - | idx >= (fromIntegral $ BSL.length content) = throw $ WasmError "GetHexaIndex: no 0x0b found" + | idx >= (fromIntegral $ BSL.length content) = + throw $ WasmError "GetHexaIndex: no 0x0b found" | (head $ BSL.unpack $ BSL.drop (fromIntegral idx) content) == 0x0b = idx | otherwise = getHexaIndex content (idx + 1) extractExpression :: BSL.ByteString -> (BSL.ByteString, BSL.ByteString) -extractExpression content = do - let idx = getHexaIndex content 0 - let expression = BSL.take (fromIntegral (idx + 1)) content - let rest = BSL.drop (fromIntegral (idx + 1)) content - (expression, rest) +extractExpression content = (expression, rest) + where + idx = getHexaIndex content 0 + expression = BSL.take (fromIntegral (idx + 1)) content + rest = BSL.drop (fromIntegral (idx + 1)) content parseGlobal :: BSL.ByteString -> (Global, BSL.ByteString) parseGlobal content = do @@ -66,12 +68,13 @@ parseGlobal content = do parseGlobals :: Int64 -> Int64 -> BSL.ByteString -> [Global] parseGlobals idx maxIdx content | idx >= maxIdx = [] - | otherwise = do - let (global, rest) = parseGlobal content - global : parseGlobals (idx + 1) maxIdx rest + | otherwise = global : parseGlobals (idx + 1) maxIdx rest + where + (global, rest) = parseGlobal content getGlobals :: Section -> [Global] -getGlobals (Section GlobalID _ content) = do - let (vecSize, rest) = getLEB128ToI64 content +getGlobals (Section GlobalID _ content) = parseGlobals 0 vecSize rest + where + (vecSize, rest) = getLEB128ToI64 content getGlobals _ = throw $ WasmError "getGlobals: bad section" From ab268b64fe6ded1bb29f8f0571ac671f56abfe24 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:18:17 +0100 Subject: [PATCH 37/47] fix some norm error --- lvtrun/src/Parsing/Code.hs | 3 ++- lvtrun/src/Parsing/Exports.hs | 17 +++++++---------- lvtrun/src/Parsing/Functions.hs | 5 ++--- lvtrun/src/Parsing/Parser.hs | 18 ++++++++---------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/lvtrun/src/Parsing/Code.hs b/lvtrun/src/Parsing/Code.hs index 16b9e40..ab9160b 100644 --- a/lvtrun/src/Parsing/Code.hs +++ b/lvtrun/src/Parsing/Code.hs @@ -54,7 +54,8 @@ extractLocals id idMax bytes parseInstruction :: BSL.ByteString -> (Instruction, BSL.ByteString) parseInstruction bytes - | BSL.length bytes == 0 = throw $ WasmError "ParseInstruction: no instruction" + | BSL.length bytes == 0 = + throw $ WasmError "ParseInstruction: no instruction" | otherwise = createInstruction opCode rest where (opCode, rest) = extractOpCode bytes diff --git a/lvtrun/src/Parsing/Exports.hs b/lvtrun/src/Parsing/Exports.hs index a0d28df..f2617d7 100644 --- a/lvtrun/src/Parsing/Exports.hs +++ b/lvtrun/src/Parsing/Exports.hs @@ -46,16 +46,13 @@ parseExports :: Int32 -> Int64 -> Bs.ByteString -> [Export] parseExports idx maxIdx content | idx >= (fromIntegral maxIdx) = [] | Bs.length content == 0 = [] - | otherwise = do - let (nameLen, rest) = getLEB128ToI64 content - when (nameLen == 0) (throw $ WasmError "parseExports: bad export") - when (Bs.length rest == 0) (throw $ WasmError "parseExports: bad export") - let (name, rest2) = Bs.splitAt nameLen rest - when (Bs.length rest2 == 0) (throw $ WasmError "parseExports: bad export") - let exportType = head (Bs.unpack rest2) - let (exportValue, rest3) = getLEB128ToI32 (Bs.drop 1 rest2) - let export = createExport (Bs.unpack name) exportType exportValue - export : parseExports (idx + 1) maxIdx rest3 + | otherwise = export : parseExports (idx + 1) maxIdx rest3 + where + (nameLen, rest) = getLEB128ToI64 content + (name, rest2) = Bs.splitAt nameLen rest + exportType = head (Bs.unpack rest2) + (exportValue, rest3) = getLEB128ToI32 (Bs.drop 1 rest2) + export = createExport (Bs.unpack name) exportType exportValue getExports :: Section -> [Export] getExports (Section ExportID _ content) = parseExports 0 exprtsNb rest diff --git a/lvtrun/src/Parsing/Functions.hs b/lvtrun/src/Parsing/Functions.hs index 15fa49f..f220956 100644 --- a/lvtrun/src/Parsing/Functions.hs +++ b/lvtrun/src/Parsing/Functions.hs @@ -32,7 +32,6 @@ parseFunctionsIndex idx maxIdx content where (typeIdx, rest) = getLEB128ToI32 content getFunctions :: Section -> [Function] -getFunctions (Section FunctionID _ content) = do - let (vecSize, rest) = getLEB128ToI64 content - parseFunctionsIndex 0 vecSize rest +getFunctions (Section FunctionID _ content) = parseFunctionsIndex 0 vecSize rest + where (vecSize, rest) = getLEB128ToI64 content getFunctions _ = throw $ WasmError "getFunctions: bad section" diff --git a/lvtrun/src/Parsing/Parser.hs b/lvtrun/src/Parsing/Parser.hs index b379fd1..cb81338 100644 --- a/lvtrun/src/Parsing/Parser.hs +++ b/lvtrun/src/Parsing/Parser.hs @@ -23,13 +23,11 @@ import qualified Parsing.Code as C parseModule :: FileContent -> WasmModule parseModule bytes = WasmModule { - header = PH.getModHeader (S.getSectionWithId sections CustomID), - types = FT.getFuncTypes (S.getSectionWithId sections TypeID), - imports = [], functions = C.getFuncCode codeSection funcs, - tables = [], globals = G.getGlobals (S.getSectionWithId sections GlobalID), - memory = M.getMemories (S.getSectionWithId sections MemoryID), - exports = E.getExports (S.getSectionWithId sections ExportID)} - where - sections = S.getSections bytes - codeSection = S.getSectionWithId sections CodeID - funcs = FN.getFunctions (S.getSectionWithId sections FunctionID) + header = PH.getModHeader (S.getSectionWithId sections CustomID), + types = FT.getFuncTypes (S.getSectionWithId sections TypeID), + imports = [], functions = C.getFuncCode (S.getSectionWithId sections CodeID) + (FN.getFunctions (S.getSectionWithId sections FunctionID)), + tables = [], globals = G.getGlobals (S.getSectionWithId sections GlobalID), + memory = M.getMemories (S.getSectionWithId sections MemoryID), + exports = E.getExports (S.getSectionWithId sections ExportID)} + where sections = S.getSections bytes From aacdacf520dcd80f7e4830d2367a398aab2b3de5 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:19:54 +0100 Subject: [PATCH 38/47] fix some norm error --- lvtrun/src/Parsing/Functions.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lvtrun/src/Parsing/Functions.hs b/lvtrun/src/Parsing/Functions.hs index f220956..1bb7c4b 100644 --- a/lvtrun/src/Parsing/Functions.hs +++ b/lvtrun/src/Parsing/Functions.hs @@ -32,6 +32,7 @@ parseFunctionsIndex idx maxIdx content where (typeIdx, rest) = getLEB128ToI32 content getFunctions :: Section -> [Function] -getFunctions (Section FunctionID _ content) = parseFunctionsIndex 0 vecSize rest +getFunctions (Section FunctionID _ content) = + parseFunctionsIndex 0 vecSize rest where (vecSize, rest) = getLEB128ToI64 content getFunctions _ = throw $ WasmError "getFunctions: bad section" From 905178f54e43840c85df225cb87d6221d89d4f8a Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:21:59 +0100 Subject: [PATCH 39/47] fix some norm error --- lvtrun/src/Parsing/Global.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lvtrun/src/Parsing/Global.hs b/lvtrun/src/Parsing/Global.hs index 5842091..81c6a76 100644 --- a/lvtrun/src/Parsing/Global.hs +++ b/lvtrun/src/Parsing/Global.hs @@ -58,12 +58,12 @@ extractExpression content = (expression, rest) rest = BSL.drop (fromIntegral (idx + 1)) content parseGlobal :: BSL.ByteString -> (Global, BSL.ByteString) -parseGlobal content = do - let globalType = getTypeFromByte (head $ BSL.unpack content) - let mutability = parseMutability (head $ BSL.unpack $ BSL.drop 1 content) - let (expression, rest) = extractExpression (BSL.drop 2 content) - let instructions = parseInstructions expression - (Global globalType mutability instructions, rest) +parseGlobal content = (Global globalType mutability instructions, rest) + where + globalType = getTypeFromByte (head $ BSL.unpack content) + mutability = parseMutability (head $ BSL.unpack $ BSL.drop 1 content) + (expression, rest) = extractExpression (BSL.drop 2 content) + instructions = parseInstructions expression parseGlobals :: Int64 -> Int64 -> BSL.ByteString -> [Global] parseGlobals idx maxIdx content From dd5b17a8768e5e9fdc7df3fe1718a004033bb212 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:24:21 +0100 Subject: [PATCH 40/47] fix some norm error --- lvtrun/src/Parsing/Code.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lvtrun/src/Parsing/Code.hs b/lvtrun/src/Parsing/Code.hs index ab9160b..12f992d 100644 --- a/lvtrun/src/Parsing/Code.hs +++ b/lvtrun/src/Parsing/Code.hs @@ -12,8 +12,8 @@ module Parsing.Code where import Data.Int (Int64) -import Control.Exception (throw) import Control.Monad (when) +import Control.Exception (throw) import qualified Data.ByteString.Lazy as BSL import Types @@ -80,10 +80,9 @@ parseFunctions _ [] = throw $ WasmError "parseFunctions: bad section" parseFunctions (x:xs) (y:ys) = parseFunction x y : parseFunctions xs ys getFuncCode :: Section -> [Function] -> [Function] -getFuncCode (Section CodeID _ content) functions = do - let (nbFunc, rest) = getLEB128ToI64 content - let funcCodes = diviseBytes rest - when (nbFunc /= fromIntegral (length funcCodes)) $ - throw $ WasmError "getFuncCode: bad section" +getFuncCode (Section CodeID _ content) functions = parseFunctions funcCodes functions + where + (nbFunc, rest) = getLEB128ToI64 content + funcCodes = diviseBytes rest getFuncCode _ _ = throw $ WasmError "getFuncCode: bad section" From 5be902296f8faa90650dce475f82a0b9b87ef8e6 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:39:21 +0100 Subject: [PATCH 41/47] fix some norm error --- lvtrun/src/Run/Types.hs | 15 ++++++++++- lvtrun/src/Run/Vm.hs | 56 ++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/lvtrun/src/Run/Types.hs b/lvtrun/src/Run/Types.hs index ddbb8da..8618365 100644 --- a/lvtrun/src/Run/Types.hs +++ b/lvtrun/src/Run/Types.hs @@ -11,7 +11,8 @@ module Run.Types InstMemory(..), VM(..), createVm, - incrementInstIdx + incrementInstIdx, + createEmptyExec ) where @@ -58,3 +59,15 @@ createVm wasmMod = VM { vmStack = [], incrementInstIdx :: CurrentExec -> CurrentExec incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } + +createEmptyExec :: CurrentExec +createEmptyExec = CurrentExec { + ceLocals = [], + ceStack = [], + ceInstructions = [], + ceInstIdx = 0, + ceLabels = [], + ceParams = [], + ceResults = [], + crBlockIndents = 0 +} diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index c7d1720..47811c5 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -111,40 +111,34 @@ execOpCodes vm instructions where cEx = currentExec vm execFunction :: VM -> VM -execFunction vm = do - let newCEx = execOpCodes vm (ceInstructions (currentExec vm)) - vm { currentExec = newCEx, vmStack = (pushResults (vmStack vm) (ceStack newCEx) (ceResults newCEx)) } +execFunction vm = vm { currentExec = newCEx, vmStack = stackWithRes } + where + newCEx = execOpCodes vm (ceInstructions (currentExec vm)) + stackWithRes = pushResults (vmStack vm) (ceStack newCEx) + (ceResults newCEx) execFunctionWithIdx :: VM -> FuncIdx -> Stack -> VM -execFunctionWithIdx vm funcIdx currentStack = do - let function = getFunctionFromId funcIdx (functions (wasmModule vm)) - let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) - let (newLocals, newStack) = initLocals (locals function) (params funcTypee) currentStack - let cexec = CurrentExec { - ceLocals = newLocals, - ceStack = newStack, - ceInstructions = body function, - ceInstIdx = 0, - ceLabels = [], - ceParams = params funcTypee, - ceResults = results funcTypee, - crBlockIndents = 0 - } +execFunctionWithIdx vm funcIdx currentStack = execFunction vm { currentExec = cexec } + where + function = getFunctionFromId funcIdx (functions (wasmModule vm)) + funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) + (newLocals, newStack) = + initLocals (locals function) (params funcTypee) currentStack + cexec = createEmptyExec { + ceLocals = newLocals, ceStack = newStack, ceInstructions = body function, + ceParams = params funcTypee, ceResults = results funcTypee} runMain :: VM -> FuncIdx -> Stack -runMain vm funcIdx = do - let function = getFunctionFromId funcIdx (functions (wasmModule vm)) - let funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) - let cexec = CurrentExec { - ceLocals = createEmptyLocals [] (locals function), - ceStack = [], - ceInstructions = body function, - ceInstIdx = 0, - ceLabels = [], - ceParams = params funcTypee, - ceResults = results funcTypee, - crBlockIndents = 0 - } - let newVm = execFunction vm { currentExec = cexec } +runMain vm funcIdx = pushResults [] (vmStack newVm) (ceResults (currentExec newVm)) + where + function = getFunctionFromId funcIdx (functions (wasmModule vm)) + funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) + cexec = createEmptyExec { + ceLocals = createEmptyLocals [] (locals function), + ceInstructions = body function, + ceParams = params funcTypee, + ceResults = results funcTypee + } + newVm = execFunction vm { currentExec = cexec } From 1c78d3374540fdad65691ed3f417409daf5be078 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:41:23 +0100 Subject: [PATCH 42/47] fix some norm error --- lvtrun/src/Run/Vm.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index 47811c5..d4cd9ac 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -104,11 +104,11 @@ execOpCodes vm instructions | ceInstIdx cEx < 0 = throw $ RuntimeError "execOpCodes: bad index" | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx | (instructions !! ceInstIdx cEx) == Return = cEx { ceInstIdx = (length instructions) } - | otherwise = do - let newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) - let newVm = vm { currentExec = (incrementInstIdx newCEx) } - execOpCodes newVm instructions - where cEx = currentExec vm + | otherwise = execOpCodes newVm instructions + where + cEx = currentExec vm + newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) + newVm = vm { currentExec = (incrementInstIdx newCEx) } execFunction :: VM -> VM execFunction vm = vm { currentExec = newCEx, vmStack = stackWithRes } From b33e6bd9c30ca547bd7c882c7751d3005262f81d Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:43:50 +0100 Subject: [PATCH 43/47] fix some norm error --- lvtrun/src/Run/Vm.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index d4cd9ac..fbb3d53 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -130,15 +130,12 @@ execFunctionWithIdx vm funcIdx currentStack = ceParams = params funcTypee, ceResults = results funcTypee} runMain :: VM -> FuncIdx -> Stack -runMain vm funcIdx = - pushResults [] (vmStack newVm) (ceResults (currentExec newVm)) +runMain vm funcIdx = pushResults[](vmStack newVm)(ceResults(currentExec newVm)) where function = getFunctionFromId funcIdx (functions (wasmModule vm)) funcTypee = getFuncTypeFromId (funcType function) (types (wasmModule vm)) cexec = createEmptyExec { ceLocals = createEmptyLocals [] (locals function), ceInstructions = body function, - ceParams = params funcTypee, - ceResults = results funcTypee - } + ceParams = params funcTypee, ceResults = results funcTypee} newVm = execFunction vm { currentExec = cexec } From 15175f9bff25248ffa08b054a32eb5888b65f609 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 18:49:00 +0100 Subject: [PATCH 44/47] fix some norm error --- lvtrun/src/Run/Vm.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index fbb3d53..c92b27b 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -102,13 +102,13 @@ execOpCodes vm [] = currentExec vm execOpCodes vm instructions | ceInstIdx cEx >= length instructions = cEx | ceInstIdx cEx < 0 = throw $ RuntimeError "execOpCodes: bad index" - | (instructions !! ceInstIdx cEx) == End && crBlockIndents cEx == 0 = cEx - | (instructions !! ceInstIdx cEx) == Return = cEx { ceInstIdx = (length instructions) } + | currentInst == End && crBlockIndents cEx == 0 = cEx + | currentInst == Return = cEx { ceInstIdx = (length instructions) } | otherwise = execOpCodes newVm instructions - where - cEx = currentExec vm - newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) - newVm = vm { currentExec = (incrementInstIdx newCEx) } + where cEx = currentExec vm + newCEx = execOpCode vm cEx (instructions !! ceInstIdx cEx) + newVm = vm { currentExec = (incrementInstIdx newCEx) } + currentInst = instructions !! ceInstIdx cEx execFunction :: VM -> VM execFunction vm = vm { currentExec = newCEx, vmStack = stackWithRes } From 663d44f3999a06905171b0c956e85b883110eef4 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 19:38:54 +0100 Subject: [PATCH 45/47] fix some norm error --- lvtrun/src/Run/Types.hs | 6 ++- lvtrun/src/Run/Vm.hs | 101 +++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/lvtrun/src/Run/Types.hs b/lvtrun/src/Run/Types.hs index 8618365..e3f8de6 100644 --- a/lvtrun/src/Run/Types.hs +++ b/lvtrun/src/Run/Types.hs @@ -12,7 +12,8 @@ module Run.Types VM(..), createVm, incrementInstIdx, - createEmptyExec + createEmptyExec, + decrementBlockIdx ) where @@ -60,6 +61,9 @@ createVm wasmMod = VM { vmStack = [], incrementInstIdx :: CurrentExec -> CurrentExec incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } +decrementBlockIdx :: CurrentExec -> CurrentExec +decrementBlockIdx cEx = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } + createEmptyExec :: CurrentExec createEmptyExec = CurrentExec { ceLocals = [], diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index c92b27b..d33140b 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -13,7 +13,7 @@ module Run.Vm ) where -import Data.Word (Word8) +import Data.Int (Int32) import Control.Exception (throw) import Types @@ -21,7 +21,7 @@ import Run.Types import Run.Locals import Errors (CustomException(..)) import Run.Functions (getFunctionFromId, getFuncTypeFromId) -import Run.Stack (Stack, stackPush, stackPop, stackTop, pushResults) +import Run.Stack (Stack, stackPush, stackPop, stackTop, pushResults, stackPopN) goToEndInstruction :: CurrentExec -> CurrentExec goToEndInstruction cexec @@ -32,46 +32,64 @@ goToEndInstruction cexec where currentOpCode = (ceInstructions cexec) !! (ceInstIdx cexec) -execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec -execOpCode _ cEx (I32Const val) = cEx { ceStack = stackPush (ceStack cEx) (I_32 val) } -execOpCode _ cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } -execOpCode _ cEx (I32Eqz) = do - let value = stackTop (ceStack cEx) - case value of - I_32 0 -> cEx { ceStack = stackPush (ceStack cEx) (I_32 1) } - I_32 _ -> cEx { ceStack = stackPush (ceStack cEx) (I_32 0) } +execI32Const :: CurrentExec -> Int32 -> CurrentExec +execI32Const cEx val = cEx {ceStack = stackPush (ceStack cEx) (I_32 val)} + +execI32Eqz :: CurrentExec -> CurrentExec +execI32Eqz cEx@(CurrentExec {ceStack = stack}) = + case (stackTop stack) of + I_32 0 -> cEx { ceStack = stackPush stack (I_32 1) } + I_32 _ -> cEx { ceStack = stackPush stack (I_32 0) } _ -> throw $ RuntimeError "exec I32eqz: bad type" -execOpCode _ cEx (I32Eq) = do - let (value2, newStack1) = stackPop (ceStack cEx) - let (value1, newStack2) = stackPop newStack1 - case (value1, value2) of - (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (if val1 == val2 then 1 else 0)) } + +execI32Add :: CurrentExec -> CurrentExec +execI32Add cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> + cEx { ceStack = stackPush newStack (I_32 (val1 + val2)) } + _ -> throw $ RuntimeError "exec I32add: bad type" + +execI32Eq :: CurrentExec -> CurrentExec +execI32Eq cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 value2, I_32 value1], newStack) -> case (value1 == value2) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } _ -> throw $ RuntimeError "exec I32Eq: bad type" -execOpCode _ cEx (I32Add) = do - let (value2, newStack1) = stackPop (ceStack cEx) - let (value1, newStack2) = stackPop newStack1 - case (value1, value2) of - (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 + val2)) } - _ -> throw $ RuntimeError "exec I32Add: bad type" -execOpCode _ cEx (I32Sub) = do - let (value2, newStack1) = stackPop (ceStack cEx) - let (value1, newStack2) = stackPop newStack1 - case (value1, value2) of - (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 - val2)) } - _ -> throw $ RuntimeError "exec I32Sub: bad type" -execOpCode _ cEx (I32Mul) = do - let (value2, newStack1) = stackPop (ceStack cEx) - let (value1, newStack2) = stackPop newStack1 - case (value1, value2) of - (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 * val2)) } - _ -> throw $ RuntimeError "exec I32Mul: bad type" -execOpCode _ cEx (I32Divs) = do - let (value2, newStack1) = stackPop (ceStack cEx) - let (value1, newStack2) = stackPop newStack1 - case (value1, value2) of - (I_32 _, I_32 0) -> throw $ RuntimeError "exec I32Divs: division by zero" - (I_32 val1, I_32 val2) -> cEx { ceStack = stackPush newStack2 (I_32 (val1 `div` val2)) } - _ -> throw $ RuntimeError "exec I32Divs: bad type" + +execI32Sub :: CurrentExec -> CurrentExec +execI32Sub cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> + cEx { ceStack = stackPush newStack (I_32 (val1 - val2)) } + _ -> throw $ RuntimeError "exec I32sub: bad type" + +execI32Mul :: CurrentExec -> CurrentExec +execI32Mul cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> + cEx { ceStack = stackPush newStack (I_32 (val1 * val2)) } + _ -> throw $ RuntimeError "exec I32mul: bad type" + +execI32Divs :: CurrentExec -> CurrentExec +execI32Divs cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> + cEx { ceStack = stackPush newStack (I_32 (val1 `div` val2)) } + _ -> throw $ RuntimeError "exec I32divs: bad type" + +execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec +execOpCode _ cEx (Unreachable) = throw $ RuntimeError "execOpCode: unreachable" +execOpCode _ cEx (End) = decrementBlockIdx cEx +execOpCode _ cEx (Return) = decrementBlockIdx cEx +execOpCode _ cEx (I32Const val) = execI32Const cEx val +execOpCode _ cEx (I32Eqz) = execI32Eqz cEx +execOpCode _ cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } +execOpCode _ cEx (I32Eq) = execI32Eq cEx +execOpCode _ cEx (I32Add) = execI32Add cEx +execOpCode _ cEx (I32Sub) = execI32Sub cEx +execOpCode _ cEx (I32Mul) = execI32Mul cEx +execOpCode _ cEx (I32Divs) = execI32Divs cEx execOpCode _ cEx (BrIf labelIdx) = case stackTop (ceStack cEx) of I_32 0 -> cEx I_32 _ -> cEx { ceInstIdx = (fromIntegral labelIdx) } @@ -80,9 +98,6 @@ execOpCode vm cEx (Call funcIdx) = do let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) cEx { ceStack = newStack } -execOpCode _ cEx (End) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode _ cEx (Return) = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } -execOpCode _ cEx (Unreachable) = throw $ RuntimeError "execOpCode: unreachable" execOpCode _ cEx (GetLocal localIdx) = do let value = getLocalFromId (ceLocals cEx) localIdx cEx { ceStack = stackPush (ceStack cEx) value } From 26f3ac18320dacd94519b59a7f11ab04480beb81 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 19:47:14 +0100 Subject: [PATCH 46/47] fix some norm error --- lvtrun/src/Run/Vm.hs | 54 ++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index d33140b..db2f8b7 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -78,6 +78,35 @@ execI32Divs cEx@(CurrentExec {ceStack = stack}) = cEx { ceStack = stackPush newStack (I_32 (val1 `div` val2)) } _ -> throw $ RuntimeError "exec I32divs: bad type" +execGetLocal :: CurrentExec -> LocalIdx -> CurrentExec +execGetLocal cEx localIdx = cEx { ceStack = + stackPush (ceStack cEx) (getLocalFromId (ceLocals cEx) localIdx)} + +execSetLocal :: CurrentExec -> LocalIdx -> CurrentExec +execSetLocal cEx localIdx = cEx { ceStack = newStack, + ceLocals = setLocalWithId 0 (ceLocals cEx) value localIdx} + where (value, newStack) = stackPop (ceStack cEx) + +execBrIf :: CurrentExec -> CurrentExec +execBrIf cEx@(CurrentExec {ceStack = stack}) = + case (stackTop stack) of + I_32 0 -> cEx + I_32 _ -> cEx { ceInstIdx = (ceInstIdx cEx) } + _ -> throw $ RuntimeError "exec brIf: bad type" + +execCall :: VM -> CurrentExec -> FuncIdx -> CurrentExec +execCall vm cEx funcIdx = do + let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) + let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) + cEx { ceStack = newStack } + +execIf :: CurrentExec -> CurrentExec +execIf cEx@(CurrentExec {ceStack = stack}) = case stackTop stack of + I_32 0 -> goToEndInstruction cEx + I_32 1 -> cEx { crBlockIndents = (crBlockIndents cEx) + 1 } + I_32 _ -> throw $ RuntimeError "execIf: bad if statement" + _ -> throw $ RuntimeError "execIf: bad type" + execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec execOpCode _ cEx (Unreachable) = throw $ RuntimeError "execOpCode: unreachable" execOpCode _ cEx (End) = decrementBlockIdx cEx @@ -90,26 +119,11 @@ execOpCode _ cEx (I32Add) = execI32Add cEx execOpCode _ cEx (I32Sub) = execI32Sub cEx execOpCode _ cEx (I32Mul) = execI32Mul cEx execOpCode _ cEx (I32Divs) = execI32Divs cEx -execOpCode _ cEx (BrIf labelIdx) = case stackTop (ceStack cEx) of - I_32 0 -> cEx - I_32 _ -> cEx { ceInstIdx = (fromIntegral labelIdx) } - _ -> throw $ RuntimeError "exec brIf: bad type" -execOpCode vm cEx (Call funcIdx) = do - let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) - let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) - cEx { ceStack = newStack } -execOpCode _ cEx (GetLocal localIdx) = do - let value = getLocalFromId (ceLocals cEx) localIdx - cEx { ceStack = stackPush (ceStack cEx) value } -execOpCode _ cEx (SetLocal localIdx) = do - let (value, newStack) = stackPop (ceStack cEx) - let newLocals = setLocalWithId 0 (ceLocals cEx) value localIdx - cEx { ceStack = newStack, ceLocals = newLocals } -execOpCode _ cEx (If) = case stackTop (ceStack cEx) of - I_32 0 -> goToEndInstruction cEx - I_32 1 -> cEx { crBlockIndents = (crBlockIndents cEx) + 1 } - I_32 _ -> throw $ RuntimeError "execOpCode: bad if statement" - _ -> throw $ RuntimeError "execOpCode: bad type" +execOpCode _ cEx (GetLocal localIdx) = execGetLocal cEx localIdx +execOpCode _ cEx (SetLocal localIdx) = execSetLocal cEx localIdx +execOpCode _ cEx (BrIf labelIdx) = execBrIf cEx +execOpCode vm cEx (Call funcIdx) = execCall vm cEx funcIdx +execOpCode _ cEx (If) = execIf cEx execOpCode _ cEx _ = cEx execOpCodes :: VM -> [Instruction] -> CurrentExec From 86b9607839a9c3bc8db014ace19dfe3585c5fc05 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sun, 14 Jan 2024 19:49:05 +0100 Subject: [PATCH 47/47] fix some norm error --- lvtrun/src/Run/Vm.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index db2f8b7..6bee410 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -95,10 +95,12 @@ execBrIf cEx@(CurrentExec {ceStack = stack}) = _ -> throw $ RuntimeError "exec brIf: bad type" execCall :: VM -> CurrentExec -> FuncIdx -> CurrentExec -execCall vm cEx funcIdx = do - let newVm = execFunctionWithIdx vm funcIdx (ceStack cEx) - let newStack = pushResults (ceStack cEx) (vmStack newVm) (ceResults (currentExec newVm)) - cEx { ceStack = newStack } +execCall vm cEx funcIdx = cEx { ceStack = newStack } + where + newVm = execFunctionWithIdx vm funcIdx currentStack + newStack = pushResults currentStack (vmStack newVm) res + currentStack = ceStack cEx + res = ceResults (currentExec newVm) execIf :: CurrentExec -> CurrentExec execIf cEx@(CurrentExec {ceStack = stack}) = case stackTop stack of