BrowserTrees.hs (3262B)
1 -- Private, run-local handles for the JS interaction interpreter inside the worker. 2 -- No handles cross RuntimeSession. Registration never forces a tree; observation 3 -- forces only its outer constructor. Sweep occurs between interpreter turns. 4 module BrowserTrees (begin, command, clear, active) where 5 6 import qualified BrowserRuntime as R 7 import Runtime.Tree 8 import Control.Exception (evaluate) 9 import Control.Monad (unless) 10 import Data.IORef 11 import qualified Data.IntMap.Lazy as IM 12 import System.IO.Unsafe (unsafePerformIO) 13 import Text.Read (readMaybe) 14 15 {-# NOINLINE trees #-} 16 trees :: IORef (Maybe (IM.IntMap T)) 17 trees = unsafePerformIO (newIORef Nothing) 18 {-# NOINLINE next #-} 19 next :: IORef Integer 20 next = unsafePerformIO (newIORef 1) 21 22 active :: IO Bool 23 active = maybe False (const True) <$> readIORef trees 24 clear :: IO () 25 clear = writeIORef trees Nothing 26 store :: IO (IM.IntMap T) 27 store = readIORef trees >>= maybe (fail "no private tree run") pure 28 put :: T -> IO Int 29 put value = do 30 table <- store 31 key <- readIORef next 32 -- wasm32 Int is narrower than a JS safe integer; never wrap either domain. 33 unless (key <= min 9007199254740991 (toInteger (maxBound :: Int))) (fail "private tree identity exhausted") 34 let ident = fromInteger key 35 writeIORef trees (Just (IM.insert ident value table)) 36 writeIORef next (key+1) 37 pure ident 38 ident :: String -> IO Int 39 ident text = case readMaybe text :: Maybe Integer of 40 Just value | value > 0 && value <= toInteger (maxBound :: Int) && show value == text -> pure (fromInteger value) 41 _ -> fail "invalid private tree handle" 42 get :: String -> IO T 43 get text = do 44 key <- ident text 45 table <- store 46 maybe (fail "stale private tree handle") pure (IM.lookup key table) 47 number :: Int -> R.J 48 number = R.N . toInteger 49 begin :: T -> IO R.J 50 begin action = do 51 busy <- active 52 unless (not busy) (fail "private tree run already active") 53 writeIORef trees (Just IM.empty) 54 leaf <- put Leaf 55 root <- put action 56 pure (R.O [("leaf",number leaf),("action",number root)]) 57 command :: [String] -> IO R.J 58 command fields = case fields of 59 ["observe",handle] -> do 60 tree <- get handle >>= evaluate 61 node <- case tree of 62 Leaf -> pure (R.O [("kind",R.S "leaf")]) 63 Stem child -> do 64 key <- put child 65 pure (R.O [("kind",R.S "stem"),("child",number key)]) 66 Fork left right -> do 67 a <- put left; b <- put right 68 pure (R.O [("kind",R.S "fork"),("left",number a),("right",number b)]) 69 pure (R.O [("node",node)]) 70 ["stem",a] -> get a >>= put . Stem >>= pure . number 71 ["fork",a,b] -> do 72 left <- get a; right <- get b 73 put (Fork left right) >>= pure . number 74 ["app",a,b] -> do 75 left <- get a; right <- get b 76 put (apply left right) >>= pure . number 77 "retain":handles -> do 78 unless (length handles <= 100000) (fail "private root limit") 79 keys <- mapM ident handles 80 table <- store 81 unless (all (`IM.member` table) keys) (fail "stale private tree handle") 82 writeIORef trees (Just (IM.fromList [(key,table IM.! key) | key <- keys])) 83 pure (number (length keys)) 84 ["view",handle,codec] -> get handle >>= R.view codec 85 ["count"] -> number . IM.size <$> store 86 ["clear"] -> clear >> pure (R.O []) 87 _ -> fail "invalid private tree operation"