wasm-platform-kit-build 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +2 -3
- package/haskell/HostSDK/Canonical.hs +369 -0
- package/haskell/LICENSE +21 -0
- package/haskell/host-sdk-guest.cabal +12 -0
- package/package.json +26 -3
- package/tools/component.mjs +269 -0
- package/tools/facade.mjs +133 -0
- package/tools/generate.mjs +362 -0
- package/tools/index.mjs +8 -0
- package/tools/native.mjs +194 -0
- package/tools/paths.mjs +43 -0
- package/wit/host.wit +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 brain-fuel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
|
1
|
+
# Wasm Platform Kit build tools
|
|
2
|
+
Direct WIT-to-Haskell generation and application-owned component packaging. See the SDK repository documentation.
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
{-# LANGUAGE CPP #-}
|
|
2
|
+
#if !defined(wasm32_HOST_ARCH)
|
|
3
|
+
#error HostSDK.Canonical requires the wasm32 GHC backend
|
|
4
|
+
#endif
|
|
5
|
+
{-# LANGUAGE RecordWildCards #-}
|
|
6
|
+
|
|
7
|
+
-- | Memory32 canonical ABI support. Layouts come from upstream wit-parser.
|
|
8
|
+
-- Application modules never manipulate these pointers. ref:DEC-host-sdk-direct-haskell
|
|
9
|
+
module HostSDK.Canonical where
|
|
10
|
+
|
|
11
|
+
import Control.Exception (bracket, finally, onException)
|
|
12
|
+
import Control.Monad (forM, forM_, unless)
|
|
13
|
+
import Control.Monad.IO.Class (liftIO)
|
|
14
|
+
import Control.Monad.Trans.State.Strict (StateT, get, put, runStateT)
|
|
15
|
+
import qualified Data.ByteString as B
|
|
16
|
+
import Data.IORef
|
|
17
|
+
import Data.Int
|
|
18
|
+
import Data.Text (Text)
|
|
19
|
+
import qualified Data.Text.Encoding as T
|
|
20
|
+
import Data.Word
|
|
21
|
+
import Foreign.Marshal.Alloc (free, mallocBytes)
|
|
22
|
+
import Foreign.Marshal.Utils (copyBytes, fillBytes)
|
|
23
|
+
import Foreign.Ptr
|
|
24
|
+
import Foreign.StablePtr
|
|
25
|
+
import Foreign.Storable
|
|
26
|
+
import System.IO.Unsafe (unsafePerformIO)
|
|
27
|
+
import System.Mem (performMajorGC)
|
|
28
|
+
|
|
29
|
+
-- | Identifies whether lifting transfers responsibility for the input allocation.
|
|
30
|
+
data Ownership = Borrowed | Owned deriving (Eq)
|
|
31
|
+
|
|
32
|
+
-- | Tracks allocations for one call or one exported result, including nested values.
|
|
33
|
+
newtype Arena = Arena (IORef [Ptr Word8])
|
|
34
|
+
|
|
35
|
+
-- | Consumes the flattened integer lanes selected by upstream canonical layout.
|
|
36
|
+
type Lift = StateT [Word64] IO
|
|
37
|
+
|
|
38
|
+
-- | Pairs a Haskell type with its canonical memory and flattened representations.
|
|
39
|
+
data Codec a = Codec
|
|
40
|
+
{ byteSize :: Int,
|
|
41
|
+
laneCount :: Int,
|
|
42
|
+
load :: Ownership -> Ptr Word8 -> IO a,
|
|
43
|
+
store :: Arena -> Ptr Word8 -> a -> IO (),
|
|
44
|
+
lower :: Arena -> a -> IO [Word64],
|
|
45
|
+
liftValue :: Ownership -> Lift a
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
-- | Starts an empty allocation scope before a call crosses the component boundary.
|
|
49
|
+
newArena :: IO Arena
|
|
50
|
+
newArena = Arena <$> newIORef []
|
|
51
|
+
|
|
52
|
+
-- | Frees every recorded allocation once, including on exceptional exits.
|
|
53
|
+
release :: Arena -> IO ()
|
|
54
|
+
release (Arena ref) = atomicModifyIORef' ref (\ps -> ([], ps)) >>= mapM_ free
|
|
55
|
+
|
|
56
|
+
-- | Brackets imported-call allocations so failed host operations also release them.
|
|
57
|
+
withArena :: (Arena -> IO a) -> IO a
|
|
58
|
+
withArena = bracket newArena release
|
|
59
|
+
|
|
60
|
+
-- | Records zeroed canonical storage in the current scope before exposing its address.
|
|
61
|
+
allocate :: Arena -> Int -> IO (Ptr Word8)
|
|
62
|
+
allocate (Arena ref) n = do
|
|
63
|
+
unless (n >= 0) (fail "canonical allocation overflow")
|
|
64
|
+
p <- mallocBytes (max 1 n)
|
|
65
|
+
modifyIORef' ref (p :)
|
|
66
|
+
fillBytes p 0 (max 1 n)
|
|
67
|
+
pure p
|
|
68
|
+
|
|
69
|
+
-- | Converts a pointer to its memory32 lane; this module rejects other architectures.
|
|
70
|
+
address :: Ptr a -> Word32
|
|
71
|
+
address = fromIntegral . ptrToWordPtr
|
|
72
|
+
|
|
73
|
+
-- | Reconstructs a memory32 address supplied by the canonical ABI.
|
|
74
|
+
pointer :: Word32 -> Ptr Word8
|
|
75
|
+
pointer = wordPtrToPtr . fromIntegral
|
|
76
|
+
|
|
77
|
+
-- | Consumes one lane and rejects an incomplete flattened argument.
|
|
78
|
+
word :: Lift Word32
|
|
79
|
+
word = do
|
|
80
|
+
xs <- get
|
|
81
|
+
case xs of
|
|
82
|
+
x : rest -> put rest >> pure (fromIntegral x)
|
|
83
|
+
_ -> liftIO (fail "canonical ABI: missing lane")
|
|
84
|
+
|
|
85
|
+
-- | Decodes a complete generated value from its flattened lane payload.
|
|
86
|
+
runLift :: Codec a -> Ownership -> [Word64] -> IO a
|
|
87
|
+
runLift codec own values = fst <$> runStateT (liftValue codec own) values
|
|
88
|
+
|
|
89
|
+
-- | Isolates a variant payload so its decoder cannot consume the next argument.
|
|
90
|
+
lanes :: Int -> Lift [Word64]
|
|
91
|
+
lanes n = do
|
|
92
|
+
xs <- get
|
|
93
|
+
unless (length xs >= n) (liftIO (fail "canonical ABI: missing payload"))
|
|
94
|
+
put (drop n xs)
|
|
95
|
+
pure (take n xs)
|
|
96
|
+
|
|
97
|
+
-- | Fills unused variant lanes to the width chosen by the canonical ABI.
|
|
98
|
+
pad :: Int -> [Word64] -> [Word64]
|
|
99
|
+
pad n xs = take n (xs ++ repeat 0)
|
|
100
|
+
|
|
101
|
+
-- | Releases transferred input storage while retaining borrowed memory.
|
|
102
|
+
freeOwned :: Ownership -> Ptr a -> IO ()
|
|
103
|
+
freeOwned own p = if own == Owned then free p else pure ()
|
|
104
|
+
|
|
105
|
+
-- | Represents an absent result payload without allocating any lanes.
|
|
106
|
+
unitCodec :: Codec ()
|
|
107
|
+
unitCodec = Codec 0 0 (\_ _ -> pure ()) (\_ _ _ -> pure ()) (\_ _ -> pure []) (\_ -> pure ())
|
|
108
|
+
|
|
109
|
+
-- | Uses the ABI little-endian memory32 representation for unsigned words.
|
|
110
|
+
u32Codec :: Codec Word32
|
|
111
|
+
u32Codec = integralCodec 4
|
|
112
|
+
|
|
113
|
+
-- | Keeps byte storage distinct from its widened i32 call representation.
|
|
114
|
+
u8Codec :: Codec Word8
|
|
115
|
+
u8Codec = integralCodec 1
|
|
116
|
+
|
|
117
|
+
-- | Keeps a complete i64 lane without converting through a machine-sized Int.
|
|
118
|
+
wideWord :: Lift Word64
|
|
119
|
+
wideWord = do
|
|
120
|
+
xs <- get
|
|
121
|
+
case xs of
|
|
122
|
+
x : rest -> put rest >> pure x
|
|
123
|
+
_ -> liftIO (fail "canonical ABI: missing lane")
|
|
124
|
+
|
|
125
|
+
-- | Memory width and flattening width are distinct for small integer types.
|
|
126
|
+
integralCodec :: (Integral a, Storable a) => Int -> Codec a
|
|
127
|
+
integralCodec size =
|
|
128
|
+
Codec
|
|
129
|
+
size
|
|
130
|
+
1
|
|
131
|
+
(\_ p -> peek (castPtr p))
|
|
132
|
+
(\_ p x -> poke (castPtr p) x)
|
|
133
|
+
(\_ x -> pure [fromIntegral x])
|
|
134
|
+
(\_ -> fromIntegral <$> wideWord)
|
|
135
|
+
|
|
136
|
+
u16Codec :: Codec Word16
|
|
137
|
+
u16Codec = integralCodec 2
|
|
138
|
+
|
|
139
|
+
u64Codec :: Codec Word64
|
|
140
|
+
u64Codec = integralCodec 8
|
|
141
|
+
|
|
142
|
+
s8Codec :: Codec Int8
|
|
143
|
+
s8Codec = integralCodec 1
|
|
144
|
+
|
|
145
|
+
s16Codec :: Codec Int16
|
|
146
|
+
s16Codec = integralCodec 2
|
|
147
|
+
|
|
148
|
+
s32Codec :: Codec Int32
|
|
149
|
+
s32Codec = integralCodec 4
|
|
150
|
+
|
|
151
|
+
s64Codec :: Codec Int64
|
|
152
|
+
s64Codec = integralCodec 8
|
|
153
|
+
|
|
154
|
+
-- | Maps canonical boolean bytes and lanes to Haskell Bool.
|
|
155
|
+
boolCodec :: Codec Bool
|
|
156
|
+
boolCodec = Codec 1 1 (\_ p -> (/= 0) <$> peek p) (\_ p x -> poke p (if x then 1 else 0)) (\_ x -> pure [if x then 1 else 0]) (\_ -> (/= 0) <$> word)
|
|
157
|
+
|
|
158
|
+
-- | Copies UTF-8 into Haskell ownership and rejects invalid text on lifting.
|
|
159
|
+
textCodec :: Codec Text
|
|
160
|
+
textCodec = Codec 8 2 readText writeText lowerText liftText
|
|
161
|
+
where
|
|
162
|
+
readText own p = do
|
|
163
|
+
ptr <- peekByteOff p 0
|
|
164
|
+
len <- peekByteOff p 4
|
|
165
|
+
decode own ptr len
|
|
166
|
+
decode own ptr len = do
|
|
167
|
+
bytes <- B.packCStringLen (castPtr (pointer ptr), fromIntegral (len :: Word32))
|
|
168
|
+
freeOwned own (pointer ptr)
|
|
169
|
+
either (fail . show) pure (T.decodeUtf8' bytes)
|
|
170
|
+
lowerText arena value = do
|
|
171
|
+
let bytes = T.encodeUtf8 value
|
|
172
|
+
p <- allocate arena (B.length bytes)
|
|
173
|
+
B.useAsCStringLen bytes $ \(src, n) -> copyBytes p (castPtr src) n
|
|
174
|
+
pure [fromIntegral (address p), fromIntegral (B.length bytes)]
|
|
175
|
+
writeText arena p value = do
|
|
176
|
+
values <- lowerText arena value
|
|
177
|
+
case values of
|
|
178
|
+
[ptr, len] -> pokeByteOff p 0 (fromIntegral ptr :: Word32) >> pokeByteOff p 4 (fromIntegral len :: Word32)
|
|
179
|
+
_ -> fail "canonical text layout"
|
|
180
|
+
liftText own = do
|
|
181
|
+
ptr <- word
|
|
182
|
+
len <- word
|
|
183
|
+
liftIO (decode own ptr len)
|
|
184
|
+
|
|
185
|
+
-- | Uses upstream element stride and separately owns the array and nested values.
|
|
186
|
+
listCodec :: Codec a -> Codec [a]
|
|
187
|
+
listCodec item = Codec 8 2 readList writeList lowerList liftList
|
|
188
|
+
where
|
|
189
|
+
decode own ptr len = do
|
|
190
|
+
let p = pointer ptr
|
|
191
|
+
values <- forM [0 .. fromIntegral (len :: Word32) - 1] $ \i -> load item own (p `plusPtr` (i * byteSize item))
|
|
192
|
+
freeOwned own p
|
|
193
|
+
pure values
|
|
194
|
+
readList own p = do
|
|
195
|
+
ptr <- peekByteOff p 0
|
|
196
|
+
len <- peekByteOff p 4
|
|
197
|
+
decode own ptr len
|
|
198
|
+
lowerList arena values = do
|
|
199
|
+
let count = length values
|
|
200
|
+
unless (toInteger count * toInteger (byteSize item) <= 2147483647) (fail "canonical list allocation overflow")
|
|
201
|
+
p <- allocate arena (count * byteSize item)
|
|
202
|
+
forM_ (zip [0 ..] values) $ \(i, value) -> store item arena (p `plusPtr` (i * byteSize item)) value
|
|
203
|
+
pure [fromIntegral (address p), fromIntegral count]
|
|
204
|
+
writeList arena p values = do
|
|
205
|
+
raw <- lowerList arena values
|
|
206
|
+
case raw of
|
|
207
|
+
[ptr, len] -> pokeByteOff p 0 (fromIntegral ptr :: Word32) >> pokeByteOff p 4 (fromIntegral len :: Word32)
|
|
208
|
+
_ -> fail "canonical list layout"
|
|
209
|
+
liftList own = do
|
|
210
|
+
ptr <- word
|
|
211
|
+
len <- word
|
|
212
|
+
liftIO (decode own ptr len)
|
|
213
|
+
|
|
214
|
+
-- | Retains only the outstanding exported result until canonical post-return runs.
|
|
215
|
+
{-# NOINLINE returned #-}
|
|
216
|
+
returned :: IORef (Maybe Arena)
|
|
217
|
+
returned = unsafePerformIO (newIORef Nothing)
|
|
218
|
+
|
|
219
|
+
-- | Keeps exported buffers alive through lifting and detects a missing post-return.
|
|
220
|
+
exportResult :: Codec a -> IO a -> IO Word32
|
|
221
|
+
exportResult codec action = do
|
|
222
|
+
performMajorGC
|
|
223
|
+
value <- action
|
|
224
|
+
arena <- newArena
|
|
225
|
+
( do
|
|
226
|
+
p <- allocate arena (byteSize codec)
|
|
227
|
+
store codec arena p value
|
|
228
|
+
old <- atomicModifyIORef' returned (\previous -> (Just arena, previous))
|
|
229
|
+
case old of
|
|
230
|
+
Nothing -> pure (address p)
|
|
231
|
+
Just previous -> release previous >> fail "canonical ABI: missing post-return"
|
|
232
|
+
)
|
|
233
|
+
`onException` release arena
|
|
234
|
+
|
|
235
|
+
-- | Releases the outstanding result after the host has copied its canonical value.
|
|
236
|
+
postReturn :: Word32 -> IO ()
|
|
237
|
+
postReturn _ = do
|
|
238
|
+
arena <- atomicModifyIORef' returned (\previous -> (Nothing, previous))
|
|
239
|
+
maybe (pure ()) release arena
|
|
240
|
+
|
|
241
|
+
-- | Direct results have one scalar lane and no aggregate return allocation.
|
|
242
|
+
exportScalar :: (Integral b) => Codec a -> IO a -> IO b
|
|
243
|
+
exportScalar codec action = withArena $ \arena -> do
|
|
244
|
+
value <- action
|
|
245
|
+
raw <- lower codec arena value
|
|
246
|
+
case raw of
|
|
247
|
+
[lane] -> pure (fromIntegral lane)
|
|
248
|
+
_ -> fail "canonical ABI: expected one result lane"
|
|
249
|
+
|
|
250
|
+
-- | An owned handle and a borrowed representation have distinct canonical meanings.
|
|
251
|
+
-- Constructors stay inside generated internals; applications use typed accessors.
|
|
252
|
+
data GuestResource a
|
|
253
|
+
= GuestOwned Word32 (IORef Bool)
|
|
254
|
+
| GuestBorrowed Word32 Integer
|
|
255
|
+
deriving (Eq)
|
|
256
|
+
|
|
257
|
+
instance Show (GuestResource a) where
|
|
258
|
+
show (GuestOwned _ _) = "<owned resource>"
|
|
259
|
+
show (GuestBorrowed _ _) = "<borrowed resource>"
|
|
260
|
+
|
|
261
|
+
-- | Tracks dynamic borrow scopes without claiming Haskell has linear lifetimes.
|
|
262
|
+
{-# NOINLINE guestCall #-}
|
|
263
|
+
guestCall :: IORef (Integer, Maybe Integer)
|
|
264
|
+
guestCall = unsafePerformIO (newIORef (0, Nothing))
|
|
265
|
+
|
|
266
|
+
withGuestCall :: IO a -> IO a
|
|
267
|
+
withGuestCall action = bracket enter leave (const action)
|
|
268
|
+
where
|
|
269
|
+
enter = atomicModifyIORef' guestCall $ \(next, active) ->
|
|
270
|
+
((next + 1, Just next), active)
|
|
271
|
+
leave previous = modifyIORef' guestCall (\(next, _) -> (next, previous))
|
|
272
|
+
|
|
273
|
+
ownedGuestResource :: Word32 -> IO (GuestResource a)
|
|
274
|
+
ownedGuestResource handle = GuestOwned handle <$> newIORef True
|
|
275
|
+
|
|
276
|
+
borrowedGuestResource :: Word32 -> IO (GuestResource a)
|
|
277
|
+
borrowedGuestResource rep = do
|
|
278
|
+
(_, active) <- readIORef guestCall
|
|
279
|
+
maybe (fail "canonical ABI: missing borrow scope") (pure . GuestBorrowed rep) active
|
|
280
|
+
|
|
281
|
+
-- | Retains typed application state until the component resource destructor runs.
|
|
282
|
+
newGuestResource :: (Word32 -> IO Word32) -> a -> IO (GuestResource a)
|
|
283
|
+
newGuestResource create value = do
|
|
284
|
+
stable <- newStablePtr value
|
|
285
|
+
(create (address (castStablePtrToPtr stable)) >>= ownedGuestResource)
|
|
286
|
+
`onException` freeStablePtr stable
|
|
287
|
+
|
|
288
|
+
-- | Borrowed representations are usable only during the call that created them.
|
|
289
|
+
readGuestResource :: (Word32 -> IO Word32) -> GuestResource a -> IO a
|
|
290
|
+
readGuestResource rep resource = do
|
|
291
|
+
raw <- case resource of
|
|
292
|
+
GuestOwned _ _ -> guestHandle resource >>= rep
|
|
293
|
+
GuestBorrowed value epoch -> do
|
|
294
|
+
(_, active) <- readIORef guestCall
|
|
295
|
+
unless (active == Just epoch) (fail "canonical ABI: expired resource borrow")
|
|
296
|
+
pure value
|
|
297
|
+
deRefStablePtr (castPtrToStablePtr (castPtr (pointer raw)))
|
|
298
|
+
|
|
299
|
+
-- | Runs application cleanup exactly at destruction, including cleanup exceptions.
|
|
300
|
+
destroyGuestResource :: (a -> IO ()) -> Word32 -> IO ()
|
|
301
|
+
destroyGuestResource destroy raw = do
|
|
302
|
+
let stable = castPtrToStablePtr (castPtr (pointer raw))
|
|
303
|
+
(deRefStablePtr stable >>= destroy) `finally` freeStablePtr stable
|
|
304
|
+
|
|
305
|
+
-- | Borrowed handles cannot be dropped or transferred as owned values.
|
|
306
|
+
guestHandle :: GuestResource a -> IO Word32
|
|
307
|
+
guestHandle (GuestOwned handle live) = do
|
|
308
|
+
valid <- readIORef live
|
|
309
|
+
unless valid (fail "canonical ABI: resource ownership already transferred")
|
|
310
|
+
pure handle
|
|
311
|
+
guestHandle (GuestBorrowed _ _) = fail "canonical ABI: borrowed resource cannot escape its call"
|
|
312
|
+
|
|
313
|
+
-- | Invalidates aliases before an owned handle crosses the boundary or is dropped.
|
|
314
|
+
takeGuestHandle :: GuestResource a -> IO Word32
|
|
315
|
+
takeGuestHandle resource@(GuestOwned _ live) = do
|
|
316
|
+
handle <- guestHandle resource
|
|
317
|
+
writeIORef live False
|
|
318
|
+
pure handle
|
|
319
|
+
takeGuestHandle (GuestBorrowed _ _) = fail "canonical ABI: borrowed resource cannot transfer ownership"
|
|
320
|
+
|
|
321
|
+
guestResourceCodec :: Bool -> Codec (GuestResource a)
|
|
322
|
+
guestResourceCodec borrowed =
|
|
323
|
+
Codec
|
|
324
|
+
4
|
|
325
|
+
1
|
|
326
|
+
(\own p -> load u32Codec own p >>= construct)
|
|
327
|
+
(\arena p value -> lowerHandle value >>= store u32Codec arena p)
|
|
328
|
+
(\arena value -> lowerHandle value >>= lower u32Codec arena)
|
|
329
|
+
(\_ -> word >>= liftIO . construct)
|
|
330
|
+
where
|
|
331
|
+
construct = if borrowed then borrowedGuestResource else ownedGuestResource
|
|
332
|
+
lowerHandle = if borrowed then guestHandle else takeGuestHandle
|
|
333
|
+
|
|
334
|
+
-- | Wraps an opaque resource without exposing its representation to application code.
|
|
335
|
+
mapCodec :: (a -> b) -> (b -> a) -> Codec a -> Codec b
|
|
336
|
+
mapCodec wrap unwrap codec =
|
|
337
|
+
Codec
|
|
338
|
+
(byteSize codec)
|
|
339
|
+
(laneCount codec)
|
|
340
|
+
(\own p -> wrap <$> load codec own p)
|
|
341
|
+
(\arena p value -> store codec arena p (unwrap value))
|
|
342
|
+
(\arena value -> lower codec arena (unwrap value))
|
|
343
|
+
(\own -> wrap <$> liftValue codec own)
|
|
344
|
+
|
|
345
|
+
-- | Bulk binary values use ByteString rather than one heap object per octet.
|
|
346
|
+
bytesCodec :: Codec B.ByteString
|
|
347
|
+
bytesCodec = Codec 8 2 readBytes writeBytes lowerBytes liftBytes
|
|
348
|
+
where
|
|
349
|
+
decode own ptr len = do
|
|
350
|
+
value <- B.packCStringLen (castPtr (pointer ptr), fromIntegral (len :: Word32))
|
|
351
|
+
freeOwned own (pointer ptr)
|
|
352
|
+
pure value
|
|
353
|
+
readBytes own p = do
|
|
354
|
+
ptr <- peekByteOff p 0
|
|
355
|
+
len <- peekByteOff p 4
|
|
356
|
+
decode own ptr len
|
|
357
|
+
lowerBytes arena value = do
|
|
358
|
+
p <- allocate arena (B.length value)
|
|
359
|
+
B.useAsCStringLen value $ \(source, count) -> copyBytes p (castPtr source) count
|
|
360
|
+
pure [fromIntegral (address p), fromIntegral (B.length value)]
|
|
361
|
+
writeBytes arena p value = do
|
|
362
|
+
raw <- lowerBytes arena value
|
|
363
|
+
case raw of
|
|
364
|
+
[ptr, len] -> pokeByteOff p 0 (fromIntegral ptr :: Word32) >> pokeByteOff p 4 (fromIntegral len :: Word32)
|
|
365
|
+
_ -> fail "canonical byte buffer layout"
|
|
366
|
+
liftBytes own = do
|
|
367
|
+
ptr <- word
|
|
368
|
+
len <- word
|
|
369
|
+
liftIO (decode own ptr len)
|
package/haskell/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 brain-fuel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
cabal-version: 3.0
|
|
2
|
+
name: host-sdk-guest
|
|
3
|
+
version: 0.2.0
|
|
4
|
+
license: MIT
|
|
5
|
+
license-file: LICENSE
|
|
6
|
+
build-type: Simple
|
|
7
|
+
synopsis: Canonical memory32 support for generated typed Haskell components
|
|
8
|
+
library
|
|
9
|
+
exposed-modules: HostSDK.Canonical
|
|
10
|
+
hs-source-dirs: .
|
|
11
|
+
build-depends: base >= 4.18 && < 5, bytestring, text, transformers
|
|
12
|
+
default-language: Haskell2010
|
package/package.json
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wasm-platform-kit-build",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./tools/index.mjs",
|
|
8
|
+
"./package.json": "./package.json"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"tools",
|
|
12
|
+
"haskell",
|
|
13
|
+
"wit",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"wasm-platform-kit": "0.2.0",
|
|
19
|
+
"@bytecodealliance/jco": "1.41.0",
|
|
20
|
+
"prettier": "3.9.10",
|
|
21
|
+
"prettier-plugin-brace-style": "0.10.4"
|
|
22
|
+
},
|
|
23
|
+
"optionalDependencies": {
|
|
24
|
+
"wasm-platform-kit-darwin-arm64": "0.2.0",
|
|
25
|
+
"wasm-platform-kit-darwin-x64": "0.2.0",
|
|
26
|
+
"wasm-platform-kit-linux-arm64": "0.2.0",
|
|
27
|
+
"wasm-platform-kit-linux-x64": "0.2.0"
|
|
28
|
+
}
|
|
6
29
|
}
|