Reactor.hs (14387B)
1 {-# LANGUAGE ForeignFunctionInterface #-} 2 {-# LANGUAGE ScopedTypeVariables #-} 3 module Reactor where 4 5 import qualified CompilerProbe as C 6 import qualified BrowserRuntime as R 7 import qualified BrowserModules as M 8 import Data.Array (Array, (!)) 9 import Text.Read (readMaybe) 10 import Runtime.Tree 11 import Runtime.Value 12 import Control.Exception (SomeException, SomeAsyncException, catch, evaluate, fromException, displayException) 13 import Control.Monad (unless) 14 import Data.IORef 15 import Data.List (nub) 16 import qualified Data.ByteString as BS 17 import qualified Data.Text as Text 18 import qualified Data.Text.Encoding as Text 19 import Foreign.Ptr (Ptr, castPtr) 20 import Foreign.C.Types (CInt(..)) 21 import Foreign.Marshal.Utils (copyBytes) 22 import System.IO.Unsafe (unsafePerformIO) 23 import System.Mem (performGC) 24 import System.IO.Error (isUserError, ioeGetErrorString) 25 26 {-# NOINLINE context #-} 27 context :: IORef (Maybe (T,T)) 28 context = unsafePerformIO (newIORef Nothing) 29 {-# NOINLINE current #-} 30 current :: IORef [(String,T)] 31 current = unsafePerformIO (newIORef []) 32 {-# NOINLINE compilerLibrary #-} 33 compilerLibrary :: IORef M.Bindings 34 compilerLibrary = unsafePerformIO (newIORef []) 35 {-# NOINLINE expressionBindings #-} 36 expressionBindings :: IORef M.Bindings 37 expressionBindings = unsafePerformIO (newIORef []) 38 {-# NOINLINE pendingModule #-} 39 pendingModule :: IORef (Maybe (M.Stage,[(String,M.Bindings)])) 40 pendingModule = unsafePerformIO (newIORef Nothing) 41 {-# NOINLINE response #-} 42 response :: IORef BS.ByteString 43 response = unsafePerformIO (newIORef BS.empty) 44 45 -- One owned response at a time; no tree handles or Haskell heap addresses cross 46 -- the boundary. JS copies it out before another request. Inputs are copied in. 47 reply :: String -> IO () 48 reply s = do 49 let bounded=take (16777216+1) s 50 unless (length bounded<=16777216) (fail "response limit exceeded") 51 let bytes=Text.encodeUtf8 (Text.pack bounded) 52 unless (BS.length bytes<=16777216) (fail "response byte limit exceeded") 53 _<-evaluate (BS.length bytes) 54 writeIORef response bytes 55 56 guardRequest :: IO () -> IO CInt 57 guardRequest action = (action >> pure 0) `catch` \(e::SomeException) -> do 58 let message=case fromException e of 59 Just ioError | isUserError ioError -> ioeGetErrorString ioError 60 _ -> displayException e 61 reply ("error\n"++take 2048 message) 62 pure (case (fromException e :: Maybe SomeAsyncException) of Just _->2;Nothing->1) 63 64 input :: Ptr () -> CInt -> IO String 65 input ptr len = do 66 unless (len>=0 && len<=16777216) (fail "input byte limit exceeded") 67 bytes<-BS.packCStringLen (castPtr ptr,fromIntegral len) 68 either (fail . show) (pure . Text.unpack) (Text.decodeUtf8' bytes) 69 70 initialize :: IO CInt 71 initialize = guardRequest $ do 72 existing<-readIORef context 73 case existing of Just _->fail "already initialized"; Nothing->pure () 74 (compiler,env,library)<-C.loadLibrary "/data" 75 writeIORef context (Just (compiler,env)) 76 writeIORef compilerLibrary library 77 reply "ok\nready" 78 79 compileSource :: Ptr () -> CInt -> IO CInt 80 compileSource ptr len = guardRequest $ do 81 source<-input ptr len 82 ctx<-readIORef context 83 (compiler,env)<-maybe (fail "not initialized") pure ctx 84 compileWith compiler env source 85 86 compileWith :: T -> T -> String -> IO () 87 compileWith compiler env source = do 88 requireIdle 89 unless(not(null source)&&length source<=16384)(fail "source scalar limit exceeded") 90 let argument=ofString source 91 _<-evaluate(C.force argument) 92 defs<-case C.definitions(C.compile compiler env argument) of 93 Left message->fail message 94 Right values->pure values 95 let names=map fst(take 17 defs) 96 unless(not(null names)&&length names<=16&&all (\n->not(null n)&&length n<=128) names&&length(nub names)==length names) 97 (fail "invalid compiler export names") 98 reply("ok\n"++unlines names) 99 writeIORef current defs 100 writeIORef expressionBindings defs 101 writeIORef pendingModule Nothing 102 writeIORef ioResult Nothing 103 104 observe :: Ptr () -> CInt -> IO CInt 105 observe ptr len = guardRequest $ do 106 request<-input ptr len 107 (name,args)<-case lines request of []->fail "missing export"; n:as->pure(n,as) 108 unless (length args<=16 && all ((<=50000).length) args) (fail "argument limit exceeded") 109 defs<-readIORef current 110 term<-maybe (fail "missing export") pure (lookup name defs) 111 let result=foldl apply term (map C.fromTernary args) 112 reply ("ok\n"++C.ternary result) 113 114 dump :: IO CInt 115 dump = guardRequest $ do 116 defs<-readIORef current 117 reply ("ok\n"++unlines [name++"\t"++C.ternary term | (name,term)<-defs]) 118 119 reset :: IO CInt 120 reset = guardRequest $ do 121 writeIORef current [] 122 writeIORef expressionBindings [] 123 writeIORef pendingModule Nothing 124 writeIORef interaction Nothing 125 writeIORef ioResult Nothing 126 writeIORef response BS.empty 127 performGC 128 reply "ok\nreset" 129 130 collect :: IO CInt 131 collect = guardRequest (performGC >> reply "ok\ncollected") 132 133 responseSize :: IO CInt 134 responseSize = fromIntegral . BS.length <$> readIORef response 135 responseCopy :: Ptr () -> CInt -> IO CInt 136 responseCopy ptr capacity = do 137 bytes<-readIORef response 138 if fromIntegral capacity<BS.length bytes then pure (-1) else do 139 BS.useAsCStringLen bytes $ \(src,len)->copyBytes ptr (castPtr src) len 140 writeIORef response BS.empty 141 pure (fromIntegral (BS.length bytes)) 142 143 144 {-# NOINLINE interaction #-} 145 interaction :: IORef (Maybe (Integer,R.Pending)) 146 interaction = unsafePerformIO (newIORef Nothing) 147 {-# NOINLINE nextToken #-} 148 nextToken :: IORef Integer 149 nextToken = unsafePerformIO (newIORef 0) 150 {-# NOINLINE ioResult #-} 151 ioResult :: IORef (Maybe T) 152 ioResult = unsafePerformIO (newIORef Nothing) 153 154 requireIdle :: IO () 155 requireIdle = do 156 active<-readIORef interaction 157 case active of Nothing->pure ();Just _->fail "IO interaction is pending" 158 159 emitEvent :: R.Pending -> IO () 160 emitEvent pending=do 161 token<-(+1) <$> readIORef nextToken 162 value<-R.event token pending 163 reply ("ok\n"++R.json value) 164 writeIORef nextToken token 165 case pending of 166 R.Pending (R.Done value)->do 167 writeIORef ioResult (Just value) 168 writeIORef interaction Nothing 169 _->writeIORef interaction (Just(token,pending)) 170 171 select :: BS.ByteString -> BS.ByteString -> [BS.ByteString] -> IO T 172 select nameBytes dag roots=do 173 name<-R.utf8 nameBytes 174 defs<-readIORef current 175 worker<-maybe (fail "missing export") pure (lookup name defs) 176 unless(length roots<=16)(fail "argument limit exceeded") 177 (trees,n)<-R.loadDAG dag 178 indices<-mapM (R.index n) roots 179 pure(foldl apply worker [trees!i | i<-indices]) 180 181 bindings :: Array Int T -> Int -> [BS.ByteString] -> IO [(String,T)] 182 bindings trees n entries=do 183 unless(length entries<=8192&&even(length entries))(fail "invalid binding table") 184 let pairs []=pure [] 185 pairs (nameBytes:rootBytes:rest)=do 186 name<-R.utf8 nameBytes 187 unless(not(null name)&&length name<=4096)(fail "invalid binding name") 188 root<-R.index n rootBytes 189 ((name,trees!root):) <$> pairs rest 190 pairs _=fail "invalid binding table" 191 defs<-pairs entries 192 unless(length(nub(map fst defs))==length defs)(fail "duplicate binding name") 193 pure defs 194 195 request :: Ptr () -> CInt -> IO CInt 196 request ptr len = guardRequest $ do 197 unless(len>=0 && len<=16777216)(fail "input byte limit exceeded") 198 bytes<-BS.packCStringLen(castPtr ptr,fromIntegral len) 199 fs<-R.fields bytes 200 (op,arguments)<-case fs of 201 tag:rest->do name<-R.utf8 tag;pure(name,rest) 202 _->fail "missing operation" 203 case (op,arguments) of 204 ("module-header",[sourceBytes])->do 205 requireIdle 206 initialized<-readIORef context 207 case initialized of Nothing->fail "compiler not initialized"; Just _->pure () 208 library<-readIORef compilerLibrary 209 source<-R.utf8 sourceBytes 210 staged<-M.stage library source 211 reply("ok\n"++R.json(M.stageJSON staged)) 212 ("module-stage",[sourceBytes])->do 213 requireIdle 214 writeIORef current [] 215 writeIORef expressionBindings [] 216 writeIORef pendingModule Nothing 217 writeIORef ioResult Nothing 218 initialized<-readIORef context 219 case initialized of Nothing->fail "compiler not initialized"; Just _->pure () 220 library<-readIORef compilerLibrary 221 source<-R.utf8 sourceBytes 222 staged<-M.stage library source 223 reply("ok\n"++R.json(M.stageJSON staged)) 224 writeIORef pendingModule (Just (staged,[])) 225 ("module-dependency",refBytes:dag:entries)->do 226 requireIdle 227 saved<-readIORef pendingModule 228 (staged,modules)<-maybe (fail "No pending module") pure saved 229 ref<-R.utf8 refBytes 230 unless(length ref==64 && all (`elem` ("0123456789abcdef"::String)) ref && not(ref `elem` map fst modules)) 231 (fail "Invalid or duplicate module reference") 232 (trees,n)<-R.loadDAG dag 233 values<-bindings trees n entries 234 writeIORef pendingModule (Just(staged,modules++[(ref,values)])) 235 reply "ok\nloaded" 236 ("module-compile",[])->do 237 requireIdle 238 saved<-readIORef pendingModule 239 (staged,modules)<-maybe (fail "No pending module") pure saved 240 ctx<-readIORef context 241 (compiler,_)<-maybe (fail "compiler not initialized") pure ctx 242 library<-readIORef compilerLibrary 243 (public,scope)<-M.compile library compiler staged modules 244 reply("ok\n"++unlines(map fst public)) 245 writeIORef current public 246 writeIORef expressionBindings scope 247 writeIORef pendingModule Nothing 248 ("module-scope",[])->do 249 scope<-readIORef expressionBindings 250 reply("ok\n"++R.json(R.A(map (R.S . fst) scope))) 251 ("module-abort",[])->do 252 requireIdle 253 writeIORef current [] 254 writeIORef expressionBindings [] 255 writeIORef pendingModule Nothing 256 writeIORef ioResult Nothing 257 reply "ok\naborted" 258 ("compile",sourceBytes:dag:entries)->do 259 requireIdle 260 ctx<-readIORef context 261 (compiler,_)<-maybe (fail "compiler not initialized") pure ctx 262 source<-R.utf8 sourceBytes 263 (trees,n)<-R.loadDAG dag 264 defs<-bindings trees n entries 265 compileWith compiler (ofList [Fork (ofString name) tree | (name,tree)<-defs]) source 266 -- Compile against existing named trees without serializing, evaluating or 267 -- replacing them. The temporary result lives only in this call/IO session. 268 ("expression",[nameBytes,sourceBytes,modeBytes,codec])->do 269 requireIdle 270 ctx<-readIORef context 271 (compiler,_)<-maybe (fail "compiler not initialized") pure ctx 272 name<-R.utf8 nameBytes 273 source<-R.utf8 sourceBytes 274 mode<-R.utf8 modeBytes 275 kind<-R.utf8 codec 276 unless(not(null source)&&length source<=16384)(fail "source scalar limit exceeded") 277 defs<-readIORef expressionBindings 278 let env=ofList [Fork (ofString key) tree | (key,tree)<-defs] 279 values<-either fail pure (C.definitions(C.compile compiler env (ofString source))) 280 term<-case values of 281 [(key,value)] | key==name->pure value 282 _->fail "Enter one expression, not a source unit" 283 case mode of 284 "pure"->do 285 value<-R.view kind term 286 reply("ok\n"++R.json value) 287 "io"->do 288 phase<-R.beginIO term 289 emitEvent(R.Pending phase) 290 writeIORef ioResult Nothing 291 _->fail "invalid expression mode" 292 ("load",dag:entries)->do 293 requireIdle 294 (trees,n)<-R.loadDAG dag 295 unless(not(null entries)&&length entries<=8192&&even(length entries))(fail "invalid export table") 296 defs<-bindings trees n entries 297 reply("ok\n"++R.json(R.A(map (R.S . fst) defs))) 298 writeIORef current defs 299 writeIORef expressionBindings defs 300 writeIORef pendingModule Nothing 301 writeIORef ioResult Nothing 302 ("evaluate",name:codec:dag:roots)->do 303 kind<-R.utf8 codec 304 term<-select name dag roots 305 value<-R.view kind term 306 reply("ok\n"++R.json value) 307 ("preview",name:limit:dag:roots)->do 308 size<-R.index 257 limit 309 term<-select name dag roots 310 value<-R.preview size term 311 reply("ok\n"++R.json value) 312 ("module-snapshot",[])->do 313 requireIdle 314 defs<-readIORef current 315 unless(not(null defs) && length defs<=4096)(fail "No compiled module") 316 value<-R.preview 100000 (foldr (Fork . snd) Leaf defs) 317 reply("ok\n"++R.json(R.O [("names",R.A(map (R.S . fst) defs)),("snapshot",value)])) 318 ("snapshot",name:limit:dag:roots)->do 319 size<-R.index 100001 limit 320 term<-select name dag roots 321 value<-R.preview size term 322 reply("ok\n"++R.json value) 323 ("io-start",name:dag:roots)->do 324 requireIdle 325 term<-select name dag roots 326 phase<-R.beginIO term 327 emitEvent(R.Pending phase) 328 writeIORef ioResult Nothing 329 ("io-resume",tokenBytes:valueBytes:[])->do 330 tokenText<-R.utf8 tokenBytes 331 supplied<-maybe (fail "invalid resume token") pure (readMaybe tokenText) 332 saved<-readIORef interaction 333 (token,pending)<-maybe (fail "no IO interaction") pure saved 334 unless(supplied==token)(fail "stale IO resume token") 335 value<-R.utf8 valueBytes 336 next<-R.resumeIO pending value 337 emitEvent next 338 ("io-cancel",[])->do 339 writeIORef interaction Nothing 340 writeIORef ioResult Nothing 341 reply("ok\n"++R.json(R.O[("kind",R.S "cancelled")])) 342 ("io-snapshot",[limit])->do 343 saved<-readIORef ioResult 344 result<-maybe (fail "no completed IO result") pure saved 345 size<-R.index 100001 limit 346 value<-R.preview size result 347 reply("ok\n"++R.json value) 348 ("io-view",[codec])->do 349 saved<-readIORef ioResult 350 result<-maybe (fail "no completed IO result") pure saved 351 kind<-R.utf8 codec 352 value<-R.view kind result 353 reply("ok\n"++R.json value) 354 _->fail "invalid operation fields" 355 356 initializeEmpty :: IO CInt 357 initializeEmpty = guardRequest (reply "ok\nready") 358 359 foreign export ccall "tricu_request" request :: Ptr () -> CInt -> IO CInt 360 foreign export ccall "tricu_initialize_empty" initializeEmpty :: IO CInt 361 362 foreign export ccall "tricu_initialize" initialize :: IO CInt 363 foreign export ccall "tricu_compile" compileSource :: Ptr () -> CInt -> IO CInt 364 foreign export ccall "tricu_observe" observe :: Ptr () -> CInt -> IO CInt 365 foreign export ccall "tricu_dump" dump :: IO CInt 366 foreign export ccall "tricu_reset" reset :: IO CInt 367 foreign export ccall "tricu_collect" collect :: IO CInt 368 foreign export ccall "tricu_response_size" responseSize :: IO CInt 369 foreign export ccall "tricu_response_copy" responseCopy :: Ptr () -> CInt -> IO CInt