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