PortableTestCompiler.hs (3340B)
1 -- Small test-only adapter for retained compiler fixtures. Not a module resolver: 2 -- imports select published fixture interfaces, and the caller selects definitions 3 -- to inspect/pack. No recursive source loading, injected kernels or fallback. 4 module PortableTestCompiler (compileFixtureDefinitions, compileFixtureFile) where 5 6 import Runtime 7 import ReferenceFixtures (compiledLibraries, fixtureExports) 8 import SupportFixture (readTestSource) 9 import Control.Exception (evaluate) 10 import Control.Monad (foldM) 11 import Data.List (isPrefixOf) 12 import qualified Data.Map as Map 13 import System.Timeout (timeout) 14 import Text.Read (readMaybe) 15 16 -- Preserve lazy definition values (including unused divergent bodies). Only the 17 -- Result/list/name spine is validated, under a fixed preparation deadline. 18 compileFixtureDefinitions :: Env -> String -> IO Env 19 compileFixtureDefinitions external source = do 20 prepared <- timeout (30 * 1000000) $ do 21 let bindings = ofList [Fork (ofString name) value | (name, value) <- Map.toList external] 22 answer = foldl apply (fixtureExports "source.unit" Map.! "compileStableModuleDefinitions") 23 [bindings, ofString source] 24 decode Leaf env = pure env 25 decode (Fork (Fork name value) rest) env = do 26 key <- either (fail . ("invalid compiled name: " ++)) pure (toString name) 27 if Map.member key env 28 then fail ("duplicate compiled definition: " ++ key) 29 else decode rest (Map.insert key value env) 30 decode _ _ = fail "malformed compiled definition list" 31 case answer of 32 Fork (Stem Leaf) (Fork definitions Leaf) -> decode definitions Map.empty >>= evaluate 33 other -> fail ("portable test compilation failed: " ++ show other) 34 maybe (fail "portable test compilation exceeded 30 seconds") pure prepared 35 36 compileFixtureFile :: FilePath -> IO Env 37 compileFixtureFile path = do 38 source <- readTestSource path 39 let rows = map words (lines source) 40 legacyFor alias = case [legacy | (legacy, mapped, _) <- fst compiledLibraries, mapped == alias] of 41 [legacy] -> pure legacy 42 _ -> fail ("unknown fixture import: " ++ alias) 43 select legacy namespace = 44 let exports = fixtureExports legacy 45 in pure $ maybe exports (\prefix -> Map.mapKeys ((prefix ++ ".") ++) exports) namespace 46 imported row = case row of 47 ["!import", encoded, namespace] | Just legacy <- readMaybe encoded -> 48 select legacy (if namespace == "!Local" then Nothing else Just namespace) 49 ["!import", alias, "*"] -> legacyFor alias >>= \legacy -> select legacy Nothing 50 ["!import", alias, "as", namespace] -> legacyFor alias >>= \legacy -> select legacy (Just namespace) 51 ["!import", alias] -> legacyFor alias >>= \legacy -> select legacy (Just alias) 52 _ -> fail ("unsupported fixture test import: " ++ unwords row) 53 merge env row = do 54 next <- imported row 55 if Map.null (Map.intersection env next) 56 then pure (Map.union env next) 57 else fail "overlapping fixture test imports" 58 body = unlines [line | line <- lines source, 59 not (any (`isPrefixOf` line) ["!import ", "!export ", "!source "])] 60 external <- foldM merge Map.empty [row | row@(first : _) <- rows, first == "!import"] 61 compileFixtureDefinitions external body