packages feed

haste-compiler 0.4.4.4 → 0.5.0

raw patch · 86 files changed

+6474/−2983 lines, 86 filesdep +Cabaldep +bin-package-dbdep +ghc-simpledep ~basedep ~directorydep ~ghcsetup-changednew-component:exe:haste-cabalPVP ok

version bump matches the API change (PVP)

Dependencies added: Cabal, bin-package-db, ghc-simple, terminfo, unix

Dependency ranges changed: base, directory, ghc, process

API changes (from Hackage documentation)

- Haste: JSFun :: (Ptr a) -> JSFun a
- Haste: OnBlur :: Event m (m ())
- Haste: OnChange :: Event m (m ())
- Haste: OnClick :: Event m (Int -> (Int, Int) -> m ())
- Haste: OnDblClick :: Event m (Int -> (Int, Int) -> m ())
- Haste: OnFocus :: Event m (m ())
- Haste: OnKeyDown :: Event m (Int -> m ())
- Haste: OnKeyPress :: Event m (Int -> m ())
- Haste: OnKeyUp :: Event m (Int -> m ())
- Haste: OnLoad :: Event m (m ())
- Haste: OnMouseDown :: Event m (Int -> (Int, Int) -> m ())
- Haste: OnMouseMove :: Event m ((Int, Int) -> m ())
- Haste: OnMouseOut :: Event m (m ())
- Haste: OnMouseOver :: Event m ((Int, Int) -> m ())
- Haste: OnMouseUp :: Event m (Int -> (Int, Int) -> m ())
- Haste: OnSubmit :: Event m (m ())
- Haste: OnUnload :: Event m (m ())
- Haste: OnWheel :: Event m ((Int, Int) -> (Double, Double, Double) -> m ())
- Haste: class Callback a
- Haste: class GenericCallback a m where type family CB a
- Haste: constCallback :: Callback a => IO () -> a
- Haste: data Event m a
- Haste: evtName :: IsString s => Event m a -> s
- Haste: jsSetCB :: Elem -> JSString -> JSFun a -> IO Bool
- Haste: jsSetTimeout :: Int -> JSFun a -> IO ()
- Haste: mkCallback :: a -> JSFun a
- Haste: mkIOfier :: GenericCallback a m => a -> m (m () -> IO ())
- Haste: mkcb :: GenericCallback a m => (m () -> IO ()) -> a -> CB a
- Haste: newtype JSFun a
- Haste: onEvent :: MonadIO m => Elem -> Event IO a -> a -> m Bool
- Haste: onEvent' :: (ToConcurrent a, MonadIO m) => Elem -> Event CIO a -> Async a -> m Bool
- Haste: setCallback :: MonadIO m => Elem -> Event IO a -> a -> m Bool
- Haste: setCallback' :: (ToConcurrent a, MonadIO m) => Elem -> Event CIO a -> Async a -> m Bool
- Haste: setTimeout :: MonadIO m => Int -> IO () -> m ()
- Haste: setTimeout' :: MonadIO m => Int -> CIO () -> m ()
- Haste: toCallback :: (Monad m, GenericCallback a m) => a -> m (CB a)
- Haste: type JSAny = Ptr Any
- Haste.Ajax: jsonRequest :: MonadIO m => Method -> URL -> [(Key, Val)] -> (Maybe JSON -> IO ()) -> m ()
- Haste.Ajax: jsonRequest_ :: MonadIO m => Method -> JSString -> [(JSString, JSString)] -> (Maybe JSON -> IO ()) -> m ()
- Haste.Ajax: textRequest :: MonadIO m => Method -> URL -> [(Key, Val)] -> (Maybe String -> IO ()) -> m ()
- Haste.Ajax: textRequest_ :: MonadIO m => Method -> JSString -> [(JSString, JSString)] -> (Maybe JSString -> IO ()) -> m ()
- Haste.Ajax: type Key = String
- Haste.Ajax: type Val = String
- Haste.App: OnBlur :: Event m (m ())
- Haste.App: OnChange :: Event m (m ())
- Haste.App: OnClick :: Event m (Int -> (Int, Int) -> m ())
- Haste.App: OnDblClick :: Event m (Int -> (Int, Int) -> m ())
- Haste.App: OnFocus :: Event m (m ())
- Haste.App: OnKeyDown :: Event m (Int -> m ())
- Haste.App: OnKeyPress :: Event m (Int -> m ())
- Haste.App: OnKeyUp :: Event m (Int -> m ())
- Haste.App: OnLoad :: Event m (m ())
- Haste.App: OnMouseDown :: Event m (Int -> (Int, Int) -> m ())
- Haste.App: OnMouseMove :: Event m ((Int, Int) -> m ())
- Haste.App: OnMouseOut :: Event m (m ())
- Haste.App: OnMouseOver :: Event m ((Int, Int) -> m ())
- Haste.App: OnMouseUp :: Event m (Int -> (Int, Int) -> m ())
- Haste.App: OnSubmit :: Event m (m ())
- Haste.App: OnUnload :: Event m (m ())
- Haste.App: OnWheel :: Event m ((Int, Int) -> (Double, Double, Double) -> m ())
- Haste.App: cfgURL :: AppCfg -> String
- Haste.App: class ClientCallback a
- Haste.App: data Event m a
- Haste.App: evtName :: IsString s => Event m a -> s
- Haste.App: onEvent :: ClientCallback a => Elem -> Event Client a -> a -> Client ()
- Haste.App: setTimeout :: Int -> Client () -> Client ()
- Haste.App: type JSAny = Ptr Any
- Haste.Binary: instance [overlap ok] (Binary a, Binary b) => Binary (Either a b)
- Haste.Binary: instance [overlap ok] (Binary a, Binary b) => Binary (a, b)
- Haste.Binary: instance [overlap ok] (GBinary a, GBinary b) => GBinary (a :*: b)
- Haste.Binary: instance [overlap ok] (GSum a, GSum b, GBinary a, GBinary b) => GSum (a :+: b)
- Haste.Binary: instance [overlap ok] (GSum a, GSum b, GBinary a, GBinary b, SumSize a, SumSize b) => GBinary (a :+: b)
- Haste.Binary: instance [overlap ok] (SumSize a, SumSize b) => SumSize (a :+: b)
- Haste.Binary: instance [overlap ok] Binary ()
- Haste.Binary: instance [overlap ok] Binary Blob
- Haste.Binary: instance [overlap ok] Binary Char
- Haste.Binary: instance [overlap ok] Binary Double
- Haste.Binary: instance [overlap ok] Binary Float
- Haste.Binary: instance [overlap ok] Binary Int
- Haste.Binary: instance [overlap ok] Binary Int16
- Haste.Binary: instance [overlap ok] Binary Int32
- Haste.Binary: instance [overlap ok] Binary Int8
- Haste.Binary: instance [overlap ok] Binary Word16
- Haste.Binary: instance [overlap ok] Binary Word32
- Haste.Binary: instance [overlap ok] Binary Word8
- Haste.Binary: instance [overlap ok] Binary a => Binary (Maybe a)
- Haste.Binary: instance [overlap ok] Binary a => Binary [a]
- Haste.Binary: instance [overlap ok] Binary a => GBinary (K1 i a)
- Haste.Binary: instance [overlap ok] GBinary U1
- Haste.Binary: instance [overlap ok] GBinary V1
- Haste.Binary: instance [overlap ok] GBinary a => GBinary (M1 i c a)
- Haste.Binary: instance [overlap ok] GBinary a => GSum (C1 c a)
- Haste.Binary: instance [overlap ok] MonadBlob CIO
- Haste.Binary: instance [overlap ok] SumSize (C1 c a)
- Haste.Concurrent: instance GenericCallback (CIO ()) CIO
- Haste.DOM: Elem :: JSAny -> Elem
- Haste.DOM: addChild :: MonadIO m => Elem -> Elem -> m ()
- Haste.DOM: addChildBefore :: MonadIO m => Elem -> Elem -> Elem -> m ()
- Haste.DOM: blur :: MonadIO m => Elem -> m ()
- Haste.DOM: clearChildren :: MonadIO m => Elem -> m ()
- Haste.DOM: click :: MonadIO m => Elem -> m ()
- Haste.DOM: data Attribute
- Haste.DOM: document :: Elem
- Haste.DOM: documentBody :: Elem
- Haste.DOM: focus :: MonadIO m => Elem -> m ()
- Haste.DOM: getChildBefore :: MonadIO m => Elem -> m (Maybe Elem)
- Haste.DOM: getChildren :: MonadIO m => Elem -> m [Elem]
- Haste.DOM: getFirstChild :: MonadIO m => Elem -> m (Maybe Elem)
- Haste.DOM: getLastChild :: MonadIO m => Elem -> m (Maybe Elem)
- Haste.DOM: getProp' :: MonadIO m => Elem -> JSString -> m JSString
- Haste.DOM: getStyle' :: MonadIO m => Elem -> JSString -> m JSString
- Haste.DOM: instance IsString AttrName
- Haste.DOM: instance Pack Elem
- Haste.DOM: instance Unpack Elem
- Haste.DOM: newtype Elem
- Haste.DOM: removeChild :: MonadIO m => Elem -> Elem -> m ()
- Haste.DOM: set :: MonadIO m => Elem -> [Attribute] -> m ()
- Haste.DOM: setChildren :: MonadIO m => Elem -> [Elem] -> m ()
- Haste.DOM: setProp' :: MonadIO m => Elem -> JSString -> JSString -> m ()
- Haste.DOM: setStyle' :: MonadIO m => Elem -> JSString -> JSString -> m ()
- Haste.DOM: with :: MonadIO m => m Elem -> [Attribute] -> m Elem
- Haste.Foreign: class (Pack a, Unpack a) => Marshal a
- Haste.Foreign: class Pack a where pack = unsafePack
- Haste.Foreign: class Unpack a where unpack = unsafeUnpack
- Haste.Foreign: data Unpacked
- Haste.Foreign: instance [overlap ok] (Pack a, IOFun b) => IOFun (a -> b)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b) => Pack (a, b)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c) => Pack (a, b, c)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d) => Pack (a, b, c, d)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e) => Pack (a, b, c, d, e)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e, Pack f) => Pack (a, b, c, d, e, f)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e, Pack f, Pack g) => Pack (a, b, c, d, e, f, g)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e, Pack f, Pack g, Pack h) => Pack (a, b, c, d, e, f, g, h)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e, Pack f, Pack g, Pack h, Pack i) => Pack (a, b, c, d, e, f, g, h, i)
- Haste.Foreign: instance [overlap ok] (Pack a, Pack b, Pack c, Pack d, Pack e, Pack f, Pack g, Pack h, Pack i, Pack j) => Pack (a, b, c, d, e, f, g, h, i, j)
- Haste.Foreign: instance [overlap ok] (Pack a, Unpack a) => Marshal a
- Haste.Foreign: instance [overlap ok] (Unpack a, FFI b) => FFI (a -> b)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b) => Unpack (a, b)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c) => Unpack (a, b, c)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d) => Unpack (a, b, c, d)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e) => Unpack (a, b, c, d, e)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e, Unpack f) => Unpack (a, b, c, d, e, f)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e, Unpack f, Unpack g) => Unpack (a, b, c, d, e, f, g)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e, Unpack f, Unpack g, Unpack h) => Unpack (a, b, c, d, e, f, g, h)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e, Unpack f, Unpack g, Unpack h, Unpack i) => Unpack (a, b, c, d, e, f, g, h, i)
- Haste.Foreign: instance [overlap ok] (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e, Unpack f, Unpack g, Unpack h, Unpack i, Unpack j) => Unpack (a, b, c, d, e, f, g, h, i, j)
- Haste.Foreign: instance [overlap ok] IOFun (a -> b) => Unpack (a -> b)
- Haste.Foreign: instance [overlap ok] Pack ()
- Haste.Foreign: instance [overlap ok] Pack (Opaque a)
- Haste.Foreign: instance [overlap ok] Pack Bool
- Haste.Foreign: instance [overlap ok] Pack Double
- Haste.Foreign: instance [overlap ok] Pack Float
- Haste.Foreign: instance [overlap ok] Pack Int
- Haste.Foreign: instance [overlap ok] Pack Int16
- Haste.Foreign: instance [overlap ok] Pack Int32
- Haste.Foreign: instance [overlap ok] Pack Int8
- Haste.Foreign: instance [overlap ok] Pack JSAny
- Haste.Foreign: instance [overlap ok] Pack JSString
- Haste.Foreign: instance [overlap ok] Pack String
- Haste.Foreign: instance [overlap ok] Pack Unpacked
- Haste.Foreign: instance [overlap ok] Pack Word
- Haste.Foreign: instance [overlap ok] Pack Word16
- Haste.Foreign: instance [overlap ok] Pack Word32
- Haste.Foreign: instance [overlap ok] Pack Word8
- Haste.Foreign: instance [overlap ok] Pack a => FFI (IO a)
- Haste.Foreign: instance [overlap ok] Pack a => Pack (Maybe a)
- Haste.Foreign: instance [overlap ok] Pack a => Pack [a]
- Haste.Foreign: instance [overlap ok] Unpack ()
- Haste.Foreign: instance [overlap ok] Unpack (Opaque a)
- Haste.Foreign: instance [overlap ok] Unpack Bool
- Haste.Foreign: instance [overlap ok] Unpack Double
- Haste.Foreign: instance [overlap ok] Unpack Float
- Haste.Foreign: instance [overlap ok] Unpack Int
- Haste.Foreign: instance [overlap ok] Unpack Int16
- Haste.Foreign: instance [overlap ok] Unpack Int32
- Haste.Foreign: instance [overlap ok] Unpack Int8
- Haste.Foreign: instance [overlap ok] Unpack JSAny
- Haste.Foreign: instance [overlap ok] Unpack JSString
- Haste.Foreign: instance [overlap ok] Unpack String
- Haste.Foreign: instance [overlap ok] Unpack Unpacked
- Haste.Foreign: instance [overlap ok] Unpack Word
- Haste.Foreign: instance [overlap ok] Unpack Word16
- Haste.Foreign: instance [overlap ok] Unpack Word32
- Haste.Foreign: instance [overlap ok] Unpack Word8
- Haste.Foreign: instance [overlap ok] Unpack a => IOFun (IO a)
- Haste.Foreign: instance [overlap ok] Unpack a => Unpack (IO a)
- Haste.Foreign: instance [overlap ok] Unpack a => Unpack (Maybe a)
- Haste.Foreign: instance [overlap ok] Unpack a => Unpack [a]
- Haste.Foreign: pack :: Pack a => Unpacked -> a
- Haste.Foreign: unpack :: Unpack a => a -> Unpacked
- Haste.Graphics.Canvas: bitmapElem :: Bitmap -> Elem
- Haste.Graphics.Canvas: canvasElem :: Canvas -> Elem
- Haste.Graphics.Canvas: instance Pack Bitmap
- Haste.Graphics.Canvas: instance Pack Canvas
- Haste.Graphics.Canvas: instance Pack Ctx
- Haste.Graphics.Canvas: instance Unpack Bitmap
- Haste.Graphics.Canvas: instance Unpack Canvas
- Haste.Graphics.Canvas: instance Unpack Ctx
- Haste.Prim: data JSString
- Haste.Prim: instance [overlap ok] Eq JSString
- Haste.Prim: instance [overlap ok] IsString JSString
- Haste.Prim: instance [overlap ok] Ord JSString
- Haste.Prim: instance [overlap ok] Show JSString
- Haste.Prim: type JSAny = Ptr Any
- Haste.WebSockets: instance Pack WSComputation
- Haste.WebSockets: instance Pack WSOnBinMsg
- Haste.WebSockets: instance Pack WSOnError
- Haste.WebSockets: instance Pack WSOnMsg
- Haste.WebSockets: instance Pack WebSocket
- Haste.WebSockets: instance Unpack WSComputation
- Haste.WebSockets: instance Unpack WSOnBinMsg
- Haste.WebSockets: instance Unpack WSOnError
- Haste.WebSockets: instance Unpack WSOnMsg
- Haste.WebSockets: instance Unpack WebSocket
+ Haste: AttrName :: !JSString -> AttrName
+ Haste: Elem :: JSAny -> Elem
+ Haste: Once :: !Int -> Interval
+ Haste: PropName :: !JSString -> AttrName
+ Haste: Repeat :: !Int -> Interval
+ Haste: StyleName :: !JSString -> AttrName
+ Haste: addChild :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> m ()
+ Haste: addChildBefore :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> child -> m ()
+ Haste: appendChild :: (IsElem parent, IsElem child, MonadIO m) => parent -> child -> m ()
+ Haste: attribute :: AttrName -> JSString -> Attribute
+ Haste: blur :: (IsElem e, MonadIO m) => e -> m ()
+ Haste: children :: [Elem] -> Attribute
+ Haste: class IsElem a where fromElem = const $ return Nothing
+ Haste: clearChildren :: (IsElem e, MonadIO m) => e -> m ()
+ Haste: click :: (IsElem e, MonadIO m) => e -> m ()
+ Haste: data AttrName
+ Haste: data Attribute
+ Haste: data Interval
+ Haste: data JSAny
+ Haste: data Timer
+ Haste: deleteChild :: (IsElem parent, IsElem child, MonadIO m) => parent -> child -> m ()
+ Haste: document :: Elem
+ Haste: documentBody :: Elem
+ Haste: elemOf :: IsElem a => a -> Elem
+ Haste: focus :: (IsElem e, MonadIO m) => e -> m ()
+ Haste: fromElem :: IsElem a => Elem -> IO (Maybe a)
+ Haste: getChildBefore :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste: getChildren :: (IsElem e, MonadIO m) => e -> m [Elem]
+ Haste: getFirstChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste: getLastChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste: insertChildBefore :: (IsElem parent, IsElem before, IsElem child, MonadIO m) => parent -> before -> child -> m ()
+ Haste: newtype Elem
+ Haste: removeChild :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> m ()
+ Haste: set :: (IsElem e, MonadIO m) => e -> [Attribute] -> m ()
+ Haste: setChildren :: (IsElem parent, IsElem child, MonadIO m) => parent -> [child] -> m ()
+ Haste: setTimer :: MonadEvent m => Interval -> m () -> m Timer
+ Haste: stopTimer :: MonadIO m => Timer -> m ()
+ Haste: with :: (IsElem e, MonadIO m) => m e -> [Attribute] -> m e
+ Haste.Ajax: ajaxRequest :: (MonadIO m, JSType a, JSType b, JSType c) => Method -> URL -> [(a, b)] -> (Maybe c -> IO ()) -> m ()
+ Haste.Ajax: noParams :: [((), ())]
+ Haste.App: AttrName :: !JSString -> AttrName
+ Haste.App: Elem :: JSAny -> Elem
+ Haste.App: PropName :: !JSString -> AttrName
+ Haste.App: StyleName :: !JSString -> AttrName
+ Haste.App: addChild :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> m ()
+ Haste.App: addChildBefore :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> child -> m ()
+ Haste.App: appendChild :: (IsElem parent, IsElem child, MonadIO m) => parent -> child -> m ()
+ Haste.App: attribute :: AttrName -> JSString -> Attribute
+ Haste.App: blur :: (IsElem e, MonadIO m) => e -> m ()
+ Haste.App: cfgHost :: AppCfg -> String
+ Haste.App: children :: [Elem] -> Attribute
+ Haste.App: class IsElem a where fromElem = const $ return Nothing
+ Haste.App: clearChildren :: (IsElem e, MonadIO m) => e -> m ()
+ Haste.App: click :: (IsElem e, MonadIO m) => e -> m ()
+ Haste.App: data AttrName
+ Haste.App: data Attribute
+ Haste.App: data JSAny
+ Haste.App: deleteChild :: (IsElem parent, IsElem child, MonadIO m) => parent -> child -> m ()
+ Haste.App: document :: Elem
+ Haste.App: documentBody :: Elem
+ Haste.App: elemOf :: IsElem a => a -> Elem
+ Haste.App: focus :: (IsElem e, MonadIO m) => e -> m ()
+ Haste.App: fromElem :: IsElem a => Elem -> IO (Maybe a)
+ Haste.App: getChildBefore :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste.App: getChildren :: (IsElem e, MonadIO m) => e -> m [Elem]
+ Haste.App: getFirstChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste.App: getLastChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)
+ Haste.App: insertChildBefore :: (IsElem parent, IsElem before, IsElem child, MonadIO m) => parent -> before -> child -> m ()
+ Haste.App: newtype Elem
+ Haste.App: removeChild :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> m ()
+ Haste.App: set :: (IsElem e, MonadIO m) => e -> [Attribute] -> m ()
+ Haste.App: setChildren :: (IsElem parent, IsElem child, MonadIO m) => parent -> [child] -> m ()
+ Haste.App: with :: (IsElem e, MonadIO m) => m e -> [Attribute] -> m e
+ Haste.Audio: AudioSettings :: !Bool -> !Bool -> !Bool -> !AudioPreload -> !Bool -> !Double -> AudioSettings
+ Haste.Audio: AudioSource :: !AudioType -> !JSString -> AudioSource
+ Haste.Audio: Auto :: AudioPreload
+ Haste.Audio: End :: Seek
+ Haste.Audio: Ended :: AudioState
+ Haste.Audio: MP3 :: AudioType
+ Haste.Audio: Metadata :: AudioPreload
+ Haste.Audio: None :: AudioPreload
+ Haste.Audio: OGG :: AudioType
+ Haste.Audio: Paused :: AudioState
+ Haste.Audio: Playing :: AudioState
+ Haste.Audio: Seconds :: Double -> Seek
+ Haste.Audio: Start :: Seek
+ Haste.Audio: WAV :: AudioType
+ Haste.Audio: audioAutoplay :: AudioSettings -> !Bool
+ Haste.Audio: audioControls :: AudioSettings -> !Bool
+ Haste.Audio: audioLooping :: AudioSettings -> !Bool
+ Haste.Audio: audioMuted :: AudioSettings -> !Bool
+ Haste.Audio: audioPreload :: AudioSettings -> !AudioPreload
+ Haste.Audio: audioVolume :: AudioSettings -> !Double
+ Haste.Audio: data Audio
+ Haste.Audio: data AudioPreload
+ Haste.Audio: data AudioSettings
+ Haste.Audio: data AudioSource
+ Haste.Audio: data AudioState
+ Haste.Audio: data AudioType
+ Haste.Audio: data Seek
+ Haste.Audio: defaultAudioSettings :: AudioSettings
+ Haste.Audio: getCurrentTime :: MonadIO m => Audio -> m Double
+ Haste.Audio: getDuration :: MonadIO m => Audio -> m Double
+ Haste.Audio: getState :: MonadIO m => Audio -> m AudioState
+ Haste.Audio: getVolume :: MonadIO m => Audio -> m Double
+ Haste.Audio: instance Eq AudioPreload
+ Haste.Audio: instance Eq AudioSource
+ Haste.Audio: instance Eq AudioState
+ Haste.Audio: instance Eq AudioType
+ Haste.Audio: instance Eq Seek
+ Haste.Audio: instance IsElem Audio
+ Haste.Audio: instance IsString AudioSource
+ Haste.Audio: instance JSType AudioPreload
+ Haste.Audio: instance Show AudioSource
+ Haste.Audio: instance Show AudioState
+ Haste.Audio: instance Show AudioType
+ Haste.Audio: isLooping :: MonadIO m => Audio -> m Bool
+ Haste.Audio: isMute :: MonadIO m => Audio -> m Bool
+ Haste.Audio: mkSource :: JSString -> Maybe AudioSource
+ Haste.Audio: modVolume :: MonadIO m => Audio -> Double -> m ()
+ Haste.Audio: newAudio :: MonadIO m => AudioSettings -> [AudioSource] -> m Audio
+ Haste.Audio: pause :: MonadIO m => Audio -> m ()
+ Haste.Audio: play :: MonadIO m => Audio -> m ()
+ Haste.Audio: seek :: MonadIO m => Audio -> Seek -> m ()
+ Haste.Audio: setLooping :: MonadIO m => Audio -> Bool -> m ()
+ Haste.Audio: setMute :: MonadIO m => Audio -> Bool -> m ()
+ Haste.Audio: setSource :: MonadIO m => Audio -> AudioSource -> m ()
+ Haste.Audio: setVolume :: MonadIO m => Audio -> Double -> m ()
+ Haste.Audio: stop :: MonadIO m => Audio -> m ()
+ Haste.Audio: toggleLooping :: MonadIO m => Audio -> m ()
+ Haste.Audio: toggleMute :: MonadIO m => Audio -> m ()
+ Haste.Audio: togglePlaying :: MonadIO m => Audio -> m ()
+ Haste.Binary: decodeBlob :: (MonadBlob m, Binary a) => Blob -> m (Either String a)
+ Haste.Binary: getJSString :: Int -> Get JSString
+ Haste.Binary: instance (Binary a, Binary b) => Binary (Either a b)
+ Haste.Binary: instance (Binary a, Binary b) => Binary (a, b)
+ Haste.Binary: instance (GBinary a, GBinary b) => GBinary (a :*: b)
+ Haste.Binary: instance (GSum a, GSum b, GBinary a, GBinary b) => GSum (a :+: b)
+ Haste.Binary: instance (GSum a, GSum b, GBinary a, GBinary b, SumSize a, SumSize b) => GBinary (a :+: b)
+ Haste.Binary: instance (SumSize a, SumSize b) => SumSize (a :+: b)
+ Haste.Binary: instance Binary ()
+ Haste.Binary: instance Binary Blob
+ Haste.Binary: instance Binary Char
+ Haste.Binary: instance Binary Double
+ Haste.Binary: instance Binary Float
+ Haste.Binary: instance Binary Int
+ Haste.Binary: instance Binary Int16
+ Haste.Binary: instance Binary Int32
+ Haste.Binary: instance Binary Int8
+ Haste.Binary: instance Binary JSString
+ Haste.Binary: instance Binary Word16
+ Haste.Binary: instance Binary Word32
+ Haste.Binary: instance Binary Word8
+ Haste.Binary: instance Binary a => Binary (Maybe a)
+ Haste.Binary: instance Binary a => Binary [a]
+ Haste.Binary: instance Binary a => GBinary (K1 i a)
+ Haste.Binary: instance GBinary U1
+ Haste.Binary: instance GBinary V1
+ Haste.Binary: instance GBinary a => GBinary (M1 i c a)
+ Haste.Binary: instance GBinary a => GSum (C1 c a)
+ Haste.Binary: instance MonadBlob CIO
+ Haste.Binary: instance SumSize (C1 c a)
+ Haste.Binary: putJSString :: JSString -> Put
+ Haste.Compiler: DontMinify :: MinifyFlag
+ Haste.Compiler: Minify :: (Maybe FilePath) -> [ClosureOpt] -> MinifyFlag
+ Haste.Compiler: cfUseStrict :: CompileFlags -> Bool
+ Haste.Compiler: data MinifyFlag
+ Haste.Compiler: defaultFlags :: CompileFlags
+ Haste.Compiler: type ClosureOpt = String
+ Haste.DOM: class IsElem a where fromElem = const $ return Nothing
+ Haste.DOM: data Elem
+ Haste.DOM: elemOf :: IsElem a => a -> Elem
+ Haste.DOM: fromElem :: IsElem a => Elem -> IO (Maybe a)
+ Haste.DOM.JSString: (=:) :: AttrName -> AttrValue -> Attribute
+ Haste.DOM.JSString: attr :: JSString -> AttrName
+ Haste.DOM.JSString: class IsElem a where fromElem = const $ return Nothing
+ Haste.DOM.JSString: data AttrName
+ Haste.DOM.JSString: data Elem
+ Haste.DOM.JSString: elemById :: MonadIO m => ElemID -> m (Maybe Elem)
+ Haste.DOM.JSString: elemOf :: IsElem a => a -> Elem
+ Haste.DOM.JSString: elemsByClass :: MonadIO m => ElemClass -> m [Elem]
+ Haste.DOM.JSString: elemsByQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> m [Elem]
+ Haste.DOM.JSString: fromElem :: IsElem a => Elem -> IO (Maybe a)
+ Haste.DOM.JSString: getAttr :: (IsElem e, MonadIO m) => e -> PropID -> m JSString
+ Haste.DOM.JSString: getFileData :: (IsElem e, MonadIO m) => e -> Int -> m (Maybe Blob)
+ Haste.DOM.JSString: getFileName :: (IsElem e, MonadIO m) => e -> m JSString
+ Haste.DOM.JSString: getProp :: (IsElem e, MonadIO m) => e -> PropID -> m JSString
+ Haste.DOM.JSString: getStyle :: (IsElem e, MonadIO m) => e -> PropID -> m JSString
+ Haste.DOM.JSString: getValue :: (IsElem e, MonadIO m, JSType a) => e -> m (Maybe a)
+ Haste.DOM.JSString: hasClass :: (IsElem e, MonadIO m) => e -> JSString -> m Bool
+ Haste.DOM.JSString: mapQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m [a]
+ Haste.DOM.JSString: mapQS_ :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m ()
+ Haste.DOM.JSString: newElem :: MonadIO m => JSString -> m Elem
+ Haste.DOM.JSString: newTextElem :: MonadIO m => JSString -> m Elem
+ Haste.DOM.JSString: setAttr :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()
+ Haste.DOM.JSString: setClass :: (IsElem e, MonadIO m) => e -> JSString -> Bool -> m ()
+ Haste.DOM.JSString: setProp :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()
+ Haste.DOM.JSString: setStyle :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()
+ Haste.DOM.JSString: style :: JSString -> AttrName
+ Haste.DOM.JSString: toggleClass :: (IsElem e, MonadIO m) => e -> JSString -> m ()
+ Haste.DOM.JSString: type AttrValue = JSString
+ Haste.DOM.JSString: type ElemClass = JSString
+ Haste.DOM.JSString: type ElemID = JSString
+ Haste.DOM.JSString: type PropID = JSString
+ Haste.DOM.JSString: type QuerySelector = JSString
+ Haste.DOM.JSString: withElem :: MonadIO m => ElemID -> (Elem -> m a) -> m a
+ Haste.DOM.JSString: withElems :: MonadIO m => [ElemID] -> ([Elem] -> m a) -> m a
+ Haste.DOM.JSString: withElemsQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> ([Elem] -> m a) -> m a
+ Haste.Foreign: JSAny :: (Ptr Any) -> JSAny
+ Haste.Foreign: class FromAny a where listFromAny = mapM fromAny . fromPtr . __arr2lst 0
+ Haste.Foreign: class Generic a
+ Haste.Foreign: class JSFunc a
+ Haste.Foreign: class ToAny a where toAny x = case gToAny False g of { Tree x' -> toObject x' One x' -> if isEnum g then x' else toAny [x'] List x' -> toAny x' } where g = from x listToAny = __lst2arr . toPtr . map toAny
+ Haste.Foreign: constant :: FromAny a => JSString -> a
+ Haste.Foreign: fromAny :: FromAny a => JSAny -> IO a
+ Haste.Foreign: get :: FromAny a => JSAny -> JSString -> IO a
+ Haste.Foreign: has :: JSAny -> JSString -> IO Bool
+ Haste.Foreign: index :: FromAny a => JSAny -> Int -> IO a
+ Haste.Foreign: instance [overlap ok] (FromAny a, JSFunc b) => JSFunc (a -> b)
+ Haste.Foreign: instance [overlap ok] (FromAny a, JSFunc b) => ToAny (a -> b)
+ Haste.Foreign: instance [overlap ok] (ToAny a, FFI b) => FFI (a -> b)
+ Haste.Foreign: instance [overlap ok] (ToAny a, JS a ~ JSAny) => JSFunc a
+ Haste.Foreign: instance [overlap ok] FFI a => FromAny a
+ Haste.Foreign: instance [overlap ok] FromAny a => FFI (IO a)
+ Haste.Foreign: instance [overlap ok] ToAny a => JSFunc (IO a)
+ Haste.Foreign: instance [overlap ok] ToAny a => ToAny (IO a)
+ Haste.Foreign: listFromAny :: FromAny a => JSAny -> IO [a]
+ Haste.Foreign: listToAny :: ToAny a => [a] -> JSAny
+ Haste.Foreign: newtype JSAny
+ Haste.Foreign: nullValue :: JSAny
+ Haste.Foreign: toAny :: ToAny a => a -> JSAny
+ Haste.Foreign: toObject :: [(JSString, JSAny)] -> JSAny
+ Haste.Graphics.AnimationFrame: cancelAnimationFrame :: FrameRequest -> IO ()
+ Haste.Graphics.AnimationFrame: data FrameRequest
+ Haste.Graphics.AnimationFrame: instance FromAny FrameRequest
+ Haste.Graphics.AnimationFrame: instance ToAny FrameRequest
+ Haste.Graphics.AnimationFrame: requestAnimationFrame :: (HRTimeStamp -> IO ()) -> IO FrameRequest
+ Haste.Graphics.AnimationFrame: type HRTimeStamp = Double
+ Haste.Graphics.Canvas: instance FromAny Bitmap
+ Haste.Graphics.Canvas: instance FromAny Canvas
+ Haste.Graphics.Canvas: instance FromAny Ctx
+ Haste.Graphics.Canvas: instance IsElem Bitmap
+ Haste.Graphics.Canvas: instance IsElem Canvas
+ Haste.Graphics.Canvas: instance ToAny Bitmap
+ Haste.Graphics.Canvas: instance ToAny Canvas
+ Haste.Graphics.Canvas: instance ToAny Ctx
+ Haste.JSON: instance JSType JSON
+ Haste.JSString: all :: (Char -> Bool) -> JSString -> Bool
+ Haste.JSString: any :: (Char -> Bool) -> JSString -> Bool
+ Haste.JSString: append :: JSString -> JSString -> JSString
+ Haste.JSString: concat :: [JSString] -> JSString
+ Haste.JSString: concatMap :: (Char -> JSString) -> JSString -> JSString
+ Haste.JSString: cons :: Char -> JSString -> JSString
+ Haste.JSString: data RegEx
+ Haste.JSString: drop :: Int -> JSString -> JSString
+ Haste.JSString: empty :: JSString
+ Haste.JSString: foldl' :: (ToAny a, FromAny a) => (a -> Char -> a) -> a -> JSString -> a
+ Haste.JSString: foldr :: (ToAny a, FromAny a) => (Char -> a -> a) -> a -> JSString -> a
+ Haste.JSString: head :: JSString -> Char
+ Haste.JSString: init :: JSString -> JSString
+ Haste.JSString: instance IsString RegEx
+ Haste.JSString: intercalate :: JSString -> [JSString] -> JSString
+ Haste.JSString: last :: JSString -> Char
+ Haste.JSString: length :: JSString -> Int
+ Haste.JSString: map :: (Char -> Char) -> JSString -> JSString
+ Haste.JSString: match :: RegEx -> JSString -> [JSString]
+ Haste.JSString: matches :: JSString -> RegEx -> Bool
+ Haste.JSString: null :: JSString -> Bool
+ Haste.JSString: pack :: [Char] -> JSString
+ Haste.JSString: regex :: JSString -> JSString -> RegEx
+ Haste.JSString: replace :: JSString -> RegEx -> JSString -> JSString
+ Haste.JSString: replicate :: Int -> Char -> JSString
+ Haste.JSString: reverse :: JSString -> JSString
+ Haste.JSString: singleton :: Char -> JSString
+ Haste.JSString: snoc :: JSString -> Char -> JSString
+ Haste.JSString: splitAt :: Int -> JSString -> (JSString, JSString)
+ Haste.JSString: tail :: JSString -> JSString
+ Haste.JSString: take :: Int -> JSString -> JSString
+ Haste.JSString: unpack :: JSString -> [Char]
+ Haste.Object: (#) :: JSLookup a => a -> JSString -> IO JSObj
+ Haste.Object: TBoolean :: Type
+ Haste.Object: TFunction :: Type
+ Haste.Object: TNumber :: Type
+ Haste.Object: TObject :: Type
+ Haste.Object: TString :: Type
+ Haste.Object: TUndefined :: Type
+ Haste.Object: asBool :: JSObj -> IO (Maybe Bool)
+ Haste.Object: asNumber :: JSObj -> IO (Maybe Double)
+ Haste.Object: asString :: JSObj -> IO (Maybe JSString)
+ Haste.Object: data Type
+ Haste.Object: instance Enum Type
+ Haste.Object: instance Eq Type
+ Haste.Object: instance FromAny Type
+ Haste.Object: instance JSLookup JSObj
+ Haste.Object: instance JSLookup a => JSLookup (IO a)
+ Haste.Object: instance Show Type
+ Haste.Object: lookupPath :: JSObj -> [JSString] -> IO JSObj
+ Haste.Object: toObject :: [(JSString, JSAny)] -> JSAny
+ Haste.Object: type JSObj = Maybe JSAny
+ Haste.Object: typeOf :: JSObj -> IO Type
+ Haste.Parsing: instance Alternative Parse
+ Haste.Performance: navigationStart :: IO Double
+ Haste.Performance: now :: IO HRTimeStamp
+ Haste.Performance: type HRTimeStamp = Double
+ Haste.Prim: JSAny :: (Ptr Any) -> JSAny
+ Haste.Prim: JSString :: String -> JSString
+ Haste.Prim: instance Eq JSAny
+ Haste.Prim: instance Eq JSString
+ Haste.Prim: instance IsString JSString
+ Haste.Prim: instance Ord JSString
+ Haste.Prim: instance Show JSString
+ Haste.Prim: newtype JSAny
+ Haste.Prim: newtype JSString
+ Haste.Prim: veryUnsafePerformIO :: IO a -> a
+ Haste.WebSockets: instance FromAny WebSocket
+ Haste.WebSockets: instance ToAny WebSocket
- Haste: onHashChange :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ()) => (String -> String -> m ()) -> m ()
+ Haste: onHashChange :: MonadIO m => (String -> String -> IO ()) -> m ()
- Haste: onHashChange' :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ()) => (JSString -> JSString -> m ()) -> m ()
+ Haste: onHashChange' :: MonadIO m => (JSString -> JSString -> IO ()) -> m ()
- Haste.App: onHashChange :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ()) => (String -> String -> m ()) -> m ()
+ Haste.App: onHashChange :: MonadIO m => (String -> String -> IO ()) -> m ()
- Haste.App: onHashChange' :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ()) => (JSString -> JSString -> m ()) -> m ()
+ Haste.App: onHashChange' :: MonadIO m => (JSString -> JSString -> IO ()) -> m ()
- Haste.Compiler: cfMinify :: CompileFlags -> Bool
+ Haste.Compiler: cfMinify :: CompileFlags -> MinifyFlag
- Haste.DOM: getAttr :: MonadIO m => Elem -> PropID -> m String
+ Haste.DOM: getAttr :: (IsElem e, MonadIO m) => e -> PropID -> m String
- Haste.DOM: getFileData :: MonadIO m => Elem -> Int -> m (Maybe Blob)
+ Haste.DOM: getFileData :: (IsElem e, MonadIO m) => e -> Int -> m (Maybe Blob)
- Haste.DOM: getFileName :: MonadIO m => Elem -> m String
+ Haste.DOM: getFileName :: (IsElem e, MonadIO m) => e -> m String
- Haste.DOM: getProp :: MonadIO m => Elem -> PropID -> m String
+ Haste.DOM: getProp :: (IsElem e, MonadIO m) => e -> PropID -> m String
- Haste.DOM: getStyle :: MonadIO m => Elem -> PropID -> m String
+ Haste.DOM: getStyle :: (IsElem e, MonadIO m) => e -> PropID -> m String
- Haste.DOM: getValue :: (MonadIO m, JSType a) => Elem -> m (Maybe a)
+ Haste.DOM: getValue :: (IsElem e, MonadIO m, JSType a) => e -> m (Maybe a)
- Haste.DOM: hasClass :: MonadIO m => Elem -> String -> m Bool
+ Haste.DOM: hasClass :: (IsElem e, MonadIO m) => e -> String -> m Bool
- Haste.DOM: mapQS :: MonadIO m => Elem -> QuerySelector -> (Elem -> m a) -> m [a]
+ Haste.DOM: mapQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m [a]
- Haste.DOM: mapQS_ :: MonadIO m => Elem -> QuerySelector -> (Elem -> m a) -> m ()
+ Haste.DOM: mapQS_ :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m ()
- Haste.DOM: setAttr :: MonadIO m => Elem -> PropID -> String -> m ()
+ Haste.DOM: setAttr :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()
- Haste.DOM: setClass :: MonadIO m => Elem -> String -> Bool -> m ()
+ Haste.DOM: setClass :: (IsElem e, MonadIO m) => e -> String -> Bool -> m ()
- Haste.DOM: setProp :: MonadIO m => Elem -> PropID -> String -> m ()
+ Haste.DOM: setProp :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()
- Haste.DOM: setStyle :: MonadIO m => Elem -> PropID -> String -> m ()
+ Haste.DOM: setStyle :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()
- Haste.DOM: toggleClass :: MonadIO m => Elem -> String -> m ()
+ Haste.DOM: toggleClass :: (IsElem e, MonadIO m) => e -> String -> m ()
- Haste.DOM: withElemsQS :: MonadIO m => Elem -> QuerySelector -> ([Elem] -> m a) -> m a
+ Haste.DOM: withElemsQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> ([Elem] -> m a) -> m a
- Haste.Foreign: export :: Unpack a => JSString -> a -> IO ()
+ Haste.Foreign: export :: ToAny a => JSString -> a -> IO ()
- Haste.Graphics.Canvas: createCanvas :: Int -> Int -> IO (Maybe Canvas)
+ Haste.Graphics.Canvas: createCanvas :: Int -> Int -> IO Canvas
- Haste.Graphics.Canvas: getCanvasById :: MonadIO m => ElemID -> m (Maybe Canvas)
+ Haste.Graphics.Canvas: getCanvasById :: MonadIO m => String -> m (Maybe Canvas)
- Haste.JSON: Bool :: {-# UNPACK #-} !Bool -> JSON
+ Haste.JSON: Bool :: !Bool -> JSON
- Haste.JSON: Str :: {-# UNPACK #-} !JSString -> JSON
+ Haste.JSON: Str :: !JSString -> JSON

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright Anton Ekblad 2012-2014+Copyright Anton Ekblad 2012-2015  All rights reserved. 
Setup.hs view
@@ -7,45 +7,81 @@ import System.Directory import System.FilePath import GHC.Paths+import Data.Version -main = defaultMainWithHooks $ simpleUserHooks {-    postBuild = \args buildflags pkgdesc buildinfo -> do-       when (buildinfo `has` "portable" ||-             buildinfo `has` "portable-compiler") $ do-         -- Figure out paths-         let dirname = "haste-compiler"-             exes = [ exeName exe-                    | exe <- executables pkgdesc]-             builddir = buildDir buildinfo-             outdir = dirname-             datadir = dataDir $ localPkgDescr buildinfo-             jsfiles = dataFiles $ localPkgDescr buildinfo-             hastedirfile = ".hastedir" -- does Haste "own" this directory?+showBootVersion :: Version -> LocalBuildInfo -> String+showBootVersion ver lbi =+    "haste-" ++ showVersion ver ++ "_ghc-" ++ ghcver+  where+    ghcver =+      case compilerId $ compiler lbi of+        CompilerId GHC ver -> showVersion ver+        _                  -> error "Haste only supports building with GHC!"++portablePostBuild :: PackageDescription -> LocalBuildInfo -> IO ()+portablePostBuild pkgdesc buildinfo = do+  -- Figure out paths+  let dirname = "haste-compiler"+      exes = [ exeName exe+             | exe <- executables pkgdesc]+      builddir = buildDir buildinfo+      outdir = dirname+      datadir = dataDir $ localPkgDescr buildinfo+      jsfiles = dataFiles $ localPkgDescr buildinfo+      hastedirfile = ".hastedir" -- does Haste "own" this directory?          -         dirExists <- doesDirectoryExist outdir-         isHasteDir <- doesFileExist (outdir </> hastedirfile)-         when (dirExists && not isHasteDir) $-           error $ "The output directory " ++ outdir ++ " already exists, "-                 ++ "and doesn't seem to be a Haste installation."-         when (dirExists && isHasteDir) $-           removeDirectoryRecursive outdir+  dirExists <- doesDirectoryExist outdir+  isHasteDir <- doesFileExist (outdir </> hastedirfile)+  when (dirExists && not isHasteDir) $+    error $ "The output directory " ++ outdir ++ " already exists, "+            ++ "and doesn't seem to be a Haste installation." -         -- Create directory and mark as ours-         createDirectoryIfMissing True (outdir </> "js")-         createDirectoryIfMissing True (outdir </> "bin")-         writeFile (outdir </> ".hastedir") ""+  when (dirExists && isHasteDir) $+    removeDirectoryRecursive outdir -         -- Copy executables-         forM_ exes $ \exe -> do-           exists <- doesFileExist $ builddir </> exe </> exe-           if exists-             then copyFile (builddir </> exe </> exe) (outdir </> "bin" </> exe)-             else copyFile (builddir </> exe </> exe <.> "exe")-                           (outdir </> "bin" </> exe <.> "exe")-         -         -- Copy libs-         forM_ jsfiles $ \js -> do-           copyFile (datadir </> js) (outdir </> "js" </> js)+  -- Create directory and mark as ours+  createDirectoryIfMissing True (outdir </> "js")+  createDirectoryIfMissing True (outdir </> "bin")+  writeFile (outdir </> ".hastedir") ""++  -- Copy settings and tools, except on Windows where we need a working+  -- Haskell Platform anyway.+#ifndef mingw32_HOST_OS+  copyGhcSettings outdir+#endif++  -- Copy executables+  forM_ exes $ \exe -> do+    exists <- doesFileExist $ builddir </> exe </> exe+    if exists+      then copyFile (builddir </> exe </> exe)+                    (outdir </> "bin" </> exe)+      else copyFile (builddir </> exe </> exe <.> "exe")+                    (outdir </> "bin" </> exe <.> "exe")++  -- Copy libs+  forM_ jsfiles $ \js -> do+    copyFile (datadir </> js) (outdir </> "js" </> js)++cabalPostBuild :: PackageDescription -> LocalBuildInfo -> IO ()+cabalPostBuild pkgdesc lbi = do+  appdir <- getAppUserDataDirectory "haste"+  let dest = appdir </> showBootVersion (pkgVersion $ package pkgdesc) lbi+  createDirectoryIfMissing True dest+  copyGhcSettings dest++-- | Copy GHC settings and utils into the given directory.+copyGhcSettings :: FilePath -> IO ()+copyGhcSettings dest = do+  copyFile (libdir </> "settings") (dest </> "settings")+  copyFile (libdir </> "platformConstants") (dest </> "platformConstants")+  copyFile (libdir </> "unlit") (dest </> "unlit")++main = defaultMainWithHooks $ simpleUserHooks {+    postBuild = \_ _ pkgdesc buildinfo -> do+      if (buildinfo `has` "portable" || buildinfo `has` "portable-compiler")+        then portablePostBuild pkgdesc buildinfo+        else cabalPostBuild pkgdesc buildinfo   }  has :: LocalBuildInfo -> String -> Bool
haste-compiler.cabal view
@@ -1,13 +1,13 @@ Name:           haste-compiler-Version:        0.4.4.4+Version:        0.5.0 License:        BSD3 License-File:   LICENSE Synopsis:       Haskell To ECMAScript compiler Description:    This package provides a featureful compiler from Haskell to-                Javascript. It generates small, fast code, makes use of-                standard Haskell libraries, integrates with Cabal, supports-                most GHC extensions and works on Windows, Linux and OSX.-                Bug reports are highly appreciated.+                JavaScript based on GHC. It generates small, fast code, makes+                use of standard Haskell libraries, integrates with Cabal,+                supports most GHC extensions and works on Windows, Linux and+                OSX. Category:       Javascript, Compiler, Web Cabal-Version:  >= 1.10 Build-Type:     Custom@@ -36,6 +36,11 @@     Canvas.js     Handle.js     Weak.js+    endian.js+    floatdecode.js+    jsflow.js+    jsstring.js+    Foreign.js  Flag portable     Description:@@ -103,7 +108,7 @@     Build-Depends:         base < 5,         ghc-prim,-        ghc >= 7.4,+        ghc >= 7.8 && < 7.10,         mtl,         binary,         containers,@@ -116,9 +121,10 @@         system-fileio,         shellmate >= 0.1.5,         either,-        directory+        directory,+        ghc-simple >= 0.1.2.1     Main-Is:-        Main.hs+        hastec.hs     Other-Modules:         Haste         Haste.Args@@ -145,10 +151,10 @@         Data.JSTarget.Traversal     default-language: Haskell98 -Executable haste-inst+Executable haste-cabal     if flag(only-library)         buildable: False-    Main-Is: haste-inst.hs+    Main-Is: haste-cabal.hs     Hs-Source-Dirs: src     if flag(portable)         CPP-Options: -DPORTABLE@@ -167,27 +173,44 @@     default-language: Haskell98  Executable haste-pkg+    Default-Language: Haskell2010+    Main-Is: haste-pkg.hs+    Other-Modules:+    Other-Extensions: CPP+    Hs-Source-Dirs:+      src+      utils/haste-pkg+     if flag(only-library)         buildable: False-    Main-Is: haste-pkg.hs-    Hs-Source-Dirs: src+     if flag(portable)         CPP-Options: -DPORTABLE+     if flag(static)         if os(windows)           GHC-Options: -static -optl-static         else           if os(linux)             GHC-Options: -static -optl-static -optl-pthread-    Build-Depends:-        base < 5,-        shellmate,-        ghc-paths,-        ghc,-        process,-        directory-    default-language: Haskell98 +    Build-Depends: base       >= 4   && < 5,+                   directory  >= 1   && < 1.3,+                   process    >= 1   && < 1.3,+                   Cabal,+                   containers,+                   filepath,+                   binary,+                   bin-package-db,+                   bytestring,+                   shellmate,+                   ghc+    if !os(windows)+        Build-Depends: unix,+                       terminfo+    if os(windows)+        c-sources: utils/haste-pkg/CRT_noglob.c+ Executable haste-install-his     if flag(only-library)         buildable: False@@ -231,6 +254,12 @@     Hs-Source-Dirs: src     if flag(portable)         CPP-Options: -DPORTABLE+    if flag(static)+        if os(windows)+          GHC-Options: -static -optl-static+        else+          if os(linux)+            GHC-Options: -static -optl-static -optl-pthread     Build-Depends:         base < 5,         shellmate >= 0.1.5,@@ -243,7 +272,9 @@         array,         random,         data-default,-        directory+        mtl,+        directory,+        utf8-string     default-language: Haskell98  Library@@ -251,45 +282,59 @@     GHC-Options: -Wall -O2     Exposed-Modules:         Haste+        Haste.Ajax         Haste.App         Haste.App.Concurrent+        Haste.Audio         Haste.Binary         Haste.Compiler-        Haste.JSON-        Haste.Ajax-        Haste.DOM-        Haste.Prim         Haste.Concurrent-        Haste.Graphics.Canvas+        Haste.DOM+        Haste.DOM.JSString+        Haste.Events         Haste.Foreign-        Haste.Serialize+        Haste.Graphics.AnimationFrame+        Haste.Graphics.Canvas+        Haste.JSON+        Haste.JSString+        Haste.LocalStorage+        Haste.Object         Haste.Parsing+        Haste.Performance+        Haste.Prim+        Haste.Serialize         Haste.WebSockets-        Haste.LocalStorage     Other-Modules:-        Haste.JSType-        Haste.Callback-        Haste.Compiler.Flags-        Haste.Version-        Haste.Environment-        Haste.GHCPaths-        Paths_haste_compiler-        Haste.Hash-        Haste.Random-        Haste.Concurrent.Monad-        Haste.Concurrent.Ajax+        Haste.Any         Haste.App.Client-        Haste.App.Events         Haste.App.Monad         Haste.App.Protocol+        Haste.Audio.Events         Haste.Binary.Get         Haste.Binary.Put         Haste.Binary.Types+        Haste.Compiler.Flags+        Haste.Concurrent.Monad+        Haste.Concurrent.Ajax+        Haste.DOM.Core+        Haste.Environment+        Haste.Events.BasicEvents+        Haste.Events.Core+        Haste.Events.KeyEvents+        Haste.Events.MouseEvents+        Haste.GHCPaths+        Haste.Hash+        Haste.JSType+        Haste.Random+        Haste.Timer+        Haste.Version+        Paths_haste_compiler     Build-Depends:         transformers,         monads-tf,         containers,         base < 5,+        ghc-prim,         random,         binary,         data-binary-ieee754,
+ lib/Foreign.js view
@@ -0,0 +1,75 @@+/* For foreign import ccall "wrapper" */+function createAdjustor(args, f, a, b) {+    return function(){+        var x = f.apply(null, arguments);+        while(x instanceof F) {x = x.f();}+        return x;+    };+}++var __apply = function(f,as) {+    var arr = [];+    for(; as[0] === 1; as = as[2]) {+        arr.push(as[1]);+    }+    arr.reverse();+    return f.apply(null, arr);+}+var __app0 = function(f) {return f();}+var __app1 = function(f,a) {return f(a);}+var __app2 = function(f,a,b) {return f(a,b);}+var __app3 = function(f,a,b,c) {return f(a,b,c);}+var __app4 = function(f,a,b,c,d) {return f(a,b,c,d);}+var __app5 = function(f,a,b,c,d,e) {return f(a,b,c,d,e);}+var __jsNull = function() {return null;}+var __jsTrue = function() {return true;}+var __jsFalse = function() {return false;}+var __eq = function(a,b) {return a===b;}+var __createJSFunc = function(arity, f){+    if(f instanceof Function && arity === f.length) {+        return (function() {+            var x = f.apply(null,arguments);+            if(x instanceof T) {+                if(x.f !== __blackhole) {+                    var ff = x.f;+                    x.f = __blackhole;+                    return x.x = ff();+                }+                return x.x;+            } else {+                while(x instanceof F) {+                    x = x.f();+                }+                return E(x);+            }+        });+    } else {+        return (function(){+            var as = Array.prototype.slice.call(arguments);+            as.push(0);+            return E(B(A(f,as)));+        });+    }+}+++function __arr2lst(elem,arr) {+    if(elem >= arr.length) {+        return [0];+    }+    return [1, arr[elem],new T(function(){return __arr2lst(elem+1,arr);})]+}++function __lst2arr(xs) {+    var arr = [];+    xs = E(xs);+    for(;xs[0] === 1; xs = E(xs[2])) {+        arr.push(E(xs[1]));+    }+    return arr;+}++var __new = function() {return ({});}+var __set = function(o,k,v) {o[k]=v;}+var __get = function(o,k) {return o[k];}+var __has = function(o,k) {return o[k]!==undefined;}
lib/StableName.js view
@@ -7,15 +7,20 @@ // as its stable name.  var __next_stable_name = 1;+var __stable_table;  function makeStableName(x) {-    if(!x.stableName) {-        x.stableName = __next_stable_name;-        __next_stable_name += 1;+    if(x instanceof Object) {+        if(!x.stableName) {+            x.stableName = __next_stable_name;+            __next_stable_name += 1;+        }+        return {type: 'obj', name: x.stableName};+    } else {+        return {type: 'prim', name: Number(x)};     }-    return x.stableName; }  function eqStableName(x, y) {-    return (x == y) ? 1 : 0;+    return (x.type == y.type && x.name == y.name) ? 1 : 0; }
+ lib/endian.js view
@@ -0,0 +1,29 @@+// Create a little endian ArrayBuffer representation of something.+function toABHost(v, n, x) {+    var a = new ArrayBuffer(n);+    new window[v](a)[0] = x;+    return a;+}++function toABSwap(v, n, x) {+    var a = new ArrayBuffer(n);+    new window[v](a)[0] = x;+    var bs = new Uint8Array(a);+    for(var i = 0, j = n-1; i < j; ++i, --j) {+        var tmp = bs[i];+        bs[i] = bs[j];+        bs[j] = tmp;+    }+    return a;+}++window['toABle'] = toABHost;+window['toABbe'] = toABSwap;++// Swap byte order if host is not little endian.+var buffer = new ArrayBuffer(2);+new DataView(buffer).setInt16(0, 256, true);+if(new Int16Array(buffer)[0] !== 256) {+    window['toABle'] = toABSwap;+    window['toABbe'] = toABHost;+}
+ lib/floatdecode.js view
@@ -0,0 +1,32 @@+// Scratch space for byte arrays.+var rts_scratchBuf = new ArrayBuffer(8);+var rts_scratchW32 = new Uint32Array(rts_scratchBuf);+var rts_scratchFloat = new Float32Array(rts_scratchBuf);+var rts_scratchDouble = new Float64Array(rts_scratchBuf);++function decodeFloat(x) {+    rts_scratchFloat[0] = x;+    var sign = x < 0 ? -1 : 1;+    var exp = ((rts_scratchW32[0] >> 23) & 0xff) - 150;+    var man = rts_scratchW32[0] & 0x7fffff;+    if(exp === 0) {+        ++exp;+    } else {+        man |= (1 << 23);+    }+    return [0, sign*man, exp];+}++function decodeDouble(x) {+    rts_scratchDouble[0] = x;+    var sign = x < 0 ? -1 : 1;+    var manHigh = rts_scratchW32[1] & 0xfffff;+    var manLow = rts_scratchW32[0];+    var exp = ((rts_scratchW32[1] >> 20) & 0x7ff) - 1075;+    if(exp === 0) {+        ++exp;+    } else {+        manHigh |= (1 << 20);+    }+    return [0, sign, manHigh, manLow, exp];+}
+ lib/jsflow.js view
@@ -0,0 +1,5 @@+/* JSFlow support functions. */+console = {+    'log': lprint,+    'warn': lprint+  };
+ lib/jsstring.js view
@@ -0,0 +1,48 @@+/* Utility functions for working with JSStrings. */++var _jss_singleton = String.fromCharCode;++function _jss_cons(c,s) {return String.fromCharCode(c)+s;}+function _jss_snoc(s,c) {return s+String.fromCharCode(c);}+function _jss_append(a,b) {return a+b;}+function _jss_len(s) {return s.length;}+function _jss_index(s,i) {return s.charCodeAt(i);}+function _jss_drop(s,i) {return s.substr(i);}+function _jss_substr(s,a,b) {return s.substr(a,b);}+function _jss_take(n,s) {return s.substr(0,n);}+// TODO: incorrect for some unusual characters.+function _jss_rev(s) {return s.split("").reverse().join("");}++function _jss_map(f,s) {+    f = E(f);+    var s2 = '';+    for(var i in s) {+        s2 += String.fromCharCode(E(f(s.charCodeAt(i))));+    }+    return s2;+}++function _jss_foldl(f,x,s) {+    f = E(f);+    for(var i in s) {+        x = A(f,[x,s.charCodeAt(i)]);+    }+    return x;+}++function _jss_re_match(s,re) {return s.search(re)>=0;}+function _jss_re_compile(re,fs) {return new RegExp(re,fs);}+function _jss_re_replace(s,re,rep) {return s.replace(re,rep);}++function _jss_re_find(re,s) {+    var a = s.match(re);+    return a ? mklst(a) : [0];+}++function mklst(arr) {+    var l = [0], len = arr.length-1;+    for(var i = 0; i <= len; ++i) {+        l = [1,arr[len-i],l];+    }+    return l;+}
lib/rts.js view
@@ -12,58 +12,66 @@     } } +/* Hint to optimizer that an imported symbol is strict. */+function __strict(x) {return x}++// A tailcall. function F(f) {     this.f = f; } +// A partially applied function. Invariant: members are never thunks.+function PAP(f, args) {+    this.f = f;+    this.args = args;+    this.arity = f.length - args.length;+}+ // Special object used for blackholing. var __blackhole = {};  // Used to indicate that an object is updatable. var __updatable = {}; -/* Apply-   Applies the function f to the arguments args. If the application is under--   saturated, a closure is returned, awaiting further arguments. If it is over--   saturated, the function is fully applied, and the result (assumed to be a-   function) is then applied to the remaining arguments.+/* Generic apply.+   Applies a function *or* a partial application object to a list of arguments.+   See https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/HaskellExecution/FunctionCalls+   for more information. */ function A(f, args) {-    if(f instanceof T) {+    while(true) {         f = E(f);-    }-    // Closure does some funny stuff with functions that occasionally-    // results in non-functions getting applied, so we have to deal with-    // it.-    if(!(f instanceof Function)) {-        f = B(f);-        if(!(f instanceof Function)) {+        if(f instanceof F) {+            f = E(B(f));+        }+        if(f instanceof PAP) {+            // f is a partial application+            if(args.length == f.arity) {+                // Saturated application+                return f.f.apply(null, f.args.concat(args));+            } else if(args.length < f.arity) {+                // Application is still unsaturated+                return new PAP(f.f, f.args.concat(args));+            } else {+                // Application is oversaturated; +                var f2 = f.f.apply(null, f.args.concat(args.slice(0, f.arity)));+                args = args.slice(f.arity);+                f = B(f2);+            }+        } else if(f instanceof Function) {+            if(args.length == f.length) {+                return f.apply(null, args);+            } else if(args.length < f.length) {+                return new PAP(f, args);+            } else {+                var f2 = f.apply(null, args.slice(0, f.length));+                args = args.slice(f.length);+                f = B(f2);+            }+        } else {             return f;         }     }--    if(f.arity === undefined) {-        f.arity = f.length;-    }-    if(args.length === f.arity) {-        switch(f.arity) {-            case 0:  return f();-            case 1:  return f(args[0]);-            default: return f.apply(null, args);-        }-    } else if(args.length > f.arity) {-        switch(f.arity) {-            case 0:  return f();-            case 1:  return A(f(args.shift()), args);-            default: return A(f.apply(null, args.splice(0, f.arity)), args);-        }-    } else {-        var g = function() {-            return A(f, args.concat(Array.prototype.slice.call(arguments)));-        };-        g.arity = f.arity - args.length;-        return g;-    } }  /* Eval@@ -114,7 +122,7 @@    We need to be able to use throw as an exception so we wrap it in a function. */ function die(err) {-    throw err;+    throw E(err); }  function quot(a, b) {@@ -162,39 +170,6 @@     return (Math.exp(arg) + Math.exp(-arg)) / 2; } -// Scratch space for byte arrays.-var rts_scratchBuf = new ArrayBuffer(8);-var rts_scratchW32 = new Uint32Array(rts_scratchBuf);-var rts_scratchFloat = new Float32Array(rts_scratchBuf);-var rts_scratchDouble = new Float64Array(rts_scratchBuf);--function decodeFloat(x) {-    rts_scratchFloat[0] = x;-    var sign = x < 0 ? -1 : 1;-    var exp = ((rts_scratchW32[0] >> 23) & 0xff) - 150;-    var man = rts_scratchW32[0] & 0x7fffff;-    if(exp === 0) {-        ++exp;-    } else {-        man |= (1 << 23);-    }-    return [0, sign*man, exp];-}--function decodeDouble(x) {-    rts_scratchDouble[0] = x;-    var sign = x < 0 ? -1 : 1;-    var manHigh = rts_scratchW32[1] & 0xfffff;-    var manLow = rts_scratchW32[0];-    var exp = ((rts_scratchW32[1] >> 20) & 0x7ff) - 1075;-    if(exp === 0) {-        ++exp;-    } else {-        manHigh |= (1 << 20);-    }-    return [0, sign, manHigh, manLow, exp];-}- function isFloatFinite(x) {     return isFinite(x); }@@ -215,7 +190,7 @@ function unFoldrCStr(str, f, z) {     var acc = z;     for(var i = str.length-1; i >= 0; --i) {-        acc = B(A(f, [[0, str.charCodeAt(i)], acc]));+        acc = B(A(f, [str.charCodeAt(i), acc]));     }     return acc; }@@ -225,7 +200,7 @@     if(i >= str.length) {         return E(chrs);     } else {-        return [1,[0,str.charCodeAt(i)],new T(function() {+        return [1,str.charCodeAt(i),new T(function() {             return unAppCStr(str,chrs,i+1);         })];     }@@ -240,7 +215,7 @@ function toJSStr(hsstr) {     var s = '';     for(var str = E(hsstr); str[0] == 1; str = E(str[2])) {-        s += String.fromCharCode(E(str[1])[1]);+        s += String.fromCharCode(E(str[1]));     }     return s; }@@ -296,11 +271,11 @@  function strOrd(a, b) {     if(a < b) {-        return [0];+        return 0;     } else if(a == b) {-        return [1];+        return 1;     }-    return [2];+    return 2; }  function jsCatch(act, handler) {
lib/stdlib.js view
@@ -40,7 +40,7 @@     return val == Math.round(val) ? ret + '.0' : ret; } -function jsGetMouseCoords(e) {+window['jsGetMouseCoords'] = function jsGetMouseCoords(e) {     var posx = 0;     var posy = 0;     if (!e) var e = window.event;@@ -58,76 +58,6 @@ 	    posy - (e.currentTarget.offsetTop || 0)]; } -function jsSetCB(elem, evt, cb) {-    // Count return press in single line text box as a change event.-    if(evt == 'change' && elem.type.toLowerCase() == 'text') {-        setCB(elem, 'keyup', function(k) {-            if(k == '\n'.charCodeAt(0)) {-                B(A(cb,[[0,k.keyCode],0]));-            }-        });-    }--    var fun;-    switch(evt) {-    case 'click':-    case 'dblclick':-    case 'mouseup':-    case 'mousedown':-        fun = function(x) {-            var mpos = jsGetMouseCoords(x);-            var mx = [0,mpos[0]];-            var my = [0,mpos[1]];-            B(A(cb,[[0,x.button],[0,mx,my],0]));-        };-        break;-    case 'mousemove':-    case 'mouseover':-        fun = function(x) {-            var mpos = jsGetMouseCoords(x);-            var mx = [0,mpos[0]];-            var my = [0,mpos[1]];-            B(A(cb,[[0,mx,my],0]));-        };-        break;-    case 'keypress':-    case 'keyup':-    case 'keydown':-        fun = function(x) {B(A(cb,[[0,x.keyCode],0]));};-        break;-    case 'wheel':-        fun = function(x) {-            var mpos = jsGetMouseCoords(x);-            var mx = [0,mpos[0]];-            var my = [0,mpos[1]];-            var mdx = [0,x.deltaX];-            var mdy = [0,x.deltaY];-            var mdz = [0,x.deltaZ];-            B(A(cb,[[0,mx,my],[0,mdx,mdy,mdz],0]));-        };-        break;-    default:-        fun = function() {B(A(cb,[0]));};-        break;-    }-    return setCB(elem, evt, fun);-}--function setCB(elem, evt, cb) {-    if(elem.addEventListener) {-        elem.addEventListener(evt, cb, false);-        return true;-    } else if(elem.attachEvent) {-        elem.attachEvent('on'+evt, cb);-        return true;-    }-    return false;-}--function jsSetTimeout(msecs, cb) {-    window.setTimeout(function() {B(A(cb,[0]));}, msecs);-}- function jsGet(elem, prop) {     return elem[prop].toString(); }@@ -169,7 +99,7 @@ function jsFind(elem) {     var e = document.getElementById(elem)     if(e) {-        return [1,[0,e]];+        return [1,e];     }     return [0]; }@@ -179,7 +109,7 @@     var els = [0];      for (var i = es.length-1; i >= 0; --i) {-        els = [1, [0, es[i]], els];+        els = [1, es[i], els];     }     return els; }@@ -194,7 +124,7 @@     nl = elem.querySelectorAll(query);      for (var i = nl.length-1; i >= 0; --i) {-        els = [1, [0, nl[i]], els];+        els = [1, nl[i], els];     }      return els;@@ -212,7 +142,7 @@     elem = elem.previousSibling;     while(elem) {         if(typeof elem.tagName != 'undefined') {-            return [1,[0,elem]];+            return [1,elem];         }         elem = elem.previousSibling;     }@@ -223,7 +153,7 @@     var len = elem.childNodes.length;     for(var i = len-1; i >= 0; --i) {         if(typeof elem.childNodes[i].tagName != 'undefined') {-            return [1,[0,elem.childNodes[i]]];+            return [1,elem.childNodes[i]];         }     }     return [0];@@ -234,7 +164,7 @@     var len = elem.childNodes.length;     for(var i = 0; i < len; i++) {         if(typeof elem.childNodes[i].tagName != 'undefined') {-            return [1,[0,elem.childNodes[i]]];+            return [1,elem.childNodes[i]];         }     }     return [0];@@ -246,7 +176,7 @@     var len = elem.childNodes.length;     for(var i = len-1; i >= 0; --i) {         if(typeof elem.childNodes[i].tagName != 'undefined') {-            children = [1, [0,elem.childNodes[i]], children];+            children = [1, elem.childNodes[i], children];         }     }     return children;@@ -256,7 +186,7 @@     children = E(children);     jsClearChildren(elem, 0);     while(children[0] === 1) {-        elem.appendChild(E(E(children[1])[1]));+        elem.appendChild(E(children[1]));         children = E(children[2]);     } }@@ -277,14 +207,12 @@     strs = E(strs);     while(strs[0]) {         strs = E(strs);-        arr.push(E(strs[1])[1]);+        arr.push(E(strs[1]));         strs = E(strs[2]);     }     return arr.join(sep); } -var jsJSONParse = JSON.parse;- // JSON stringify a string function jsStringify(str) {     return JSON.stringify(str);@@ -332,7 +260,7 @@             }             var xs = [0];             for(var i = 0; i < ks.length; i++) {-                xs = [1, [0, [0,ks[i]], toHS(obj[ks[i]])], xs];+                xs = [1, [0, ks[i], toHS(obj[ks[i]])], xs];             }             return [4, xs];         }@@ -346,23 +274,6 @@     return [1, toHS(arr[elem]), new T(function() {return arr2lst_json(arr,elem+1);}),true] } -function arr2lst(arr, elem) {-    if(elem >= arr.length) {-        return [0];-    }-    return [1, arr[elem], new T(function() {return arr2lst(arr,elem+1);})]-}-window['arr2lst'] = arr2lst;--function lst2arr(xs) {-    var arr = [];-    for(; xs[0]; xs = E(xs[2])) {-        arr.push(E(xs[1]));-    }-    return arr;-}-window['lst2arr'] = lst2arr;- function ajaxReq(method, url, async, postdata, cb) {     var xhr = new XMLHttpRequest();     xhr.open(method, url, async);@@ -374,7 +285,7 @@     xhr.onreadystatechange = function() {         if(xhr.readyState == 4) {             if(xhr.status == 200) {-                B(A(cb,[[1,[0,xhr.responseText]],0]));+                B(A(cb,[[1,xhr.responseText],0]));             } else {                 B(A(cb,[[0],0])); // Nothing             }@@ -383,32 +294,10 @@     xhr.send(postdata); } -// Create a little endian ArrayBuffer representation of something.-function toABHost(v, n, x) {-    var a = new ArrayBuffer(n);-    new window[v](a)[0] = x;-    return a;-}--function toABSwap(v, n, x) {-    var a = new ArrayBuffer(n);-    new window[v](a)[0] = x;-    var bs = new Uint8Array(a);-    for(var i = 0, j = n-1; i < j; ++i, --j) {-        var tmp = bs[i];-        bs[i] = bs[j];-        bs[j] = tmp;-    }-    return a;-}--window['toABle'] = toABHost;-window['toABbe'] = toABSwap;--// Swap byte order if host is not little endian.-var buffer = new ArrayBuffer(2);-new DataView(buffer).setInt16(0, 256, true);-if(new Int16Array(buffer)[0] !== 256) {-    window['toABle'] = toABSwap;-    window['toABbe'] = toABHost;+/* gettimeofday(2) */+function gettimeofday(tv, _tz) {+    var t = new Date().getTime();+    writeOffAddr("i32", 4, tv, 0, (t/1000)|0);+    writeOffAddr("i32", 4, tv, 1, ((t%1000)*1000)|0);+    return 0; }
libraries/haste-lib/src/Haste.hs view
@@ -1,21 +1,21 @@-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, CPP #-}+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, CPP,+             OverloadedStrings #-} -- | Haste's companion to the Prelude. -- --   Note that this module should *not* be imported together with --   "Haste.App", which provides the same functionality but slightly modified --   for automatic program slicing. module Haste (-    JSString, JSAny, URL, CB.GenericCallback,+    JSString, JSAny, URL,     alert, prompt, eval, writeLog, catJSStr, fromJSStr,-    module Haste.JSType, module Haste.DOM, module Haste.Callback,+    module Haste.JSType, module Haste.DOM.Core, module Haste.Timer,     module Haste.Random, module Haste.Hash   ) where import Haste.Prim-import Haste.Callback hiding (jsSetCB, jsSetTimeout, GenericCallback)-import qualified Haste.Callback as CB (GenericCallback)+import Haste.Timer import Haste.Random import Haste.JSType-import Haste.DOM+import Haste.DOM.Core import Haste.Hash import Control.Monad.IO.Class @@ -24,6 +24,7 @@ foreign import ccall jsLog    :: JSString -> IO () foreign import ccall jsPrompt :: JSString -> IO JSString foreign import ccall jsEval   :: JSString -> IO JSString+ #else jsAlert  :: JSString -> IO () jsAlert = error "Tried to use jsAlert on server side!"
libraries/haste-lib/src/Haste/Ajax.hs view
@@ -2,11 +2,9 @@ {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE OverloadedStrings        #-} -- | Low level XMLHttpRequest support. IE6 and older are not supported.-module Haste.Ajax (Method (..), URL, Key, Val, textRequest, textRequest_,-                   jsonRequest, jsonRequest_) where+module Haste.Ajax (Method (..), URL, ajaxRequest, noParams) where import Haste.Prim-import Haste.Callback-import Haste.JSON+import Haste.JSType import Control.Monad.IO.Class  #ifdef __HASTE__@@ -14,81 +12,48 @@                              -> JSString    -- url                              -> Bool        -- async?                              -> JSString    -- POST data-                             -> JSFun (Maybe JSString -> IO ())+                             -> Ptr (Maybe JSString -> IO ())                              -> IO () #else-ajaxReq :: JSString -> JSString -> Bool -> JSString -> JSFun (Maybe JSString -> IO ()) -> IO ()+ajaxReq :: JSString -> JSString -> Bool -> JSString -> Ptr (Maybe JSString -> IO ()) -> IO () ajaxReq = error "Tried to use ajaxReq in native code!" #endif  data Method = GET | POST deriving Show-type Key = String-type Val = String --- | Make an AJAX request to a URL, treating the response as plain text.-textRequest :: MonadIO m-            => Method-            -> URL-            -> [(Key, Val)]-            -> (Maybe String -> IO ())-            -> m ()-textRequest m url kv cb = do-  _ <- liftIO $ ajaxReq (toJSStr $ show m) url' True "" cb'-  return ()-  where-    cb' = mkCallback $ cb . fmap fromJSStr-    kv' = map (\(k,v) -> (toJSStr k, toJSStr v)) kv-    url' = if null kv-             then toJSStr url-             else catJSStr "?" [toJSStr url, toQueryString kv']---- | Same as 'textRequest' but deals with JSStrings instead of Strings.-textRequest_ :: MonadIO m-             => Method-             -> JSString-             -> [(JSString, JSString)]-             -> (Maybe JSString -> IO ())-             -> m ()-textRequest_ m url kv cb = liftIO $ do-  _ <- ajaxReq (toJSStr $ show m) url' True "" (mkCallback cb)-  return ()-  where-    url' = if null kv then url else catJSStr "?" [url, toQueryString kv]+-- | Pass to 'ajaxRequest' instead of @[]@ when no parameters are needed, to+--   avoid type ambiguity errors.+noParams :: [((), ())]+noParams = [] --- | Make an AJAX request to a URL, interpreting the response as JSON.-jsonRequest :: MonadIO m-            => Method-            -> URL-            -> [(Key, Val)]-            -> (Maybe JSON -> IO ())+-- | Perform an AJAX request.+ajaxRequest :: (MonadIO m, JSType a, JSType b, JSType c)+            => Method   -- ^ GET or POST. For GET, pass all params in URL.+                        --   For POST, pass all params as post data.+            -> URL      -- ^ URL to make AJAX request to.+            -> [(a, b)] -- ^ A list of (key, value) parameters.+            -> (Maybe c -> IO ()) -- ^ Callback to invoke on completion.             -> m ()-jsonRequest m url kv cb = liftIO $ do-  jsonRequest_ m (toJSStr url)-                 (map (\(k,v) -> (toJSStr k, toJSStr v)) kv)-                 cb---- | Does the same thing as 'jsonRequest' but uses 'JSString's instead of---   Strings.-jsonRequest_ :: MonadIO m-             => Method-             -> JSString-             -> [(JSString, JSString)]-             -> (Maybe JSON -> IO ())-             -> m ()-jsonRequest_ m url kv cb = liftIO $ do-    _ <- ajaxReq (toJSStr $ show m) url' True pd cb'+ajaxRequest m url kv cb = liftIO $ do+    _ <- ajaxReq (showm m) url' True pd cb'     return ()   where-    liftEither (Right x) = Just x-    liftEither _         = Nothing-    cb' = mkCallback $ \mjson -> cb (mjson >>= liftEither . decodeJSON)+    showm GET  = "GET"+    showm POST = "POST"+    cb' = toPtr $ cb . fromJSS+    fromJSS (Just jss) = fromJSString jss+    fromJSS _          = Nothing     url' = case m of-             GET -> if null kv then url else catJSStr "?" [url, toQueryString kv]-             POST -> url+           GET+             | null kv   -> toJSString url+             | otherwise -> catJSStr "?" [toJSString url, toQueryString kv]+           POST -> toJSString url     pd = case m of            GET -> ""-           POST -> if null kv then "" else toQueryString kv-+           POST+             | null kv   -> ""+             | otherwise -> toQueryString kv -toQueryString :: [(JSString, JSString)] -> JSString-toQueryString = catJSStr "&" . map (\(k,v) -> catJSStr "=" [k,v])+toQueryString :: (JSType a, JSType b) =>[(a, b)] -> JSString+toQueryString = catJSStr "&" . map f+  where f (k, v) = catJSStr "=" [toJSString k,toJSString v]
+ libraries/haste-lib/src/Haste/Any.hs view
@@ -0,0 +1,350 @@+-- For the FFI+{-# LANGUAGE ForeignFunctionInterface, PatternGuards, CPP, BangPatterns #-}++-- For generic default instances+{-# LANGUAGE TypeOperators, ScopedTypeVariables, FlexibleInstances,+             FlexibleContexts, OverloadedStrings, DefaultSignatures,+             OverlappingInstances #-}++-- For less annoying instances+{-# LANGUAGE TupleSections #-}++-- | Converting to/from JS-native data.+module Haste.Any (+    ToAny (..), FromAny (..), Generic, JSAny (..),+    Opaque, toOpaque, fromOpaque,+    nullValue, toObject, has, get, index+  ) where+import GHC.Generics+import Haste.Prim+import Haste.JSType+import Data.Int+import Data.Word+import Unsafe.Coerce+import Control.Applicative++#ifdef __HASTE__+foreign import ccall __lst2arr :: Ptr [a] -> JSAny+foreign import ccall __arr2lst :: Int -> JSAny -> Ptr [a]+foreign import ccall "String" jsString :: JSAny -> JSString+foreign import ccall "Number" jsNumber :: JSAny -> Double+foreign import ccall "__jsNull" jsNull :: JSAny+foreign import ccall "__jsTrue" jsTrue :: JSAny+foreign import ccall "__jsFalse" jsFalse :: JSAny+foreign import ccall __new :: IO JSAny+foreign import ccall __set :: JSAny -> JSString -> JSAny -> IO ()+foreign import ccall __get :: JSAny -> JSString -> IO JSAny+foreign import ccall __has :: JSAny -> JSString -> IO Bool+#else+__new :: IO JSAny+__new = return undefined+__get :: JSAny -> JSString -> IO JSAny+__get _ _ = return undefined+__set :: JSAny -> JSString -> JSAny -> IO ()+__set _ _ _ = return ()+__has :: JSAny -> JSString -> IO Bool+__has _ _ = return False+__lst2arr :: Ptr [a] -> JSAny+__lst2arr _ = undefined+__arr2lst :: Int -> JSAny -> Ptr [a]+__arr2lst _ _ = undefined+jsString :: JSAny -> JSString+jsString _ = undefined+jsNumber :: JSAny -> Double+jsNumber _ = undefined+jsNull, jsTrue, jsFalse :: JSAny+jsNull = undefined+jsTrue = undefined+jsFalse = undefined+#endif+{-+  For theoretical purposes, imagine the following here:+  foreign import ccall __intToAny :: Int -> JSAny+  ...+  In practice, however, we use unsafeCoerce for that to avoid the roundtrip.+-}++-- | The JS value null.+nullValue :: JSAny+nullValue = jsNull++-- | Build a new JS object from a list of key:value pairs.+toObject :: [(JSString, JSAny)] -> JSAny+toObject ps = veryUnsafePerformIO $ do+  o <- __new+  mapM_ (uncurry $ __set o) ps+  return o++-- | Read a member from a JS object. Throws an error if the member can not be+--   marshalled into a value of type @a@.+{-# INLINE get #-}+get :: FromAny a => JSAny -> JSString -> IO a+get o k = __get o k >>= fromAny++{-# INLINE index #-}+-- | Read an element from a JS array. Throws an error if the member can not be+--   marshalled into a value of type @a@.+index :: FromAny a => JSAny -> Int -> IO a+index o k = __get o (unsafeCoerce k) >>= fromAny++-- | Check if a JS object has a particular member.+{-# INLINE has #-}+has :: JSAny -> JSString -> IO Bool+has = __has++-- | Any type that can be converted into a JavaScript value.+class ToAny a where+  -- | Build a JS object from a Haskell value.+  --   The default instance creates an object from any type that derives+  --   'Generic' according to the following rules:+  --   * Records turn into plain JS objects, with record names as field names.+  --   * Non-record product types turn into objects containing a @$data@ field+  --     which contains all of the constructor's unnamed fields.+  --   * Values of enum types turn into strings matching their constructors.+  --   * Non-enum types with more than one constructor gain an extra field,+  --     @$tag@, which contains the name of the constructor used to create the+  --     object.+  toAny :: a -> JSAny+  default toAny :: (GToAny (Rep a), Generic a) => a -> JSAny+  toAny x =+    case gToAny False g of+      Tree x' -> toObject x'+      One  x' -> if isEnum g then x' else toAny [x']+      List x' -> toAny x'+    where g = from x++  listToAny :: [a] -> JSAny+  listToAny = __lst2arr . toPtr . map toAny++-- | Any type that can be converted from a JavaScript value.+class FromAny a where+  -- | Convert a value from JS with a reasonable conversion if an exact match+  --   is not possible. Examples of reasonable conversions would be truncating+  --   floating point numbers to integers, or turning signed integers into+  --   unsigned.+  fromAny :: JSAny -> IO a++  listFromAny :: JSAny -> IO [a]+  listFromAny = mapM fromAny . fromPtr . __arr2lst 0++-- | The Opaque type is inhabited by values that can be passed to JavaScript+--   using their raw Haskell representation. Opaque values are completely+--   useless to JS code, and should not be inspected. This is useful for,+--   for instance, storing data in some JS-native data structure for later+--   retrieval.+newtype Opaque a = Opaque {fromOpaque :: a}++toOpaque :: a -> Opaque a+toOpaque = Opaque++++-- ToAny instances+instance ToAny JSAny where toAny = unsafeCoerce+instance ToAny (Ptr a) where toAny = unsafeCoerce+instance ToAny JSString where toAny = unsafeCoerce+instance ToAny Int where toAny = unsafeCoerce+instance ToAny Int8 where toAny = unsafeCoerce+instance ToAny Int16 where toAny = unsafeCoerce+instance ToAny Int32 where toAny = unsafeCoerce+instance ToAny Word where toAny = unsafeCoerce+instance ToAny Word8 where toAny = unsafeCoerce+instance ToAny Word16 where toAny = unsafeCoerce+instance ToAny Word32 where toAny = unsafeCoerce+instance ToAny Float where toAny = unsafeCoerce+instance ToAny Double where toAny = unsafeCoerce+instance ToAny Char where+  toAny = unsafeCoerce+  listToAny = toAny . toJSStr+instance ToAny () where+  toAny _ = jsNull+instance ToAny (Opaque a) where+  toAny (Opaque x) = unsafeCoerce $ toPtr x+instance ToAny Bool where+  toAny True  = jsTrue+  toAny False = jsFalse++-- | Lists are marshalled into arrays, with the exception of 'String'.+instance ToAny a => ToAny [a] where+  toAny = listToAny++-- | Maybe is simply a nullable type. Nothing is equivalent to null, and any+--   non-null value is equivalent to x in Just x.+instance ToAny a => ToAny (Maybe a) where+  toAny Nothing  = jsNull+  toAny (Just x) = toAny x++-- | Tuples are marshalled into arrays.+instance (ToAny a, ToAny b) => ToAny (a, b) where+  toAny (a, b) = toAny [toAny a, toAny b]++instance (ToAny a, ToAny b, ToAny c) => ToAny (a, b, c) where+  toAny (a, b, c) = toAny [toAny a, toAny b, toAny c]++instance (ToAny a, ToAny b, ToAny c, ToAny d) =>+          ToAny (a, b, c, d) where+  toAny (a, b, c, d) = toAny [toAny a, toAny b, toAny c, toAny d]++instance (ToAny a, ToAny b, ToAny c, ToAny d, ToAny e) =>+          ToAny (a, b, c, d, e) where+  toAny (a, b, c, d, e) = toAny [toAny a,toAny b,toAny c,toAny d,toAny e]++instance (ToAny a, ToAny b, ToAny c, ToAny d, ToAny e,+          ToAny f) => ToAny (a, b, c, d, e, f) where+  toAny (a, b, c, d, e, f) =+    toAny [toAny a, toAny b, toAny c, toAny d, toAny e, toAny f]++instance (ToAny a, ToAny b, ToAny c, ToAny d, ToAny e,+          ToAny f, ToAny g) => ToAny (a, b, c, d, e, f, g) where+  toAny (a, b, c, d, e, f, g) =+    toAny [toAny a,toAny b,toAny c,toAny d,toAny e,toAny f,toAny g]++++instance FromAny JSAny where+  fromAny x = return (unsafeCoerce x)+instance FromAny (Ptr a) where+  fromAny x = return (unsafeCoerce x)+instance FromAny JSString where+  fromAny x = return (jsString x)+instance FromAny Int where+  fromAny x = return (convert (jsNumber x))+instance FromAny Int8 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Int16 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Int32 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Word where+  fromAny x = return (convert (jsNumber x))+instance FromAny Word8 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Word16 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Word32 where+  fromAny x = return (convert (jsNumber x))+instance FromAny Float where+  fromAny x = return (unsafeCoerce (jsNumber x))+instance FromAny Double where+  fromAny x = return (unsafeCoerce x) -- return (jsNumber x)+instance FromAny Char where+  fromAny x = return (unsafeCoerce (jsNumber x))+  listFromAny x = fromJSStr <$> fromAny x+instance FromAny () where+  fromAny _ = return ()+instance FromAny (Opaque a) where+  fromAny x = Opaque . fromPtr <$> fromAny x+instance FromAny Bool where+  fromAny x | x == jsTrue = return True+            | otherwise   = return False++instance FromAny a => FromAny [a] where+  fromAny = listFromAny++instance FromAny a => FromAny (Maybe a) where+  fromAny x | x == jsNull = return Nothing+            | otherwise   = Just <$> fromAny x++instance (FromAny a, FromAny b) => FromAny (a, b) where+  fromAny x = do+    [a,b] <- fromAny x+    (,) <$> fromAny a <*> fromAny b++instance (FromAny a, FromAny b, FromAny c) => FromAny (a, b, c) where+  fromAny x = do+    [a,b,c] <- fromAny x+    (,,) <$> fromAny a <*> fromAny b <*> fromAny c++instance (FromAny a, FromAny b, FromAny c, FromAny d) =>+          FromAny (a, b, c, d) where+  fromAny x = do+    [a,b,c,d] <- fromAny x+    (,,,) <$> fromAny a <*> fromAny b <*> fromAny c <*> fromAny d++instance (FromAny a, FromAny b, FromAny c, FromAny d, FromAny e) =>+          FromAny (a, b, c, d, e) where+  fromAny x = do+    [a,b,c,d,e] <- fromAny x+    (,,,,) <$> fromAny a <*> fromAny b <*> fromAny c+           <*> fromAny d <*> fromAny e++instance (FromAny a, FromAny b, FromAny c, FromAny d, FromAny e, FromAny f) =>+          FromAny (a, b, c, d, e, f) where+  fromAny x = do+    [a,b,c,d,e,f] <- fromAny x+    (,,,,,) <$> fromAny a <*> fromAny b <*> fromAny c+            <*> fromAny d <*> fromAny e <*> fromAny f++instance (FromAny a, FromAny b, FromAny c, FromAny d,+          FromAny e, FromAny f, FromAny g) =>+          FromAny (a, b, c, d, e, f, g) where+  fromAny x = do+    [a,b,c,d,e,f,g] <- fromAny x+    (,,,,,,) <$> fromAny a <*> fromAny b <*> fromAny c <*> fromAny d+             <*> fromAny e <*> fromAny f <*> fromAny g++data Value = One !JSAny | List ![JSAny] | Tree ![(JSString, JSAny)]++-- Generic instances+class GToAny f where+  gToAny :: Bool -> f a -> Value+  isEnum :: f a -> Bool++instance GToAny U1 where+  gToAny _ U1 = error "U1: unpossible!"+  isEnum _    = True++instance ToAny a => GToAny (K1 i a) where+  gToAny _ (K1 x) = One (toAny x)+  isEnum _ = False++instance (Selector c, GToAny a) => GToAny (M1 S c a) where+  gToAny mcs (M1 x) = do+    case name of+      "" -> One  value+      _  -> Tree [(name, value)]+    where name  = toJSStr (selName (undefined :: M1 S c a ()))+          value =+            case gToAny mcs x of+              Tree x' -> toObject x'+              One  x' -> toAny x'+              List x' -> toAny x'+  isEnum _ = isEnum (undefined :: a ())++instance Constructor c => GToAny (M1 C c U1) where+  gToAny _ _ = One (toAny $ conName (undefined :: M1 C c U1 ()))+  isEnum _ = True++instance (Constructor c, GToAny a) => GToAny (M1 C c a) where+  gToAny many_constrs (M1 x)+    | many_constrs =+      case args of+        Tree args' -> Tree (("$tag", toAny tag) : args')+        One  arg   -> Tree [("$tag", toAny tag), ("$data", arg)]+        List args' -> Tree [("$tag", toAny tag), ("$data", toAny args')]+    | otherwise =+      args+    where+      tag  = conName (undefined :: M1 C c a ())+      args = gToAny many_constrs x+  isEnum _ = isEnum (undefined :: a ())++instance GToAny a => GToAny (M1 D c a) where+  gToAny cs (M1 x) = gToAny cs x+  isEnum _ = isEnum (undefined :: a ())++instance (GToAny a, GToAny b) => GToAny (a :*: b) where+  gToAny cs (a :*: b) =+    case (gToAny cs a, gToAny cs b) of+      (One l,   One r)   -> List [l, r]+      (One x,   List xs) -> List (x:xs)+      (List xs, One x)   -> List (xs ++ [x])+      (List l,  List r)  -> List (l ++ r)+      (Tree l,  Tree r)  -> Tree (l ++ r)+      (_,       _)       -> error "Tree :*: non-tree!"+  isEnum _ = False++instance (GToAny a, GToAny b) => GToAny (a :+: b) where+  gToAny _ (L1 x) = gToAny True x+  gToAny _ (R1 x) = gToAny True x+  isEnum _ = isEnum (undefined :: a ()) && isEnum (undefined :: b ())
libraries/haste-lib/src/Haste/App.hs view
@@ -9,26 +9,22 @@     Sessions, SessionID,     liftServerIO, forkServerIO, remote, runApp,     (<.>), getSessionID, getActiveSessions, onSessionEnd,-    AppCfg, cfgURL, cfgPort, mkConfig,+    AppCfg, cfgHost, cfgPort, mkConfig,     Client,     runClient, onServer, liftIO,     JSString, JSAny, URL, alert, prompt, eval, writeLog, catJSStr, fromJSStr,-    module Haste.App.Events,-    module Haste.DOM,+    module Haste.DOM.Core,     module Haste.Random,     module Haste.JSType,     module Haste.Hash,-    module Haste.Binary,-    module Data.Default+    module Haste.Binary   ) where import Haste.App.Client import Haste.App.Monad-import Haste.App.Events import Haste.Binary (Binary (..))-import Haste.DOM+import Haste.DOM.Core import Haste.Random import Haste.JSType import Haste.Hash import Haste import Control.Monad.IO.Class-import Data.Default
libraries/haste-lib/src/Haste/App/Client.hs view
@@ -6,10 +6,13 @@   ) where import Haste import Haste.WebSockets+import Haste.Events.Core import Haste.Binary hiding (get) import Haste.App.Monad import Haste.App.Protocol+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Monad (ap, join) import Control.Monad.IO.Class import Control.Exception (throw)@@ -55,17 +58,14 @@     x <- liftIO m     return x -instance GenericCallback (Client ()) Client where-  type CB (Client ()) = IO ()-  mkcb toIO m = toIO m-  mkIOfier _ = do-    st <- get id-    return $ concurrent . runClientCIO st- instance MonadBlob Client where   getBlobData = liftCIO . getBlobData   getBlobText' = liftCIO . getBlobText' +instance MonadEvent Client where+  mkHandler f = do+    st <- get id+    return $ concurrent . runClientCIO st . f  -- | Lift a CIO action into the Client monad. liftCIO :: CIO a -> Client a@@ -122,7 +122,8 @@ --   the program terminates. runClient :: Client () -> App Done runClient m = do-  url <- cfgURL `fmap` getAppConfig+  cfg <- getAppConfig+  let url = "ws://" ++ cfgHost cfg ++ ":" ++ show (cfgPort cfg)   return . Done $ runClient_ url m  -- | Run a client computation from the CIO monad, using a pre-specified state.
libraries/haste-lib/src/Haste/App/Concurrent.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE EmptyDataDecls #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} -- | Wraps Haste.Concurrent to work with Haste.App. --   Task switching happens whenever a thread is blocked in an MVar, so things --   like polling an IORef in a loop will starve all other threads.
− libraries/haste-lib/src/Haste/App/Events.hs
@@ -1,40 +0,0 @@-{-# LANGUAGE TypeFamilies, GADTs, FlexibleInstances, ForeignFunctionInterface,-             CPP #-}--- | Event handlers for Haste.App. If you're using Haste.App, you should use---   the functions provided by this module rather than the ones from---   Haste.Callback.-module Haste.App.Events (-    ClientCallback, CB.Event (..),-    onEvent, setTimeout, CB.evtName-  ) where-import qualified Haste.Callback as CB-import Haste.App.Client-import Haste.Concurrent-import Haste.DOM---- | Bake a value of type a -> ... -> Client b into a -> ... -> IO b-class ClientCallback a where-  type T a-  cbify :: ClientState -> a -> T a--instance ClientCallback (Client ()) where-  type T (Client ()) = IO ()-  cbify cs = concurrent . runClientCIO cs--instance ClientCallback b => ClientCallback (a -> b) where-  type T (a -> b) = a -> T b-  cbify cs f = \x -> cbify cs (f x)---- | Set a handler for a given event.-onEvent :: ClientCallback a => Elem -> CB.Event Client a -> a -> Client ()-onEvent e evt f = do-    cs <- get id-    _ <- liftIO . CB.jsSetCB e (CB.evtName evt) . CB.mkCallback $! cbify cs f-    return ()---- | Wrapper for window.setTimeout; execute the given computation after a delay---   given in milliseconds.-setTimeout :: Int -> Client () -> Client ()-setTimeout delay cb = do-  cs <- get id-  liftIO $ CB.jsSetTimeout delay (CB.mkCallback $! cbify cs cb)
libraries/haste-lib/src/Haste/App/Monad.hs view
@@ -1,52 +1,51 @@-{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}+{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, PatternGuards #-} -- | Haste.App startup monad and configuration. module Haste.App.Monad (     Remotable,     App, Server, Sessions, SessionID, Remote (..), Done (..),-    AppCfg, def, mkConfig, cfgURL, cfgPort,+    AppCfg, defaultConfig, mkConfig, cfgHost, cfgPort,     liftServerIO, forkServerIO, remote, getAppConfig,     runApp, (<.>), getSessionID, getActiveSessions, onSessionEnd   ) where+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Monad (ap) import Control.Monad.IO.Class import Haste.Binary-import Haste.Binary.Types import qualified Data.Map as M import qualified Data.Set as S import Haste.App.Protocol import Data.Word import Control.Concurrent (ThreadId) import Data.IORef-import Data.Default import System.IO.Unsafe #ifndef __HASTE__ import Haste.Binary.Types import Control.Concurrent (forkIO)-import Haste.Prim (toJSStr, fromJSStr) import Network.WebSockets as WS import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.UTF8 as BU import Control.Exception-import System.Random+import System.Random hiding (next) import Data.List (foldl') import Data.String #endif  data AppCfg = AppCfg {-    cfgURL                :: String,+    cfgHost               :: String,     cfgPort               :: Int,     cfgSessionEndHandlers :: [SessionID -> Server ()]   } -instance Default AppCfg where-  def = mkConfig "ws://localhost:24601" 24601+defaultConfig :: AppCfg+defaultConfig = mkConfig "localhost" 24601 --- | Create a default configuration from an URL and a port number.+-- | Create a default configuration from a host name and a port number. mkConfig :: String -> Int -> AppCfg-mkConfig url port = AppCfg {-    cfgURL = url,+mkConfig host port = AppCfg {+    cfgHost = host,     cfgPort = port,     cfgSessionEndHandlers = []   }@@ -104,8 +103,6 @@ --   used server-side. liftServerIO :: IO a -> App (Server a) #ifdef __HASTE__-{-# RULES "throw away liftServerIO"-          forall x. liftServerIO x = return Server #-} liftServerIO _ = return Server #else liftServerIO m = App $ \cfg _ cid exports -> do@@ -122,8 +119,6 @@ --   expected. forkServerIO :: Server () -> App (Server ThreadId) #ifdef __HASTE__-{-# RULES "throw away forkServerIO"-          forall x. forkServerIO x = return Server #-} forkServerIO _ = return Server #else forkServerIO (Server m) = App $ \cfg sessions cid exports -> do@@ -149,7 +144,7 @@ #else   serializify f (x:xs) = serializify (f $! fromEither $ decode (toBD x)) xs     where-      toBD (Blob x) = BlobData x+      toBD (Blob x') = BlobData x'       fromEither (Right val) = val       fromEither (Left e)    = error $ "Unable to deserialize data: " ++ e   serializify _ _      = error "The impossible happened in serializify!"@@ -158,9 +153,6 @@ -- | Make a function available to the client as an API call. remote :: Remotable a => a -> App (Remote a) #ifdef __HASTE__-{-# RULES "throw away remote's argument"-          forall x. remote x =-            App $ \c _ cid _ -> return (Remote cid [], cid+1, undefined, c) #-} remote _ = App $ \c _ cid _ ->     return (Remote cid [], cid+1, undefined, c) #else@@ -173,8 +165,6 @@ --   the order they were registered. onSessionEnd :: (SessionID -> Server ()) -> App () #ifdef __HASTE__-{-# RULES "throw away onSessionEnd argument"-          forall x. onSessionEnd x = return () #-} onSessionEnd _ = return () #else onSessionEnd s = App $ \cfg _ cid exports -> return $@@ -233,7 +223,7 @@       where         go = do           msg <- receiveData c-          forkIO $ do+          _ <- forkIO $ do             case decode (BlobData msg) of               Right (ServerCall nonce method args)                 | Just m <- M.lookup method exports -> do
libraries/haste-lib/src/Haste/App/Protocol.hs view
@@ -1,7 +1,9 @@-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, CPP #-} -- | Haste.App client-server protocol. module Haste.App.Protocol where+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Exception import Data.Typeable import Haste.Binary
+ libraries/haste-lib/src/Haste/Audio.hs view
@@ -0,0 +1,245 @@+{-# LANGUAGE OverloadedStrings, CPP #-}+-- | High-ish level bindings to the HTML5 audio tag and JS API.+module Haste.Audio (+    module Events,+    Audio, AudioSettings (..), AudioType (..), AudioSource (..),+    AudioPreload (..), AudioState (..), Seek (..),+    defaultAudioSettings,+    mkSource, newAudio, setSource,+    getState,+    setMute, isMute, toggleMute,+    setLooping, isLooping, toggleLooping,+    getVolume, setVolume, modVolume,+    play, pause, stop, togglePlaying,+    seek, getDuration, getCurrentTime+  ) where+import Haste.Audio.Events as Events+import Haste.DOM.JSString+import Haste.Foreign+import Haste.JSType+import Haste.Prim+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Control.Monad+import Control.Monad.IO.Class+import Data.String++-- | Represents an audio player.+data Audio = Audio Elem++instance IsElem Audio where+  elemOf (Audio e) = e+  fromElem e = do+    tn <- getProp e "tagName"+    return $ case tn of+      "AUDIO" -> Just $ Audio e+      _       -> Nothing++data AudioState = Playing | Paused | Ended+  deriving (Show, Eq)+data AudioType = MP3 | OGG | WAV+  deriving (Show, Eq)+data AudioSource = AudioSource !AudioType !JSString+  deriving (Show, Eq)+data AudioPreload = None | Metadata | Auto+  deriving Eq+data Seek = Start | End | Seconds Double+  deriving Eq++instance JSType AudioPreload where+  toJSString None     = "none"+  toJSString Metadata = "metadata"+  toJSString Auto     = "auto"+  fromJSString "none"     = Just None+  fromJSString "metadata" = Just Metadata+  fromJSString "auto"     = Just Auto+  fromJSString _          = Nothing++data AudioSettings = AudioSettings {+    -- | Show controls?+    --   Default: False+    audioControls :: !Bool,+    -- | Immediately start playing?+    --   Default: False+    audioAutoplay :: !Bool,+    -- | Initially looping?+    --   Default: False+    audioLooping  :: !Bool,+    -- | How much audio to preload.+    --   Default: Auto+    audioPreload  :: !AudioPreload,+    -- | Initially muted?+    --   Default: False+    audioMuted    :: !Bool,+    -- | Initial volume+    --   Default: 0+    audioVolume   :: !Double+  }++defaultAudioSettings :: AudioSettings+defaultAudioSettings = AudioSettings {+    audioControls = False,+    audioAutoplay = False,+    audioLooping = False,+    audioPreload = Auto,+    audioMuted = False,+    audioVolume = 0+  }++-- | Create an audio source with automatically detected media type, based on+--   the given URL's file extension.+--   Returns Nothing if the given URL has an unrecognized media type.+mkSource :: JSString -> Maybe AudioSource+mkSource url =+  case take 3 $ reverse $ fromJSStr url of+    "3pm" -> Just $ AudioSource MP3 url+    "ggo" -> Just $ AudioSource OGG url+    "vaw" -> Just $ AudioSource WAV url+    _     -> Nothing++instance IsString AudioSource where+  fromString s =+    case mkSource $ Data.String.fromString s of+      Just src -> src+      _        -> error $ "Not a valid audio source: " ++ s++mimeStr :: AudioType -> JSString+mimeStr MP3 = "audio/mpeg"+mimeStr OGG = "audio/ogg"+mimeStr WAV = "audio/wav"++-- | Create a new audio element.+newAudio :: MonadIO m => AudioSettings -> [AudioSource] -> m Audio+newAudio cfg sources = liftIO $ do+  srcs <- forM sources $ \(AudioSource t url) -> do+    newElem "source" `with` ["type" =: mimeStr t, "src" =: toJSString url]+  Audio <$> newElem "audio" `with` [+      "controls" =: falseAsEmpty (audioControls cfg),+      "autoplay" =: falseAsEmpty (audioAutoplay cfg),+      "loop"     =: falseAsEmpty (audioLooping cfg),+      "muted"    =: falseAsEmpty (audioMuted cfg),+      "volume"   =: toJSString (audioVolume cfg),+      "preload"  =: toJSString (audioPreload cfg),+      children srcs+    ]++-- | Returns "true" or "", depending on the given boolean.+falseAsEmpty :: Bool -> JSString+falseAsEmpty True = "true"+falseAsEmpty _    = ""++-- | (Un)mute the given audio object.+setMute :: MonadIO m => Audio -> Bool -> m ()+setMute (Audio e) = setAttr e "muted" . falseAsEmpty++-- | Is the given audio object muted?+isMute :: MonadIO m => Audio -> m Bool+isMute (Audio e) = liftIO $ maybe False id . fromJSString <$> getProp e "muted"++-- | Mute/unmute.+toggleMute :: MonadIO m => Audio -> m ()+toggleMute a = isMute a >>= setMute a . not++-- | Set whether the given sound should loop upon completion or not.+setLooping :: MonadIO m => Audio -> Bool -> m ()+setLooping (Audio e) = setAttr e "loop" . falseAsEmpty++-- | Is the given audio object looping?+isLooping :: MonadIO m => Audio -> m Bool+isLooping (Audio e) =+  liftIO $ maybe False id . fromJSString <$> getProp e "looping"++-- | Toggle looping on/off.+toggleLooping :: MonadIO m => Audio -> m ()+toggleLooping a = isLooping a >>= setLooping a . not++-- | Starts playing audio from the given element.+play :: MonadIO m => Audio -> m ()+play a@(Audio e) = do+    st <- getState a+    when (st == Ended) $ seek a Start+    liftIO $ play' e+  where+    play' :: Elem -> IO ()+    play' = ffi "(function(x){x.play();})"++-- | Get the current state of the given audio object.+getState :: MonadIO m => Audio -> m AudioState+getState (Audio e) = liftIO $ do+  ended <- maybe False id . fromJSString <$> getProp e "ended"+  if ended+    then return Ended+    else maybe Playing paused . fromJSString <$> getProp e "paused"+  where+    paused True = Paused+    paused _    = Playing++-- | Pause the given audio element.+pause :: MonadIO m => Audio -> m ()+pause (Audio e) = liftIO $ pause' e++pause' :: Elem -> IO ()+pause' = ffi "(function(x){x.pause();})"++-- | If playing, stop. Otherwise, start playing.+togglePlaying :: MonadIO m => Audio -> m ()+togglePlaying a = do+  st <- getState a+  case st of+    Playing    -> pause a+    Ended      -> seek a Start >> play a+    Paused     -> play a++-- | Stop playing a track, and seek back to its beginning.+stop :: MonadIO m => Audio -> m ()+stop a = pause a >> seek a Start++-- | Get the volume for the given audio element as a value between 0 and 1.+getVolume :: MonadIO m => Audio -> m Double+getVolume (Audio e) = liftIO $ maybe 0 id . fromJSString <$> getProp e "volume"++-- | Set the volume for the given audio element. The value will be clamped to+--   [0, 1].+setVolume :: MonadIO m => Audio -> Double -> m ()+setVolume (Audio e) = setProp e "volume" . toJSString . clamp++-- | Modify the volume for the given audio element. The resulting volume will+--   be clamped to [0, 1].+modVolume :: MonadIO m => Audio -> Double -> m ()+modVolume a diff = getVolume a >>= setVolume a . (+ diff)++-- | Clamp a value to [0, 1].+clamp :: Double -> Double+clamp = max 0 . min 1++-- | Seek to the specified time.+seek :: MonadIO m => Audio -> Seek -> m ()+seek a@(Audio e) st = liftIO $ do+    case st of+      Start     -> seek' e 0+      End       -> getDuration a >>= seek' e+      Seconds s -> seek' e s+  where+    seek' :: Elem -> Double -> IO ()+    seek' = ffi "(function(e,t) {e.currentTime = t;})"++-- | Get the duration of the loaded sound, in seconds.+getDuration :: MonadIO m => Audio -> m Double+getDuration (Audio e) = do+  dur <- getProp e "duration"+  case fromJSString dur of+    Just d -> return d+    _      -> return 0++-- | Get the current play time of the loaded sound, in seconds.+getCurrentTime :: MonadIO m => Audio -> m Double+getCurrentTime (Audio e) = do+  dur <- getProp e "currentTime"+  case fromJSString dur of+    Just d -> return d+    _      -> return 0++-- | Set the source of the given audio element.+setSource :: MonadIO m => Audio -> AudioSource -> m ()+setSource (Audio e) (AudioSource _ url) = setProp e "src" (toJSString url)
+ libraries/haste-lib/src/Haste/Audio/Events.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE TypeFamilies, OverloadedStrings #-}+-- | Audio related events.+module Haste.Audio.Events where+import Haste.Events.Core++data AudioEvent+  = AudioEnded       -- ^ Audio playback ended.+  | AudioError       -- ^ There was some kind of error.+  | AudioPaused      -- ^ Audio paused.+  | AudioResumed     -- ^ Resumed playing after pause.+  | AudioPlaying     -- ^ Audio started playing, initially or after pause.+  | AudioSeekBegins  -- ^ Seek operation starts.+  | AudioSeekEnds    -- ^ Seek operation completes.+  | AudioTimeUpdate  -- ^ Audio object't current time changed.+  | AudioProgress    -- ^ Progress was made downloading audio.+  | AudioStalled     -- ^ Audio download stalled.+  | AudioLoadStart   -- ^ Start downloading audio.+  | AudioLoadSuspend -- ^ Finished or paused downloading audio.++instance Event AudioEvent where+  type EventData AudioEvent = ()+  eventName AudioEnded       = "ended"+  eventName AudioError       = "error"+  eventName AudioPaused      = "pause"+  eventName AudioResumed     = "play"+  eventName AudioPlaying     = "playing"+  eventName AudioSeekBegins  = "seeking"+  eventName AudioSeekEnds    = "seeked"+  eventName AudioTimeUpdate  = "timeupdate"+  eventName AudioProgress    = "progress"+  eventName AudioStalled     = "stalled"+  eventName AudioLoadStart   = "loadstart"+  eventName AudioLoadSuspend = "suspend"+  eventData _ _ = return ()
libraries/haste-lib/src/Haste/Binary.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE MagicHash, CPP, MultiParamTypeClasses, OverloadedStrings,-             TypeSynonymInstances , FlexibleInstances, OverlappingInstances,-             GeneralizedNewtypeDeriving, BangPatterns, TypeOperators, KindSignatures, DefaultSignatures, FlexibleInstances, TypeSynonymInstances, FlexibleContexts, ScopedTypeVariables #-}+             TypeSynonymInstances , FlexibleInstances,+             GeneralizedNewtypeDeriving, BangPatterns, TypeOperators,+             KindSignatures, DefaultSignatures, FlexibleInstances,+             TypeSynonymInstances, FlexibleContexts, ScopedTypeVariables #-} -- | Handling of Javascript-native binary blobs. -- -- Generics borrowed from the binary package by Lennart Kolmodin (released under BSD3)@@ -10,20 +12,26 @@     MonadBlob (..), Binary (..), getBlobText,     Blob, BlobData,     blobSize, blobDataSize, toByteString, toBlob, strToBlob,-    encode, decode+    encode, decode, decodeBlob   )where import Data.Int import Data.Word import Data.Char+import qualified Haste.JSString as J (length) import Haste.Prim-import Haste.Concurrent hiding (encode, decode)-import Haste.Foreign+import Haste.Concurrent+import Haste.Foreign hiding (get) import Haste.Binary.Types import Haste.Binary.Put import Haste.Binary.Get+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import GHC.Generics import Data.Bits+#ifndef __HASTE__+import qualified Data.ByteString.Lazy.Char8 as BS (unpack)+#endif  class Monad m => MonadBlob m where   -- | Retrieve the raw data from a blob.@@ -36,30 +44,31 @@ getBlobText b = getBlobText' b >>= return . fromJSStr  instance MonadBlob CIO where+#ifdef __HASTE__   getBlobData b = do       res <- newEmptyMVar-      liftIO $ convertBlob b (toOpaque $ mkBlobData res (blobSize b))+      liftIO $ convertBlob b (mkBlobData res (blobSize b))       takeMVar res     where-#ifdef __HASTE__       mkBlobData res len x = concurrent $ do         putMVar res (BlobData 0 len x)-#else-      mkBlobData = undefined-#endif -      convertBlob :: Blob -> Opaque (Unpacked -> IO ()) -> IO ()+      convertBlob :: Blob -> (JSAny -> IO ()) -> IO ()       convertBlob = ffi-        "(function(b,cb){var r=new FileReader();r.onload=function(){B(A(cb,[new DataView(r.result),0]));};r.readAsArrayBuffer(b);})"+        "(function(b,cb){var r=new FileReader();r.onload=function(){cb(new DataView(r.result));};r.readAsArrayBuffer(b);})"    getBlobText' b = do       res <- newEmptyMVar-      liftIO $ convertBlob b (toOpaque $ concurrent . putMVar res)+      liftIO $ convertBlob b (concurrent . putMVar res)       takeMVar res     where-      convertBlob :: Blob -> Opaque (JSString -> IO ()) -> IO ()+      convertBlob :: Blob -> (JSString -> IO ()) -> IO ()       convertBlob = ffi-        "(function(b,cb){var r=new FileReader();r.onload=function(){B(A(cb,[[0,r.result],0]));};r.readAsText(b);})"+        "(function(b,cb){var r=new FileReader();r.onload=function(){cb(r.result);};r.readAsText(b);})"+#else+  getBlobData (Blob b) = return (BlobData b)+  getBlobText' (Blob b) = return . toJSStr $ BS.unpack b+#endif  -- | Somewhat efficient serialization/deserialization to/from binary Blobs. --   The layout of the binaries produced/read by get/put and encode/decode may@@ -152,9 +161,20 @@     putWord32le (fromIntegral $ length xs)     mapM_ put xs   get = do-    len <- getWord32le-    flip mapM [1..len] $ \_ -> get+      len <- getWord32le+      getList len []+    where+      getList 0 xs = return $ reverse xs+      getList n xs = get >>= \x -> getList (n-1) (x:xs) +instance Binary JSString where+  {-# NOINLINE put #-}+  put s = do+    putWord32le $ fromIntegral $ J.length s+    putJSString s+  {-# NOINLINE get #-}+  get = get >>= getJSString+     instance Binary Blob where   {-# NOINLINE put #-}   put b = do@@ -168,14 +188,23 @@  instance Binary Char where   put = put . ord-  get = chr <$> get+  get = get >>= \x ->+    case chr x of+      !x' -> return x' +-- | Encode any serializable data into a 'Blob'. encode :: Binary a => a -> Blob encode x = runPut (put x) +-- | Decode any deserializable data from a 'BlobData'. decode :: Binary a => BlobData -> Either String a decode = runGet get +-- | Decode a 'Blob' into some deserializable value, inconveniently locked up+--   inside the 'CIO' monad (or any other concurrent monad) due to the somewhat+--   special way JavaScript uses to deal with binary data.+decodeBlob :: (MonadBlob m, Binary a) => Blob -> m (Either String a)+decodeBlob b = getBlobData b >>= return . decode  -- Type without constructors instance GBinary V1 where
libraries/haste-lib/src/Haste/Binary/Get.hs view
@@ -4,120 +4,129 @@     getWord8, getWord16le, getWord32le,     getInt8, getInt16le, getInt32le,     getFloat32le, getFloat64le,-    getBytes, skip,+    getBytes, getJSString, skip,     runGet   ) where import Data.Int import Data.Word import Haste.Prim-import Haste.Foreign import Haste.Binary.Types+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Monad import System.IO.Unsafe-#ifndef __HASTE__+#ifdef __HASTE__+import Haste.Foreign hiding (get)+#else+import qualified Control.Exception as Ex+import Data.Char (chr) import qualified Data.Binary as B import qualified Data.Binary.IEEE754 as BI import qualified Data.Binary.Get as BG-import qualified Control.Exception as Ex #endif  #ifdef __HASTE__-data Get a = Get {unG :: Unpacked -> Int -> Either String (Int, a)}+data Result a = Ok !Int !a | Fail !String+data Get a = Get {unG :: JSAny -> Int -> Result a}  instance Functor Get where-  fmap f (Get m) = Get $ \buf next -> fmap (fmap f) (m buf next)+  fmap f (Get m) = Get $ \buf next ->+    case m buf next of+      Ok next' x -> Ok next' (f x)+      Fail s     -> Fail s  instance Applicative Get where   (<*>) = ap   pure  = return  instance Monad Get where-  return x = Get $ \_ next -> Right (next, x)+  return x = Get $ \_ next -> Ok next x   (Get m) >>= f = Get $ \buf next ->     case m buf next of-      Right (next', x) -> unG (f x) buf next'-      Left e           -> Left e-  fail s = Get $ \_ _ -> Left s+      Ok next' x -> unG (f x) buf next'+      Fail e     -> Fail e+  fail s = Get $ \_ _ -> Fail s -{-# NOINLINE getW8 #-}-getW8 :: Unpacked -> Int -> IO Word8+getW8 :: JSAny -> Int -> IO Word8 getW8 = ffi "(function(b,i){return b.getUint8(i);})"  getWord8 :: Get Word8 getWord8 =-  Get $ \buf next -> Right (next+1, unsafePerformIO $ getW8 buf next)+  Get $ \buf next -> Ok (next+1) (unsafePerformIO $ getW8 buf next) -{-# NOINLINE getW16le #-}-getW16le :: Unpacked -> Int -> IO Word16+getW16le :: JSAny -> Int -> IO Word16 getW16le = ffi "(function(b,i){return b.getUint16(i,true);})"  getWord16le :: Get Word16 getWord16le =-  Get $ \buf next -> Right (next+2, unsafePerformIO $ getW16le buf next)+  Get $ \buf next -> Ok (next+2) (unsafePerformIO $ getW16le buf next) -{-# NOINLINE getW32le #-}-getW32le :: Unpacked -> Int -> IO Word32+getW32le :: JSAny -> Int -> IO Word32 getW32le = ffi "(function(b,i){return b.getUint32(i,true);})"  getWord32le :: Get Word32 getWord32le =-  Get $ \buf next -> Right (next+4, unsafePerformIO $ getW32le buf next)+  Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getW32le buf next) -{-# NOINLINE getI8 #-}-getI8 :: Unpacked -> Int -> IO Int8+getI8 :: JSAny -> Int -> IO Int8 getI8 = ffi "(function(b,i){return b.getInt8(i);})"  getInt8 :: Get Int8 getInt8 =-  Get $ \buf next -> Right (next+1, unsafePerformIO $ getI8 buf next)+  Get $ \buf next -> Ok (next+1) (unsafePerformIO $ getI8 buf next) -{-# NOINLINE getI16le #-}-getI16le :: Unpacked -> Int -> IO Int16+getI16le :: JSAny -> Int -> IO Int16 getI16le = ffi "(function(b,i){return b.getInt16(i,true);})"  getInt16le :: Get Int16 getInt16le =-  Get $ \buf next -> Right (next+2, unsafePerformIO $ getI16le buf next)+  Get $ \buf next -> Ok (next+2) (unsafePerformIO $ getI16le buf next) -{-# NOINLINE getI32le #-}-getI32le :: Unpacked -> Int -> IO Int32+getI32le :: JSAny -> Int -> IO Int32 getI32le = ffi "(function(b,i){return b.getInt32(i,true);})"  getInt32le :: Get Int32 getInt32le =-  Get $ \buf next -> Right (next+4, unsafePerformIO $ getI32le buf next)+  Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getI32le buf next) -{-# NOINLINE getF32le #-}-getF32le :: Unpacked -> Int -> IO Float+getF32le :: JSAny -> Int -> IO Float getF32le = ffi "(function(b,i){return b.getFloat32(i,true);})"  getFloat32le :: Get Float getFloat32le =-  Get $ \buf next -> Right (next+4, unsafePerformIO $ getF32le buf next)+  Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getF32le buf next) -{-# NOINLINE getF64le #-}-getF64le :: Unpacked -> Int -> IO Double+getF64le :: JSAny -> Int -> IO Double getF64le = ffi "(function(b,i){return b.getFloat64(i,true);})"  getFloat64le :: Get Double getFloat64le =-  Get $ \buf next -> Right (next+8, unsafePerformIO $ getF64le buf next)+  Get $ \buf next -> Ok (next+8) (unsafePerformIO $ getF64le buf next)  getBytes :: Int -> Get BlobData-getBytes len = Get $ \buf next -> Right (next+len, BlobData next len buf)+getBytes len = Get $ \buf next -> Ok (next+len) (BlobData next len buf) +-- | Read a 'JSString' of @n@ characters. Encoding is assumed to be UTF-16.+getJSString :: Word32 -> Get JSString+getJSString len = Get $ \buf next ->+  Ok (next+fromIntegral (len+len)) (unsafePerformIO $ getJSS buf next len)++getJSS :: JSAny -> Int -> Word32 -> IO JSString+getJSS = ffi "(function(b,off,len){return String.fromCharCode.apply(null,new Uint16Array(b.buffer,off,len));})"+ -- | Skip n bytes of input. skip :: Int -> Get ()-skip len = Get $ \buf next -> Right (next+len, ())+skip len = Get $ \_buf next -> Ok (next+len) ()  -- | Run a Get computation. runGet :: Get a -> BlobData -> Either String a runGet (Get p) (BlobData off len bd) = do-    (consumed, x) <- p bd off-    if consumed <= len-      then Right x-      else Left "Not enough data!"+  case p bd off of+    Ok consumed x+      | consumed <= len -> Right x+      | otherwise       -> Left "Not enough data!"+    Fail s              -> Left s  #else @@ -158,6 +167,10 @@ getBytes len = Get $ do   bs <- BG.getLazyByteString (fromIntegral len)   return (BlobData bs)++getJSString :: Int -> Get JSString+getJSString len = Get $ do+  toJSStr `fmap` forM [1..len] (\_ -> fmap (chr . fromIntegral) BG.getWord16le)  -- | Skip n bytes of input. skip :: Int -> Get ()
libraries/haste-lib/src/Haste/Binary/Put.hs view
@@ -4,32 +4,35 @@     putWord8, putWord16le, putWord32le,     putInt8, putInt16le, putInt32le,     putFloat32le, putFloat64le,-    putBlob,+    putBlob, putJSString,     runPut   ) where import Data.Int import Data.Word import Haste.Prim-import Haste.Foreign import Haste.Binary.Types+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif+#ifdef __HASTE__ import Control.Monad+import Haste.Foreign import System.IO.Unsafe-#ifndef __HASTE__+#else+import Data.Char (ord) import qualified Data.Binary as B import qualified Data.Binary.IEEE754 as BI import qualified Data.Binary.Put as BP-import qualified Data.Binary.Put as BP #endif  type Put = PutM ()  #ifdef __HASTE__-type JSArr = Unpacked+type JSArr = JSAny newArr :: IO JSArr newArr = ffi "(function(){return [];})" -push :: Marshal a => JSArr -> a -> IO ()+push :: JSArr -> JSAny -> IO () push = ffi "(function(a,x) {a.push(x);})"  data PutM a = PutM {unP :: JSArr -> IO a}@@ -68,38 +71,51 @@ putFloat32le :: Float -> Put putFloat32le f = PutM $ \a -> push a (unsafePerformIO $ f2ab f) -{-# NOINLINE f2ab #-}-f2ab :: Float -> IO Unpacked+f2ab :: Float -> IO JSAny f2ab = ffi "(function(f) {var a=new ArrayBuffer(4);new DataView(a).setFloat32(0,f,true);return a;})"  putFloat64le :: Double -> Put putFloat64le f = PutM $ \a -> push a (unsafePerformIO $ d2ab f) -{-# NOINLINE d2ab #-}-d2ab :: Double -> IO Unpacked+d2ab :: Double -> IO JSAny d2ab = ffi "(function(f) {var a=new ArrayBuffer(8);new DataView(a).setFloat64(0,f,true);return a;})"  -- | Write a Blob verbatim into the output stream. putBlob :: Blob -> Put-putBlob b = PutM $ \a -> push a (unpack b)+putBlob b = PutM $ \a -> push a (toAny b) -toAB :: Marshal a => JSString -> Int -> a -> Unpacked-toAB view size elem = unsafePerformIO $ toABle view size (unpack elem)+toAB :: ToAny a => JSString -> Int -> a -> JSAny+toAB view size el = unsafePerformIO $ toABle view size (toAny el) -{-# NOINLINE toABle #-}-toABle :: Marshal a => JSString -> Int -> a -> IO Unpacked-toABle = ffi "window['toABle']"+toABle :: ToAny a => JSString -> Int -> a -> IO JSAny+toABle s n x = jsToABle s n (toAny x) +jsToABle :: JSString -> Int -> JSAny -> IO JSAny+jsToABle = ffi "window['toABle']"++-- | Serialize a 'JSString' as UTF-16 (somewhat) efficiently.+putJSString :: JSString -> Put+putJSString s = PutM $ \a -> push a (unsafePerformIO $ str2ab s)++str2ab :: JSString -> IO JSAny+str2ab = ffi "(function(s) {\+  var l = s.length;\+  var v = new Uint16Array(new ArrayBuffer(l*2));\+  for (var i=0; i<l; ++i) {\+    v[i]=s.charCodeAt(i);\+  }\+  return v.buffer;})"+ -- | Run a Put computation. runPut :: Put -> Blob runPut (PutM putEverything) = unsafePerformIO $ do     a <- newArr     putEverything a-    go a-  where-    go :: JSArr -> IO Blob-    go = ffi "(function(parts){return new Blob(parts);})"+    jsGetBlob a +jsGetBlob :: JSArr -> IO Blob+jsGetBlob = ffi "(function(parts){return new Blob(parts);})"+ #else  newtype PutM a = PutM (BP.PutM a) deriving (Functor, Applicative, Monad)@@ -133,5 +149,9 @@  putBlob :: Blob -> Put putBlob (Blob b) = PutM $ BP.putLazyByteString b++-- | Serialize a 'JSString' as UTF-16 (somewhat) efficiently.+putJSString :: JSString -> Put+putJSString = mapM_ (putWord16le . fromIntegral . ord) . fromJSStr  #endif
libraries/haste-lib/src/Haste/Binary/Types.hs view
@@ -5,16 +5,22 @@   ) where import Haste.Prim import Haste.Foreign-import System.IO.Unsafe import qualified Data.ByteString.Lazy as BS #ifndef __HASTE__ import qualified Data.ByteString.UTF8 as BU+#else+import System.IO.Unsafe #endif  #ifdef __HASTE__-data BlobData = BlobData Int Int Unpacked-newtype Blob = Blob Unpacked deriving (Pack, Unpack)+-- | In a browser context, BlobData is essentially a DataView, with an+--   accompanying offset and length for fast slicing.+--   In a server context, it is simply a 'BS.ByteString'.+data BlobData = BlobData Int Int JSAny +-- | A JavaScript Blob on the client, a 'BS.ByteString' on the server.+newtype Blob = Blob JSAny deriving (ToAny, FromAny)+ -- | The size, in bytes, of the contents of the given blob. blobSize :: Blob -> Int blobSize = unsafePerformIO . ffi "(function(b){return b.size;})"@@ -39,28 +45,37 @@  -- | Create a Blob from a JSString. strToBlob :: JSString -> Blob-strToBlob = newBlob . unpack+strToBlob = newBlob . toAny  sliceBlob :: Blob -> Int -> Int -> Blob-sliceBlob b off len = unsafePerformIO $ do-  ffi "(function(b,off,len){return b.slice(off,len);})" b off len+sliceBlob b off len = unsafePerformIO $ jsSlice b off len -newBlob :: Unpacked -> Blob+jsSlice :: Blob -> Int -> Int -> IO Blob+jsSlice = ffi "(function(b,off,len){return b.slice(off,len);})"++newBlob :: JSAny -> Blob newBlob = unsafePerformIO . jsNewBlob -jsNewBlob :: Unpacked -> IO Blob+jsNewBlob :: JSAny -> IO Blob jsNewBlob =   ffi "(function(b){try {return new Blob([b]);} catch (e) {return new Blob([b.buffer]);}})" #else +-- | In a browser context, BlobData is essentially a DataView, with an+--   accompanying offset and length for fast slicing.+--   In a server context, it is simply a 'BS.ByteString'. newtype BlobData = BlobData BS.ByteString++-- | A JavaScript Blob on the client, a 'BS.ByteString' on the server. newtype Blob = Blob BS.ByteString  -- Never used except for type checking-instance Pack BlobData-instance Unpack BlobData-instance Pack Blob-instance Unpack Blob+clientOnly :: a+clientOnly = error "ToAny/FromAny only usable client-side!"+instance ToAny BlobData where toAny = clientOnly+instance FromAny BlobData where fromAny = clientOnly+instance ToAny Blob where toAny = clientOnly+instance FromAny Blob where fromAny = clientOnly  -- | The size, in bytes, of the contents of the given blob. blobSize :: Blob -> Int
− libraries/haste-lib/src/Haste/Callback.hs
@@ -1,158 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, GADTs,-             FlexibleInstances, OverloadedStrings, CPP, MultiParamTypeClasses,-             TypeFamilies, FlexibleContexts #-}-module Haste.Callback (-    GenericCallback (..), toCallback,-    setCallback, setCallback', JSFun (..), mkCallback, Event (..),-    setTimeout, setTimeout', Callback (..), onEvent, onEvent',-    jsSetCB, jsSetTimeout, evtName-  ) where-import Haste.Prim-import Haste.DOM-import Haste.Concurrent.Monad-import Data.String-import Control.Monad.IO.Class--newtype JSFun a = JSFun (Ptr a)--#ifdef __HASTE__-foreign import ccall jsSetCB :: Elem -> JSString -> JSFun a -> IO Bool-foreign import ccall jsSetTimeout :: Int -> JSFun a -> IO ()-#else-jsSetCB :: Elem -> JSString -> JSFun a -> IO Bool-jsSetCB = error "Tried to use jsSetCB on server side!"-jsSetTimeout :: Int -> JSFun a -> IO ()-jsSetTimeout = error "Tried to use jsSetTimeout on server side!"-#endif---- | Turn a function of type a -> ... -> m () into a function of type---   a -> ... -> IO (), for use with generic JS callbacks.-toCallback :: (Monad m, GenericCallback a m) => a -> m (CB a)-toCallback f = do-  iofy <- mkIOfier f-  return $ mkcb iofy f--class GenericCallback a m where-  type CB a-  -- | Build a callback from an IOfier and a function.-  mkcb :: (m () -> IO ()) -> a -> CB a-  -- | Never evaluate the first argument to mkIOfier, it's only there to fix-  --   the types.-  mkIOfier :: a -> m (m () -> IO ())--instance GenericCallback (IO ()) IO where-  type CB (IO ()) = IO ()-  mkcb toIO m = toIO m-  mkIOfier _ = return id--instance GenericCallback b m => GenericCallback (a -> b) m where-  type CB (a -> b) = a -> CB b-  mkcb toIO f = \x -> mkcb toIO (f x)-  mkIOfier f = mkIOfier (f undefined)---- | Turn a computation into a callback that can be passed to a JS---   function.-mkCallback :: a -> JSFun a-mkCallback = JSFun . toPtr--class Callback a where-  constCallback :: IO () -> a--instance Callback (IO ()) where-  constCallback = id--instance Callback (a -> IO ()) where-  constCallback = const---- | These constructors correspond to their namesake DOM events. Mouse related---   callbacks receive the coordinates of the mouse pointer at the time the---   event was fired, relative to the top left corner of the element that fired---   the event. The click events also receive the mouse button that was---   pressed.------   The key up/down/press events receive the character code of the key that---   was pressed.-data Event m a where-  OnLoad      :: Event m (m ())-  OnUnload    :: Event m (m ())-  OnChange    :: Event m (m ())-  OnFocus     :: Event m (m ())-  OnBlur      :: Event m (m ())-  OnMouseMove :: Event m ((Int, Int) -> m ())-  OnMouseOver :: Event m ((Int, Int) -> m ())-  OnMouseOut  :: Event m (m ())-  OnClick     :: Event m (Int -> (Int, Int) -> m ())-  OnDblClick  :: Event m (Int -> (Int, Int) -> m ())-  OnMouseDown :: Event m (Int -> (Int, Int) -> m ())-  OnMouseUp   :: Event m (Int -> (Int, Int) -> m ())-  OnKeyPress  :: Event m (Int -> m ())-  OnKeyUp     :: Event m (Int -> m ())-  OnKeyDown   :: Event m (Int -> m ())-  OnSubmit    :: Event m (m ())-  OnWheel     :: Event m ((Int, Int) -> (Double, Double, Double) -> m ())--asEvtTypeOf :: Event m a -> a -> a-asEvtTypeOf _ = id--instance Eq (Event m a) where-  a == b = evtName a == (evtName b :: String)--instance Ord (Event m a) where-  compare a b = compare (evtName a) (evtName b :: String)---- | The name of a given event.-evtName :: IsString s => Event m a -> s-evtName evt =-  case evt of-    OnLoad      -> "load"-    OnUnload    -> "unload"-    OnClick     -> "click"-    OnDblClick  -> "dblclick"-    OnMouseDown -> "mousedown"-    OnMouseUp   -> "mouseup"-    OnMouseMove -> "mousemove"-    OnMouseOver -> "mouseover"-    OnMouseOut  -> "mouseout"-    OnKeyPress  -> "keypress"-    OnKeyUp     -> "keyup"-    OnKeyDown   -> "keydown"-    OnChange    -> "change"-    OnFocus     -> "focus"-    OnBlur      -> "blur"-    OnSubmit    -> "submit"-    OnWheel     -> "wheel"---- | Friendlier name for @setCallback@.-onEvent :: MonadIO m => Elem -> Event IO a -> a -> m Bool-onEvent = setCallback---- | Friendlier name for @setCallback'@.-onEvent' :: (ToConcurrent a, MonadIO m) => Elem -> Event CIO a -> Async a -> m Bool-onEvent' = setCallback'---- | Set a callback for the given event.-setCallback :: MonadIO m => Elem -> Event IO a -> a -> m Bool-setCallback e evt f =-  liftIO $ jsSetCB e (evtName evt) (mkCallback $! f)---- | Like @setCallback@, but takes a callback in the CIO monad instead of IO.-setCallback' :: (ToConcurrent a, MonadIO m)-             => Elem-             -> Event CIO a-             -> Async a-             -> m Bool-setCallback' e evt f =-    liftIO $ jsSetCB e (evtName evt) (mkCallback $! f')-  where-    f' = asEvtTypeOf evt (async f)---- | Wrapper for window.setTimeout; execute the given computation after a delay---   given in milliseconds.-setTimeout :: MonadIO m => Int -> IO () -> m ()-setTimeout delay cb =-  liftIO $ jsSetTimeout delay (mkCallback $! cb)---- | Like 'setTimeout', but takes a callback in the CIO monad instead of IO.-setTimeout' :: MonadIO m => Int -> CIO () -> m ()-setTimeout' delay cb =-  liftIO $ jsSetTimeout delay (mkCallback $! concurrent cb)
libraries/haste-lib/src/Haste/Compiler.hs view
@@ -13,7 +13,6 @@ #ifndef __HASTE__ import Control.Shell import Haste.Environment-import Data.Maybe import Data.List (intercalate) #endif @@ -29,25 +28,25 @@ compile _ _ _ = return $ Failure "Haste can only compile programs server-side." #else compile cf dir inp = do-  res <- shell $ do+  eresult <- shell $ do     curdir <- pwd     inTempDirectory $ do-      f <- case inp of+      fil <- case inp of         InFile f   -> return $ if isRelative f then incdir curdir </> f else f         InString s -> file "Main.hs" s >>= \() -> return "Main.hs"-      (f, o, e) <- genericRun hasteBinary (f : idir curdir : mkFlags cf) ""+      (f,_,e) <- genericRun hasteBinary (fil : idir curdir : mkFlags cf) ""       if not f         then do           return $ Failure e         else do           case cfTarget cf of-            TargetFile f -> do-              return $ Success $ OutFile f+            TargetFile tgt -> do+              return $ Success $ OutFile tgt             TargetString -> do               (Success . OutString) `fmap` file "haste.out"-  case res of-    Right res ->-      return res+  case eresult of+    Right result ->+      return result     Left e ->       return $ Failure $ "Run-time failure during compilation: " ++ e   where@@ -56,25 +55,30 @@  -- | Turn flags into command line argument. mkFlags :: CompileFlags -> [String]-mkFlags cf = catMaybes [+mkFlags cf = concat [     case cfOptimize cf of-      None         -> Just "-O0"-      Basic        -> Nothing-      WholeProgram -> Just "--opt-whole-program",+      None         -> ["-O0", "--ddisable-js-opts"]+      Basic        -> []+      WholeProgram -> ["--opt-whole-program"],     case cfStart cf of-      ASAP     -> Just "--start=asap"-      OnLoad   -> Nothing-      Custom s -> Just $ "--start=" ++ s,+      ASAP     -> ["--onexec"]+      OnLoad   -> ["--onload"]+      Custom s -> ["--start=" ++ s],     case cfTarget cf of-      TargetFile fp -> Just $ "--out=" ++ fp-      TargetString  -> Just $ "--out=haste.out",+      TargetFile fp -> ["--out=" ++ fp]+      TargetString  -> ["--out=haste.out"],+    case cfMinify cf of+      DontMinify         -> []+      Minify (Just p) fs -> ("--opt-minify="++p) : map appendMinifyFlag fs+      Minify _ fs        -> "--opt-minify" : map appendMinifyFlag fs,     when cfDebug        "--debug",-    when cfMinify       "--opt-google-closure",     when cfFullUnicode  "--full-unicode",     when cfOwnNamespace "--separate-namespace",-    when (not . null . cfJSFiles) ("--with-js=" ++ jsFileList)+    when (not . null . cfJSFiles) ("--with-js=" ++ jsFileList),+    when (not . cfUseStrict) "--no-use-strict"   ]   where+    appendMinifyFlag f = "--opt-minify-flag=" ++ f     jsFileList = intercalate "," $ cfJSFiles cf-    when opt arg = if opt cf then Just arg else Nothing+    when opt arg = if opt cf then [arg] else [] #endif
libraries/haste-lib/src/Haste/Compiler/Flags.hs view
@@ -1,10 +1,13 @@ module Haste.Compiler.Flags (-    OptLevel (..), ProgStart (..), HasteTarget (..), CompileFlags,+    OptLevel (..), ProgStart (..), HasteTarget (..), MinifyFlag (..),+    ClosureOpt,+    CompileFlags,+    defaultFlags,     cfOptimize, cfDebug, cfMinify, cfFullUnicode, cfOwnNamespace, cfStart,-    cfJSFiles, cfTarget+    cfJSFiles, cfTarget, cfUseStrict   ) where-import Data.Default-+type ClosureOpt = String+data MinifyFlag = DontMinify | Minify (Maybe FilePath) [ClosureOpt] data OptLevel = None | Basic | WholeProgram data ProgStart = ASAP | OnLoad | Custom String data HasteTarget = TargetFile FilePath | TargetString@@ -22,7 +25,7 @@     -- | Should the program be minified? This will strip any debug information     --   from the resulting program.     --   Default: False-    cfMinify       :: Bool,+    cfMinify       :: MinifyFlag,     -- | Use full Unicode compatibility for Data.Char and friends?     --   Default: False     cfFullUnicode  :: Bool,@@ -37,17 +40,22 @@     cfJSFiles      :: [FilePath],     -- | Where to place the compilation output.     --   Default: TargetString-    cfTarget       :: HasteTarget+    cfTarget       :: HasteTarget,+    -- | @'use strict';@?+    --   Default: True+    cfUseStrict    :: Bool   } -instance Default CompileFlags where-  def = CompileFlags {-      cfOptimize = Basic,-      cfDebug = False,-      cfMinify = False,-      cfFullUnicode = False,-      cfOwnNamespace = False,-      cfStart = OnLoad,-      cfJSFiles = [],-      cfTarget = TargetString-    }+-- | Default compiler flags.+defaultFlags :: CompileFlags+defaultFlags = CompileFlags {+    cfOptimize = Basic,+    cfDebug = False,+    cfMinify = DontMinify,+    cfFullUnicode = False,+    cfOwnNamespace = False,+    cfStart = OnLoad,+    cfJSFiles = [],+    cfTarget = TargetString,+    cfUseStrict = True+  }
libraries/haste-lib/src/Haste/Concurrent.hs view
@@ -8,20 +8,15 @@     wait   ) where import Haste.Concurrent.Monad-import Haste.Concurrent.Ajax as Ajax hiding ((!))-import Haste.Callback+import Haste.Concurrent.Ajax as Ajax+import Haste.Timer  -- | Wait for n milliseconds. wait :: Int -> CIO () wait ms = do   v <- newEmptyMVar-  liftIO $ setTimeout' ms $ putMVar v ()+  _ <- liftIO $ setTimer (Once ms) $ concurrent $ putMVar v ()   takeMVar v--instance GenericCallback (CIO ()) CIO where-  type CB (CIO ()) = IO ()-  mkcb toIO m = toIO m-  mkIOfier _ = return concurrent  -- | An MBox is a read/write-only MVar, depending on its first type parameter. --   Used to communicate with server processes.
libraries/haste-lib/src/Haste/Concurrent/Ajax.hs view
@@ -1,48 +1,23 @@ {-# LANGUAGE OverloadedStrings, FlexibleInstances, UndecidableInstances #-} -- | Concurrent Ajax calls. IE6 and older are not supported. module Haste.Concurrent.Ajax (-    Key, Val, JSON (..), (!), (~>), Method (..), URL, AjaxData (..),-    ajaxRequest+    Method (..), URL, noParams,+    Haste.Concurrent.Ajax.ajaxRequest   ) where import Haste.Concurrent.Monad import Haste.Ajax import Haste.JSType-import Haste.JSON-import Haste.Prim (JSString) --- | Data that can be received from an AJAX call.-class AjaxData a where-  encode :: a -> JSString-  decode :: JSString -> Maybe a--instance JSType a => AjaxData a where-  encode = toJSString-  decode = fromJSString--instance AjaxData JSON where-  encode = encodeJSON-  decode x =-    case decodeJSON x of-      Right x' -> Just x'-      _        -> Nothing---- | Make an Ajax GET request to a URL. The function will block until a---   response is received.-ajaxRequest :: AjaxData a-            => URL          -- ^ Base URL to call. The query string generated-                            --   from the second argument will be appended to-                            --   this to construct the final URL.-            -> [(Key, Val)] -- ^ Key value pairs to construct the query part-                            --   of the URL from.-                            --   Passing @[("foo","0"), ("bar", "1")]@ will-                            --   result in a request URL that ends with-                            --   @?foo=0&bar=1@.-            -> CIO (Maybe a)-ajaxRequest url querypart = do-    v <- newEmptyMVar-    let cb x = concurrent $ putMVar v $ x >>= decode-    liftIO $ textRequest_ GET url' querypart' cb-    takeMVar v-  where-    url' = toJSString url-    querypart' = [(toJSString k, toJSString v) | (k, v) <- querypart]+-- | Make a blocking AJAX request in the CIO monad.+--   Note that unlike using the browser's native blocking AJAX facilities,+--   this does *not* freeze the browser.+ajaxRequest :: (JSType a, JSType b, JSType c)+            => Method   -- ^ GET or POST. For GET, pass all params in URL.+                        --   For POST, pass all params as post data.+            -> URL      -- ^ URL to make AJAX request to.+            -> [(a, b)] -- ^ A list of (key, value) parameters.+            -> CIO (Maybe c)+ajaxRequest method url kv = do+  v <- newEmptyMVar+  liftIO $ Haste.Ajax.ajaxRequest method url kv $ concurrent . putMVar v+  takeMVar v
libraries/haste-lib/src/Haste/Concurrent/Monad.hs view
@@ -8,8 +8,11 @@ import Control.Monad.IO.Class import Control.Monad.Cont.Class import Control.Monad+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Data.IORef+import Haste.Events.Core (MonadEvent (..))  -- | Any monad which supports concurrency. class Monad m => MonadConc m where@@ -20,6 +23,9 @@   liftConc = id   fork = forkIO +instance MonadEvent CIO where+  mkHandler = return . fmap concurrent+ -- | Embed concurrent computations into non-concurrent ones. class ToConcurrent a where   type Async a@@ -152,7 +158,6 @@ --   share MVars; if this is the case, then a call to `concurrent` may return --   before all the threads it spawned finish executing. concurrent :: CIO () -> IO ()-#ifdef __HASTE__ concurrent (C m) = scheduler [m (const Stop)]   where     scheduler (p:ps) =@@ -166,6 +171,3 @@           scheduler ps     scheduler _ =       return ()-#else-concurrent = error "concurrent called in a non-browser environment!"-#endif
libraries/haste-lib/src/Haste/DOM.hs view
@@ -1,33 +1,25 @@-{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, CPP,-             GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GADTs #-}+-- | DOM manipulation functions using 'String' for string representation. module Haste.DOM (-    Elem (..), PropID, ElemID, QuerySelector, ElemClass,-    Attribute, AttrName, AttrValue,-    set, style, attr, with, (=:),+    module Core,+    IsElem (..), Elem, PropID, ElemID, QuerySelector, ElemClass,+    AttrName, AttrValue,+    style, attr, (=:),     newElem, newTextElem,     elemById, elemsByQS, elemsByClass,-    setProp, getProp, setAttr, getAttr, setProp', getProp', getValue,+    setProp, getProp, setAttr, getAttr, J.getValue,     withElem , withElems, withElemsQS, mapQS, mapQS_,-    addChild, addChildBefore, removeChild, clearChildren , getChildBefore,-    getFirstChild, getLastChild, getChildren, setChildren,-    getStyle, setStyle, getStyle', setStyle',-    getFileData, getFileName,-    setClass, toggleClass, hasClass,-    click, focus, blur,-    document, documentBody+    getStyle, setStyle,+    J.getFileData, getFileName,+    setClass, toggleClass, hasClass   ) where-import Haste.Prim-import Haste.JSType-import Data.Maybe (isNothing, fromJust)+import qualified Haste.DOM.JSString as J+import qualified Haste.DOM.Core as Core+  hiding (Elem (..), AttrName (..))+import Haste.DOM.Core+import Haste.Prim (fromJSStr, toJSStr) import Control.Monad.IO.Class-import Haste.Foreign-import Haste.Binary.Types-import System.IO.Unsafe (unsafePerformIO)-import qualified Data.String as S -newtype Elem = Elem JSAny-  deriving (Pack, Unpack)- type PropID = String type ElemID = String type QuerySelector = String@@ -46,319 +38,98 @@ (=:) :: AttrName -> AttrValue -> Attribute name =: val = attribute name (toJSStr val) -#ifdef __HASTE__-foreign import ccall jsGet :: Elem -> JSString -> IO JSString-foreign import ccall jsSet :: Elem -> JSString -> JSString -> IO ()-foreign import ccall jsGetAttr :: Elem -> JSString -> IO JSString-foreign import ccall jsSetAttr :: Elem -> JSString -> JSString -> IO ()-foreign import ccall jsGetStyle :: Elem -> JSString -> IO JSString-foreign import ccall jsSetStyle :: Elem -> JSString -> JSString -> IO ()-foreign import ccall jsFind :: JSString -> IO (Ptr (Maybe Elem))-foreign import ccall jsQuerySelectorAll :: Elem -> JSString -> IO (Ptr [Elem])-foreign import ccall jsElemsByClassName :: JSString -> IO (Ptr [Elem])-foreign import ccall jsCreateElem :: JSString -> IO Elem-foreign import ccall jsCreateTextNode :: JSString -> IO Elem-foreign import ccall jsAppendChild :: Elem -> Elem -> IO ()-foreign import ccall jsGetFirstChild :: Elem -> IO (Ptr (Maybe Elem))-foreign import ccall jsGetLastChild :: Elem -> IO (Ptr (Maybe Elem))-foreign import ccall jsGetChildren :: Elem -> IO (Ptr [Elem])-foreign import ccall jsSetChildren :: Elem -> Ptr [Elem] -> IO ()-foreign import ccall jsAddChildBefore :: Elem -> Elem -> Elem -> IO ()-foreign import ccall jsGetChildBefore :: Elem -> IO (Ptr (Maybe Elem))-foreign import ccall jsKillChild :: Elem -> Elem -> IO ()-foreign import ccall jsClearChildren :: Elem -> IO ()-#else-jsGet = error "Tried to use jsGet on server side!"-jsSet = error "Tried to use jsSet on server side!"-jsGetAttr = error "Tried to use jsGetAttr on server side!"-jsSetAttr = error "Tried to use jsSetAttr on server side!"-jsGetStyle = error "Tried to use jsGetStyle on server side!"-jsSetStyle = error "Tried to use jsSetStyle on server side!"-jsFind = error "Tried to use jsFind on server side!"-jsElemsByClassName = error "Tried to use jsElemsByClassName on server side!"-jsQuerySelectorAll = error "Tried to use jsQuerySelectorAll on server side!"-jsCreateElem = error "Tried to use jsCreateElem on server side!"-jsCreateTextNode = error "Tried to use jsCreateTextNode on server side!"-jsAppendChild = error "Tried to use jsAppendChild on server side!"-jsGetFirstChild = error "Tried to use jsGetFirstChild on server side!"-jsGetLastChild = error "Tried to use jsGetLastChild on server side!"-jsGetChildren = error "Tried to use jsGetChildren on server side!"-jsSetChildren = error "Tried to use jsSetChildren on server side!"-jsAddChildBefore = error "Tried to use jsAddChildBefore on server side!"-jsGetChildBefore = error "Tried to use jsGetChildBefore on server side!"-jsKillChild = error "Tried to use jsKillChild on server side!"-jsClearChildren = error "Tried to use jsClearChildren on server side!"-#endif---- | Append the first element as a child of the second element.-addChild :: MonadIO m => Elem -> Elem -> m ()-addChild child parent = liftIO $ jsAppendChild child parent---- | Insert the first element as a child into the second, before the third.---   For instance:--- @---   addChildBefore childToAdd theContainer olderChild--- @-addChildBefore :: MonadIO m => Elem -> Elem -> Elem -> m ()-addChildBefore child parent oldChild =-  liftIO $ jsAddChildBefore child parent oldChild---- | Get the sibling before the given one, if any.-getChildBefore :: MonadIO m => Elem -> m (Maybe Elem)-getChildBefore e = liftIO $ fromPtr `fmap` jsGetChildBefore e---- | Get the first of an element's children.-getFirstChild :: MonadIO m => Elem -> m (Maybe Elem)-getFirstChild e = liftIO $ fromPtr `fmap` jsGetFirstChild e---- | Get the last of an element's children.-getLastChild :: MonadIO m => Elem -> m (Maybe Elem)-getLastChild e = liftIO $ fromPtr `fmap` jsGetLastChild e---- | Get a list of all children belonging to a certain element.-getChildren :: MonadIO m => Elem -> m [Elem]-getChildren e = liftIO $ fromPtr `fmap` jsGetChildren e---- | Clear the given element's list of children, and append all given children---   to it.-setChildren :: MonadIO m => Elem -> [Elem] -> m ()-setChildren e ch = liftIO $ jsSetChildren e (toPtr ch)- -- | Create an element. newElem :: MonadIO m => String -> m Elem-newElem = liftIO . jsCreateElem . toJSStr+newElem = J.newElem . toJSStr  -- | Create a text node. newTextElem :: MonadIO m => String -> m Elem-newTextElem = liftIO . jsCreateTextNode . toJSStr+newTextElem = J.newTextElem . toJSStr  -- | Set a property of the given element.-setProp :: MonadIO m => Elem -> PropID -> String -> m ()-setProp e prop val = liftIO $ jsSet e (toJSStr prop) (toJSStr val)---- | Set a property of the given element, JSString edition.-setProp' :: MonadIO m => Elem -> JSString -> JSString -> m ()-setProp' e prop val = liftIO $ jsSet e prop val+setProp :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()+setProp e prop val = J.setProp e (toJSStr prop) (toJSStr val)  -- | Set an attribute of the given element.-setAttr :: MonadIO m => Elem -> PropID -> String -> m ()-setAttr e prop val = liftIO $ jsSetAttr e (toJSStr prop) (toJSStr val)---- | Get the value property of an element; a handy shortcut.-getValue :: (MonadIO m, JSType a) => Elem -> m (Maybe a)-getValue e = liftIO $ fromJSString `fmap` jsGet e "value"+setAttr :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()+setAttr e prop val = J.setAttr e (toJSStr prop) (toJSStr val)  -- | Get a property of an element.-getProp :: MonadIO m => Elem -> PropID -> m String-getProp e prop = liftIO $ fromJSStr `fmap` jsGet e (toJSStr prop)---- | Get a property of an element, JSString edition.-getProp' :: MonadIO m => Elem -> JSString -> m JSString-getProp' e prop = liftIO $ jsGet e prop+getProp :: (IsElem e, MonadIO m) => e -> PropID -> m String+getProp e prop = J.getProp e (toJSStr prop) >>= return . fromJSStr  -- | Get an attribute of an element.-getAttr :: MonadIO m => Elem -> PropID -> m String-getAttr e prop = liftIO $ fromJSStr `fmap` jsGetAttr e (toJSStr prop)+getAttr :: (IsElem e, MonadIO m) => e -> PropID -> m String+getAttr e prop = J.getAttr e (toJSStr prop) >>= return . fromJSStr  -- | Get a CSS style property of an element.-getStyle :: MonadIO m => Elem -> PropID -> m String-getStyle e prop = liftIO $ fromJSStr `fmap` jsGetStyle e (toJSStr prop)---- | Get a CSS style property of an element, JSString style.-getStyle' :: MonadIO m => Elem -> JSString -> m JSString-getStyle' e prop = liftIO $ jsGetStyle e prop+getStyle :: (IsElem e, MonadIO m) => e -> PropID -> m String+getStyle e prop = J.getStyle e (toJSStr prop) >>= return . fromJSStr  -- | Set a CSS style property on an element.-setStyle :: MonadIO m => Elem -> PropID -> String -> m ()-setStyle e prop val = liftIO $ jsSetStyle e (toJSStr prop) (toJSStr val)---- | Set a CSS style property on an element, JSString style.-setStyle' :: MonadIO m => Elem -> JSString -> JSString -> m ()-setStyle' e prop val = liftIO $ jsSetStyle e prop val+setStyle :: (IsElem e, MonadIO m) => e -> PropID -> String -> m ()+setStyle e prop val = J.setStyle e (toJSStr prop) (toJSStr val)  -- | Get an element by its HTML ID attribute. elemById :: MonadIO m => ElemID -> m (Maybe Elem)-elemById eid = liftIO $ fromPtr `fmap` (jsFind $ toJSStr eid)+elemById = J.elemById . toJSStr  -- | Get all elements of the given class. elemsByClass :: MonadIO m => ElemClass -> m [Elem]-elemsByClass cls = liftIO $ fromPtr `fmap` (jsElemsByClassName (toJSStr cls))+elemsByClass = J.elemsByClass . toJSStr  -- | Get all children elements matching a query selector. elemsByQS :: MonadIO m => Elem -> QuerySelector -> m [Elem]-elemsByQS el sel = liftIO $ fromPtr `fmap` (jsQuerySelectorAll el (toJSStr sel))+elemsByQS el = J.elemsByQS el . toJSStr  -- | Perform an IO action on an element. withElem :: MonadIO m => ElemID -> (Elem -> m a) -> m a-withElem e act = do-  me' <- elemById e-  case me' of-    Just e' -> act e'-    _       -> error $ "No element with ID " ++ e ++ " could be found!"+withElem = J.withElem . toJSStr  -- | Perform an IO action over several elements. Throws an error if some of the --   elements are not found. withElems :: MonadIO m => [ElemID] -> ([Elem] -> m a) -> m a-withElems es act = do-    mes <- mapM elemById es-    if any isNothing mes-      then error $ "Elements with the following IDs could not be found: "-                 ++ show (findElems es mes)-      else act $ map fromJust mes-  where-    findElems (i:is) (Nothing:mes) = i : findElems is mes-    findElems (_:is) (_:mes)       = findElems is mes-    findElems _ _                  = []+withElems = J.withElems . map toJSStr  -- | Perform an IO action over the a list of elements matching a query --   selector.-withElemsQS :: MonadIO m => Elem -> QuerySelector -> ([Elem] -> m a) -> m a-withElemsQS el sel act = elemsByQS el sel >>= act+withElemsQS :: (IsElem e, MonadIO m)+            => e+            -> QuerySelector+            -> ([Elem] -> m a)+            -> m a+withElemsQS el = J.withElemsQS el . toJSStr  -- | Map an IO computation over the list of elements matching a query selector.-mapQS :: MonadIO m => Elem -> QuerySelector -> (Elem -> m a) -> m [a]-mapQS el sel act = elemsByQS el sel >>= mapM act+mapQS :: (IsElem e, MonadIO m)+      => e+      -> QuerySelector+      -> (Elem -> m a)+      -> m [a]+mapQS el = J.mapQS el . toJSStr  -- | Like @mapQS@ but returns no value.-mapQS_ :: MonadIO m => Elem -> QuerySelector -> (Elem -> m a) -> m ()-mapQS_ el sel act = elemsByQS el sel >>= mapM_ act---- | Remove all children from the given element.-clearChildren :: MonadIO m => Elem -> m ()-clearChildren = liftIO . jsClearChildren---- | Remove the first element from the second's children.-removeChild :: MonadIO m => Elem -> Elem -> m ()-removeChild child parent = liftIO $ jsKillChild child parent---- | Get a file from a file input element.-getFileData :: MonadIO m => Elem -> Int -> m (Maybe Blob)-getFileData e ix = liftIO $ do-    num <- getFiles e-    if ix < num-      then Just `fmap` getFile e ix-      else return Nothing-  where-    {-# NOINLINE getFiles #-}-    getFiles :: Elem -> IO Int-    getFiles = ffi "(function(e){return e.files.length;})"-    {-# NOINLINE getFile #-}-    getFile :: Elem -> Int -> IO Blob-    getFile = ffi "(function(e,ix){return e.files[ix];})"+mapQS_ :: (IsElem e, MonadIO m)+       => e+       -> QuerySelector+       -> (Elem -> m a)+       -> m ()+mapQS_ el = J.mapQS_ el . toJSStr  -- | Get the name of the currently selected file from a file input element. --   Any directory information is stripped, and only the actual file name is --   returned, as the directory information is useless (and faked) anyway.-getFileName :: MonadIO m => Elem -> m String-getFileName e = liftIO $ do-    fn <- getProp e "value"-    return $ reverse $ takeWhile (not . separator) $ reverse fn-  where-    separator '/'  = True-    separator '\\' = True-    separator _    = False+getFileName :: (IsElem e, MonadIO m) => e -> m String+getFileName e = J.getFileName e >>= return . fromJSStr  -- | Add or remove a class from an element's class list.-setClass :: MonadIO m => Elem -> String -> Bool -> m ()-setClass e c x = liftIO $ setc e c x-  where-    {-# NOINLINE setc #-}-    setc :: Elem -> String -> Bool -> IO ()-    setc = ffi "(function(e,c,x){x?e.classList.add(c):e.classList.remove(c);})"+setClass :: (IsElem e, MonadIO m) => e -> String -> Bool -> m ()+setClass e sel = J.setClass e (toJSStr sel)  -- | Toggle the existence of a class within an elements class list.-toggleClass :: MonadIO m => Elem -> String -> m ()-toggleClass e c = liftIO $ toggc e c-  where-    {-# NOINLINE toggc #-}-    toggc :: Elem -> String -> IO ()-    toggc = ffi "(function(e,c) {e.classList.toggle(c);})"+toggleClass :: (IsElem e, MonadIO m) => e -> String -> m ()+toggleClass e = J.toggleClass e . toJSStr  -- | Does the given element have a particular class?-hasClass :: MonadIO m => Elem -> String -> m Bool-hasClass e c = liftIO $ getc e c-  where-    {-# NOINLINE getc #-}-    getc :: Elem -> String -> IO Bool-    getc = ffi "(function(e,c) {return e.classList.contains(c);})"---- | Generate a click event on an element.-click :: MonadIO m => Elem -> m ()-click = liftIO . click'-  where-    {-# NOINLINE click' #-}-    click' :: Elem -> IO ()-    click' = ffi "(function(e) {e.click();})"---- | Generate a focus event on an element.-focus :: MonadIO m => Elem -> m ()-focus = liftIO . focus'-  where-    {-# NOINLINE focus' #-}-    focus' :: Elem -> IO ()-    focus' = ffi "(function(e) {e.focus();})"---- | Generate a blur event on an element.-blur :: MonadIO m => Elem -> m ()-blur = liftIO . blur'-  where-    {-# NOINLINE blur' #-}-    blur' :: Elem -> IO ()-    blur' = ffi "(function(e) {e.blur();})"---- | The DOM node corresponding to document.-document :: Elem-document = unsafePerformIO getDocument-  where-    {-# NOINLINE getDocument #-}-    getDocument :: IO Elem-    getDocument = ffi "document"---- | The DOM node corresponding to document.body.-documentBody :: Elem-documentBody = unsafePerformIO getBody-  where-    {-# NOINLINE getBody #-}-    getBody :: IO Elem-    getBody = ffi "document.body"---- | The name of an attribute. May be either a common property, an HTML---   attribute or a style attribute.-data AttrName-  = PropName  JSString-  | StyleName JSString-  | AttrName  JSString--instance S.IsString AttrName where-  fromString = PropName . S.fromString---- | A key/value pair representing the value of an attribute.---   May represent a property, an HTML attribute or a style attribute.-data Attribute = Attribute AttrName JSString---- | Construct an 'Attribute'.-attribute :: AttrName -> JSString -> Attribute-attribute = Attribute---- | Set a number of 'Attribute's on an element.-set :: MonadIO m => Elem -> [Attribute] -> m ()-set e as =-    liftIO $ mapM_ set' as-  where-    set' (Attribute (PropName k) v)  = jsSet e k v-    set' (Attribute (StyleName k) v) = jsSetStyle e k v-    set' (Attribute (AttrName k) v)  = jsSetAttr e k v---- | Set a number of 'Attribute's on the element produced by an IO action.---   Gives more convenient syntax when creating elements:------     newElem "div" `with` [---         style "border" := "1px solid black",---         ...---       ]----with :: MonadIO m => m Elem -> [Attribute] -> m Elem-with m attrs = do-  x <- m-  set x attrs-  return x+hasClass :: (IsElem e, MonadIO m) => e -> String -> m Bool+hasClass e = J.hasClass e . toJSStr
+ libraries/haste-lib/src/Haste/DOM/Core.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving,+             OverloadedStrings #-}+-- | Core types and operations for DOM manipulation.+module Haste.DOM.Core (+    Elem (..), IsElem (..), Attribute, AttrName (..),+    set, with, attribute, children,+    click, focus, blur,+    document, documentBody,+    deleteChild, clearChildren,+    setChildren, getChildren,+    getLastChild, getFirstChild, getChildBefore,+    insertChildBefore, appendChild,+    -- Deprecated+    removeChild, addChild, addChildBefore+  ) where+import Haste.Prim+import Control.Monad.IO.Class+import Haste.Foreign+import Data.String++#ifdef __HASTE__+foreign import ccall jsSet :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsSetAttr :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsSetStyle :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsAppendChild :: Elem -> Elem -> IO ()+foreign import ccall jsGetFirstChild :: Elem -> IO (Ptr (Maybe Elem))+foreign import ccall jsGetLastChild :: Elem -> IO (Ptr (Maybe Elem))+foreign import ccall jsGetChildren :: Elem -> IO (Ptr [Elem])+foreign import ccall jsSetChildren :: Elem -> Ptr [Elem] -> IO ()+foreign import ccall jsAddChildBefore :: Elem -> Elem -> Elem -> IO ()+foreign import ccall jsGetChildBefore :: Elem -> IO (Ptr (Maybe Elem))+foreign import ccall jsKillChild :: Elem -> Elem -> IO ()+foreign import ccall jsClearChildren :: Elem -> IO ()+#else+jsSet :: Elem -> JSString -> JSString -> IO ()+jsSet = error "Tried to use jsSet on server side!"+jsSetAttr :: Elem -> JSString -> JSString -> IO ()+jsSetAttr = error "Tried to use jsSetAttr on server side!"+jsSetStyle :: Elem -> JSString -> JSString -> IO ()+jsSetStyle = error "Tried to use jsSetStyle on server side!"+jsAppendChild :: Elem -> Elem -> IO ()+jsAppendChild = error "Tried to use jsAppendChild on server side!"+jsGetFirstChild :: Elem -> IO (Ptr (Maybe Elem))+jsGetFirstChild = error "Tried to use jsGetFirstChild on server side!"+jsGetLastChild :: Elem -> IO (Ptr (Maybe Elem))+jsGetLastChild = error "Tried to use jsGetLastChild on server side!"+jsGetChildren :: Elem -> IO (Ptr [Elem])+jsGetChildren = error "Tried to use jsGetChildren on server side!"+jsSetChildren :: Elem -> Ptr [Elem] -> IO ()+jsSetChildren = error "Tried to use jsSetChildren on server side!"+jsAddChildBefore :: Elem -> Elem -> Elem -> IO ()+jsAddChildBefore = error "Tried to use jsAddChildBefore on server side!"+jsGetChildBefore :: Elem -> IO (Ptr (Maybe Elem))+jsGetChildBefore = error "Tried to use jsGetChildBefore on server side!"+jsKillChild :: Elem -> Elem -> IO ()+jsKillChild = error "Tried to use jsKillChild on server side!"+jsClearChildren :: Elem -> IO ()+jsClearChildren = error "Tried to use jsClearChildren on server side!"+#endif++-- | A DOM node.+newtype Elem = Elem JSAny+  deriving (ToAny, FromAny)++-- | The class of types backed by DOM elements.+class IsElem a where+  -- | Get the element representing the object.+  elemOf :: a -> Elem++  -- | Attempt to create an object from an 'Elem'.+  fromElem :: Elem -> IO (Maybe a)+  fromElem = const $ return Nothing++instance IsElem Elem where+  elemOf = id+  fromElem = return . Just++-- | The name of an attribute. May be either a common property, an HTML+--   attribute or a style attribute.+data AttrName+  = PropName  !JSString+  | StyleName !JSString+  | AttrName  !JSString++instance IsString AttrName where+  fromString = PropName . fromString++-- | A key/value pair representing the value of an attribute.+--   May represent a property, an HTML attribute, a style attribute or a list+--   of child elements.+data Attribute+  = Attribute !AttrName !JSString+  | Children ![Elem]++-- | Construct an 'Attribute'.+attribute :: AttrName -> JSString -> Attribute+attribute = Attribute++-- | Set a number of 'Attribute's on an element.+set :: (IsElem e, MonadIO m) => e -> [Attribute] -> m ()+set e as =+    liftIO $ mapM_ set' as+  where+    e' = elemOf e+    set' (Attribute (PropName k) v)  = jsSet e' k v+    set' (Attribute (StyleName k) v) = jsSetStyle e' k v+    set' (Attribute (AttrName k) v)  = jsSetAttr e' k v+    set' (Children cs)               = mapM_ (flip jsAppendChild e') cs++-- | Attribute adding a list of child nodes to an element.+children :: [Elem] -> Attribute+children = Children++-- | Set a number of 'Attribute's on the element produced by an IO action.+--   Gives more convenient syntax when creating elements:+--+--     newElem "div" `with` [+--         style "border" =: "1px solid black",+--         ...+--       ]+--+with :: (IsElem e, MonadIO m) => m e -> [Attribute] -> m e+with m attrs = do+  x <- m+  set x attrs+  return x++-- | Generate a click event on an element.+click :: (IsElem e, MonadIO m) => e -> m ()+click = liftIO . click' . elemOf+  where+    {-# NOINLINE click' #-}+    click' :: Elem -> IO ()+    click' = ffi "(function(e) {e.click();})"++-- | Generate a focus event on an element.+focus :: (IsElem e, MonadIO m) => e -> m ()+focus = liftIO . focus' . elemOf+  where+    {-# NOINLINE focus' #-}+    focus' :: Elem -> IO ()+    focus' = ffi "(function(e) {e.focus();})"++-- | Generate a blur event on an element.+blur :: (IsElem e, MonadIO m) => e -> m ()+blur = liftIO . blur' . elemOf+  where+    {-# NOINLINE blur' #-}+    blur' :: Elem -> IO ()+    blur' = ffi "(function(e) {e.blur();})"++-- | The DOM node corresponding to document.+document :: Elem+document = constant "document"++-- | The DOM node corresponding to document.body.+documentBody :: Elem+documentBody = constant "document.body"++-- | Append the first element as a child of the second element.+appendChild :: (IsElem parent, IsElem child, MonadIO m) => parent -> child -> m ()+appendChild parent child = liftIO $ jsAppendChild (elemOf child) (elemOf parent)++{-# DEPRECATED addChild "Use appendChild instead" #-}+-- | DEPRECATED: use 'appendChild' instead!+addChild :: (IsElem parent, IsElem child, MonadIO m) => child -> parent -> m ()+addChild = flip appendChild++-- | Insert an element into a container, before another element.+--   For instance:+-- @+--   insertChildBefore theContainer olderChild childToAdd+-- @+insertChildBefore :: (IsElem parent, IsElem before, IsElem child, MonadIO m)+               => parent -> before -> child -> m ()+insertChildBefore parent oldChild child =+  liftIO $ jsAddChildBefore (elemOf child) (elemOf parent) (elemOf oldChild)++{-# DEPRECATED addChildBefore "Use insertChildBefore instead" #-}+-- | DEPRECATED: use 'insertChildBefore' instead!+addChildBefore :: (IsElem parent, IsElem child, MonadIO m)+               => child -> parent -> child -> m ()+addChildBefore child parent oldChild = insertChildBefore parent oldChild child++-- | Get the sibling before the given one, if any.+getChildBefore :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)+getChildBefore e = liftIO $ fromPtr `fmap` jsGetChildBefore (elemOf e)++-- | Get the first of an element's children.+getFirstChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)+getFirstChild e = liftIO $ fromPtr `fmap` jsGetFirstChild (elemOf e)++-- | Get the last of an element's children.+getLastChild :: (IsElem e, MonadIO m) => e -> m (Maybe Elem)+getLastChild e = liftIO $ fromPtr `fmap` jsGetLastChild (elemOf e)++-- | Get a list of all children belonging to a certain element.+getChildren :: (IsElem e, MonadIO m) => e -> m [Elem]+getChildren e = liftIO $ fromPtr `fmap` jsGetChildren (elemOf e)++-- | Clear the given element's list of children, and append all given children+--   to it.+setChildren :: (IsElem parent, IsElem child, MonadIO m)+            => parent+            -> [child]+            -> m ()+setChildren e ch = liftIO $ jsSetChildren (elemOf e) (toPtr $ map elemOf ch)++-- | Remove all children from the given element.+clearChildren :: (IsElem e, MonadIO m) => e -> m ()+clearChildren = liftIO . jsClearChildren . elemOf++-- | Remove the second element from the first's children.+deleteChild :: (IsElem parent, IsElem child, MonadIO m)+            => parent+            -> child+            -> m ()+deleteChild parent child = liftIO $ jsKillChild (elemOf child) (elemOf parent)++{-# DEPRECATED removeChild "Use deleteChild instead" #-}+-- | DEPRECATED: use 'deleteChild' instead!+removeChild :: (IsElem parent, IsElem child, MonadIO m)+            => child+            -> parent+            -> m ()+removeChild child parent = liftIO $ jsKillChild (elemOf child) (elemOf parent)
+ libraries/haste-lib/src/Haste/DOM/JSString.hs view
@@ -0,0 +1,213 @@+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, CPP #-}+-- | DOM manipulation functions using 'JSString' for string representation.+module Haste.DOM.JSString (+    module Core,+    IsElem (..), Elem, PropID, ElemID, QuerySelector, ElemClass,+    AttrName, AttrValue,+    style, attr, (=:),+    newElem, newTextElem,+    elemById, elemsByQS, elemsByClass,+    setProp, getProp, setAttr, getAttr, getValue,+    withElem , withElems, withElemsQS, mapQS, mapQS_,+    getStyle, setStyle,+    getFileData, getFileName,+    setClass, toggleClass, hasClass+  ) where+import Haste.Prim+import Haste.JSType+import qualified Haste.DOM.Core as Core+  hiding (Elem (..), AttrName (..))+import Haste.DOM.Core+import Data.Maybe (isNothing, fromJust)+import Control.Monad.IO.Class+import Haste.Foreign+import Haste.Binary.Types++type PropID = JSString+type ElemID = JSString+type QuerySelector = JSString+type ElemClass = JSString+type AttrValue = JSString++#ifdef __HASTE__+foreign import ccall jsGet :: Elem -> JSString -> IO JSString+foreign import ccall jsSet :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsGetAttr :: Elem -> JSString -> IO JSString+foreign import ccall jsSetAttr :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsGetStyle :: Elem -> JSString -> IO JSString+foreign import ccall jsSetStyle :: Elem -> JSString -> JSString -> IO ()+foreign import ccall jsFind :: JSString -> IO (Ptr (Maybe Elem))+foreign import ccall jsQuerySelectorAll :: Elem -> JSString -> IO (Ptr [Elem])+foreign import ccall jsElemsByClassName :: JSString -> IO (Ptr [Elem])+foreign import ccall jsCreateElem :: JSString -> IO Elem+foreign import ccall jsCreateTextNode :: JSString -> IO Elem+#else+jsGet :: Elem -> JSString -> IO JSString+jsGet = error "Tried to use jsGet on server side!"+jsSet :: Elem -> JSString -> JSString -> IO ()+jsSet = error "Tried to use jsSet on server side!"+jsGetAttr :: Elem -> JSString -> IO JSString+jsGetAttr = error "Tried to use jsGetAttr on server side!"+jsSetAttr :: Elem -> JSString -> JSString -> IO ()+jsSetAttr = error "Tried to use jsSetAttr on server side!"+jsGetStyle :: Elem -> JSString -> IO JSString+jsGetStyle = error "Tried to use jsGetStyle on server side!"+jsSetStyle :: Elem -> JSString -> JSString -> IO ()+jsSetStyle = error "Tried to use jsSetStyle on server side!"+jsFind :: JSString -> IO (Ptr (Maybe Elem))+jsFind = error "Tried to use jsFind on server side!"+jsQuerySelectorAll :: Elem -> JSString -> IO (Ptr [Elem])+jsQuerySelectorAll = error "Tried to use jsQuerySelectorAll on server side!"+jsElemsByClassName :: JSString -> IO (Ptr [Elem])+jsElemsByClassName = error "Tried to use jsElemsByClassName on server side!"+jsCreateElem :: JSString -> IO Elem+jsCreateElem = error "Tried to use jsCreateElem on server side!"+jsCreateTextNode :: JSString -> IO Elem+jsCreateTextNode = error "Tried to use jsCreateTextNode on server side!"+#endif++-- | Create a style attribute name.+style :: JSString -> AttrName+style = StyleName++-- | Create an HTML attribute name.+attr :: JSString -> AttrName+attr = AttrName++-- | Create an 'Attribute'.+(=:) :: AttrName -> AttrValue -> Attribute+(=:) = attribute++-- | Create an element.+newElem :: MonadIO m => JSString -> m Elem+newElem = liftIO . jsCreateElem++-- | Create a text node.+newTextElem :: MonadIO m => JSString -> m Elem+newTextElem = liftIO . jsCreateTextNode++-- | Set a property of the given element.+setProp :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()+setProp e prop val = liftIO $ jsSet (elemOf e) prop val++-- | Set an attribute of the given element.+setAttr :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()+setAttr e prop val = liftIO $ jsSetAttr (elemOf e) prop val++-- | Get the value property of an element; a handy shortcut.+getValue :: (IsElem e, MonadIO m, JSType a) => e -> m (Maybe a)+getValue e = liftIO $ fromJSString `fmap` jsGet (elemOf e) "value"++-- | Get a property of an element.+getProp :: (IsElem e, MonadIO m) => e -> PropID -> m JSString+getProp e prop = liftIO $ jsGet (elemOf e) prop++-- | Get an attribute of an element.+getAttr :: (IsElem e, MonadIO m) => e -> PropID -> m JSString+getAttr e prop = liftIO $ jsGetAttr (elemOf e) prop++-- | Get a CSS style property of an element.+getStyle :: (IsElem e, MonadIO m) => e -> PropID -> m JSString+getStyle e prop = liftIO $ jsGetStyle (elemOf e) prop++-- | Set a CSS style property on an element.+setStyle :: (IsElem e, MonadIO m) => e -> PropID -> JSString -> m ()+setStyle e prop val = liftIO $ jsSetStyle (elemOf e) prop val++-- | Get an element by its HTML ID attribute.+elemById :: MonadIO m => ElemID -> m (Maybe Elem)+elemById eid = liftIO $ fromPtr `fmap` (jsFind eid)++-- | Get all elements of the given class.+elemsByClass :: MonadIO m => ElemClass -> m [Elem]+elemsByClass cls = liftIO $ fromPtr `fmap` (jsElemsByClassName cls)++-- | Get all children elements matching a query selector.+elemsByQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> m [Elem]+elemsByQS el sel = liftIO $ fromPtr `fmap` (jsQuerySelectorAll (elemOf el) sel)++-- | Perform an IO action on an element.+withElem :: MonadIO m => ElemID -> (Elem -> m a) -> m a+withElem e act = do+  me' <- elemById e+  case me' of+    Just e' -> act e'+    _       -> error $ "No element with ID " ++ fromJSStr e ++ " found!"++-- | Perform an IO action over several elements. Throws an error if some of the+--   elements are not found.+withElems :: MonadIO m => [ElemID] -> ([Elem] -> m a) -> m a+withElems es act = do+    mes <- mapM elemById es+    if any isNothing mes+      then error $ "Elements with the following IDs could not be found: "+                 ++ show (findElems es mes)+      else act $ map fromJust mes+  where+    findElems (i:is) (Nothing:mes) = i : findElems is mes+    findElems (_:is) (_:mes)       = findElems is mes+    findElems _ _                  = []++-- | Perform an IO action over the a list of elements matching a query+--   selector.+withElemsQS :: (IsElem e, MonadIO m)+            => e+            -> QuerySelector+            -> ([Elem] -> m a)+            -> m a+withElemsQS el sel act = elemsByQS el sel >>= act++-- | Map an IO computation over the list of elements matching a query selector.+mapQS :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m [a]+mapQS el sel act = elemsByQS el sel >>= mapM act++-- | Like @mapQS@ but returns no value.+mapQS_ :: (IsElem e, MonadIO m) => e -> QuerySelector -> (Elem -> m a) -> m ()+mapQS_ el sel act = elemsByQS el sel >>= mapM_ act++-- | Get a file from a file input element.+getFileData :: (IsElem e, MonadIO m) => e -> Int -> m (Maybe Blob)+getFileData e ix = liftIO $ do+    num <- getFiles (elemOf e)+    if ix < num+      then Just `fmap` getFile (elemOf e) ix+      else return Nothing++getFiles :: Elem -> IO Int+getFiles = ffi "(function(e){return e.files.length;})"++getFile :: Elem -> Int -> IO Blob+getFile = ffi "(function(e,ix){return e.files[ix];})"++-- | Get the name of the currently selected file from a file input element.+--   Any directory information is stripped, and only the actual file name is+--   returned, as the directory information is useless (and faked) anyway.+getFileName :: (IsElem e, MonadIO m) => e -> m JSString+getFileName e = liftIO $ do+    fn <- fromJSStr `fmap` getProp e "value"+    return $ toJSStr $ reverse $ takeWhile (not . separator) $ reverse fn+  where+    separator '/'  = True+    separator '\\' = True+    separator _    = False++-- | Add or remove a class from an element's class list.+setClass :: (IsElem e, MonadIO m) => e -> JSString -> Bool -> m ()+setClass e c x = liftIO $ setc (elemOf e) c x++setc :: Elem -> JSString -> Bool -> IO ()+setc = ffi "(function(e,c,x){x?e.classList.add(c):e.classList.remove(c);})"++-- | Toggle the existence of a class within an elements class list.+toggleClass :: (IsElem e, MonadIO m) => e -> JSString -> m ()+toggleClass e c = liftIO $ toggc (elemOf e) c++toggc :: Elem -> JSString -> IO ()+toggc = ffi "(function(e,c) {e.classList.toggle(c);})"++-- | Does the given element have a particular class?+hasClass :: (IsElem e, MonadIO m) => e -> JSString -> m Bool+hasClass e c = liftIO $ getc (elemOf e) c++getc :: Elem -> JSString -> IO Bool+getc = ffi "(function(e,c) {return e.classList.contains(c);})"
+ libraries/haste-lib/src/Haste/Events.hs view
@@ -0,0 +1,11 @@+-- | Event handling for Haste.+module Haste.Events (+    module Core,+    module BasicEvents,+    module KeyEvents,+    module MouseEvents+  ) where+import Haste.Events.Core as Core+import Haste.Events.BasicEvents as BasicEvents+import Haste.Events.KeyEvents as KeyEvents+import Haste.Events.MouseEvents as MouseEvents
+ libraries/haste-lib/src/Haste/Events/BasicEvents.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE OverloadedStrings, TypeFamilies #-}+-- | Basic events: load, unload, focus, submit, etc.+module Haste.Events.BasicEvents (BasicEvent (..)) where+import Haste.Events.Core++data BasicEvent+  = Load+  | Unload+  | Change+  | Focus+  | Blur+  | Submit+  | Scroll++instance Event BasicEvent where+  type EventData BasicEvent = ()+  eventName Load   = "load"+  eventName Unload = "unload"+  eventName Change = "change"+  eventName Focus  = "focus"+  eventName Blur   = "blur"+  eventName Submit = "submit"+  eventName Scroll = "scroll"+  eventData _ _    = return ()
+ libraries/haste-lib/src/Haste/Events/Core.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings, TypeFamilies, FlexibleContexts #-}+-- | Basic framework for event handling.+module Haste.Events.Core (+    Event (..), MonadEvent (..),+    HandlerInfo,+    unregisterHandler, onEvent, preventDefault+  ) where+import Haste.Prim+import Haste.DOM.Core+import Haste.Foreign+import Control.Monad.IO.Class+import Data.IORef+import System.IO.Unsafe++-- | Any monad in which we're able to handle events.+class MonadIO m => MonadEvent m where+  mkHandler :: (a -> m ()) -> m (a -> IO ())++instance MonadEvent IO where+  mkHandler = return++-- | Any type that describes an event.+class Event evt where+  -- | The type of data to pass to handlers for this event.+  type EventData evt++  -- | The name of this event, as expected by the DOM.+  eventName :: evt -> JSString++  -- | Construct event data from the event identifier and the JS event object.+  eventData :: evt -> JSAny -> IO (EventData evt)++-- | Information about an event handler.+data HandlerInfo = HandlerInfo {+    -- | Name of the handler's event.+    handlerEvent :: JSString,+    -- | Element the handler is set on.+    handlerElem  :: Elem,+    -- | Handle to handler function.+    handlerFun   :: JSAny+  }++-- | Unregister an event handler.+unregisterHandler :: HandlerInfo -> IO ()+unregisterHandler (HandlerInfo ev el f) = unregEvt el ev f++-- | Reference to the event currently being handled.+{-# NOINLINE evtRef #-}+evtRef :: IORef (Maybe JSAny)+evtRef = unsafePerformIO $ newIORef Nothing++{-# INLINE setEvtRef #-}+setEvtRef :: JSAny -> IO ()+setEvtRef = writeIORef evtRef . Just++-- | Prevent the event being handled from resolving normally.+--   Does nothing if called outside an event handler.+preventDefault :: IO ()+preventDefault = readIORef evtRef >>= go+  where+    go :: Maybe JSAny -> IO ()+    go = ffi "(function(e){if(e){e.preventDefault();}})"++-- | Set an event handler on a DOM element.+onEvent :: (MonadEvent m, IsElem el, Event evt)+        => el            -- ^ Element to set handler on.+        -> evt           -- ^ Event to handle.+        -> (EventData evt -> m ()) -- ^ Event handler.+        -> m HandlerInfo -- ^ Information about the handler.+onEvent el evt f = do+  f' <- mkHandler $ \o -> prepareEvent o >>= f+  hdl <- liftIO $ setEvt e name f'+  return $ HandlerInfo {+      handlerEvent = name,+      handlerElem  = e,+      handlerFun   = hdl+    }+  where+    name = eventName evt+    e    = elemOf el+    prepareEvent o = liftIO $ do+      setEvtRef o+      eventData evt o++-- | Set an event handler on an element, returning a reference to the handler+--   exactly as seen by @addEventListener@. We can't reuse the reference to+--   the Haskell function as the FFI does some marshalling to functions,+--   meaning that the same function marshalled twice won't be reference equal+--   to each other.+setEvt :: Elem -> JSString -> (JSAny -> IO ()) -> IO JSAny+setEvt = ffi "(function(e,name,f){e.addEventListener(name,f,false);\+             \return [f];})"++-- | Unregister an event.+--   Note @f[0]@ and corresponding @[f]@ in 'setEvt'; this is a workaround for+--   a bug causing functions being packed into anything to be accidentally+--   called. Remove when properly fixed.+unregEvt :: Elem -> JSString -> JSAny -> IO ()+unregEvt = ffi "(function(e,name,f){e.removeEventListener(name,f[0]);})"
+ libraries/haste-lib/src/Haste/Events/KeyEvents.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE OverloadedStrings, TypeFamilies, CPP #-}+-- | Events relating to mouse keyboard input.+module Haste.Events.KeyEvents (KeyEvent (..), KeyData (..), mkKeyData) where+import Haste.Any+import Haste.Events.Core+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif++-- | Event data for keyboard events.+data KeyData = KeyData {+    keyCode  :: !Int,+    keyCtrl  :: !Bool,+    keyAlt   :: !Bool,+    keyShift :: !Bool,+    keyMeta  :: !Bool+  } deriving (Show, Eq)++-- | Build a 'KeyData' object for the given key, without any modifier keys+--   pressed.+mkKeyData :: Int -> KeyData+mkKeyData n = KeyData {+    keyCode  = fromIntegral n,+    keyCtrl  = False,+    keyAlt   = False,+    keyShift = False,+    keyMeta  = False+  }++-- | Num instance for KeyData to enable pattern matching against numeric+--   key codes.+instance Num KeyData where+  fromInteger = mkKeyData . fromInteger+  a + b    = a {keyCode = keyCode a  +  keyCode b}+  a * b    = a {keyCode = keyCode a  *  keyCode b}+  a - b    = a {keyCode = keyCode a  -  keyCode b}+  negate a = a {keyCode = negate $ keyCode a}+  signum a = a {keyCode = signum $ keyCode a}+  abs a    = a {keyCode = abs $ keyCode a}++data KeyEvent+  = KeyPress+  | KeyUp+  | KeyDown++instance Event KeyEvent where+  type EventData KeyEvent = KeyData+  eventName KeyPress = "keypress"+  eventName KeyUp    = "keyup"+  eventName KeyDown  = "keydown"+  eventData _ e =+    KeyData <$> get e "keyCode"+            <*> get e "ctrlKey"+            <*> get e "altKey"+            <*> get e "shiftKey"+            <*> get e "metaKey"
+ libraries/haste-lib/src/Haste/Events/MouseEvents.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings, TypeFamilies, TupleSections, CPP #-}+-- | Events relating to mouse clicks and movement.+module Haste.Events.MouseEvents (+    MouseEvent (..), MouseData (..), MouseButton (..)+  ) where+import Haste.Events.Core+import Haste.Foreign+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif++data MouseButton = MouseLeft | MouseMiddle | MouseRight+  deriving (Show, Eq, Enum)++instance FromAny MouseButton where+  fromAny = fmap toEnum . fromAny++-- | Event data for mouse events.+data MouseData = MouseData {+    -- | Mouse coordinates.+    mouseCoords      :: !(Int, Int),+    -- | Pressed mouse button, if any.+    mouseButton      :: !(Maybe MouseButton),+    -- | (x, y, z) mouse wheel delta. Always all zeroes except for 'Wheel'.+    mouseWheelDeltas :: !(Double, Double, Double)+  }++data MouseEvent+  = Click+  | DblClick+  | MouseDown+  | MouseUp+  | MouseMove+  | MouseOver+  | MouseOut+  | Wheel++instance Event MouseEvent where+  type EventData MouseEvent = MouseData+  eventName Click     = "click"+  eventName DblClick  = "dblclick"+  eventName MouseDown = "mousedown"+  eventName MouseUp   = "mouseup"+  eventName MouseMove = "mousemove"+  eventName MouseOver = "mouseover"+  eventName MouseOut  = "mouseout"+  eventName Wheel     = "wheel"+  eventData Wheel e =+    MouseData <$> jsGetMouseCoords e+              <*> pure Nothing+              <*> ((,,) <$> (get e "deltaX")+                        <*> (get e "deltaY")+                        <*> (get e "deltaZ"))++  eventData _ e =+    MouseData <$> jsGetMouseCoords e+              <*> get e "button"+              <*> pure (0,0,0)++jsGetMouseCoords :: JSAny -> IO (Int, Int)+jsGetMouseCoords = ffi "jsGetMouseCoords"
libraries/haste-lib/src/Haste/Foreign.hs view
@@ -1,341 +1,159 @@-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, TypeSynonymInstances,-             FlexibleInstances, TypeFamilies, OverlappingInstances, CPP,-             OverloadedStrings, UndecidableInstances #-}--- | Create functions on the fly from JS strings.---   Slower but more flexible alternative to the standard FFI.+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, BangPatterns, CPP #-}+{-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+#if __GLASGOW_HASKELL__ < 710+{-# LANGUAGE OverlappingInstances #-}+#endif+-- | High level interface for interfacing with JavaScript. module Haste.Foreign (-    FFI, Pack (..), Unpack (..), Marshal,-    Unpacked, Opaque,-    ffi, export, toOpaque, fromOpaque+    module Haste.Any,+    FFI, JSFunc,+    ffi, constant, export   ) where import Haste.Prim-import Haste.JSType-import Data.Word-import Data.Int-import System.IO.Unsafe-import Unsafe.Coerce+import Haste.Any +-- | A JS function.+type JSFun = JSAny+ #ifdef __HASTE__-foreign import ccall eval :: JSString -> IO (Ptr a)-foreign import ccall "String" jsString :: Double -> JSString+foreign import ccall "eval" __eval :: JSString -> JSFun+foreign import ccall __apply :: JSFun -> Ptr [JSAny] -> IO JSAny+foreign import ccall __app0  :: JSFun -> IO JSAny+foreign import ccall __app1  :: JSFun -> JSAny -> IO JSAny+foreign import ccall __app2  :: JSFun -> JSAny -> JSAny -> IO JSAny+foreign import ccall __app3  :: JSFun -> JSAny -> JSAny -> JSAny -> IO JSAny+foreign import ccall __app4  :: JSFun+                             -> JSAny -> JSAny -> JSAny -> JSAny -> IO JSAny+foreign import ccall __app5  :: JSFun+                             -> JSAny -> JSAny -> JSAny -> JSAny -> JSAny+                             -> IO JSAny+foreign import ccall __createJSFunc :: Int -> JSAny -> IO JSAny #else-eval :: JSString -> IO (Ptr a)-eval = error "Tried to use eval on server side!"-jsString :: Double -> JSString-jsString = error "Tried to use jsString on server side!"+__eval :: JSString -> JSFun+__eval _ = undefined+__apply :: JSFun -> Ptr [JSAny] -> IO JSAny+__apply _ _ = return undefined+__app0  :: JSFun -> IO JSAny+__app0 _ = return undefined+__app1  :: JSFun -> JSAny -> IO JSAny+__app1 _ _ = return undefined+__app2  :: JSFun -> JSAny -> JSAny -> IO JSAny+__app2 _ _ _ = return undefined+__app3  :: JSFun -> JSAny -> JSAny -> JSAny -> IO JSAny+__app3 _ _ _ _ = return undefined+__app4  :: JSFun -> JSAny -> JSAny -> JSAny -> JSAny -> IO JSAny+__app4 _ _ _ _ _ = return undefined+__app5  :: JSFun -> JSAny -> JSAny -> JSAny -> JSAny -> JSAny -> IO JSAny+__app5 _ _ _ _ _ _ = return undefined+__createJSFunc :: Int -> JSAny -> IO JSAny+__createJSFunc _ = return undefined #endif --- | Opaque type representing a raw, unpacked JS value. The constructors have---   no meaning, but are only there to make sure GHC doesn't optimize the low---   level hackery in this module into oblivion.-data Unpacked = A | B---- | The Opaque type is inhabited by values that can be passed to Javascript---   using their raw Haskell representation. Opaque values are completely---   useless to Javascript code, and should not be inspected. This is useful---   for, for instance, storing data in some Javascript-native data structure---   for later retrieval.-newtype Opaque a = Opaque Unpacked--toOpaque :: a -> Opaque a-toOpaque = unsafeCoerce--fromOpaque :: Opaque a -> a-fromOpaque = unsafeCoerce--data Dummy = Dummy Unpacked--class Pack a where-  pack :: Unpacked -> a-  pack = unsafePack--class Unpack a where-  unpack :: a -> Unpacked-  unpack = unsafeUnpack---- | Class for marshallable types. Pack takes an opaque JS value and turns it---   into the type's proper Haste representation, and unpack is its inverse.---   The default instances make an effort to prevent wrongly typed values---   through, but you could probably break them with enough creativity.-class (Pack a, Unpack a) => Marshal a-instance (Pack a, Unpack a) => Marshal a--instance Pack Float-instance Pack Double-instance Pack JSAny-instance Pack JSString where-  pack = jsString . unsafePack-instance Pack Int where-  pack x = convert (unsafePack x :: Double)-instance Pack Int8 where-  pack x = convert (unsafePack x :: Double)-instance Pack Int16 where-  pack x = convert (unsafePack x :: Double)-instance Pack Int32 where-  pack x = convert (unsafePack x :: Double)-instance Pack Word where-  pack x = convert (unsafePack x :: Double)-instance Pack Word8 where-  pack x = convert (unsafePack x :: Double)-instance Pack Word16 where-  pack x = convert (unsafePack x :: Double)-instance Pack Word32 where-  pack x = convert (unsafePack x :: Double)-instance Pack () where-  pack _   = ()-instance Pack String where-  pack = fromJSStr . pack-instance Pack Unpacked where-  pack = id-instance Pack (Opaque a) where-  pack = Opaque-instance Pack Bool where-  pack x = if pack x > (0 :: Double) then True else False---- | Lists are marshalled into arrays.-instance Pack a => Pack [a] where-  pack arr = map pack . fromOpaque $ arr2lst arr 0---- | Maybe is simply a nullable type. Nothing is equivalent to null, and any---   non-null value is equivalent to x in Just x.-instance Pack a => Pack (Maybe a) where-  pack x = if isNull x then Nothing else Just (pack x)---- | Tuples are marshalled into arrays.-instance (Pack a, Pack b) => Pack (a, b) where-  pack x = case pack x of [a, b] -> (pack a, pack b)--instance (Pack a, Pack b, Pack c) => Pack (a, b, c) where-  pack x = case pack x of [a, b, c] -> (pack a, pack b, pack c)--instance (Pack a, Pack b, Pack c, Pack d) =>-         Pack (a, b, c, d) where-  pack x = case pack x of [a, b, c, d] -> (pack a, pack b, pack c, pack d)--instance (Pack a, Pack b, Pack c, Pack d, Pack e) =>-         Pack (a, b, c, d, e) where-  pack x = case pack x of [a,b,c,d,e] -> (pack a, pack b, pack c, pack d, pack e)--instance (Pack a, Pack b, Pack c, Pack d, Pack e,-          Pack f) => Pack (a, b, c, d, e, f) where-  pack x = case pack x of-    [a, b, c, d, e, f] -> (pack a, pack b, pack c, pack d, pack e, pack f)--instance (Pack a, Pack b, Pack c, Pack d, Pack e,-          Pack f, Pack g) => Pack (a, b, c, d, e, f, g) where-  pack x = case pack x of-    [a, b, c, d, e, f, g] -> (pack a,pack b,pack c,pack d,pack e,pack f,pack g)--instance (Pack a, Pack b, Pack c, Pack d, Pack e,-          Pack f, Pack g, Pack h) =>-         Pack (a, b, c, d, e, f, g, h) where-  pack x = case pack x of-    [a, b, c, d, e, f, g, h] -> (pack a, pack b, pack c, pack d, pack e,-                                 pack f, pack g, pack h)--instance (Pack a, Pack b, Pack c, Pack d, Pack e,-          Pack f, Pack g, Pack h, Pack i) =>-         Pack (a, b, c, d, e, f, g, h, i) where-  pack x = case pack x of-    [a, b, c, d, e, f, g, h, i] -> (pack a, pack b, pack c, pack d, pack e,-                                    pack f, pack g, pack h, pack i)--instance (Pack a, Pack b, Pack c, Pack d, Pack e,-          Pack f, Pack g, Pack h, Pack i, Pack j) =>-         Pack (a, b, c, d, e, f, g, h, i, j) where-  pack x = case pack x of-    [a, b, c, d, e, f, g, h, i, j] -> (pack a, pack b, pack c, pack d, pack e,-                                       pack f, pack g, pack h, pack i, pack j)--instance Unpack Float-instance Unpack Double-instance Unpack JSAny-instance Unpack JSString-instance Unpack Int-instance Unpack Int8-instance Unpack Int16-instance Unpack Int32-instance Unpack Word-instance Unpack Word8-instance Unpack Word16-instance Unpack Word32-instance Unpack () where-  unpack _ = unpack (0 :: Double)-instance Unpack String where-  unpack = unpack . toJSStr-instance Unpack Unpacked where-  unpack = id-instance Unpack (Opaque a) where-  unpack (Opaque x) = x-instance Unpack Bool where-  unpack True  = jsTrue-  unpack False = jsFalse---- | Lists are marshalled into arrays.-instance Unpack a => Unpack [a] where-  unpack = lst2arr . toOpaque . map unpack---- | Maybe is simply a nullable type. Nothing is equivalent to null, and any---   non-null value is equivalent to x in Just x.-instance Unpack a => Unpack (Maybe a) where-  unpack Nothing  = jsNull-  unpack (Just x) = unpack x---- | Tuples are marshalled into arrays.-instance (Unpack a, Unpack b) => Unpack (a, b) where-  unpack (a, b) = unpack [unpack a, unpack b]--instance (Unpack a, Unpack b, Unpack c) => Unpack (a, b, c) where-  unpack (a, b, c) = unpack [unpack a, unpack b, unpack c]--instance (Unpack a, Unpack b, Unpack c, Unpack d) =>-         Unpack (a, b, c, d) where-  unpack (a, b, c, d) = unpack [unpack a, unpack b, unpack c, unpack d]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e) =>-         Unpack (a, b, c, d, e) where-  unpack (a, b, c, d, e) = unpack [unpack a,unpack b,unpack c,unpack d,unpack e]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e,-          Unpack f) => Unpack (a, b, c, d, e, f) where-  unpack (a, b, c, d, e, f) =-    unpack [unpack a, unpack b, unpack c, unpack d, unpack e, unpack f]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e,-          Unpack f, Unpack g) => Unpack (a, b, c, d, e, f, g) where-  unpack (a, b, c, d, e, f, g) =-    unpack [unpack a,unpack b,unpack c,unpack d,unpack e,unpack f,unpack g]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e,-          Unpack f, Unpack g, Unpack h) =>-         Unpack (a, b, c, d, e, f, g, h) where-  unpack (a, b, c, d, e, f, g, h) =-    unpack [unpack a, unpack b, unpack c, unpack d, unpack e,-            unpack f, unpack g, unpack h]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e,-          Unpack f, Unpack g, Unpack h, Unpack i) =>-         Unpack (a, b, c, d, e, f, g, h, i) where-  unpack (a, b, c, d, e, f, g, h, i) =-    unpack [unpack a, unpack b, unpack c, unpack d, unpack e,-            unpack f, unpack g, unpack h, unpack i]--instance (Unpack a, Unpack b, Unpack c, Unpack d, Unpack e,-          Unpack f, Unpack g, Unpack h, Unpack i, Unpack j) =>-         Unpack (a, b, c, d, e, f, g, h, i, j) where-  unpack (a, b, c, d, e, f, g, h, i, j) =-    unpack [unpack a, unpack b, unpack c, unpack d, unpack e,-            unpack f, unpack g, unpack h, unpack i, unpack j]--{-# RULES "unpack array/Unpacked" forall x. unpack x = lst2arr (toOpaque x) #-}-{-# RULES "pack array/Unpacked" forall x. pack x = fromOpaque (arr2lst x 0) #-}--lst2arr :: Opaque [Unpacked] -> Unpacked-lst2arr = unsafePerformIO . ffi "lst2arr"--arr2lst :: Unpacked -> Int -> Opaque [Unpacked]-arr2lst arr ix = unsafePerformIO $ ffi "arr2lst" arr ix--jsNull, jsTrue, jsFalse :: Unpacked-jsTrue = unsafePerformIO $ ffi "true"-jsFalse = unsafePerformIO $ ffi "false"-jsNull = unsafePerformIO $ ffi "null"--isNull :: Unpacked -> Bool-isNull = unsafePerformIO . ffi "(function(x) {return x === null;})"-+-- | Any type that can be imported from JavaScript. This means any type which+--   has an instance of 'FromAny', and any function where all argument types+--   has 'ToAny' instances and the return type is in the IO monad and has a+--   'FromAny' instance. class FFI a where-  type T a-  unpackify :: T a -> a--instance Pack a => FFI (IO a) where-  type T (IO a) = IO Unpacked-  unpackify = fmap pack--instance (Unpack a, FFI b) => FFI (a -> b) where-  type T (a -> b) = Unpacked -> T b-  unpackify f x = unpackify (f $! unpack x)--class IOFun a where-  type X a-  packify :: a -> X a--instance Unpack a => IOFun (IO a) where-  type X (IO a) = Unpacked-  packify m = unsafePerformIO $ do-    x <- m-    return $! unpack x--instance (Pack a, IOFun b) => IOFun (a -> b) where-  type X (a -> b) = Unpacked -> X b-  packify f = \x -> packify (f $! pack x)--instance Unpack a => Unpack (IO a) where-  unpack = unsafePerformIO . unpackAct . toOpaque . fmap unpack-    where-      {-# NOINLINE unpackAct #-}-      unpackAct :: Opaque (IO Unpacked) -> IO Unpacked-      unpackAct =-        ffi (toJSStr $ "(function(m){" ++-             "    return (function() {" ++-             "        return (function(){return E(B(A(m,[0])));});" ++-             "      });" ++-             "})")+  __ffi :: JSFun -> [JSAny] -> a -instance (IOFun (a -> b)) => Unpack (a -> b) where-  unpack = unpackFun+instance FromAny a => FFI (IO a) where+  {-# INLINE __ffi #-}+  __ffi = ffiio -unpackFun :: IOFun a => a -> Unpacked-unpackFun =-    unsafePerformIO . go . toOpaque . packify-  where-    {-# NOINLINE go #-}-    go :: Opaque a -> IO Unpacked-    go = ffi (toJSStr $ "(function(f) {" ++-              "  return (function() {" ++-              "      return (function(){" ++-              "        var args=Array.prototype.slice.call(arguments,0);"++-              "        args.push(0);" ++-              "        return E(B(A(f, args)));" ++-              "    });" ++-              "  });" ++-              "})")+instance (ToAny a, FFI b) => FFI (a -> b) where+  {-# INLINE __ffi #-}+  __ffi f !as !a = __ffi f (a' : as)+    where !a' = toAny a +{-# INLINE [0] ffiio #-}+-- | Apply the result of an FFI call.+ffiio :: FromAny a => JSFun -> [JSAny] -> IO a+ffiio !f !as = __apply f (toPtr as) >>= fromAny --- | Creates a function based on the given string of Javascript code. If this---   code is not well typed or is otherwise incorrect, your program may crash---   or misbehave in mystifying ways. Haste makes a best-effort try to save you---   from poorly typed JS here, but there are no guarantees.+{-# INLINE ffi #-}+-- | Creates a Haskell function from the given string of JavaScript code. If+--   this code is not well typed or is otherwise incorrect, your program may+--   crash or misbehave in mystifying ways. Haste makes a best-effort try to+--   save you from poorly typed JS here, but there are no guarantees. -- --   For instance, the following WILL cause crazy behavior due to wrong types:---   ffi "(function(x) {return x+1;})" :: Int -> Int -> IO Int+--   @ffi "(function(x) {return x+1;})" :: Int -> Int -> IO Int@ -----   In other words, this function is completely unsafe - use with caution.+--   In other words, this function is as unsafe as the JS it calls on. You+--   have been warned. -----   ALWAYS use type signatures for functions defined using this function, as---   the argument marshalling is decided by the type signature.+--   The imported JS is evaluated lazily, unless (a) it is a function object+--   in which case evaluation order does not affect the semantics of the+--   imported code, or if (b) the imported code is explicitly marked as strict:+--+--       someFunction = ffi "__strict(someJSFunction)"+--+--   Literals which depends on some third party initialization, the existence+--   of a DOM tree or some other condition which is not fulfilled at load time+--   should *not* be marked strict. ffi :: FFI a => JSString -> a-ffi = unpackify . unsafeEval+ffi s = __ffi f []+  where+    {-# NOINLINE f #-}+    f = __eval s --- | Export a symbol. That symbol may then be accessed from Javascript through+-- | Create a Haskell value from a constant JS expression.+constant :: FromAny a => JSString -> a+constant = veryUnsafePerformIO . fromAny . __eval++-- Don't build intermediate list for functions of <= 5 arguments.+{-# RULES+"app0" [1] forall f. ffiio f [] = __app0 f >>= fromAny+"app1" [1] forall f a. ffiio f [a] = __app1 f a >>= fromAny+"app2" [1] forall f a b. ffiio f [b,a] = __app2 f a b >>= fromAny+"app3" [1] forall f a b c. ffiio f [c,b,a] = __app3 f a b c >>= fromAny+"app4" [1] forall f a b c d. ffiio f [d,c,b,a] = __app4 f a b c d >>= fromAny+"app5" [1] forall f a b c d e. ffiio f [e,d,c,b,a] =+                                 __app5 f a b c d e >>= fromAny+  #-}++-- | Export a symbol. That symbol may then be accessed from JavaScript through --   Haste.name() as a normal function. Remember, however, that if you are --   using --with-js to include your JS, in conjunction with---   --opt-google-closure or any option that implies it, you will instead need+--   --opt-minify or any option that implies it, you will instead need --   to access your exports through Haste[\'name\'](), or Closure will mangle --   your function names.-{-# NOINLINE export #-}-export :: Unpack a => JSString -> a -> IO ()+{-# INLINE export #-}+export :: ToAny a => JSString -> a -> IO () export = ffi "(function(s,f){Haste[s] = f;})" -unsafeUnpack :: a -> Unpacked-unsafeUnpack x =-  case unsafeCoerce x of-    Dummy x' -> x'+type family JS a where+  JS (a -> b) = JSAny -> JS b+  JS (IO a)   = IO JSAny+  JS a        = JSAny -unsafePack :: Unpacked -> a-unsafePack = unsafeCoerce . Dummy+class JSFunc a where+  mkJSFunc :: a -> JS a+  arity    :: a -> Int -unsafeEval :: JSString -> a-unsafeEval s = unsafePerformIO $ do-  x <- eval s-  return $ fromPtr x+instance (ToAny a, JS a ~ JSAny) => JSFunc a where+  mkJSFunc = toAny+  arity _  = 0++instance ToAny a => JSFunc (IO a) where+  mkJSFunc = fmap toAny+  arity _  = 1++instance (FromAny a, JSFunc b) => JSFunc (a -> b) where+  mkJSFunc f = mkJSFunc . f . veryUnsafePerformIO . fromAny+  arity f    = 1 + arity (f undefined)++instance (FromAny a, JSFunc b) => ToAny (a -> b) where+  toAny f =+    veryUnsafePerformIO . __createJSFunc (arity f) . toAny . toOpaque $ mkJSFunc f++instance ToAny a => ToAny (IO a) where+  toAny = veryUnsafePerformIO . __createJSFunc 0 . toAny . toOpaque . mkJSFunc++#if __GLASGOW_HASKELL__ < 710+instance FFI a => FromAny a where+#else+instance {-# OVERLAPPABLE #-} FFI a => FromAny a where+#endif+  fromAny f = return $ __ffi f []
+ libraries/haste-lib/src/Haste/Graphics/AnimationFrame.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}+-- | Request and cancel animation frames from the browser.+--   Straightforward bindings to the corresponding DOM interface.+module Haste.Graphics.AnimationFrame (+    FrameRequest, HRTimeStamp,+    requestAnimationFrame,+    cancelAnimationFrame+  ) where+import Haste.Foreign+import Haste.Performance++-- | Handle to a previously issued request for an animation frame.+--   Only useful together with 'cancelAnimationFrame'.+newtype FrameRequest = FrameRequest JSAny deriving (ToAny, FromAny)++-- | Request a function to be called by the browser before the next repaint.+--   Paints generally happen in tune with the user's monitor refresh rate,+--   which usually means at 60 FPS.+--+--   Do note that you need to request *each* animation callback you plan to+--   use, similar to @setTimeout@ as opposed to @setInterval@, as they are not+--   recurring.+requestAnimationFrame :: (HRTimeStamp -> IO ()) -> IO FrameRequest+requestAnimationFrame = ffi "window.requestAnimationFrame"++-- | Cancel an animation callback previously requested by+--   'requestAnimationFrame'.+cancelAnimationFrame :: FrameRequest -> IO ()+cancelAnimationFrame = ffi "window.cancelAnimationFrame"
libraries/haste-lib/src/Haste/Graphics/Canvas.hs view
@@ -10,9 +10,7 @@   -- Classes   ImageBuffer (..), BitmapSource (..),   -- Obtaining a canvas for drawing-  getCanvasById, getCanvas, createCanvas, canvasElem,-  -- Working with bitmaps-  bitmapElem,+  getCanvasById, getCanvas, createCanvas,   -- Rendering pictures, extracting data from a canvas   render, renderOnTop, buffer, toDataURL,   -- Working with colors and opacity@@ -28,12 +26,18 @@   -- Extending the library   withContext   ) where+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Monad.IO.Class-import System.IO.Unsafe+import Data.Maybe (fromJust) import Haste+import qualified Haste.DOM.JSString as J import Haste.Concurrent (CIO) -- for SPECIALISE pragma-import Haste.Foreign (Pack (..), Unpack (..))+import Haste.Foreign (ToAny (..), FromAny (..))+#ifdef __HASTE__+import Haste.Prim (JSString (..), JSAny (..))+#endif  #ifdef __HASTE__ foreign import ccall jsHasCtx2D :: Elem -> IO Bool@@ -108,7 +112,7 @@ -- | A bitmap, backed by an IMG element. --   JS representation is a reference to the backing IMG element. newtype Bitmap = Bitmap Elem-  deriving (Pack, Unpack)+  deriving (ToAny, FromAny)  -- | Any type that contains a buffered image which can be drawn onto a canvas. class ImageBuffer a where@@ -135,8 +139,8 @@  instance BitmapSource URL where   loadBitmap url = liftIO $ do-    img <- newElem "img"-    setProp' img "src" (toJSString url)+    img <- J.newElem "img"+    J.setProp img "src" (toJSString url)     loadBitmap img  instance BitmapSource Elem where@@ -149,13 +153,11 @@   draw (AnyImageBuffer buf) = draw buf   drawClipped (AnyImageBuffer buf) = drawClipped buf --- | Get the DOM node backing a given canvas.-canvasElem :: Canvas -> Elem-canvasElem (Canvas _ctx e) = e+instance IsElem Canvas where+  elemOf (Canvas _ctx e) = e --- | Get the HTML element associated with the given bitmap.-bitmapElem :: Bitmap -> Elem-bitmapElem (Bitmap e) = e+instance IsElem Bitmap where+  elemOf (Bitmap e) = e  -- | A point in the plane. type Point = (Double, Double)@@ -190,7 +192,7 @@ -- | A drawing context; part of a canvas. --   JS representation is the drawing context object itself. newtype Ctx = Ctx JSAny-  deriving (Pack, Unpack)+  deriving (ToAny, FromAny)  -- | A canvas; a viewport into which a picture can be rendered. --   The origin of the coordinate system used by the canvas is the top left@@ -198,14 +200,15 @@ --   JS representation is a reference to the backing canvas element. data Canvas = Canvas !Ctx !Elem -instance Pack Canvas where-  pack c =-    case unsafePerformIO . getCanvas $ pack c of-      Just c' -> c'-      _       -> error "Attempted to pack a non-canvas element into a Canvas!"+instance FromAny Canvas where+  fromAny c = do+    mcan <- fromAny c >>= getCanvas+    case mcan of+      Just can -> return can+      _        -> error "Attempted to turn a non-canvas element into a Canvas!" -instance Unpack Canvas where-  unpack (Canvas _ el) = unpack el+instance ToAny Canvas where+  toAny (Canvas _ el) = toAny el  -- | A picture that can be drawn onto a canvas. newtype Picture a = Picture {unP :: Ctx -> IO a}@@ -214,16 +217,16 @@ newtype Shape a = Shape {unS :: Ctx -> IO a}  instance Functor Picture where-	fmap f p = Picture $ \ctx ->-		unP p ctx >>= return . f+  fmap f p = Picture $ \ctx ->+    unP p ctx >>= return . f  instance Applicative Picture where-	pure a = Picture $ \_ -> return a+  pure a = Picture $ \_ -> return a -	pfab <*> pa = Picture $ \ctx -> do-		fab <- unP pfab ctx-		a   <- unP pa   ctx-		return (fab a)+  pfab <*> pa = Picture $ \ctx -> do+    fab <- unP pfab ctx+    a   <- unP pa   ctx+    return (fab a)  instance Monad Picture where   return x = Picture $ \_ -> return x@@ -232,16 +235,16 @@     unP (f x) ctx  instance Functor Shape where-	fmap f s = Shape $ \ctx ->-		unS s ctx >>= return . f+  fmap f s = Shape $ \ctx ->+    unS s ctx >>= return . f  instance Applicative Shape where-	pure a = Shape $ \_ -> return a+  pure a = Shape $ \_ -> return a -	sfab <*> sa = Shape $ \ctx -> do-		fab <- unS sfab ctx-		a   <- unS sa   ctx-		return (fab a)+  sfab <*> sa = Shape $ \ctx -> do+    fab <- unS sfab ctx+    a   <- unS sa   ctx+    return (fab a)  instance Monad Shape where   return x = Shape $ \_ -> return x@@ -250,9 +253,9 @@     unS (f x) ctx  -- | Create a 2D drawing context from a DOM element identified by its ID.-getCanvasById :: MonadIO m => ElemID -> m (Maybe Canvas)+getCanvasById :: MonadIO m => String -> m (Maybe Canvas) getCanvasById eid = liftIO $ do-  e <- elemById eid+  e <- J.elemById (toJSString eid)   maybe (return Nothing) getCanvas e  -- | Create a 2D drawing context from a DOM element.@@ -266,12 +269,12 @@     _    -> return Nothing  -- | Create an off-screen buffer of the specified size.-createCanvas :: Int -> Int -> IO (Maybe Canvas)+createCanvas :: Int -> Int -> IO Canvas createCanvas w h = do-  buf <- newElem "canvas"-  setProp' buf "width" (toJSString w)-  setProp' buf "height" (toJSString h)-  getCanvas buf+  buf <- J.newElem "canvas"+  J.setProp buf "width" (toJSString w)+  J.setProp buf "height" (toJSString h)+  fromJust <$> getCanvas buf  -- | Clear a canvas, then draw a picture onto it. {-# SPECIALISE render :: Canvas -> Picture a -> IO a #-}@@ -295,13 +298,9 @@ -- | Create a new off-screen buffer and store the given picture in it. buffer :: MonadIO m => Int -> Int -> Picture () -> m Bitmap buffer w h pict = liftIO $ do-  mbuf <- createCanvas w h-  case mbuf of-    Just buf@(Canvas _ el) -> do-      render buf pict-      return $ Bitmap el-    _ -> do-      Bitmap <$> newElem "img"+  buf@(Canvas _ el) <- createCanvas w h+  render buf pict+  return $ Bitmap el  -- | Perform a computation over the drawing context of the picture. --   This is handy for operations which are either impossible, hard or@@ -312,42 +311,42 @@ -- | Set a new color for strokes. setStrokeColor :: Color -> Picture () setStrokeColor c = Picture $ \(Ctx ctx) -> do-  setProp' (Elem ctx) "strokeStyle" (color2JSString c)+  J.setProp (Elem ctx) "strokeStyle" (color2JSString c)  -- | Set a new fill color. setFillColor :: Color -> Picture () setFillColor c = Picture $ \(Ctx ctx) -> do-  setProp' (Elem ctx) "fillStyle" (color2JSString c)+  J.setProp (Elem ctx) "fillStyle" (color2JSString c)  -- | Draw a picture with the given opacity. opacity :: Double -> Picture () -> Picture () opacity alpha (Picture pict) = Picture $ \(Ctx ctx) -> do-  alpha' <- getProp' (Elem ctx) "globalAlpha"-  setProp' (Elem ctx) "globalAlpha" (toJSString alpha)+  alpha' <- J.getProp (Elem ctx) "globalAlpha"+  J.setProp (Elem ctx) "globalAlpha" (toJSString alpha)   pict (Ctx ctx)-  setProp' (Elem ctx) "globalAlpha" alpha'+  J.setProp (Elem ctx) "globalAlpha" alpha'  -- | Draw the given Picture using the specified Color for both stroke and fill, --   then restore the previous stroke and fill colors. color :: Color -> Picture () -> Picture () color c (Picture pict) = Picture $ \(Ctx ctx) -> do-    fc <- getProp' (Elem ctx) "fillStyle"-    sc <- getProp' (Elem ctx) "strokeStyle"-    setProp' (Elem ctx) "fillStyle" c'-    setProp' (Elem ctx) "strokeStyle" c'+    fc <- J.getProp (Elem ctx) "fillStyle"+    sc <- J.getProp (Elem ctx) "strokeStyle"+    J.setProp (Elem ctx) "fillStyle" c'+    J.setProp (Elem ctx) "strokeStyle" c'     pict (Ctx ctx)-    setProp' (Elem ctx) "fillStyle" fc-    setProp' (Elem ctx) "strokeStyle" sc+    J.setProp (Elem ctx) "fillStyle" fc+    J.setProp (Elem ctx) "strokeStyle" sc   where     c' = color2JSString c  -- | Draw the given picture using a new line width. lineWidth :: Double -> Picture () -> Picture () lineWidth w (Picture pict) = Picture $ \(Ctx ctx) -> do-  lw <- getProp' (Elem ctx) "lineWidth"-  setProp' (Elem ctx) "lineWidth" (toJSString w)+  lw <- J.getProp (Elem ctx) "lineWidth"+  J.setProp (Elem ctx) "lineWidth" (toJSString w)   pict (Ctx ctx)-  setProp' (Elem ctx) "lineWidth" lw+  J.setProp (Elem ctx) "lineWidth" lw  -- | Draw the specified picture using the given point as the origin. translate :: Vector -> Picture () -> Picture ()@@ -434,10 +433,10 @@ -- | Draw a picture using a certain font. Obviously only affects text. font :: String -> Picture () -> Picture () font f (Picture pict) = Picture $ \(Ctx ctx) -> do-  f' <- getProp' (Elem ctx) "font"-  setProp' (Elem ctx) "font" (toJSString f)+  f' <- J.getProp (Elem ctx) "font"+  J.setProp (Elem ctx) "font" (toJSString f)   pict (Ctx ctx)-  setProp' (Elem ctx) "font" f'+  J.setProp (Elem ctx) "font" f'  -- | Draw some text onto the canvas. text :: Point -> String -> Picture ()
libraries/haste-lib/src/Haste/Hash.hs view
@@ -5,38 +5,28 @@   ) where import Haste.Foreign import Control.Monad.IO.Class-import Haste.Callback import Haste.Prim-import Unsafe.Coerce -newtype HashCallback = HashCallback (JSString -> JSString -> IO ())--instance Pack HashCallback where-  pack = unsafeCoerce-instance Unpack HashCallback where-  unpack = unsafeCoerce- -- | Register a callback to be run whenever the URL hash changes. --   The two arguments of the callback are the new and old hash respectively.-onHashChange :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ())-              => (String -> String -> m ())-              -> m ()+onHashChange :: MonadIO m+             => (String -> String -> IO ())+             -> m () onHashChange f = do     firsthash <- getHash'-    f' <- toCallback $ \old new -> f (fromJSStr old) (fromJSStr new)-    liftIO $ jsOnHashChange firsthash (HashCallback f')+    liftIO $ jsOnHashChange firsthash cb+  where+    cb = \old new -> f (fromJSStr old) (fromJSStr new)  -- | JSString version of @onHashChange@.-onHashChange' :: (MonadIO m, GenericCallback (m ()) m, CB (m ()) ~ IO ())-              => (JSString -> JSString -> m ())+onHashChange' :: MonadIO m+              => (JSString -> JSString -> IO ())               -> m () onHashChange' f = do     firsthash <- getHash'-    f' <- toCallback f-    liftIO $ jsOnHashChange firsthash (HashCallback f')+    liftIO $ jsOnHashChange firsthash f -{-# NOINLINE jsOnHashChange #-}-jsOnHashChange :: JSString -> HashCallback -> IO ()+jsOnHashChange :: JSString -> (JSString -> JSString -> IO ()) -> IO () jsOnHashChange =   ffi "(function(firsthash,cb){\           \window.__old_hash = firsthash;\@@ -44,7 +34,7 @@             \var oldhash = window.__old_hash;\             \var newhash = window.location.hash.split('#')[1] || '';\             \window.__old_hash = newhash;\-            \B(A(cb, [[0,oldhash],[0,newhash],0]));\+            \cb(oldhash,newhash);\           \};\        \})" @@ -56,7 +46,6 @@ setHash' :: MonadIO m => JSString -> m () setHash' = liftIO . jsSetHash -{-# NOINLINE jsSetHash #-} jsSetHash :: JSString -> IO () jsSetHash = ffi "(function(h) {location.hash = '#'+h;})" @@ -68,6 +57,5 @@ getHash' :: MonadIO m => m JSString getHash' = liftIO jsGetHash -{-# NOINLINE jsGetHash #-} jsGetHash :: IO JSString jsGetHash = ffi "(function() {return location.hash.substring(1);})"
libraries/haste-lib/src/Haste/JSON.hs view
@@ -9,14 +9,18 @@ --   browser that supports JSON.parse; IE does this from version 8 and up, and --   everyone else has done it since just about forever. module Haste.JSON (JSON (..), encodeJSON, decodeJSON, toObject, (!), (~>)) where+import Prelude hiding (null) import Haste import Haste.Prim import Data.String as S #ifndef __HASTE__+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative-import Data.Char (ord)+#endif import Haste.Parsing-import Numeric (showHex)+#else+import System.IO.Unsafe+import Haste.Foreign hiding (toObject) #endif  -- | Create a Javascript object from a JSON object. Only makes sense in a@@ -24,7 +28,12 @@ toObject :: JSON -> JSAny #ifdef __HASTE__ toObject = jsJSONParse . encodeJSON-foreign import ccall jsJSONParse :: JSString -> JSAny+jsJSONParse :: JSString -> JSAny+jsJSONParse = unsafePerformIO . go+  where+    {-# NOINLINE go #-}+    go :: JSString -> IO JSAny+    go = ffi "(function(s){return JSON.parse(s);})" #else toObject j = error $ "Call to toObject in non-browser: " ++ show j #endif@@ -33,8 +42,8 @@ -- Remember to update jsParseJSON if this data type changes! data JSON   = Num  {-# UNPACK #-} !Double-  | Str  {-# UNPACK #-} !JSString-  | Bool {-# UNPACK #-} !Bool+  | Str  !JSString+  | Bool !Bool   | Arr  ![JSON]   | Dict ![(JSString, JSON)]   | Null@@ -42,6 +51,13 @@ instance IsString JSON where   fromString = Str . S.fromString +instance JSType JSON where+  toJSString = encodeJSON+  fromJSString x =+    case decodeJSON x of+      Right x' -> Just x'+      _        -> Nothing+ numFail :: a numFail = error "Num JSON: not a numeric JSON node!" @@ -78,8 +94,6 @@       | c == '\\'          = "\\\\" ++ unq cs       | otherwise          = c : unq cs     unq _          = ['"']--    unicodeChar c str = c : str #endif  -- | Look up a JSON object from a JSON dictionary. Panics if the dictionary@@ -158,26 +172,26 @@     boolean = oneOf [string "true" >> pure True, string "false" >> pure False]     null = string "null" >> pure Null     array = do-      char '[' >> possibly whitespace+      _ <- char '[' >> possibly whitespace       elements <- commaSeparated json-      possibly whitespace >> char ']'+      _ <- possibly whitespace >> char ']'       return elements     commaSeparated p =       oneOf [do x <- p-                possibly whitespace >> char ',' >> possibly whitespace+                _ <- possibly whitespace >> char ',' >> possibly whitespace                 xs <- commaSeparated p                 return (x:xs),              do x <- p                 return [x],              do return []]     object = do-      char '{' >> possibly whitespace+      _ <- char '{' >> possibly whitespace       pairs <- commaSeparated kvPair-      possibly whitespace >> char '}'+      _ <- possibly whitespace >> char '}'       return pairs     kvPair = do       k <- jsstring-      possibly whitespace >> char ':' >> possibly whitespace+      _ <- possibly whitespace >> char ':' >> possibly whitespace       v <- json       return (k, v) #endif
+ libraries/haste-lib/src/Haste/JSString.hs view
@@ -0,0 +1,318 @@+{-# OPTIONS_GHC -fno-warn-unused-binds #-}+{-# LANGUAGE OverloadedStrings, ForeignFunctionInterface, CPP, MagicHash #-}+-- | JSString standard functions, to make them a more viable alternative to+--   the horribly inefficient standard Strings.+--+--   Many functions have linear time complexity due to JavaScript engines not+--   implementing slicing, etc. in constant time.+--+--   All functions are supported on both client and server, with the exception+--   of 'match', 'matches', 'regex' and 'replace', which are wrappers on top of+--   JavaScript's native regular expressions and thus only supported on the+--   client.+module Haste.JSString (+    -- | Building JSStrings+    empty, singleton, pack, cons, snoc, append, replicate,+    -- | Deconstructing JSStrings+    unpack, head, last, tail, drop, take, init, splitAt,+    -- | Examining JSStrings+    null, length, any, all,+    -- | Modifying JSStrings+    map, reverse, intercalate, foldl', foldr, concat, concatMap,+    -- | Regular expressions (client-side only)+    RegEx, match, matches, regex, replace+  ) where+import qualified Data.List+import Prelude hiding (foldr, concat, concatMap, reverse, map, all, any,+                       length, null, splitAt, init, take, drop, tail, head,+                       last, replicate)+import Data.String+import Haste.Prim+import Haste.Foreign++#ifdef __HASTE__+import GHC.Prim+import System.IO.Unsafe++{-# INLINE d2c #-}+d2c :: Double -> Char+d2c d = unsafeCoerce# d++foreign import ccall _jss_singleton :: Char -> JSString+foreign import ccall _jss_cons :: Char -> JSString -> JSString+foreign import ccall _jss_snoc :: JSString -> Char -> JSString+foreign import ccall _jss_append :: JSString -> JSString -> JSString+foreign import ccall _jss_len :: JSString -> Int+foreign import ccall _jss_index :: JSString -> Int -> Double+foreign import ccall _jss_substr :: JSString -> Int -> JSString+foreign import ccall _jss_take :: Int -> JSString -> JSString+foreign import ccall _jss_rev :: JSString -> JSString+foreign import ccall _jss_re_match :: JSString -> RegEx -> Bool+foreign import ccall _jss_re_compile :: JSString -> JSString -> RegEx+foreign import ccall _jss_re_replace :: JSString -> RegEx -> JSString -> JSString+foreign import ccall _jss_re_find :: RegEx -> JSString -> Ptr [JSString]++{-# INLINE _jss_map #-}+_jss_map :: (Char -> Char) -> JSString -> JSString+_jss_map f = _jss_cmap (_jss_singleton . f)++{-# INLINE _jss_cmap #-}+_jss_cmap :: (Char -> JSString) -> JSString -> JSString+_jss_cmap f s = unsafePerformIO $ cmap_js (return . f) s++cmap_js :: (Char -> IO JSString) -> JSString -> IO JSString+cmap_js = ffi "(function(f,s){\+var s2 = '';\+for(var i in s) {\+   s2 += f(s.charCodeAt(i));\+}\+return s2;})"++{-# INLINE _jss_foldl #-}+_jss_foldl :: (ToAny a, FromAny a) => (a -> Char -> a) -> a -> JSString -> a+_jss_foldl f x s = fromOpaque . unsafePerformIO $ do+  foldl_js (\a c ->  toOpaque $ f (fromOpaque a) c) (toOpaque x) s++foldl_js :: (Opaque a -> Char -> Opaque a)+         -> Opaque a+         -> JSString+         -> IO (Opaque a)+foldl_js = ffi "(function(f,x,s){\+for(var i in s) {\+  x = f(x,s.charCodeAt(i));\+}\+return x;})"++{-# INLINE _jss_foldr #-}+_jss_foldr :: (ToAny a, FromAny a) => (Char -> a -> a) -> a -> JSString -> a+_jss_foldr f x s = fromOpaque . unsafePerformIO $ do+  foldr_js (\c -> toOpaque . f c . fromOpaque) (toOpaque x) s++foldr_js :: (Char -> Opaque a -> Opaque a)+         -> Opaque a+         -> JSString+         -> IO (Opaque a)+foldr_js = ffi "(function(f,x,s){\+for(var i = s.length-1; i >= 0; --i) {\+  x = f(s.charCodeAt(i),x);\+}\+return x;})"++#else++{-# INLINE d2c #-}+d2c :: Char -> Char+d2c = id++_jss_singleton :: Char -> JSString+_jss_singleton c = toJSStr [c]++_jss_cons :: Char -> JSString -> JSString+_jss_cons c s = toJSStr (c : fromJSStr s)++_jss_snoc :: JSString -> Char -> JSString+_jss_snoc s c = toJSStr (fromJSStr s ++ [c])++_jss_append :: JSString -> JSString -> JSString+_jss_append a b = catJSStr "" [a, b]++_jss_len :: JSString -> Int+_jss_len s = Data.List.length $ fromJSStr s++_jss_index :: JSString -> Int -> Char+_jss_index s n = fromJSStr s !! n++_jss_substr :: JSString -> Int -> JSString+_jss_substr s n = toJSStr $ Data.List.drop n $ fromJSStr s++_jss_take :: Int -> JSString -> JSString+_jss_take n = toJSStr . Data.List.take n . fromJSStr++_jss_map :: (Char -> Char) -> JSString -> JSString+_jss_map f = toJSStr . Data.List.map f . fromJSStr++_jss_cmap :: (Char -> JSString) -> JSString -> JSString+_jss_cmap f =+  toJSStr . Data.List.concat . Data.List.map (fromJSStr . f) . fromJSStr++_jss_rev :: JSString -> JSString+_jss_rev = toJSStr . Data.List.reverse . fromJSStr++_jss_foldl :: (a -> Char -> a) -> a -> JSString -> a+_jss_foldl f x = Data.List.foldl' f x . fromJSStr++_jss_foldr :: (Char -> a -> a) -> a -> JSString -> a+_jss_foldr f x = Data.List.foldr f x . fromJSStr++_jss_re_compile :: JSString -> JSString -> RegEx+_jss_re_compile _ _ =+  error "Regular expressions are only supported client-side!"++_jss_re_match :: JSString -> RegEx -> Bool+_jss_re_match _ _ =+  error "Regular expressions are only supported client-side!"++_jss_re_replace :: JSString -> RegEx -> JSString -> JSString+_jss_re_replace _ _ _ =+  error "Regular expressions are only supported client-side!"++_jss_re_find :: RegEx -> JSString -> Ptr [JSString]+_jss_re_find _ _ =+  error "Regular expressions are only supported client-side!"++#endif++-- | A regular expression. May be used to match and replace JSStrings.+newtype RegEx = RegEx JSAny++instance IsString RegEx where+  fromString s = _jss_re_compile (fromString s) ""++-- | O(1) The empty JSString.+empty :: JSString+empty = ""++-- | O(1) JSString consisting of a single character.+singleton :: Char -> JSString+singleton = _jss_singleton++-- | O(n) Convert a list of Char into a JSString.+pack :: [Char] -> JSString+pack = toJSStr++-- | O(n) Convert a JSString to a list of Char.+unpack :: JSString -> [Char]+unpack = fromJSStr++infixr 5 `cons`+-- | O(n) Prepend a character to a JSString.+cons :: Char -> JSString -> JSString+cons = _jss_cons++infixl 5 `snoc`+-- | O(n) Append a character to a JSString.+snoc :: JSString -> Char -> JSString+snoc = _jss_snoc++-- | O(n) Append two JSStrings.+append :: JSString -> JSString -> JSString+append = _jss_append++-- | O(1) Extract the first element of a non-empty JSString.+head :: JSString -> Char+head s =+#ifdef __HASTE__+  case _jss_index s 0 of+    c | isNaN c   -> error "Haste.JSString.head: empty JSString"+      | otherwise -> d2c c -- Double/Int/Char share representation.+#else+  Data.List.head $ fromJSStr s+#endif++-- | O(1) Extract the last element of a non-empty JSString.+last :: JSString -> Char+last s =+  case _jss_len s of+    0 -> error "Haste.JSString.head: empty JSString"+    n -> d2c (_jss_index s (n-1))++-- | O(n) All elements but the first of a JSString. Returns an empty JSString+--   if the given JSString is empty.+tail :: JSString -> JSString+tail s = _jss_substr s 1++-- | O(n) Drop 'n' elements from the given JSString.+drop :: Int -> JSString -> JSString+drop n s = _jss_substr s (max 0 n)++-- | O(n) Take 'n' elements from the given JSString.+take :: Int -> JSString -> JSString+take n s = _jss_take n s++-- | O(n) All elements but the last of a JSString. Returns an empty JSString+--   if the given JSString is empty.+init :: JSString -> JSString+init s = _jss_take (_jss_len s-1) s++-- | O(1) Test whether a JSString is empty.+null :: JSString -> Bool+null s = _jss_len s == 0++-- | O(1) Get the length of a JSString as an Int.+length :: JSString -> Int+length s = _jss_len s++-- | O(n) Map a function over the given JSString.+map :: (Char -> Char) -> JSString -> JSString+map = _jss_map++-- | O(n) reverse a JSString.+reverse :: JSString -> JSString+reverse = _jss_rev++-- | O(n) Join a list of JSStrings, with a specified separator. Equivalent to+--   'String.join'.+intercalate :: JSString -> [JSString] -> JSString+intercalate = catJSStr++-- | O(n) Left fold over a JSString.+foldl' :: (ToAny a, FromAny a) => (a -> Char -> a) -> a -> JSString -> a+foldl' = _jss_foldl++-- | O(n) Right fold over a JSString.+foldr :: (ToAny a, FromAny a) => (Char -> a -> a) -> a -> JSString -> a+foldr = _jss_foldr++-- | O(n) Concatenate a list of JSStrings.+concat :: [JSString] -> JSString+concat = catJSStr ""++-- | O(n) Map a function over a JSString, then concatenate the results.+--   Note that this function is actually faster than 'map' in most cases.+concatMap :: (Char -> JSString) -> JSString -> JSString+concatMap = _jss_cmap++-- | O(n) Determines whether any character in the string satisfies the given+--   predicate.+any :: (Char -> Bool) -> JSString -> Bool+any p = Haste.JSString.foldl' (\a x -> a || p x) False++-- | O(n) Determines whether all characters in the string satisfy the given+--   predicate.+all :: (Char -> Bool) -> JSString -> Bool+all p = Haste.JSString.foldl' (\a x -> a && p x) False++-- | O(n) Create a JSString containing 'n' instances of a single character.+replicate :: Int -> Char -> JSString+replicate n c = Haste.JSString.pack $ Data.List.replicate n c++-- | O(n) Equivalent to (take n xs, drop n xs).+splitAt :: Int -> JSString -> (JSString, JSString)+splitAt n s = (Haste.JSString.take n s, Haste.JSString.drop n s)++-- | O(n) Determines whether the given JSString matches the given regular+--   expression or not.+matches :: JSString -> RegEx -> Bool+matches = _jss_re_match++-- | O(n) Find all strings corresponding to the given regular expression.+match :: RegEx -> JSString -> [JSString]+match re s = fromPtr $ _jss_re_find re s++-- | O(n) Compile a regular expression and an (optionally empty) list of flags+--   into a 'RegEx' which can be used to match, replace, etc. on JSStrings.+--+--   The regular expression and flags are passed verbatim to the browser's+--   RegEx constructor, meaning that the syntax is the same as when using+--   regular expressions in raw JavaScript.+regex :: JSString -- ^ Regular expression.+      -> JSString -- ^ Potential flags.+      -> RegEx+regex re flags = _jss_re_compile re flags++-- | O(n) String substitution using regular expressions.+replace :: JSString -- ^ String perform substitution on.+        -> RegEx    -- ^ Regular expression to match.+        -> JSString -- ^ Replacement string.+        -> JSString+replace = _jss_re_replace
libraries/haste-lib/src/Haste/JSType.hs view
@@ -1,21 +1,24 @@ {-# LANGUAGE MultiParamTypeClasses, ForeignFunctionInterface, MagicHash,               TypeSynonymInstances, FlexibleInstances, EmptyDataDecls,-             UnliftedFFITypes, UndecidableInstances, CPP #-}+             UnliftedFFITypes, UndecidableInstances, CPP, OverloadedStrings #-} -- | Efficient conversions to and from JS native types. module Haste.JSType (     JSType (..), JSNum (..), toString, fromString, convert   ) where import GHC.Int import GHC.Word-import Haste.Prim (JSString, toJSStr, fromJSStr)+import Haste.Prim (JSString (..), toJSStr, fromJSStr) #ifdef __HASTE__+import Haste.Prim (JSAny (..)) import GHC.Prim import GHC.Integer.GMP.Internals import GHC.Types (Int (..)) #else+import Data.Char import GHC.Float #endif +-- | Any type which can be converted to/from a 'JSString'. class JSType a where   toJSString   :: a -> JSString   fromJSString :: JSString -> Maybe a@@ -58,6 +61,10 @@  -- JSNum instances +instance JSNum Char where+  fromNumber = unsafeCoerce# jsTrunc+  toNumber = unsafeCoerce#+ instance JSNum Int where   fromNumber = unsafeCoerce# jsTrunc   toNumber = unsafeCoerce#@@ -106,7 +113,12 @@   toNumber = id  -- JSType instances--- TODO: fromJSString is unsafe for Words; they may end up negative! fix asap!+instance JSType Bool where+  toJSString True = "true"+  toJSString _    = "false"+  fromJSString "true"  = Just True+  fromJSString "false" = Just False+  fromJSString _       = Nothing instance JSType Int where   toJSString = unsafeToJSString   fromJSString = unsafeIntFromJSString@@ -170,6 +182,10 @@                  | otherwise = Nothing  #else++instance JSNum Char where+  toNumber = fromIntegral . ord+  fromNumber = chr . round  instance JSNum Int where   toNumber = fromIntegral
libraries/haste-lib/src/Haste/LocalStorage.hs view
@@ -1,28 +1,30 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, CPP #-} -- | Basic bindings to HTML5 WebStorage. module Haste.LocalStorage (setItem, getItem, removeItem) where import Haste import Haste.Foreign import Haste.Serialize import Haste.JSON+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif  -- | Locally store a serializable value. setItem :: Serialize a => String -> a -> IO () setItem k = store k . encodeJSON . toJSON-  where-    store :: String -> JSString -> IO ()-    store = ffi "(function(k,v) {localStorage.setItem(k,v);})" +store :: String -> JSString -> IO ()+store = ffi "(function(k,v) {localStorage.setItem(k,v);})"+ -- | Load a serializable value from local storage. Will fail if the given key --   does not exist or if the value stored at the key does not match the --   requested type. getItem :: Serialize a => String -> IO (Either String a) getItem k = do-    maybe (Left "No such value") (\s -> decodeJSON s >>= fromJSON) <$> load k-  where-    load :: String -> IO (Maybe JSString)-    load = ffi "(function(k) {return localStorage.getItem(k);})"+  maybe (Left "No such value") (\s -> decodeJSON s >>= fromJSON) <$> load k++load :: String -> IO (Maybe JSString)+load = ffi "(function(k) {return localStorage.getItem(k);})"  -- | Remove a value from local storage. removeItem :: String -> IO ()
+ libraries/haste-lib/src/Haste/Object.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, TypeSynonymInstances,+             FlexibleInstances, MagicHash, GeneralizedNewtypeDeriving, CPP #-}+-- | Dealing with JavaScript objects on a low, low level.+module Haste.Object (+    JSObj, Type (..),+    (#), asString, asBool, asNumber, typeOf, lookupPath, toObject+  ) where+import Haste.Prim+import Haste.Foreign++-- | A JS object: either null/undefined or 'Just' an actual value.+type JSObj = Maybe JSAny++-- | Possible types of JS objects.+data Type = TUndefined | TNumber | TBoolean | TString | TFunction | TObject+  deriving (Show, Eq, Enum)++instance FromAny Type where+  fromAny = fmap toEnum . fromAny++-- | Any type on which we can look up a JS property.+class JSLookup a where+  infixl 4 #+  -- | Look up a property on an object-like value.+  (#) :: a -> JSString -> IO JSObj++instance JSLookup JSObj where+  Just o # prop = look o prop+  _      # _    = return Nothing++instance JSLookup a => JSLookup (IO a) where+  o # prop = o >>= (# prop)++-- | Lookup a whole path at once. More efficient for long paths.+--   @x `lookupPath` ["a", "b"]@ is equivalent to @x.a.b@.+lookupPath :: JSObj -> [JSString] -> IO JSObj+lookupPath = ffi "(function(o,as){\+                   for(var i in as){\+                     o = o[as[i]];\+                     if(typeof o==='undefined'){return null;}\+                   }\+                   return o;})"++look :: JSAny -> JSString -> IO (Maybe JSAny)+look = ffi "(function(o,s){return o[s] === undefined ? null : o[s];})"++-- | Convert the object to a 'JSString'.+asString :: JSObj -> IO (Maybe JSString)+asString = maybe (return Nothing) go+  where+    go = ffi "(function(o){return String(o);})"++-- | Convert the object to a 'Bool'.+asBool :: JSObj -> IO (Maybe Bool)+asBool = maybe (return Nothing) go+  where+    go = ffi "(function(o){return Boolean(o);})"++-- | Convert the object to a 'Double'.+asNumber :: JSObj -> IO (Maybe Double)+asNumber = maybe (return Nothing) go+  where+    go = ffi "(function(o){return Number(o);})"++-- | Get the type of a JS object.+typeOf :: JSObj -> IO Type+typeOf = maybe (return TUndefined) go+  where+    go = ffi "(function(o){\+               switch(typeof o){\+                 case 'undefined': return 0;\+                 case 'number':    return 1;\+                 case 'boolean':   return 2;\+                 case 'string':    return 3;\+                 case 'function':  return 4;\+                 default:          return 5;\+               }\+             })"
libraries/haste-lib/src/Haste/Parsing.hs view
@@ -23,6 +23,10 @@     (s', x) <- m s     unP (f x) s' +instance Alternative Parse where+  empty = mzero+  (<|>) = mplus+ instance MonadPlus Parse where   mplus (Parse p1) (Parse p2) = Parse $ \s ->     case p1 s of
+ libraries/haste-lib/src/Haste/Performance.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings #-}+-- | (Very incomplete) Haste bindings to the @Performance@ DOM interface.+module Haste.Performance (HRTimeStamp, now, navigationStart) where+import Haste.Foreign++type HRTimeStamp = Double++-- | Returns the number of milliseconds since 'navigationStart', with+--   (theoretically) microsecond precision.+now :: IO HRTimeStamp+now = ffi "(function(){return performance.now();})"++-- | Returns the number of milliseconds elapsed since UNIX epoch at the moment+--   when this document started loading.+navigationStart :: IO Double+navigationStart = ffi "(function(){return performance.timing.navigationStart;})"
libraries/haste-lib/src/Haste/Prim.hs view
@@ -1,40 +1,54 @@ {-# LANGUAGE EmptyDataDecls, ForeignFunctionInterface, MagicHash, -    TypeSynonymInstances, FlexibleInstances, OverlappingInstances, CPP #-}-module Haste.Prim (JSString, URL, toJSStr, fromJSStr, catJSStr, JSAny,-                   Ptr, toPtr, fromPtr) where+    TypeSynonymInstances, FlexibleInstances, CPP, UnboxedTuples #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Haste.Prim (JSString (..), URL, toJSStr, fromJSStr, catJSStr, JSAny (..),+                   Ptr, toPtr, fromPtr, veryUnsafePerformIO) where import Foreign.Ptr import Data.String #ifdef __HASTE__ import Unsafe.Coerce import GHC.CString-import GHC.Prim import qualified GHC.HastePrim as HP #else import Data.List (intercalate) #endif+import GHC.Prim+import GHC.Types (IO (..))  type URL = String-type JSAny = Ptr Haste.Prim.Any-data Any +-- | Any JS value, with one layer of indirection.+newtype JSAny = JSAny (Ptr Any)++instance Eq JSAny where+  (==) = __eq++{-# INLINE veryUnsafePerformIO #-}+-- | Strict, inlineable, dupable version of 'unsafePerformIO'. Only use if you+--   are extremely sure this is not a problem. So please don't.+veryUnsafePerformIO :: IO a -> a+veryUnsafePerformIO (IO act) =+  case act realWorld# of+    (# _, x #) -> x+ -- | Concatenate a series of JSStrings using the specified separator. catJSStr :: JSString -> [JSString] -> JSString #ifdef __HASTE__-foreign import ccall jsCat    :: Ptr [JSString] -> JSString -> JSString+foreign import ccall jsCat :: Ptr [JSString] -> JSString -> JSString+foreign import ccall __eq  :: JSAny -> JSAny -> Bool catJSStr sep strs = jsCat (toPtr strs) sep #else catJSStr sep strs = toJSStr $ intercalate (fromJSStr sep) (map fromJSStr strs)+__eq :: JSAny -> JSAny -> Bool+__eq _ _ = undefined #endif  #ifdef __HASTE__ foreign import ccall strEq :: JSString -> JSString -> Bool foreign import ccall strOrd :: JSString -> JSString -> Ptr Ordering --- | "Pointers" need to be wrapped in a data constructor.-data FakePtr a = FakePtr a--type JSString = Ptr JSChr-data JSChr+-- | Native JavaScript strings.+newtype JSString = JSString JSAny  instance Eq JSString where   (==) = strEq@@ -49,25 +63,29 @@ --   we compile to JS, however, anything can be "pointed" to and nothing needs --   to be stored. toPtr :: a -> Ptr a-toPtr = unsafeCoerce . FakePtr+toPtr = unsafeCoerce  -- | Unwrap a "pointer" to something. fromPtr :: Ptr a -> a-fromPtr ptr =-  case unsafeCoerce ptr of-    FakePtr val -> val+fromPtr = unsafeCoerce  {-# RULES "toJSS/fromJSS" forall s. toJSStr (fromJSStr s) = s #-} {-# RULES "fromJSS/toJSS" forall s. fromJSStr (toJSStr s) = s #-}-{-# RULES "toJSS/unCSTR" forall s. toJSStr (unpackCString# s) = toPtr (unsafeCoerce# s) #-}-{-# RULES "toJSS/unCSTRU8" forall s. toJSStr (unpackCStringUtf8# s) = toPtr (unsafeCoerce# s) #-}+{-# RULES "toJSS/unCSTR" forall s. toJSStr (unpackCString# s) =+    JSString (JSAny (toPtr (unsafeCoerce# s))) #-}+{-# RULES "toJSS/unCSTRU8" forall s. toJSStr (unpackCStringUtf8# s) =+    JSString (JSAny (toPtr (unsafeCoerce# s))) #-} +-- | Convert a 'String' to a 'JSString'.+{-# NOINLINE [1] toJSStr #-} toJSStr :: String -> JSString toJSStr = unsafeCoerce# HP.toJSStr  instance IsString JSString where   fromString = toJSStr +-- | Convert a 'JSString' to a 'String'.+{-# NOINLINE [1] fromJSStr #-} fromJSStr :: JSString -> String fromJSStr = unsafeCoerce# HP.fromJSStr 
libraries/haste-lib/src/Haste/Random.hs view
@@ -14,11 +14,11 @@  #ifdef __HASTE__ -newtype Seed = Seed Unpacked deriving (Pack, Unpack)+newtype Seed = Seed JSAny deriving (ToAny, FromAny)  {-# NOINLINE nxt #-} nxt :: Seed -> IO Seed-nxt = ffi "(function(s){return md51(s.join(','));})"+nxt = ffi "(function(s){return window['md51'](s.join(','));})"  {-# NOINLINE getN #-} getN :: Seed -> IO Int@@ -26,11 +26,11 @@  {-# NOINLINE toSeed #-} toSeed :: Int -> IO Seed-toSeed = ffi "(function(n){return md51(n.toString());})"+toSeed = ffi "(function(n){return window['md51'](n.toString());})"  {-# NOINLINE createSeed #-} createSeed :: IO Seed-createSeed = ffi "(function(){return md51(jsRand().toString());})"+createSeed = ffi "(function(){return window['md51'](jsRand().toString());})" #else newtype Seed = Seed (Int, SR.StdGen) 
libraries/haste-lib/src/Haste/Serialize.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings, CPP #-} -- | JSON serialization and de-serialization for Haste. module Haste.Serialize (     Serialize (..), Parser, fromJSON, (.:), (.:?)@@ -7,7 +7,9 @@ import GHC.Int import Haste.JSON import Haste.Prim (JSString, toJSStr, fromJSStr)+#if __GLASGOW_HASKELL__ < 710 import Control.Applicative+#endif import Control.Monad (ap)  class Serialize a where
+ libraries/haste-lib/src/Haste/Timer.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE OverloadedStrings, CPP #-}+module Haste.Timer (Timer, Interval (..), setTimer, stopTimer) where+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif+import Control.Monad.IO.Class+import Haste.Foreign+import Haste.Events.Core++type Identifier = Int++-- | Timer handle.+data Timer = Timer !Identifier !Interval++-- | Interval and repeat for timers.+data Interval+  = Once !Int   -- ^ Fire once, in n milliseconds.+  | Repeat !Int -- ^ Fire every n milliseconds.++-- | Set a timer.+setTimer :: MonadEvent m+         => Interval -- ^ Milliseconds until timer fires.+         -> m ()     -- ^ Function to call when timer fires.+         -> m Timer  -- ^ Timer handle for interacting with the timer.+setTimer i f = do+    f' <- mkHandler $ const f+    liftIO $ do+      flip Timer i <$> case i of+        Once n   -> timeout n (f' ())+        Repeat n -> interval n (f' ())++timeout :: Int -> IO () -> IO Int+timeout = ffi "(function(t,f){window.setTimeout(f,t);})"++interval :: Int -> IO () -> IO Int+interval = ffi "(function(t,f){window.setInterval(f,t);})"++-- | Stop a timer.+stopTimer :: MonadIO m => Timer -> m ()+stopTimer (Timer ident (Once _)) = liftIO $ clearTimeout ident+stopTimer (Timer ident (Repeat _)) = liftIO $ clearInterval ident++clearTimeout :: Int -> IO ()+clearTimeout = ffi "(function(id){window.clearTimeout(id);})"++clearInterval :: Int -> IO ()+clearInterval = ffi "(function(id){window.clearInterval(id);})"
libraries/haste-lib/src/Haste/WebSockets.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE FlexibleInstances, EmptyDataDecls, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving#-} -- | WebSockets API for Haste. module Haste.WebSockets (     module Haste.Concurrent,@@ -7,37 +7,10 @@   ) where import Haste import Haste.Foreign-import Haste.Concurrent hiding (encode, decode)+import Haste.Concurrent import Haste.Binary (Blob)-import Unsafe.Coerce -newtype WSOnMsg = WSOnMsg (WebSocket -> JSString -> IO ())-newtype WSOnBinMsg = WSOnBinMsg (WebSocket -> Blob -> IO ())-newtype WSComputation = WSComputation (WebSocket -> IO ())-newtype WSOnError = WSOnError (IO ())-data WebSocket--instance Pack WebSocket where-  pack = unsafeCoerce-instance Pack WSOnMsg where-  pack = unsafeCoerce-instance Pack WSOnBinMsg where-  pack = unsafeCoerce-instance Pack WSComputation where-  pack = unsafeCoerce-instance Pack WSOnError where-  pack = unsafeCoerce--instance Unpack WebSocket where-  unpack = unsafeCoerce-instance Unpack WSOnMsg where-  unpack = unsafeCoerce-instance Unpack WSOnBinMsg where-  unpack = unsafeCoerce-instance Unpack WSComputation where-  unpack = unsafeCoerce-instance Unpack WSOnError where-  unpack = unsafeCoerce+newtype WebSocket = WebSocket JSAny deriving (ToAny, FromAny)  -- | Run a computation with a web socket. The computation will not be executed --   until a connection to the server has been established.@@ -52,11 +25,11 @@               -> CIO a withWebSocket url cb err f = do     result <- newEmptyMVar-    let f' = WSComputation $ \ws -> concurrent $ f ws >>= putMVar result-    liftIO $ new url cb' f' $ WSOnError $ concurrent $ err >>= putMVar result+    let f' = \ws -> concurrent $ f ws >>= putMVar result+    liftIO $ new url cb' f' $ concurrent $ err >>= putMVar result     takeMVar result   where-    cb' = WSOnMsg $ \ws msg -> concurrent $ cb ws msg+    cb' = \ws msg -> concurrent $ cb ws msg  -- | Run a computation with a web socket. The computation will not be executed --   until a connection to the server has been established.@@ -71,36 +44,36 @@               -> CIO a withBinaryWebSocket url cb err f = do     result <- newEmptyMVar-    let f' = WSComputation $ \ws -> concurrent $ f ws >>= putMVar result-    liftIO $ newBin url cb' f' $ WSOnError $ concurrent $ err >>= putMVar result+    let f' = \ws -> concurrent $ f ws >>= putMVar result+    liftIO $ newBin url cb' f' $ concurrent $ err >>= putMVar result     takeMVar result   where-    cb' = WSOnBinMsg $ \ws msg -> concurrent $ cb ws msg+    cb' = \ws msg -> concurrent $ cb ws msg  new :: URL-    -> WSOnMsg-    -> WSComputation-    -> WSOnError+    -> (WebSocket -> JSString -> IO ())+    -> (WebSocket -> IO ())     -> IO ()+    -> IO () new = ffi "(function(url, cb, f, err) {\              \var ws = new WebSocket(url);\-             \ws.onmessage = function(e) {B(A(cb,[ws, [0,e.data],0]));};\-             \ws.onopen = function(e) {B(A(f,[ws,0]));};\-             \ws.onerror = function(e) {B(A(err,[0]));};\+             \ws.onmessage = function(e) {cb(ws,e.data);};\+             \ws.onopen = function(e) {f(ws);};\+             \ws.onerror = function(e) {err());};\              \return ws;\            \})"   newBin :: URL-    -> WSOnBinMsg-    -> WSComputation-    -> WSOnError-    -> IO ()+       -> (WebSocket -> Blob -> IO ())+       -> (WebSocket -> IO ())+       -> IO ()+       -> IO () newBin = ffi "(function(url, cb, f, err) {\                 \var ws = new WebSocket(url);\                 \ws.binaryType = 'blob';\-                \ws.onmessage = function(e) {B(A(cb,[ws,e.data,0]));};\-                \ws.onopen = function(e) {B(A(f,[ws,0]));};\-                \ws.onerror = function(e) {B(A(err,[0]));};\+                \ws.onmessage = function(e) {cb(ws,e.data);};\+                \ws.onopen = function(e) {f(ws);};\+                \ws.onerror = function(e) {err();};\                 \return ws;\               \})"  
src/Data/JSTarget.hs view
@@ -2,8 +2,8 @@ module Data.JSTarget (     module Constr, module Op, module Optimize, module Trav,     PPOpts (..), pretty, runPP, prettyProg, def,-    Arity, Comment, Shared, Name (..), Var (..), LHS, Call,-    Lit, Exp, Stm, Alt, AST (..), Module (..),+    Arity, Comment, Name (..), Var (..), LHS, Call,+    Lit, Exp, Stm, Alt, Module (..),     foreignModule, moduleOf, pkgOf, blackHole, blackHoleVar, merge   ) where import Data.JSTarget.AST
src/Data/JSTarget/AST.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE GADTs, GeneralizedNewtypeDeriving, FlexibleInstances, CPP #-}+{-# LANGUAGE GADTs, GeneralizedNewtypeDeriving, FlexibleInstances, CPP,+             OverloadedStrings #-} module Data.JSTarget.AST where import qualified Data.Set as S #if __GLASGOW_HASKELL__ >= 708@@ -6,59 +7,73 @@ #else import qualified Data.Map as M #endif-import System.IO.Unsafe-import System.Random (randomIO)-import Data.IORef-import Data.Word-import Control.Applicative import Data.JSTarget.Op+import qualified Data.ByteString as BS  type Arity = Int-type Comment = String+type Comment = BS.ByteString type Reorderable = Bool --- | Shared statements.-newtype Shared a = Shared Lbl deriving (Eq, Show)--data Name = Name !String !(Maybe (String, String)) deriving (Eq, Ord, Show)+-- | A Name consists of a variable name and optional (package, module)+--   information.+data Name = Name {+    nameIdent :: !BS.ByteString,+    nameQualifier :: !(Maybe (BS.ByteString, BS.ByteString))+  } deriving (Eq, Ord, Show)  class HasModule a where-  moduleOf :: a -> Maybe String-  pkgOf    :: a -> Maybe String+  moduleOf :: a -> Maybe BS.ByteString+  pkgOf    :: a -> Maybe BS.ByteString  instance HasModule Name where   moduleOf (Name _ mmod) = fmap snd mmod   pkgOf (Name _ mmod)    = fmap fst mmod  instance HasModule Var where-  moduleOf (Foreign _)    = Nothing-  moduleOf (Internal n _) = moduleOf n-  pkgOf (Foreign _)       = Nothing-  pkgOf (Internal n _)    = pkgOf n+  moduleOf (Foreign _)      = Nothing+  moduleOf (Internal n _ _) = moduleOf n+  pkgOf (Foreign _)         = Nothing+  pkgOf (Internal n _ _)    = pkgOf n +type KnownLoc = Bool+ -- | Representation of variables. data Var where-  Foreign  :: !String -> Var-  Internal :: !Name -> !Comment -> Var+  Foreign  :: !BS.ByteString -> Var+  -- | Being a "known location" means that we can never substitute this+  --   variable for another one, as it is used to hold "return values" from+  --   case statements, tail loopification and similar.+  --   If a variable is *not* a known location, then we may always perform the+  --   substitution @a=b ; exp => exp [a/b]@.+  Internal :: !Name -> !Comment -> !KnownLoc -> Var   deriving (Show) +isKnownLoc :: Var -> Bool+isKnownLoc (Internal _ _ knownloc) = knownloc+isKnownLoc _                       = False+ instance Eq Var where-  (Foreign f1)  == (Foreign f2)      = f1 == f2-  (Internal i1 _) == (Internal i2 _) = i1 == i2-  _ == _                             = False+  {-# INLINE (==) #-}+  (Foreign f1)  == (Foreign f2)          = f1 == f2+  (Internal i1 _ _) == (Internal i2 _ _) = i1 == i2+  _ == _                                 = False  instance Ord Var where-  compare (Foreign f1) (Foreign f2)       = compare f1 f2-  compare (Internal i1 _) (Internal i2 _) = compare i1 i2-  compare (Foreign _) (Internal _ _)      = Prelude.LT-  compare (Internal _ _) (Foreign _)      = Prelude.GT+  {-# INLINE compare #-}+  compare (Foreign f1) (Foreign f2)           = compare f1 f2+  compare (Internal i1 _ _) (Internal i2 _ _) = compare i1 i2+  compare (Foreign _) (Internal _ _ _)        = Prelude.LT+  compare (Internal _ _ _) (Foreign _)        = Prelude.GT  -- | Left hand side of an assignment. Normally we only assign internal vars, --   but for some primops we need to assign array elements as well.---   LhsExp is never reorderable. data LHS where+  -- | Introduce a new variable. May be reorderable.+  --   Invariant: a NewVar must be the first occurrence of a 'Var' in its+  --   scope.   NewVar :: !Reorderable -> !Var -> LHS-  LhsExp :: !Exp -> LHS+  -- | Assign a value to an arbitrary LHS expression. May be reorderable.+  LhsExp :: !Reorderable -> !Exp -> LHS   deriving (Eq, Show)  -- | Distinguish between normal, optimized and method calls.@@ -67,17 +82,17 @@ --   only be set to False when there is absolutely no possibility whatsoever --   that the called function will tailcall. data Call where-  Normal   :: !Bool -> Call-  Fast     :: !Bool -> Call-  Method   :: !String -> Call+  Normal   :: !Bool          -> Call+  Fast     :: !Bool          -> Call+  Method   :: !BS.ByteString -> Call   deriving (Eq, Show)  -- | Literals; nothing fancy to see here. data Lit where-  LNum  :: !Double  -> Lit-  LStr  :: !String  -> Lit-  LBool :: !Bool    -> Lit-  LInt  :: !Integer -> Lit+  LNum  :: !Double        -> Lit+  LStr  :: !BS.ByteString -> Lit+  LBool :: !Bool          -> Lit+  LInt  :: !Integer       -> Lit   LNull :: Lit   deriving (Eq, Show) @@ -85,9 +100,13 @@ data Exp where   Var       :: !Var -> Exp   Lit       :: !Lit -> Exp+  -- | A literal JS snippet.+  --   Invariant: JSLits must not perform side effects or significant+  --   computation.+  JSLit     :: !BS.ByteString -> Exp   Not       :: !Exp -> Exp   BinOp     :: !BinOp -> Exp -> !Exp -> Exp-  Fun       :: !(Maybe Name) -> ![Var] -> !Stm -> Exp+  Fun       :: ![Var] -> !Stm -> Exp   Call      :: !Arity -> !Call -> !Exp -> ![Exp] -> Exp   Index     :: !Exp -> !Exp -> Exp   Arr       :: ![Exp] -> Exp@@ -97,16 +116,27 @@   Thunk     :: !Bool -> !Stm -> Exp -- Thunk may be updatable or not   deriving (Eq, Show) +-- | Is the given expression guaranteed to not be a thunk?+--   @definitelyNotThunk e <=> safe to skip evaluation of e@+definitelyNotThunk :: Exp -> Bool+definitelyNotThunk (Lit {})   = True+definitelyNotThunk (JSLit {}) = True+definitelyNotThunk (Not {})   = True+definitelyNotThunk (BinOp {}) = True+definitelyNotThunk (Fun {})   = True+definitelyNotThunk (Arr {})   = True+definitelyNotThunk (Eval {})  = True+definitelyNotThunk _          = False+ -- | Statements. The only mildly interesting thing here are the Case and Jump --   constructors, which allow explicit sharing of continuations. data Stm where-  Case     :: !Exp -> !Stm -> ![Alt] -> !(Shared Stm) -> Stm+  Case     :: !Exp -> !Stm -> ![Alt] -> !Stm -> Stm   Forever  :: !Stm -> Stm   Assign   :: !LHS -> !Exp -> !Stm -> Stm   Return   :: !Exp -> Stm   Cont     :: Stm-  Jump     :: !(Shared Stm) -> Stm-  NullRet  :: Stm+  Stop     :: Stm -- Do nothing at all past this point   Tailcall :: !Exp -> Stm   ThunkRet :: !Exp -> Stm -- Return from a Thunk   deriving (Eq, Show)@@ -118,10 +148,10 @@ --   package, a dependency map of all its definitions, and a bunch of --   definitions. data Module = Module {-    modPackageId   :: !String,-    modName        :: !String,+    modPackageId   :: !BS.ByteString,+    modName        :: !BS.ByteString,     modDeps        :: !(M.Map Name (S.Set Name)),-    modDefs        :: !(M.Map Name (AST Exp))+    modDefs        :: !(M.Map Name Exp)   }  -- | Merge two modules. The module and package IDs of the second argument are@@ -147,31 +177,11 @@ -- | An LHS that's guaranteed to not ever be read, enabling the pretty --   printer to ignore assignments to it. blackHole :: LHS-blackHole =-  LhsExp $ Var blackHoleVar+blackHole = LhsExp False $ Var blackHoleVar  -- | The variable of the blackHole LHS. blackHoleVar :: Var-blackHoleVar = Internal (Name "" (Just ("$blackhole", "$blackhole"))) ""---- | An AST with local jumps.-data AST a = AST {-    astCode  :: !a,-    astJumps :: !JumpTable-  } deriving (Show, Eq)--instance Functor AST where-  fmap f (AST ast js) = AST (f ast) js--instance Applicative AST where-  pure = return-  (AST f js) <*> (AST x js') = AST (f x) (M.union js' js)--instance Monad AST where-  return x = AST x M.empty-  (AST ast js) >>= f =-    case f ast of-      AST ast' js' -> AST ast' (M.union js' js)+blackHoleVar = Internal (Name "" (Just ("$blackhole", "$blackhole"))) "" False  -- | Returns the precedence of the top level operator of the given expression. --   Everything that's not an operator has equal precedence, higher than any@@ -179,34 +189,6 @@ expPrec :: Exp -> Int expPrec (BinOp Sub (Lit (LNum 0)) _) = 500 -- 0-n is always printed as -n expPrec (BinOp op _ _)               = opPrec op+expPrec (AssignEx _ _)               = 0 expPrec (Not _)                      = 500 expPrec _                            = 1000--type JumpTable = M.Map Lbl Stm--data Lbl = Lbl !Word64 !Word64 deriving (Eq, Ord, Show)--{-# NOINLINE nextLbl #-}-nextLbl :: IORef Word64-nextLbl = unsafePerformIO $ newIORef 0--{-# NOINLINE lblNamespace #-}--- | Namespace for labels, to avoid collisions when combining modules.---   We really ought to make this f(package, module) or something, but a random---   64 bit unsigned int should suffice.-lblNamespace :: Word64-lblNamespace = unsafePerformIO $ randomIO--{-# NOINLINE lblFor #-}--- | Produce a local reference to the given statement.-lblFor :: Stm -> AST Lbl-lblFor s = do-    (r, s') <- freshRef-    AST r (M.singleton r s')-  where-    freshRef = return $! unsafePerformIO $! do-      r <- atomicModifyIORef nextLbl $ \lbl ->-        lbl `seq` (lbl+1, Lbl lblNamespace lbl)-      -- We need to depend on s, or GHC will hoist us out of lblFor, possibly-      -- causing circular dependencies between expressions.-      return (r, s)
src/Data/JSTarget/Binary.hs view
@@ -8,27 +8,29 @@ import Data.JSTarget.AST import Data.JSTarget.Op -instance Binary a => Binary (AST a) where-  put (AST x jumps) = put x >> put jumps-  get = AST <$> get <*> get- instance Binary Module where   put (Module pkgid name deps defs) =     put pkgid >> put name >> put deps >> put defs   get = Module <$> get <*> get <*> get <*> get  instance Binary Var where-  put (Foreign str)           = putWord8 0 >> put str-  put (Internal name comment) = putWord8 1 >> put name >> put comment+  put (Foreign str) =+    putWord8 0 >> put str+  put (Internal name comment knownloc) =+    putWord8 1 >> put name >> put comment >> put knownloc    get = do-    getWord8 >>= ([Foreign <$> get,Internal <$> get <*> get] !!) . fromIntegral+    which <- getWord8+    case which of+      0 -> Foreign <$> get+      1 -> Internal <$> get <*> get <*> get  instance Binary LHS where   put (NewVar r v) = putWord8 0 >> put r >> put v-  put (LhsExp e)   = putWord8 1 >> put e+  put (LhsExp r e) = putWord8 1 >> put r >> put e   -  get = getWord8 >>= ([NewVar <$>get<*>get, LhsExp <$> get] !!) . fromIntegral+  get = getWord8 >>= ([NewVar <$> get <*> get,+                       LhsExp <$> get <*> get] !!) . fromIntegral  instance Binary Call where   put (Normal tr) = putWord8 0 >> put tr@@ -52,34 +54,36 @@       fromIntegral t  instance Binary Exp where-  put (Var v)           = putWord8 0 >> put v-  put (Lit l)           = putWord8 1 >> put l-  put (Not ex)          = putWord8 2 >> put ex-  put (BinOp op a b)    = putWord8 3 >> put op >> put a >> put b-  put (Fun nam as body) = putWord8 4 >> put nam >> put as >> put body-  put (Call a c f xs)   = putWord8 5 >> put a >> put c >> put f >> put xs-  put (Index arr ix)    = putWord8 6 >> put arr >> put ix-  put (Arr exs)         = putWord8 7 >> put exs-  put (AssignEx l r)    = putWord8 8 >> put l >> put r-  put (IfEx c th el)    = putWord8 9 >> put c >> put th >> put el-  put (Eval x)          = putWord8 10 >> put x-  put (Thunk upd x)     = putWord8 11 >> put upd >> put x+  put (Var v)         = putWord8 0 >> put v+  put (Lit l)         = putWord8 1 >> put l+  put (JSLit l)       = putWord8 2 >> put l+  put (Not ex)        = putWord8 3 >> put ex+  put (BinOp op a b)  = putWord8 4 >> put op >> put a >> put b+  put (Fun as body)   = putWord8 5 >> put as >> put body+  put (Call a c f xs) = putWord8 6 >> put a >> put c >> put f >> put xs+  put (Index arr ix)  = putWord8 7 >> put arr >> put ix+  put (Arr exs)       = putWord8 8 >> put exs+  put (AssignEx l r)  = putWord8 9 >> put l >> put r+  put (IfEx c th el)  = putWord8 10 >> put c >> put th >> put el+  put (Eval x)        = putWord8 11 >> put x+  put (Thunk upd x)   = putWord8 12 >> put upd >> put x      get = do     tag <- getWord8     case tag of       0  -> Var <$> get       1  -> Lit <$> get-      2  -> Not <$> get-      3  -> BinOp <$> get <*> get <*> get-      4  -> Fun <$> get <*> get <*> get-      5  -> Call <$> get <*> get <*> get <*> get-      6  -> Index <$> get <*> get-      7  -> Arr <$> get-      8  -> AssignEx <$> get <*> get-      9  -> IfEx <$> get <*> get <*> get-      10 -> Eval <$> get-      11 -> Thunk <$> get <*> get+      2  -> JSLit <$> get+      3  -> Not <$> get+      4  -> BinOp <$> get <*> get <*> get+      5  -> Fun <$> get <*> get+      6  -> Call <$> get <*> get <*> get <*> get+      7  -> Index <$> get <*> get+      8  -> Arr <$> get+      9  -> AssignEx <$> get <*> get+      10  -> IfEx <$> get <*> get <*> get+      11 -> Eval <$> get+      12 -> Thunk <$> get <*> get       n  -> error $ "Bad tag in get :: Get Exp: " ++ show n  instance Binary Stm where@@ -93,14 +97,12 @@     putWord8 3 >> put ex   put (Cont) =     putWord8 4-  put (Jump j) =-    putWord8 5 >> put j-  put (NullRet) =-    putWord8 6+  put (Stop) =+    putWord8 5   put (Tailcall ex) =-    putWord8 7 >> put ex+    putWord8 6 >> put ex   put (ThunkRet ex) =-    putWord8 8 >> put ex+    putWord8 7 >> put ex      get = do     tag <- getWord8@@ -110,10 +112,9 @@       2 -> Assign <$> get <*> get <*> get       3 -> Return <$> get       4 -> pure Cont-      5 -> Jump <$> get-      6 -> pure NullRet-      7 -> Tailcall <$> get-      8 -> ThunkRet <$> get+      5 -> pure Stop+      6 -> Tailcall <$> get+      7 -> ThunkRet <$> get       n -> error $ "Bad tag in get :: Get Stm: " ++ show n  instance Binary BinOp where@@ -127,7 +128,7 @@   put Eq     = putWord8 7   put Neq    = putWord8 8   put LT     = putWord8 9-  put GT      = putWord8 10+  put GT     = putWord8 10   put LTE    = putWord8 11   put GTE    = putWord8 12   put Shl    = putWord8 13@@ -142,14 +143,6 @@ instance Binary Name where   put (Name name owner) = put name >> put owner   get = Name <$> get <*> get--instance Binary a => Binary (Shared a) where-  put (Shared lbl) = put lbl-  get = Shared <$> get--instance Binary Lbl where-  put (Lbl namespace lbl) = put namespace >> put lbl-  get = Lbl <$> get <*> get  opTbl :: Array Word8 BinOp opTbl =
src/Data/JSTarget/Constructors.hs view
@@ -1,76 +1,96 @@-{-# LANGUAGE FlexibleInstances, OverlappingInstances, TupleSections #-}+{-# LANGUAGE FlexibleInstances, TupleSections, CPP, OverloadedStrings #-}+#if __GLASGOW_HASKELL__ < 710+{-# LANGUAGE OverlappingInstances #-}+#endif -- | User interface for the JSTarget AST. module Data.JSTarget.Constructors where import Data.JSTarget.AST import Data.JSTarget.Op-import Control.Applicative+import qualified Data.ByteString as BS+import qualified Data.ByteString.UTF8 as BS  -- | Literal types. class Literal a where-  lit :: a -> AST Exp+  lit :: a -> Exp +instance Literal Lit where+  lit = Lit+ instance Literal Double where-  lit = pure . Lit . LNum+  lit = lit . LNum  instance Literal Integer where-  lit = pure . Lit . LInt+  lit = lit . LInt  instance Literal Bool where-  lit = pure . Lit . LBool+  lit = lit . LBool +instance Literal BS.ByteString where+  lit = lit . LStr+ instance Literal [Char] where-  lit = pure . Lit . LStr+  lit = lit . BS.fromString +#if __GLASGOW_HASKELL__ < 710 instance Literal a => Literal [a] where-  lit xs = Arr <$> mapM lit xs+#else+instance {-# OVERLAPPABLE #-} Literal a => Literal [a] where+#endif+  lit = Arr . map lit  instance Literal Exp where-  lit = pure+  lit = id  instance Literal Var where-  lit = pure . Var+  lit = Var -litN :: Double -> AST Exp+litN :: Double -> Exp litN = lit +litS :: BS.ByteString -> Exp+litS = lit+ -- | Create a foreign variable. Foreign vars will not be subject to any name --   mangling.-foreignVar :: String -> Var+foreignVar :: BS.ByteString -> Var foreignVar = Foreign  -- | A regular, internal variable. Subject to name mangling.-internalVar :: Name -> String -> Var-internalVar = Internal+internalVar :: Name -> BS.ByteString -> Var+internalVar n c = Internal n c False +-- | A variable serving as a known location, to store return values from+--   expressions that get compiled into statements.+knownLocation :: Name -> BS.ByteString -> Var+knownLocation n c = Internal n c True+ -- | Create a name, qualified or not.-name :: String -> Maybe (String, String) -> Name+name :: BS.ByteString -> Maybe (BS.ByteString, BS.ByteString) -> Name name = Name  -- | A variable expression, for convenience.-var :: Name -> String -> AST Exp-var n comment = pure $ Var $ internalVar n comment+var :: Name -> BS.ByteString -> Exp+var n comment = Var $ internalVar n comment  -- | Turn a Var into an expression.-varExp :: Var -> AST Exp-varExp = pure . Var+varExp :: Var -> Exp+varExp = Var  -- | Call to a native method on an object. Always saturated.-callMethod :: AST Exp -> String -> [AST Exp] -> AST Exp-callMethod obj meth args =-  Call 0 (Method meth) <$> obj <*> sequence args+callMethod :: Exp -> BS.ByteString -> [Exp] -> Exp+callMethod obj meth args = Call 0 (Method meth) obj args  -- | Foreign function call. Always saturated, never trampolines.-callForeign :: String -> [AST Exp] -> AST Exp-callForeign f = fmap (Call 0 (Fast False) (Var $ foreignVar f)) . sequence+callForeign :: BS.ByteString -> [Exp] -> Exp+callForeign f = Call 0 (Fast False) (Var $ foreignVar f)  -- | A normal function call. May be unsaturated. A saturated call is always --   turned into a fast call.-call :: Arity -> AST Exp -> [AST Exp] -> AST Exp-call arity f xs = do-  foldApp <$> (Call (arity - length xs) (Normal True) <$> f <*> sequence xs)+call :: Arity -> Exp -> [Exp] -> Exp+call arity f xs = foldApp $ Call (arity - length xs) (Normal True) f xs -callSaturated :: AST Exp -> [AST Exp] -> AST Exp-callSaturated f xs = Call 0 (Fast True) <$> f <*> sequence xs+callSaturated :: Exp -> [Exp] -> Exp+callSaturated f xs = Call 0 (Fast True) f xs  -- | "Fold" nested function applications into one, turning them into fast calls --   if they turn out to be saturated.@@ -80,8 +100,8 @@ foldApp (Call 0 (Normal tramp) f args) =   Call 0 (Fast tramp) f args foldApp (Call arity (Normal tramp) f args) | arity > 0 =-    Fun Nothing newargs $ Return-                        $ Call arity (Fast tramp) f (args ++ map Var newargs)+    Fun  newargs $ Return+                 $ Call arity (Fast tramp) f (args ++ map Var newargs)   where     newargs = newVars "_fa_" arity foldApp ex =@@ -90,79 +110,76 @@ -- | Introduce n new vars. newVars :: String -> Int -> [Var] newVars prefix n =-    map newVar [1..n]+    map nv [1..n]   where-    newVar i = Internal (Name (prefix ++ show i) Nothing) ""+    nv i = Internal (Name (BS.fromString $ prefix++show i) Nothing) "" False  -- | Create a thunk.-thunk :: Bool -> AST Stm -> AST Exp-thunk updatable = fmap (Thunk updatable)+thunk :: Bool -> Stm -> Exp+thunk = Thunk  -- | Evaluate an expression that may or may not be a thunk.-eval :: AST Exp -> AST Exp-eval = fmap Eval+eval :: Exp -> Exp+eval ex+  | definitelyNotThunk ex = ex+  | otherwise             = Eval ex  -- | Create a tail call.-tailcall :: AST Exp -> AST Stm-tailcall call = Tailcall <$> call+tailcall :: Exp -> Stm+tailcall = Tailcall  -- | A binary operator.-binOp :: BinOp -> AST Exp -> AST Exp -> AST Exp-binOp op a b = BinOp op <$> a <*> b+binOp :: BinOp -> Exp -> Exp -> Exp+binOp = BinOp  -- | Negate an expression.-not_ :: AST Exp -> AST Exp-not_ = fmap Not+not_ :: Exp -> Exp+not_ = Not  -- | Index into an array.-index :: AST Exp -> AST Exp -> AST Exp-index arr ix = Index <$> arr <*> ix+index :: Exp -> Exp -> Exp+index = Index  -- | Create a function.-fun :: [Var] -> AST Stm -> AST Exp-fun args = fmap (Fun Nothing args)+fun :: [Var] -> Stm -> Exp+fun = Fun  -- | Create an array of expressions.-array :: [AST Exp] -> AST Exp-array = fmap Arr . sequence+array :: [Exp] -> Exp+array = Arr  -- | Case statement. --   Takes a scrutinee expression, a default alternative, a list of more --   specific alternatives, and a continuation statement. The continuation --   will be explicitly shared among all the alternatives.-case_ :: AST Exp-      -> (AST Stm -> AST Stm)-      -> [(AST Exp, AST Stm -> AST Stm)]-      -> AST Stm-      -> AST Stm-case_ ex def alts cont = do-  ex' <- ex-  shared <- cont >>= lblFor-  let jmp = pure $ Jump (Shared shared)-  def' <- def jmp-  alts' <- sequence [(,) <$> x <*> s jmp | (x, s) <- alts]-  pure $ Case ex' def' alts' (Shared shared)+case_ :: Exp -> (Stm -> Stm) -> [(Exp, Stm -> Stm)] -> Stm -> Stm+case_ ex def alts = Case ex (def stop) (map (\(e, s) -> (e, s stop)) alts)  -- | Return from a function.-ret :: AST Exp -> AST Stm-ret = fmap Return+ret :: Exp -> Stm+ret = Return  -- | Return from a thunk.-thunkRet :: AST Exp -> AST Stm-thunkRet = fmap ThunkRet+thunkRet :: Exp -> Stm+thunkRet = ThunkRet  -- | Create a new var with a new value.-newVar :: Reorderable -> Var -> AST Exp -> AST Stm -> AST Stm-newVar r lhs = liftA2 $ \rhs -> Assign (NewVar r lhs) rhs+newVar :: Reorderable -> Var -> Exp -> Stm -> Stm+newVar r lhs = Assign (NewVar r lhs) --- | Assignment without var.-assign :: AST Exp -> AST Exp -> AST Stm -> AST Stm-assign = liftA3 $ \lhs rhs -> Assign (LhsExp lhs) rhs+-- | Reuse an old variable.+assignVar :: Reorderable -> Var -> Exp -> Stm -> Stm+assignVar r lhs = Assign (LhsExp r (Var lhs)) +-- | Assignment without var. Performed for the side effect, so never+--   reorderable.+sideEffectingAssign :: Exp -> Exp -> Stm -> Stm+sideEffectingAssign lhs = Assign (LhsExp False lhs)+ -- | Assignment expression.-assignEx :: AST Exp -> AST Exp -> AST Exp-assignEx = liftA2 AssignEx+assignEx :: Exp -> Exp -> Exp+assignEx = AssignEx  -- | Terminate a statement without doing anything at all.-nullRet :: AST Stm-nullRet = pure NullRet+stop :: Stm+stop = Stop
src/Data/JSTarget/Optimize.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE PatternGuards, TupleSections, DoAndIfThenElse #-}+{-# LANGUAGE PatternGuards, TupleSections, DoAndIfThenElse, OverloadedStrings #-} -- | Optimizations over the JSTarget AST. module Data.JSTarget.Optimize (     optimizeFun, tryTernary, topLevelInline@@ -10,13 +10,24 @@ import Control.Monad import qualified Data.Map as M import qualified Data.Set as S+import qualified Data.ByteString.Char8 as BS +-- | Turn tail recursion into loops.+fixTailCalls :: Var -> Exp -> TravM Exp+fixTailCalls fun ast = do+    ast' <- assignToSubst ast >>= tailLoopify fun+    mapJS (const True) pure loopify ast'+  where+    loopify (Assign lhs@(NewVar _ f) body next) =+      Assign lhs <$> (assignToSubst body >>= tailLoopify f) <*> pure next+    loopify stm =+      return stm+ -- TODO: tryTernary may inline calls that would otherwise be in tail position --       which is something we'd really like to avoid.--optimizeFun :: Var -> AST Exp -> AST Exp-optimizeFun f (AST ast js) =-  flip runTravM js $ do+optimizeFun :: Var -> Exp -> Exp+optimizeFun f ast =+  runTravM $ do     shrinkCase ast     >>= inlineAssigns     >>= optimizeArrays@@ -24,56 +35,66 @@     >>= zapJSStringConversions     >>= optimizeThunks     >>= optimizeArrays-    >>= tailLoopify f+    >>= fixTailCalls f+    >>= inlineShortJumpTailcall     >>= trampoline     >>= ifReturnToTernary+    >>= smallStepInline+    >>= inlineJSPrimitives+    >>= mapJS (const True) pure (pure . removeNonsenseAssigns) -topLevelInline :: AST Stm -> AST Stm-topLevelInline (AST ast js) =-  flip runTravM js $ do+topLevelInline :: Stm -> Stm+topLevelInline ast =+  runTravM $ do     unTrampoline ast---    return ast+    >>= unevalLits+    >>= inlineIntoEval     >>= inlineAssigns     >>= optimizeArrays     >>= optimizeThunks+    >>= smallStepInline     >>= optimizeArrays     >>= zapJSStringConversions  -- | Attempt to turn two case branches into a ternary operator expression. tryTernary :: Var-           -> AST Exp-           -> AST Exp-           -> (AST Stm -> AST Stm)-           -> [(AST Exp, AST Stm -> AST Stm)]-           -> Maybe (AST Exp)+           -> Exp+           -> Exp+           -> (Stm -> Stm)+           -> [(Exp, Stm -> Stm)]+           -> Maybe Exp tryTernary self scrut retEx def [(m, alt)] =-    case runTravM opt allJumps of-      AST (Just ex) js -> Just (AST ex js)-      _                -> Nothing+    runTravM opt   where-    selfOccurs (Exp (Var v)) = v == self-    selfOccurs _             = False-    def' = def $ Return <$> retEx-    alt' = alt $ Return <$> retEx-    AST _ allJumps = scrut >> m >> def' >> alt'+    selfOccurs (Exp (Var v) _) = v == self+    selfOccurs _               = False+    def' = def $ Return retEx+    alt' = alt $ Return retEx     opt = do       -- Make sure the return expression is used somewhere, then cut away all       -- useless assignments. If what's left is a single Return statement,       -- we have a pure expression suitable for use with ?:.-      def'' <- inlineAssignsLocal $ astCode def'-      alt'' <- inlineAssignsLocal $ astCode alt'+      def'' <- inlineAssignsLocal def'+      alt'' <- inlineAssignsLocal alt'       -- If self occurs in either branch, we can't inline or we risk ruining       -- tail call elimination.       selfInDef <- occurrences (const True) selfOccurs def''       selfInAlt <- occurrences (const True) selfOccurs alt''       case (selfInDef + selfInAlt, def'', alt'') of         (Never, Return el, Return th) ->-          return $ Just $ IfEx (BinOp Eq (astCode scrut) (astCode m)) th el+          return $ Just $ IfEx (BinOp Eq scrut m) th el         _ ->           return Nothing tryTernary _ _ _ _ _ =   Nothing +-- | Remove bogus assignments of the form @literal = exp@, which may arise from+--   other optimizations.+removeNonsenseAssigns :: Stm -> Stm+removeNonsenseAssigns (Assign (LhsExp _ (Lit _)) _ next)    = next+removeNonsenseAssigns (Assign (LhsExp _ a) b next) | a == b = next+removeNonsenseAssigns stm                                   = stm+ -- | How many times does an expression satisfying the given predicate occur in --   an AST (including jumps)? occurrences :: JSTrav ast@@ -88,13 +109,6 @@     count n node | p node = pure $ n + Once     count n _             = pure n --- | Replace all occurrences of an expression, without entering shared code---   paths. IO ordering is preserved even when entering lambdas thanks to---   State# RealWorld.-replaceEx :: JSTrav ast => (ASTNode -> Bool) -> Exp -> Exp -> ast -> TravM ast-replaceEx trav old new =-  mapJS trav (\x -> if x == old then pure new else pure x) pure- -- | Inline assignments where the assignee is only ever used once. --   Does not inline anything into a shared code path, as that would break --   things horribly.@@ -108,39 +122,92 @@     inlinable <- gatherInlinable ast     mapJS (const True) return (inl inlinable) ast   where-    varOccurs lhs (Exp (Var lhs')) = lhs == lhs'-    varOccurs _ _                  = False-    inl m keep@(Assign (NewVar mayReorder lhs) ex next) = do-      occursRec <- occurrences (const True) (varOccurs lhs) ex-      if occursRec == Never-        then do-          occursLocal <- occurrences (not <$> isShared) (varOccurs lhs) next-          case M.lookup lhs m of-            Just occ | occ == occursLocal ->-              case occ of-                -- Inline of any non-lambda value-                Once | mayReorder -> do-                  if computingThunk ex-                    then do-                      let notSharedOrLambda = (not <$> (isShared .|. isLambda))-                      occs <- occurrences notSharedOrLambda (varOccurs lhs) next-                      if occs == Once-                        then replaceEx notSharedOrLambda (Var lhs) ex next-                        else return keep-                    else do-                      replaceEx (not <$> isShared) (Var lhs) ex next-                -- Don't inline lambdas, but use less verbose syntax.-                _    | Fun Nothing vs body <- ex,-                       Internal lhsname _ <- lhs -> do-                  return $ Assign blackHole (Fun (Just lhsname) vs body) next-                _ -> do-                  return keep-            _ ->-              return keep-        else do-          return keep+    varOccurs lhs (Exp (Var lhs') _) = lhs == lhs'+    varOccurs _ _                    = False++    appearsLHS ex =+      foldJS (\x _ -> not x) (\x s -> return $ x || ex `isLHSOf` s) False++    -- Make an exception for expressions of the form @x = E(x)@: since we know+    -- that @x@ is a literal and thus pointless to evaluate, we simply remove+    -- any such statements.+    isLHSOf v (Stm stm _) | isEvalUpd (==v) stm        = False+    isLHSOf v (Stm (Assign (LhsExp _ (Var v')) _ _) _) = v == v'+    isLHSOf v (Exp (AssignEx (Var v') _) _)            = v == v'+    isLHSOf _ _                                        = False++    inl m keep@(Assign l ex next)+      -- Inline all non-string literals l which do not appear at the LHS of an+      -- assignment. Thunk updates of the form @x = E(x)@ where @x == l@+      -- don't count as a proper LHS occurrence, and are removed outright+      -- instead since a literal is guaranteed to never be a thunk.+      | Just lhs <- inlinableAssignLHS l, Lit x <- ex, not (stringLit x) = do+        isLHS <- appearsLHS lhs next+        if isLHS+          then do+            return keep+          else do+            next' <- mapJS (const True) pure (pure . removeUpdate (==lhs)) next+            replaceEx (const True) (Var lhs) ex next'+      | Just lhs <- inlinableAssignLHS l = do+        occursRec <- occurrences (const True) (varOccurs lhs) ex+        if occursRec == Never+          then do+            occursLocal <- occurrences isSafeForInlining (varOccurs lhs) next+            case M.lookup lhs m of+              Just Once | okToInline ex && occursLocal == Once -> do+                -- Inline any non-lambda, non-thunk, non JSLit value+                replaceEx isSafeForInlining (Var lhs) ex next+              _ | Lit _ <- ex -> do+                -- Inline any string literals provided that they don't appear+                -- more than once.+                occurs <- occurrences (const True) (varOccurs lhs) next+                if occurs == Once+                  then replaceEx (const True) (Var lhs) ex next+                  else return keep+              _ -> do+                return keep+          else do+            return keep     inl _ stm = return stm +-- | Remove an occurrence of @ex = E(ex)@ or @lit = ex@.+--   Only call this for @ex@ which are guaranteed to never be thunks.+removeUpdate :: (Var -> Bool) -> Stm -> Stm+removeUpdate p stm@(Assign _ _ next) | isEvalUpd p stm = next+removeUpdate _ stm                                     = stm++-- | Turn the common pattern @var x = e ; x = E(x)@ into @var x = E(e)@.+--   should run *after* 'unevalLits'.+inlineIntoEval :: JSTrav ast => ast -> TravM ast+inlineIntoEval ast = do+    mapJS (const True) pure (pure . inline) ast+  where+    inline (Assign l@(NewVar _ v) r s@(Assign _ _ next))+      | isEvalUpd (== v) s = Assign l (Eval r) next+    inline stm             = stm++isEvalUpd :: (Var -> Bool) -> Stm -> Bool+isEvalUpd p (Assign (LhsExp _ (Var v)) (Eval (Var v')) _) = p v && v == v'+isEvalUpd _ _                                             = False++stringLit :: Lit -> Bool+stringLit (LStr _) = True+stringLit _        = False++-- | Certain expressions are never OK to inline: lambdas, thunks and+--   JS literals (which are almost exclusively lambdas).+okToInline :: Exp -> Bool+okToInline (Fun {})   = False+okToInline (Thunk {}) = False+okToInline (JSLit {}) = False+okToInline _          = True++inlinableAssignLHS :: LHS -> Maybe Var+inlinableAssignLHS (NewVar True v)       = Just v+inlinableAssignLHS (LhsExp True (Var v)) = Just v+inlinableAssignLHS _                     = Nothing+ -- | Turn if(foo) {return bar;} else {return baz;} into return foo ? bar : baz. ifReturnToTernary :: JSTrav ast => ast -> TravM ast ifReturnToTernary ast = do@@ -182,31 +249,55 @@       return x  -- | Optimize thunks in the following ways:---   A(thunk(return f), xs)---     => A(f, xs)---   E(thunk(return x))---     => x---   E(\x ... -> ...)---     => \x ... -> ...---   thunk(x) where x is non-computing and non-recursive---     => x+--   1. A(thunk(return f), xs)+--        => A(f, xs)+--   2. thunk(x@(JSLit s)) | s is a JS function object or marked eager+--        => x+--   3. thunk(x@(Lit _))+--        => x+--   4. E(thunk(return x))+--        => x+--   5. E(x) | x is guaranteed to not be a thunk+--        => x -----   TODO: figure out efficient way to only perform the 4th optimization when x---         is not recursive.+--   Note that #2 depends on the invariant of 'JSLit': a JS literal must not+--   perform side effects or significant computation. optimizeThunks :: JSTrav ast => ast -> TravM ast optimizeThunks ast =     mapJS (const True) optEx return ast   where     optEx (Eval x)-      | Just x' <- fromThunkEx x = return x'-      | Fun _ _ _ <- x           = return x-    optEx (Call arity calltype f args) | Just f' <- fromThunkEx f =-      return $ Call arity calltype f' args---    optEx ex | Just ex' <- fromThunkEx ex, not (computingEx ex') =---      return ex'-    optEx ex =-      return ex+      | Just x' <- fromThunkEx x           = return x'+      | definitelyNotThunk x               = return x+    optEx ex@(Thunk _ _)+      | Just l@(JSLit s) <- fromThunkEx ex =+        case maybeExtractStrict s of+          Just s'           -> return $ JSLit s'+          _ | isJSFunDecl s -> return l+            | otherwise     -> return ex+    optEx ex@(Thunk _ _)+      | Just l@(Lit _) <- fromThunkEx ex   = return l+    optEx (Call arity calltype f as)+      | Just f' <- fromThunkEx f           = return $ Call arity calltype f' as+    optEx ex                               = return ex +maybeExtractStrict :: BS.ByteString -> Maybe BS.ByteString+maybeExtractStrict js+  | "__strict(" `BS.isPrefixOf` js && ")" `BS.isSuffixOf` js =+    Just $ BS.init $ BS.drop 9 js+  | otherwise =+    Nothing++-- | Conservatively approximate whether a given JS literal is a function+--   declaration or not.+--+--   TODO: proper parsing here.+isJSFunDecl :: BS.ByteString -> Bool+isJSFunDecl s+  | "function(" `BS.isPrefixOf` s && "}" `BS.isSuffixOf` s  = True+  | "(function(" `BS.isPrefixOf` s && ")" `BS.isSuffixOf` s = True+  | otherwise                                               = False+ -- | Unpack the given expression if it's a thunk. fromThunk :: Exp -> Maybe Stm fromThunk (Thunk _ body) = Just body@@ -216,34 +307,12 @@ fromThunkEx :: Exp -> Maybe Exp fromThunkEx ex =   case fromThunk ex of-    Just (Return ex') -> Just ex'-    _                 -> Nothing---- | Is the given expression a thunk which when evaluated performs some kind of---   computation?-computingThunk :: Exp -> Bool-computingThunk e =-  case fromThunkEx e of-    Just e' -> computingEx e'-    _       -> False---- | Does the given expression compute something? An expression is---   non-computing if it is a variable, a literal, a lambda abstraction,---   a thunk or an array which only has non-computing elements.-computingEx :: Exp -> Bool-computingEx ex =-  case ex of-    Var _      -> False-    Lit _      -> False-    Fun _ _ _  -> False-    Thunk _ _  -> False-    Arr arr    -> any computingEx arr-    _          -> True-+    Just (Return ex')   -> Just ex'+    Just (ThunkRet ex') -> Just ex'+    _                   -> Nothing  -- | Gather a map of all inlinable symbols; that is, the ones that are used --   exactly once.---   TODO: always inline assigns that are just aliases! gatherInlinable :: JSTrav ast => ast -> TravM (M.Map Var Occs) gatherInlinable ast = do     m <- foldJS (\_ _->True) countOccs (M.empty) ast@@ -253,10 +322,14 @@     updVar _           = Just Once     updVarAss (Just o) = Just o     updVarAss _        = Just Never-    countOccs m (Exp (Var v@(Internal _ _))) =++    {-# INLINE countOccs #-}+    countOccs m (Exp (Var v@(Internal _ _ _)) _) =       pure (M.alter updVar v m)-    countOccs m (Stm (Assign (NewVar _ v) _ _)) =+    countOccs m (Stm (Assign (NewVar _ v) _ _) _) =       pure (M.alter updVarAss v m)+    countOccs m (Stm (Assign (LhsExp True (Var v)) _ _) _) =+      pure (M.alter updVarAss v m)     countOccs m _ =       pure m @@ -271,12 +344,12 @@ mayTailcall ast = do   foldJS enter countTCs False ast   where-    enter True _              = False-    enter _ (Exp (Thunk _ _)) = False---    enter _ (Exp (Fun _ _ _)) = False-    enter _ _                 = True-    countTCs _ (Stm (Tailcall _)) = return True-    countTCs acc _                = return acc+    enter True _                = False+    enter _ (Exp (Thunk _ _) _) = False+--    enter _ (Exp (Fun _ _) _)   = False+    enter _ _                   = True+    countTCs _ (Stm (Tailcall _) _) = return True+    countTCs acc _                  = return acc  -- | Gather a map of all symbols which we know will never make tail calls. --   All calls to functions in this set can then safely be de-trampolined.@@ -284,14 +357,14 @@ gatherNonTailcalling stm = do     foldJS (\_ _ -> True) countTCs S.empty stm   where-    countTCs s (Exp (Var v@(Foreign _))) = do+    countTCs s (Exp (Var v@(Foreign _)) _) = do       return $ S.insert v s-    countTCs s (Stm (Assign (NewVar _ v) (Fun _ _ body) _)) = do+    countTCs s (Stm (Assign (NewVar _ v) (Fun _ body) _) _) = do       tc <- mayTailcall body       return $ if not tc then S.insert v s else s      -    countTCs s (Exp (Fun (Just name) _ body)) = do+    countTCs s (Stm (Assign (LhsExp True (Var v)) (Fun _ body) _) _) = do       tc <- mayTailcall body-      return $ if not tc then S.insert (Internal name "") s else s+      return $ if not tc then S.insert v s else s     countTCs s _ = do       return s @@ -321,10 +394,10 @@     unTr ntcs (Call ar (Fast True) f@(Var v) xs)       | v `S.member` ntcs =         return $ Call ar (Fast False) f xs-    unTr _ c@(Call ar (Normal True) f@(Fun _ _ body) xs) = do+    unTr _ c@(Call ar (Normal True) f@(Fun _ body) xs) = do         tc <- mayTailcall body         return $ if tc then c else Call ar (Normal False) f xs-    unTr _ c@(Call ar (Fast True) f@(Fun _ _ body) xs) = do+    unTr _ c@(Call ar (Fast True) f@(Fun _ body) xs) = do         tc <- mayTailcall body         return $ if tc then c else Call ar (Fast False) f xs     unTr _ x =@@ -337,34 +410,38 @@     unTC ntcs (Tailcall c@(Call _ _ (Var v) _))       | v `S.member` ntcs =         return $ Return c-    unTC _ tc@(Tailcall c@(Call _ _ (Fun _ _ body) _)) = do+    unTC _ tc@(Tailcall c@(Call _ _ (Fun _ body) _)) = do         maytc <- mayTailcall body         if not maytc then return (Return c) else return tc     unTC _ x =         return x --- | Like `inlineAssigns`, but doesn't care what happens beyond a jump.+-- | Like 'inlineAssigns', but doesn't care what happens beyond a jump. inlineAssignsLocal :: JSTrav ast => ast -> TravM ast inlineAssignsLocal ast = do-    mapJS (\n -> not (isLambda n || isShared n)) return inl ast+    mapJS isSafeForInlining return inl ast   where-    varOccurs lhs (Exp (Var lhs')) = lhs == lhs'-    varOccurs _ _                  = False-    inl keep@(Assign (NewVar mayReorder lhs) ex next) = do-      occurs <- occurrences (const True) (varOccurs lhs) next-      occurs' <- occurrences (const True) (varOccurs lhs) ex-      case occurs + occurs' of-        Never ->-          return (Assign blackHole ex next)-        -- Don't inline lambdas at the moment, but use less verbose syntax.-        _     | Fun Nothing vs body <- ex,-                Internal lhsname _ <- lhs ->-          return $ Assign blackHole (Fun (Just lhsname) vs body) next-        Once | mayReorder ->-          -- can't be recursive - inline-          replaceEx (not <$> isShared) (Var lhs) ex next+    varOccurs lhs (Exp (Var lhs') _) = lhs == lhs'+    varOccurs _ _                    = False+    inl keep@(Assign l ex next) | Just lhs <- inlinableAssignLHS l = do+      occursRec <- occurrences (const True) (varOccurs lhs) ex+      case occursRec of+        Never -> do+          occurs <- occurrences (const True) (varOccurs lhs) next+          occursSafe <- occurrences isSafeForInlining (varOccurs lhs) next+          case (occurs, occursSafe) of+            (Never, Never) ->+              return (Assign blackHole ex next)+            _    | Fun _ _ <- ex -> do+              -- Don't inline lambdas at the moment.+              return keep+            (Once, Once) | Nothing <- fromThunk ex ->+              -- can't be recursive - inline+              replaceEx (not <$> isShared) (Var lhs) ex next+            _ ->+              -- Really nothing to be done here.+              return keep         _ ->-          -- Really nothing to be done here.           return keep     inl stm = return stm @@ -374,41 +451,52 @@ --   care about the assignment side effect. inlineReturns :: JSTrav ast => ast -> TravM ast inlineReturns ast = do-    (s, ast') <- foldMapJS (\_ _ -> True) pure2 foldRet S.empty ast-    mapM_ (flip putRef NullRet) $ S.toList s-    return ast'+    mapJS (const True) inl pure ast   where-    pure2 s x = pure (s,x)-    foldRet s (Assign (NewVar _ lhs) rhs (Return (Var v))) | v == lhs = do-      return (s, Return rhs)-    foldRet s keep@(Assign (NewVar _ lhs) rhs (Jump (Shared lbl))) = do-      next <- getRef lbl-      case next of-        Return (Var v) | v == lhs ->-          return (S.insert lbl s, Return rhs)+    inl (Fun as body)    = Fun as <$> go Nothing body+    inl (Thunk upd body) = Thunk upd <$> go Nothing body+    inl ex               = pure ex+ +    goAlt outside (ex, stm) = (ex,) <$> go outside stm++    go outside (Case c d as next) = do+      next' <- go outside next+      case returnLike next' of+        outside'@(Just _) ->+          Case c <$> go outside' d <*> mapM (goAlt outside') as <*> pure Stop         _ ->-          return (s, keep)-    foldRet s keep = do-      return (s, keep)+          Case c <$> go Nothing d <*> mapM (goAlt Nothing) as <*> pure next'+    go _ (Forever s) = do+      Forever <$> go Nothing s+    go outside (Assign l@(NewVar _ lhs) r next) = do+      next' <- go outside next+      case (next', outside) of+        (Stop, Just (Var v, ret))   | v == lhs -> return $ ret r+        (Return (Var v), _)         | v == lhs -> return $ Return r+        (ThunkRet (Var v), _)       | v == lhs -> return $ ThunkRet r+        (Assign ll (Var v) Stop, _) | v == lhs -> return $ Assign ll r Stop+        _                                      -> return $ Assign l r next'+    go outside (Assign l r next) = do+      Assign l r <$> go outside next+    go _ stm = do+      return stm --- | Inline all occurrences of the given shared code path.---   Use with caution - preferrably not at all!-inlineShared :: JSTrav ast => Lbl -> ast -> TravM ast-inlineShared lbl =-    mapJS (const True) pure inl-  where-    inl (Jump (Shared lbl')) | lbl == lbl' = getRef lbl-    inl s                                  = pure s+-- | Extract the expression returned from a Return of ThunkRet, as well as+--   a function to recreate that type of return.+returnLike :: Stm -> Maybe (Exp, Exp -> Stm)+returnLike (Return e)   = Just (e, Return)+returnLike (ThunkRet e) = Just (e, ThunkRet)+returnLike _            = Nothing  -- | Shrink case statements as much as possible. shrinkCase :: JSTrav ast => ast -> TravM ast shrinkCase =     mapJS (const True) pure shrink   where-    shrink (Case _ def [] next@(Shared lbl))-      | def == Jump next = getRef lbl-      | otherwise        = inlineShared lbl def-    shrink stm           = return stm+    shrink (Case _ def [] next)+      | def == Stop = return next+      | otherwise   = replaceFinalStm next (== Stop) def+    shrink stm      = return stm  -- | Turn any calls in tail position into tailcalls. --   Must run after @tailLoopify@ or we won't get loops for simple tail@@ -440,7 +528,7 @@ --     } --   } tailLoopify :: Var -> Exp -> TravM Exp-tailLoopify f fun@(Fun mname args body) = do+tailLoopify f fun@(Fun args body) = do     tailrecs <- occurrences (not <$> isLambda) isTailRec body     if tailrecs > Never       then do@@ -450,32 +538,30 @@             let args' = map newName args                 ret = Return (Lit $ LNull)             b <- mapJS (not <$> isLambda) pure (replaceByAssign ret args') body-            let (AST nullRetLbl _) = lblFor NullRet-                nn = newName f+            let nn = newName f                 nv = NewVar False nn                 body' =                   Forever $-                  Assign nv (Call 0 (Fast False) (Fun Nothing args b)+                  Assign nv (Call 0 (Fast False) (Fun args b)                                                  (map Var args')) $-                  Case (Var nn) (Return (Var nn)) [(Lit $ LNull, NullRet)] $-                  (Shared nullRetLbl)-            putRef nullRetLbl NullRet-            return $ Fun mname args' body'+                  Case (Var nn) (Return (Var nn)) [(Lit $ LNull, Stop)] $+                  Stop+            return $ Fun args' body'           False -> do             let c = Cont             body' <- mapJS (not <$> isLambda) pure (replaceByAssign c args) body-            return $ Fun mname args (Forever body')+            return $ Fun args (Forever body')       else do         return fun   where-    isTailRec (Stm (Return (Call _ _ (Var f') _))) = f == f'-    isTailRec _                                    = False+    isTailRec (Stm (Return (Call _ _ (Var f') _)) _) = f == f'+    isTailRec _                                      = False          -- Only traverse until we find a closure     createsClosures = foldJS (\acc _ -> not acc) isClosure False-    isClosure _ (Exp (Fun _ _ _)) = pure True-    isClosure _ (Exp (Thunk _ _)) = pure True-    isClosure acc _               = pure acc+    isClosure _ (Exp (Fun _ _) _)   = pure True+    isClosure _ (Exp (Thunk _ _) _) = pure True+    isClosure acc _                 = pure acc      -- Assign any changed vars, then loop.     replaceByAssign end as (Return (Call _ _ (Var f') as')) | f == f' = do@@ -486,24 +572,27 @@      -- Assign an expression to a variable, unless that expression happens to     -- be the variable itself.-    assignUnlessEqual (v, (Var v')) (next, final) | v == v' =-      (next, final)-    assignUnlessEqual (v, x) (next, final) | any (x `contains`) args =-      (Assign (NewVar False (newName v)) x . next,-       Assign (LhsExp (Var v)) (Var $ newName v) final)-                                  | otherwise =-      (Assign (LhsExp (Var v)) x . next, final)+    assignUnlessEqual (v, (Var v')) (next, final)+      | v == v' =+        (next, final)+    assignUnlessEqual (v, x) (next, final)+      | any (x `contains`) args =+        (Assign (NewVar False (newName v)) x . next,+         Assign (LhsExp False (Var v)) (Var $ newName v) final)+      | otherwise =+        (Assign (LhsExp False (Var v)) x . next, final)     -    newName (Internal (Name n mmod) _) =-      Internal (Name (' ':n) mmod) ""+    newName (Internal (Name n mmod) _ _) =+      Internal (Name (BS.cons ' ' n) mmod) "" True     newName n =       n          contains (Var v) var          = v == var     contains (Lit _) _            = False+    contains (JSLit _) _          = False     contains (Not x) var          = x `contains` var     contains (BinOp _ a b) var    = a `contains` var || b `contains` var-    contains (Fun _ _ _) _        = False+    contains (Fun _ _) _          = False     contains (Call _ _ f' xs) var = f' `contains` var||any (`contains` var) xs     contains (Index a i) var      = a `contains` var || i `contains` var     contains (Arr xs) var         = any (`contains` var) xs@@ -513,3 +602,161 @@     contains (Thunk _ _) _        = False tailLoopify _ fun = do   return fun++-- | Inline a tailcalled function @f@ when:+--+--   * @f@ does not refer to itself; and+--   * @f@ is defined immediately before its call site.+--     (@let f = ... in tailcall f@)+--+--   Should be called *after* 'tailLoopify' but *before* trampoline for best+--   effect.+inlineShortJumpTailcall :: JSTrav ast => ast -> TravM ast+inlineShortJumpTailcall ast = do+    mapJS (const True) return inl ast+  where+    inl stm@(Assign (NewVar _ f) (Fun as b) tc)+      | Just (f', as') <- getTailcallInfo tc, f == f' = do+        occs <- occurrences (const True) (isEqualTo f) b+        case (occs, zipAssign (map (NewVar True) as) as' b) of+          (Never, Just b') -> return b'+          _                -> return stm+    inl stm =+      return stm+    isEqualTo v' (Exp (Var v) _) = v == v'+    isEqualTo _ _                = False++-- | Extract the function being called and its argument list from a+--   @Tailcall (Call ...)@ or @Return (Call ...)@, provided that the call is+--   completely saturated.+getTailcallInfo :: Stm -> Maybe (Var, [Exp])+getTailcallInfo (Tailcall (Call 0 _ (Var f) as)) = Just (f, as)+getTailcallInfo (Return (Call 0 _ (Var f) as))   = Just (f, as)+getTailcallInfo _                                = Nothing++-- | Assign several variables, before executing a statement.+zipAssign :: [LHS] -> [Exp] -> Stm -> Maybe Stm+zipAssign l r final+  | length l == length r = Just $ go l r+  | otherwise            = Nothing+  where+    go (v:vs) (x:xs) = Assign v x (go vs xs)+    go [] []         = final+    go _ _           = error "zipAssign: different number of lhs and rhs!"++-- | Eliminate evaluation of vars that are guaranteed not to be thunks.+--   Mainly useful in 'topLevelInline'.+unevalLits :: JSTrav ast => ast -> TravM ast+unevalLits ast = do+    lits <- foldJS (\_ _ -> True) gatherLits S.empty ast+    mapJS (const True) pure (pure . removeUpdate (`S.member` lits)) ast+  where+    gatherLits s (Stm (Assign (NewVar _ v) rhs _) _)+      | definitelyNotThunk rhs         = pure $ S.insert v s+      | Var v' <- rhs, v' `S.member` s = pure $ S.insert v s+    gatherLits s _                     = pure s++-- | Inline calls to JS @eval@, @__set@, @__get@ and @__has@ and apply+--   functions for "Haste.Foreign".+inlineJSPrimitives :: JSTrav ast => ast -> TravM ast+inlineJSPrimitives =+    inlineFuns >=> optimizeThunks+  where+    inlineFuns = mapJS (const True) (return . inl) return+    inl ex@(Call _ (Fast _) (Var (Foreign fn)) args) =+      case (fn, args) of+        ("eval", [Lit (LStr s)]) -> JSLit s+        ("__app0", [f])          -> Call 0 (Fast False) f []+        ("__app1", f:xs)         -> Call 0 (Fast False) f xs+        ("__app2", f:xs)         -> Call 0 (Fast False) f xs+        ("__app3", f:xs)         -> Call 0 (Fast False) f xs+        ("__app4", f:xs)         -> Call 0 (Fast False) f xs+        ("__app5", f:xs)         -> Call 0 (Fast False) f xs+        ("__get", [o, k])        -> Index o k+        ("__set", [o, k, v])     -> AssignEx (Index o k) v+        ("__has", [o, k])        -> BinOp Neq (Index o k) (JSLit "undefined")+        _                        -> ex+    inl ex =+      ex++-- | Turn all assignments of the form @var v1 = e ; exp@ into @exp[v1/e]@,+--   provided that @v1@ is not used as a known location and @e@ is either a+--   variable or a non-string literal.+--   Also does not inline vars into lambdas.+--   Should go before 'tailLoopify'.+assignToSubst :: JSTrav ast => ast -> TravM ast+assignToSubst ast = do+    mapJS (const True) return inl ast+  where+    inl stm@(Assign (NewVar _ v) x next) | not (isKnownLoc v) = do+      case x of+        (Var _)        -> do+          -- TODO: this can be replaced by a map (to replace) and a count of+          --       remaining occurrences of v (fold)+          (c, stm') <- replaceExWithCount (isSafeForInlining) (Var v) x next+          (c', _) <- replaceExWithCount (pure True) (Var v) x next+          if c == c'+            then return stm' -- No occurrences inside lambda+            else return stm+        (Lit (LStr _)) -> return stm+        (Lit _)        -> replaceEx (pure True) (Var v) x next+        _              -> return stm+    inl stm = do+      return stm++-- | Perform various trivially correct local inlinings:+--   var x = e; return [0, e] (boxing at the end of a thunk/function)+--    => [0, e]+--   thunk(x) where x is non-computing and non-recursive+--     => x+smallStepInline :: JSTrav ast => ast -> TravM ast+smallStepInline ast = do+    mapJS (const True) return inl ast+  where+    inl (Assign (NewVar _ v) ex (Return (Arr [l@(Lit _), Var v'])))+      | v == v' =+        return (Return (Arr [l, ex]))+    inl (Assign (NewVar _ v) ex (ThunkRet (Arr [l@(Lit _), Var v'])))+      | v == v' =+        return (ThunkRet (Arr [l, ex]))+    -- Unpack thunks which don't provide actual laziness.+    inl (Assign lhs@(NewVar _ v) t@(Thunk _ _) next)+      | Just ex <- fromThunkEx t, safeToUnThunk ex = do+        case ex of+          Thunk _ _ -> return $ Assign lhs ex next+          _         -> Assign lhs ex <$> eliminateEvalOf v next+    -- Merge @a = eval(a) ; a = eval(a)@ into a single eval.+    inl stm@(Assign (LhsExp _ (Var v1)) (Eval (Var v1')) next)+      | v1 == v1' = do+        case next of+          stm'@(Assign (LhsExp _ (Var v2)) (Eval (Var v2')) _)+            | v1 == v2 && v1' == v2' -> do+              return stm'+          _ -> do+            return stm+    inl stm =+      return stm++-- | Eliminate elimination of a variable. Only use when  *absolutely certain*+--   that the variable can never be a thunk.+eliminateEvalOf :: JSTrav ast => Var -> ast -> TravM ast+eliminateEvalOf v ast = mapJS (const True) return elim ast+  where+    elim (Assign (LhsExp _ (Var v')) (Eval (Var v'')) next)+      | v' == v'' && v == v' =+        return next+    elim stm =+        return stm++-- | Is the given expression safe to extract from a thunk?+--   An expression is safe to unthunk iff evaluating it will not cause+--   computation to take place or a variable do be dereferenced.+safeToUnThunk :: Exp -> Bool+safeToUnThunk ex =+  case ex of+    Lit _      -> True+    JSLit l    -> isJSFunDecl l+    Fun _ _    -> True+    Thunk _ _  -> True+    Arr arr    -> all safeToUnThunk arr+    _          -> False
src/Data/JSTarget/PP.hs view
@@ -13,8 +13,8 @@ import Control.Applicative import qualified Data.Map as M import qualified Data.ByteString.Lazy as BS-import Data.JSTarget.AST (AST (..), Name (..), JumpTable, Lbl, Stm)-import Data.JSTarget.Traversal (JSTrav)+import Data.ByteString (ByteString)+import Data.JSTarget.AST (Name (..)) import Data.ByteString.Builder  -- | Pretty-printing options@@ -39,15 +39,14 @@ newtype PP a = PP {unPP :: PPOpts                         -> IndentLvl                         -> NameSupply-                        -> JumpTable                         -> Builder                         -> (NameSupply, Builder, a)}  instance Monad PP where-  PP m >>= f = PP $ \opts indentlvl ns js b ->-    case m opts indentlvl ns js b of-      (ns', b', x) -> unPP (f x) opts indentlvl ns' js b'-  return x = PP $ \_ _ ns _ b -> (ns, b, x)+  PP m >>= f = PP $ \opts indentlvl ns b ->+    case m opts indentlvl ns b of+      (ns', b', x) -> unPP (f x) opts indentlvl ns' b'+  return x = PP $ \_ _ ns b -> (ns, b, x)  instance Applicative PP where   pure  = return@@ -81,38 +80,34 @@  -- | Generate the final name for a variable. finalNameFor :: Name -> PP FinalName-finalNameFor n = PP $ \_ _ ns@(nextN, m) _ b ->+finalNameFor n = PP $ \_ _ ns@(nextN, m) b ->   case M.lookup n m of     Just n' -> (ns, b, n')     _       -> ((succ nextN, M.insert n nextN m), b, nextN) --- | Look up a shared reference.-lookupLabel :: Lbl -> PP Stm-lookupLabel lbl = PP $ \_ _ ns js b -> (ns, b, js M.! lbl)- -- | Returns the value of the given pretty-printer option. getOpt :: (PPOpts -> a) -> PP a-getOpt f = PP $ \opts _ ns _ b -> (ns, b, f opts)+getOpt f = PP $ \opts _ ns b -> (ns, b, f opts)  -- | Runs the given printer iff the specifiet option is True. whenOpt :: (PPOpts -> Bool) -> PP () -> PP () whenOpt f p = getOpt f >>= \x -> when x p  -- | Pretty print an AST.-pretty :: (JSTrav a, Pretty a) => PPOpts -> AST a -> BS.ByteString-pretty opts (AST ast js) =-  case runPP opts js (pp ast) of+pretty :: Pretty a => PPOpts -> a -> BS.ByteString+pretty opts ast =+  case runPP opts (pp ast) of     (b, _) -> toLazyByteString b  -- | Run a pretty printer.-runPP :: PPOpts -> JumpTable -> PP a -> (Builder, a)-runPP opts js p =-  case unPP p opts 0 emptyNS js mempty of+runPP :: PPOpts -> PP a -> (Builder, a)+runPP opts p =+  case unPP p opts 0 emptyNS mempty of     (_, b, x) -> (b, x)  -- | Pretty-print a program and return the final name for its entry point.-prettyProg :: Pretty a => PPOpts -> Name -> AST a -> (Builder, Builder)-prettyProg opts mainSym (AST ast js) = runPP opts js $ do+prettyProg :: Pretty a => PPOpts -> Name -> a -> (Builder, Builder)+prettyProg opts mainSym ast = runPP opts $ do   pp ast   buildFinalName <$> finalNameFor mainSym @@ -141,7 +136,9 @@   put :: a -> PP ()  instance Buildable Builder where-  put x = PP $ \_ _ ns _ b -> (ns, b <> x, ())+  put x = PP $ \_ _ ns b -> (ns, b <> x, ())+instance Buildable ByteString where+  put = put . byteString instance Buildable String where   put = put . stringUtf8 instance Buildable Char where@@ -161,7 +158,7 @@  -- | Emit indentation up to the current level. ind :: PP ()-ind = PP $ \opts indentlvl ns _ b ->+ind = PP $ \opts indentlvl ns b ->   (ns, foldl' (<>) b (replicate indentlvl (indentStr opts)), ())  -- | A space character.
src/Data/JSTarget/Print.hs view
@@ -1,35 +1,39 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE FlexibleInstances, GADTs, OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances, GADTs, OverloadedStrings, CPP #-} module Data.JSTarget.Print () where import Prelude hiding (LT, GT) import Data.JSTarget.AST import Data.JSTarget.Op import Data.JSTarget.PP as PP import Data.ByteString.Builder-import Data.Monoid import Control.Monad import Data.Char import Numeric (showHex)+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.UTF8 as BS  instance Pretty Var where   pp (Foreign name) =-    put $ stringUtf8 name-  pp (Internal name comment) = do+    put name+  pp (Internal name@(Name _ _) comment _) = do     pp name     doComment <- getOpt nameComments-    when (doComment && not (null comment)) $-      put $ "/* " <> stringUtf8 comment <> " */"+    when doComment $ do+      when (not $ BS.null comment) $ do+        put $ byteString "/* "+        put comment+        put $ byteString " */"  instance Pretty Name where   pp name = finalNameFor name >>= put . buildFinalName  instance Pretty LHS where-  pp (NewVar _ v) = "var " .+. pp v-  pp (LhsExp ex)  = pp ex+  pp (NewVar _ v)  = "var " .+. pp v+  pp (LhsExp _ ex) = pp ex  instance Pretty Lit where   pp (LNum d)  = put d-  pp (LStr s)  = "\"" .+. put (fixQuotes s) .+. "\""+  pp (LStr s)  = "\"" .+. put (fixQuotes $ BS.toString s) .+. "\""     where       fixQuotes ('\\':xs) = "\\\\" ++ fixQuotes xs       fixQuotes ('"':xs)  = '\\':'"'  : fixQuotes xs@@ -37,9 +41,9 @@       fixQuotes ('\r':xs) = '\\':'r'  : fixQuotes xs       fixQuotes ('\n':xs) = '\\':'n' : fixQuotes xs       fixQuotes (x:xs)-        | ord x <= 127 = x : fixQuotes xs-        | otherwise    = toHex x ++ fixQuotes xs-      fixQuotes _             = []+        | ord x <= 127    = x : fixQuotes xs+        | otherwise       = toHex x ++ fixQuotes xs+      fixQuotes _         = []   pp (LBool b) = put b   pp (LInt n)  = put n   pp (LNull)   = "null"@@ -70,7 +74,9 @@     pp v   pp (Lit l) =     pp l-  pp (Not ex) =+  pp (JSLit l) =+    put l+  pp (Not ex) = do     case neg ex of       Just ex' -> pp ex'       _ -> if expPrec (Not ex) > expPrec ex@@ -80,32 +86,37 @@     case norm bop of       BinOp op a b -> opParens op a b       ex           -> pp ex-  pp (Fun mname args body) = do-      "function" .+. lambdaname .+. "(" .+. ppList sep args .+. "){" .+. newl-      indent $ pp body-      ind .+. "}"-    where-      lambdaname = maybe "" (\n -> " " .+. pp n) mname+  pp (Fun args body) = do+    "function(" .+. ppList sep args .+. "){" .+. newl+    indent $ pp body+    ind .+. "}"   pp (Call _ call f args) = do       case call of         Normal True  -> "B(" .+. normalCall .+. ")"         Normal False -> normalCall         Fast True    -> "B(" .+. fastCall .+. ")"         Fast False   -> fastCall-        Method m  -> pp f .+. put ('.':m) .+. "(" .+. ppList sep args .+. ")"+        Method m     ->+          pp f .+. put (BS.cons '.' m) .+. "(" .+. ppList sep args .+. ")"     where       normalCall = "A(" .+. pp f .+. ",[" .+. ppList sep args .+. "])"       fastCall = ppCallFun f .+. "(" .+. ppList sep args .+. ")"-      ppCallFun fun@(Fun _ _ _) = "(" .+. pp fun .+. ")"-      ppCallFun fun             = pp fun-  pp (Index arr ix) = do-    pp arr .+. "[" .+. pp ix .+. "]"+      ppCallFun fun@(Fun _ _) = "(" .+. pp fun .+. ")"+      ppCallFun fun           = pp fun+  pp e@(Index arr ix) = do+    if expPrec e > expPrec arr+       then "(" .+. pp arr .+. ")"+       else pp arr+    "[" .+. pp ix .+. "]"   pp (Arr exs) = do     "[" .+. ppList sep exs .+. "]"   pp (AssignEx l r) = do     pp l .+. sp .+. "=" .+. sp .+. pp r-  pp (IfEx c th el) = do-    pp c .+. sp .+. "?" .+. sp .+. pp th .+. sp .+. ":" .+. sp .+. pp el+  pp e@(IfEx c th el) = do+    if expPrec e > expPrec c+       then "(" .+. pp c .+. ")"+       else pp c+    sp .+. "?" .+. sp .+. pp th .+. sp .+. ":" .+. sp .+. pp el   pp (Eval x) = do     "E(" .+. pp x .+. ")"   pp (Thunk True x) = do@@ -134,15 +145,15 @@ finalStm :: Stm -> PP Stm finalStm s =   case s of-    Assign _ _ s'         -> finalStm s'-    Case _ _ _ (Shared l) -> lookupLabel l >>= finalStm-    Forever s'            -> finalStm s'-    _                     -> return s+    Assign _ _ s'   -> finalStm s'+    Case _ _ _ next -> finalStm next+    Forever s'      -> finalStm s'+    _               -> return s  instance Pretty Stm where-  pp (Case cond def alts (Shared nextRef)) = do+  pp (Case cond def alts next) = do     prettyCase cond def alts-    lookupLabel nextRef >>= pp+    pp next   pp (Forever stm) = do     line "while(1){"     indent $ pp stm@@ -153,17 +164,13 @@         line (pp ex .+. ";") >> pp next       NewVar _ _ ->         ppAssigns s-      LhsExp _ ->+      LhsExp _ _ ->         line (pp lhs .+. sp .+. "=" .+. sp .+. pp ex .+. ";") >> pp next   pp (Return ex) = do     line $ "return " .+. pp ex .+. ";"   pp (Cont) = do     line "continue;"-  pp (Jump _) = do-    -- Jumps are essentially fallthroughs which keep track of their-    -- continuation to make analysis and optimization easier.-    return ()-  pp (NullRet) = do+  pp (Stop) = do     return ()   pp (Tailcall call) = do     line $ "return new F(function(){return " .+. pp call .+. ";});"@@ -183,11 +190,11 @@ prettyCase :: Exp -> Stm -> [Alt] -> PP () prettyCase cond def [(con, branch)] = do   case (def, branch) of-    (_, NullRet) -> do+    (_, Stop) -> do       line $ "if(" .+. pp (neg' (test con)) .+. "){"       indent $ pp def       line "}"-    (NullRet, _) -> do+    (Stop, _) -> do       line $ "if(" .+. pp (test con) .+. "){"       indent $ pp branch       line "}"
src/Data/JSTarget/Traversal.hs view
@@ -1,36 +1,18 @@-{-# LANGUAGE FlexibleInstances, TupleSections, PatternGuards #-}+{-# LANGUAGE FlexibleInstances, TupleSections, PatternGuards, BangPatterns #-} -- | Generic traversal of JSTarget AST types. module Data.JSTarget.Traversal where import Control.Applicative import Control.Monad+import Control.Monad.Identity import Data.JSTarget.AST-import Data.Map as M ((!), insert)  -- | AST nodes we'd like to fold and map over.-data ASTNode = Exp !Exp | Stm !Stm | Label !Lbl--newtype TravM a = T (JumpTable -> (JumpTable, a))-instance Monad TravM where-  return x  = T $ \js -> (js, x)-  T m >>= f = T $ \js ->-    case m js of-      (js', x) | T f' <- f x -> f' js'--instance Applicative TravM where-  pure  = return-  (<*>) = ap--instance Functor TravM where-  fmap f (T m) = T $ \js -> fmap f (m js)--runTravM :: TravM a -> JumpTable -> AST a-runTravM (T f) js = case f js of (js', x) -> AST x js'+data ASTNode = Exp !Exp !Bool | Stm !Stm !Bool | Shared !Stm -getRef :: Lbl -> TravM Stm-getRef lbl = T $ \js -> (js, js M.! lbl)+type TravM a = Identity a -putRef :: Lbl -> Stm -> TravM ()-putRef lbl stm = T $ \js -> (M.insert lbl stm js, ())+runTravM :: TravM a -> a+runTravM = runIdentity  class Show ast => JSTrav ast where   -- | Bottom up transform over an AST.@@ -59,6 +41,7 @@ mapJS tr fe fs ast =     snd <$> foldMapJS (const tr) (const' fe) (const' fs) () ast   where+    {-# INLINE const' #-}     const' f _ x = ((),) <$> f x  instance JSTrav a => JSTrav [a] where@@ -73,200 +56,177 @@   foldJS tr f acc ast = foldM (foldJS tr f) acc ast  instance JSTrav Exp where-  foldMapJS tr fe fs acc ast = do-      (acc', x) <- if tr acc (Exp ast)-                     then do-                       case ast of-                         v@(Var _)      -> do-                           pure (acc, v)-                         l@(Lit _)      -> do-                           pure (acc, l)-                         Not ex         -> do-                           fmap Not <$> mapEx acc ex-                         BinOp op a b   -> do-                           (acc', a') <- mapEx acc a-                           (acc'', b') <- mapEx acc' b-                           return (acc'', BinOp op a' b')-                         Fun nam vs stm -> do-                           fmap (Fun nam vs) <$> foldMapJS tr fe fs acc stm-                         Call ar c f xs -> do-                           (acc', f') <- mapEx acc f-                           (acc'', xs') <- foldMapJS tr fe fs acc' xs-                           return (acc'', Call ar c f' xs')-                         Index arr ix   -> do-                           (acc', arr') <- mapEx acc arr-                           (acc'', ix') <- mapEx acc' ix-                           return (acc'', Index arr' ix')-                         Arr exs        -> do-                           fmap Arr <$> foldMapJS tr fe fs acc exs-                         AssignEx l r   -> do-                           (acc', l') <- mapEx acc l-                           (acc'', r') <- mapEx acc' r-                           return (acc'', AssignEx l' r')-                         IfEx c th el   -> do-                           (acc', c') <- mapEx acc c-                           (acc'', th') <- mapEx acc' th-                           (acc''', el') <- mapEx acc'' el-                           return (acc''', IfEx c' th' el')-                         Eval x         -> do-                           fmap Eval <$> mapEx acc x-                         Thunk upd x    -> do-                           fmap (Thunk upd) <$> foldMapJS tr fe fs acc x-                     else do-                       return (acc, ast)-      fe acc' x+  foldMapJS tr fe fs = go     where-      mapEx = foldMapJS tr fe fs+      go acc ast+        | tr acc $! Exp ast False = do+          (acc', x) <- do+            case ast of+              v@(Var _)      -> pure (acc, v)+              l@(Lit _)      -> pure (acc, l)+              l@(JSLit _)    -> pure (acc, l)+              Not ex         -> fmap Not <$> go acc ex+              BinOp op a b   -> do+                (acc', a') <- go acc a+                (acc'', b') <- go acc' b+                return (acc'', BinOp op a' b')+              Fun vs stm     -> fmap (Fun vs) <$> foldMapJS tr fe fs acc stm+              Call ar c f xs -> do+                (acc', f') <- go acc f+                (acc'', xs') <- foldMapJS tr fe fs acc' xs+                return (acc'', Call ar c f' xs')+              Index arr ix   -> do+                (acc', arr') <- go acc arr+                (acc'', ix') <- go acc' ix+                return (acc'', Index arr' ix')+              Arr exs        -> fmap Arr <$> foldMapJS tr fe fs acc exs+              AssignEx l r   -> do+                (acc', l') <- go acc l+                (acc'', r') <- go acc' r+                return (acc'', AssignEx l' r')+              IfEx c th el   -> do+                (acc', c') <- go acc c+                (acc'', th') <- if tr acc (Exp th True)+                                  then go acc' th+                                  else return (acc', th)+                (acc''', el') <- if tr acc (Exp el True)+                                   then go acc'' el+                                   else return (acc'', el)+                return (acc''', IfEx c' th' el')+              Eval x         -> fmap Eval <$> go acc x+              Thunk upd x    -> fmap (Thunk upd) <$> foldMapJS tr fe fs acc x+          fe acc' x+        | otherwise = do+          fe acc ast   -  foldJS tr f acc ast = do-    let expast = Exp ast-    acc' <- if tr acc expast-              then do-                case ast of-                  Var _         -> do-                    return acc-                  Lit _         -> do-                    return acc-                  Not ex        -> do-                    foldJS tr f acc ex-                  BinOp _ a b  -> do-                    acc' <- foldJS tr f acc a-                    foldJS tr f acc' b-                  Fun _ _ stm    -> do-                    foldJS tr f acc stm-                  Call _ _ fun xs -> do-                    acc' <- foldJS tr f acc fun-                    foldJS tr f acc' xs-                  Index arr ix  -> do-                    acc' <- foldJS tr f acc arr-                    foldJS tr f acc' ix-                  Arr exs       -> do-                    foldJS tr f acc exs-                  AssignEx l r  -> do-                    acc' <- foldJS tr f acc l-                    foldJS tr f acc' r-                  IfEx c th el  -> do-                    acc' <- foldJS tr f acc c-                    acc'' <- foldJS tr f acc' th-                    foldJS tr f acc'' el-                  Eval ex       -> do-                    foldJS tr f acc ex-                  Thunk upd stm -> do-                    foldJS tr f acc stm-              else do-                return acc-    f acc' expast+  foldJS tr f = go+    where+      go acc ast+        | tr acc $! expast = do+          flip f expast =<< do+            case ast of+              Var _           -> return acc+              Lit _           -> return acc+              JSLit _         -> return acc+              Not ex          -> go acc ex+              BinOp _ a b     -> go acc a >>= flip go b+              Fun _ stm       -> foldJS tr f acc stm+              Call _ _ fun xs -> go acc fun >>= flip (foldJS tr f) xs+              Index arr ix    -> go acc arr >>= flip go ix+              Arr exs         -> foldJS tr f acc exs+              AssignEx l r    -> go acc l >>= flip go r+              IfEx c th el    -> do+                acc' <- go acc c+                acc'' <- if tr acc $! Exp th True+                           then go acc' th+                           else return acc'+                if tr acc $! Exp th True+                  then go acc'' el+                  else return acc''+              Eval ex         -> go acc ex+              Thunk _upd stm  -> foldJS tr f acc stm+        | otherwise =+          f acc expast+        where !expast = Exp ast False  instance JSTrav Stm where-  foldMapJS tr fe fs acc ast = do-      (acc', x) <- if tr acc (Stm ast)-                     then do-                       case ast of-                         Case ex def alts next -> do-                           (acc', ex') <- foldMapJS tr fe fs acc ex-                           (acc'', def') <- foldMapJS tr fe fs acc' def-                           (acc''', alts') <- foldMapJS tr fe fs acc'' alts-                           (acc'''', next') <- foldMapJS tr fe fs acc''' next-                           return (acc'''', Case ex' def' alts' next')-                         Forever stm -> do-                           fmap Forever <$> foldMapJS tr fe fs acc stm-                         Assign lhs ex next -> do-                           (acc', lhs') <- foldMapJS tr fe fs acc lhs-                           (acc'', ex') <- foldMapJS tr fe fs acc' ex-                           (acc''', next') <- foldMapJS tr fe fs acc'' next-                           return (acc''', Assign lhs' ex' next')-                         Return ex -> do-                           fmap Return <$> foldMapJS tr fe fs acc ex-                         Cont -> do-                           return (acc, Cont)-                         Jump stm -> do-                           fmap Jump <$> foldMapJS tr fe fs acc stm-                         NullRet -> do-                           return (acc, NullRet)-                         Tailcall ex -> do-                           fmap Tailcall <$> foldMapJS tr fe fs acc ex-                         ThunkRet ex -> do-                           fmap ThunkRet <$> foldMapJS tr fe fs acc ex-                     else do-                       return (acc, ast)-      fs acc' x+  foldMapJS tr fe fs = go+    where+      go acc ast+        | tr acc $! Stm ast False = do+          (acc', x) <- do+            case ast of+              Case ex def as nxt -> do+                (acc1, ex') <- foldMapJS tr fe fs acc ex+                (acc2, def') <- go acc1 def+                (acc3, as') <- foldMapJS tr fe fs acc2 as+                (acc4, nxt') <- if tr acc $! Shared nxt+                                  then go acc3 nxt+                                  else return (acc3, nxt)+                return (acc4, Case ex' def' as' nxt')+              Assign lhs ex next -> do+                (acc', lhs') <- foldMapJS tr fe fs acc lhs+                (acc'', ex') <- foldMapJS tr fe fs acc' ex+                (acc''', next') <- go acc'' next+                return (acc''', Assign lhs' ex' next')+              Forever stm        -> fmap Forever <$> go acc stm+              Return ex          -> fmap Return <$> foldMapJS tr fe fs acc ex+              Cont               -> return (acc, ast)+              Stop               -> return (acc, ast)+              Tailcall ex        -> fmap Tailcall <$> foldMapJS tr fe fs acc ex+              ThunkRet ex        -> fmap ThunkRet <$> foldMapJS tr fe fs acc ex+          fs acc' x+        | otherwise = do+          fs acc ast -  foldJS tr f acc ast = do-    let stmast = Stm ast-    acc' <- if tr acc stmast-              then do-                case ast of-                  Case ex def alts next -> do-                    acc' <- foldJS tr f acc ex-                    acc'' <- foldJS tr f acc' def-                    acc''' <- foldJS tr f acc'' alts-                    foldJS tr f acc''' next-                  Forever stm -> do-                    foldJS tr f acc stm-                  Assign lhs ex next -> do-                    acc' <- foldJS tr f acc lhs-                    acc'' <- foldJS tr f acc' ex-                    foldJS tr f acc'' next-                  Return ex -> do-                    foldJS tr f acc ex-                  Cont -> do-                    return acc-                  Jump j -> do-                    foldJS tr f acc j-                  NullRet -> do-                    return acc-                  Tailcall ex -> do-                    foldJS tr f acc ex-                  ThunkRet ex -> do-                    foldJS tr f acc ex-              else do-                return acc-    f acc' stmast+  foldJS tr f = go+    where+      go acc ast+        | tr acc stmast = do+          flip f stmast =<< do+            case ast of+              Case ex def as nxt -> do+                acc' <- foldJS tr f acc ex >>= flip go def+                acc'' <- foldJS tr f acc' as+                if tr acc $! Shared nxt+                  then go acc'' nxt+                  else return acc''+              Assign lhs ex next -> do+                foldJS tr f acc lhs >>= flip (foldJS tr f) ex >>= flip go next+              Forever stm        -> foldJS tr f acc stm+              Return ex          -> foldJS tr f acc ex+              Cont               -> return acc+              Stop               -> return acc+              Tailcall ex        -> foldJS tr f acc ex+              ThunkRet ex        -> foldJS tr f acc ex+        | otherwise =+          f acc stmast+        where !stmast = Stm ast False  instance JSTrav (Exp, Stm) where   foldMapJS tr fe fs acc (ex, stm) = do-    (acc', stm') <- foldMapJS tr fe fs acc stm-    (acc'', ex') <- foldMapJS tr fe fs acc' ex+    (acc', stm') <- if tr acc (Stm stm True)+                      then foldMapJS tr fe fs acc stm+                      else return (acc, stm)+    (acc'', ex') <- if tr acc (Exp ex True)+                      then foldMapJS tr fe fs acc' ex+                      else return (acc', ex)     return (acc'', (ex', stm'))   foldJS tr f acc (ex, stm) = do-    acc' <- foldJS tr f acc stm-    foldJS tr f acc' ex+    acc' <- if tr acc (Stm stm True)+              then foldJS tr f acc stm+              else return acc+    if tr acc (Exp ex True)+      then foldJS tr f acc' ex+      else return acc'  instance JSTrav LHS where-  foldMapJS _ _ _ acc lhs@(NewVar _ _) = return (acc, lhs)-  foldMapJS t fe fs a (LhsExp ex)      = fmap LhsExp <$> foldMapJS t fe fs a ex-  foldJS _ _ acc (NewVar _ _)  = return acc-  foldJS tr f acc (LhsExp ex)  = foldJS tr f acc ex--instance JSTrav a => JSTrav (Shared a) where-  foldMapJS tr fe fs acc sh@(Shared lbl) = do-    if (tr acc (Label lbl)) -      then do-        stm <- getRef lbl-        (acc', stm') <- foldMapJS tr fe fs acc stm-        putRef lbl stm'-        return (acc', sh)-      else do-        return (acc, sh)-  foldJS tr f acc (Shared lbl) = do-    if (tr acc (Label lbl))-      then getRef lbl >>= foldJS tr f acc >>= \acc' -> f acc' (Label lbl)-      else f acc (Label lbl)+  foldMapJS _ _ _ acc lhs@(NewVar _ _) =+    return (acc, lhs)+  foldMapJS t fe fs a (LhsExp r ex) =+    fmap (LhsExp r) <$> foldMapJS t fe fs a ex+  foldJS _ _ acc (NewVar _ _)    = return acc+  foldJS tr f acc (LhsExp _ ex)  = foldJS tr f acc ex  -- | Returns the final statement of a line of statements. finalStm :: Stm -> TravM Stm finalStm = go   where-    go (Case _ _ _ (Shared next)) = getRef next >>= go-    go (Forever s)                = go s-    go (Assign _ _ next)          = go next-    go (Jump (Shared next))       = getRef next >>= go-    go s@(Return _)               = return s-    go (Cont)                     = return Cont-    go (NullRet)                  = return NullRet-    go s@(Tailcall _)             = return s-    go s@(ThunkRet _)             = return s+    go (Case _ _ _ next) = go next+    go (Forever s)       = go s+    go (Assign _ _ next) = go next+    go s                 = return s +-- | Replace the final statement of the given AST with a new one, but only+--   if matches the given predicate.+replaceFinalStm :: Stm -> (Stm -> Bool) -> Stm -> TravM Stm+replaceFinalStm new p = go+  where+    go (Case c d as next) = Case c d as <$> go next+    go (Forever s)        = Forever <$> go s+    go (Assign l r next)  = Assign l r <$> go next+    go s                  = return $ if p s then new else s+ -- | Returns statement's returned expression, if any. finalExp :: Stm -> TravM (Maybe Exp) finalExp stm = do@@ -280,31 +240,49 @@   (.&.) :: a -> a -> a  instance Pred (a -> b -> Bool) where+  {-# INLINE (.|.) #-}+  {-# INLINE (.&.) #-}   p .|. q = \a b -> p a b || q a b   p .&. q = \a b -> p a b && q a b  instance Pred (a -> Bool) where+  {-# INLINE (.|.) #-}+  {-# INLINE (.&.) #-}   p .|. q = \a -> p a || q a   p .&. q = \a -> p a && q a -isShared :: ASTNode -> Bool-isShared (Label _) = True-isShared _         = False- -- | Thunks and explicit lambdas count as lambda abstractions.+{-# INLINE isLambda #-} isLambda :: ASTNode -> Bool-isLambda (Exp (Fun _ _ _)) = True-isLambda (Exp (Thunk _ _)) = True-isLambda _                 = False+isLambda (Exp (Fun _ _) _)   = True+isLambda (Exp (Thunk _ _) _) = True+isLambda _                   = False -isJump :: ASTNode -> Bool-isJump (Stm (Jump _)) = True-isJump _              = False+{-# INLINE isLoop #-}+isLoop :: ASTNode -> Bool+isLoop (Stm (Forever _) _) = True+isLoop _                   = False +{-# INLINE isConditional #-}+isConditional :: ASTNode -> Bool+isConditional (Exp _ cond) = cond+isConditional (Stm _ cond) = cond+isConditional _            = False++{-# INLINE isShared #-}+isShared :: ASTNode -> Bool+isShared (Shared _) = True+isShared _          = False++{-# INLINE isSafeForInlining #-}+isSafeForInlining :: ASTNode -> Bool+isSafeForInlining = not <$> isLambda .|. isLoop .|. isShared+ -- | Counts occurrences. Use ints or something for a more exact count. data Occs = Never | Once | Lots deriving (Eq, Show)  instance Ord Occs where+  {-# INLINE compare #-}   compare Never Once = Prelude.LT   compare Never Lots = Prelude.LT   compare Once  Lots = Prelude.LT@@ -324,7 +302,35 @@   x * Once  = x   _ * _     = Lots +  Never - _ = Never+  x - Never = x+  Once - _  = Never+  Lots - _  = Lots+   abs = id    signum Never = Never   signum _     = Once++-- | Replace all occurrences of an expression, without entering shared code+--   paths. IO ordering is preserved even when entering lambdas thanks to+--   State# RealWorld.+replaceEx :: JSTrav ast => (ASTNode -> Bool) -> Exp -> Exp -> ast -> TravM ast+replaceEx trav old new =+  mapJS trav (\x -> if x == old then pure new else pure x) pure++-- | Replace all occurrences of an expression, without entering shared code+--   paths. IO ordering is preserved even when entering lambdas thanks to+--   State# RealWorld.+replaceExWithCount :: JSTrav ast+                   => (ASTNode -> Bool) -- ^ Which nodes to enter?+                   -> Exp               -- ^ Expression to replace.+                   -> Exp               -- ^ Replacement expression.+                   -> ast               -- ^ AST to perform replacement on.+                   -> TravM (Int, ast)  -- ^ New AST + count of replacements.+replaceExWithCount trav old new ast =+    foldMapJS (const trav) rep (\count x -> return (count, x)) 0 ast+  where+    rep count ex+      | ex == old = return (count+1, new)+      | otherwise = return (count, ex)
src/Haste/Builtins.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | Various functions generated as builtins module Haste.Builtins (toBuiltin) where import GhcPlugins as P
src/Haste/CodeGen.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TupleSections, PatternGuards, CPP #-}+{-# LANGUAGE TupleSections, PatternGuards, CPP, OverloadedStrings #-} module Haste.CodeGen (generate) where -- Misc. stuff import Control.Applicative@@ -9,32 +9,19 @@ import Data.Char import Data.List (partition, foldl') import Data.Maybe (isJust)-#if __GLASGOW_HASKELL__ >= 707-import qualified Data.ByteString.UTF8 as B-#endif+import qualified Data.ByteString as BS+import qualified Data.ByteString.UTF8 as BS import qualified Data.Set as S import qualified Data.Map as M+ -- STG/GHC stuff-import StgSyn-import CoreSyn (AltCon (..))-import Var (Var, varType, varName)-import IdInfo (arityInfo, IdDetails (..))-import Id (Id, idInfo, idDetails, isLocalId, isGlobalId)-import Literal as L+import Language.Haskell.GHC.Simple as GHC import FastString (unpackFS)-import ForeignCall (CCallTarget (..), ForeignCall (..), CCallSpec (..))-import PrimOp (PrimCall (..))-import OccName-import DataCon-import Module-import Name-import Type-import TysPrim-import TyCon-import BasicTypes+ -- AST stuff import Data.JSTarget as J hiding ((.&.))-import Data.JSTarget.AST (Exp (..), Stm (..), LHS (..))+import Data.JSTarget.AST as J (Exp (..), Stm (..), LHS (..))+ -- General Haste stuff import Haste.Config import Haste.Monad@@ -42,47 +29,44 @@ import Haste.PrimOps import Haste.Builtins -generate :: Config-         -> String-         -> ModuleName-         -> [StgBinding]-         -> J.Module-generate cfg pkgid modname binds =-  Module {-      modPackageId   = pkgid,-      modName        = moduleNameString modname,+-- | Generate an abstract JS module from a codegen config and an STG module.+generate :: Config -> StgModule -> J.Module+generate cfg stg =+  J.Module {+      modPackageId   = BS.fromString $ GHC.modPackageKey stg,+      J.modName      = BS.fromString $ GHC.modName stg,       modDeps        = foldl' insDep M.empty theMod,       modDefs        = foldl' insFun M.empty theMod     }   where-    theMod = genAST cfg modname binds+    opt = if optimize cfg then optimizeFun else const id+    theMod = genAST cfg (GHC.modName stg) (modCompiledModule stg) -    insFun m (_, AST (Assign (NewVar _ (Internal v _)) body _) jumps) =-      M.insert v (AST body jumps) m+    insFun m (_, Assign (NewVar _ v@(Internal n _ _)) body _) =+      M.insert n (opt v body) m     insFun m _ =       m      -- TODO: perhaps do dependency-based linking for externals as well?-    insDep m (ds, AST (Assign (NewVar _ (Internal v _)) _ _) _) =+    insDep m (ds, Assign (NewVar _ (Internal v _ _)) _ _) =       M.insert v (S.delete v ds) m     insDep m _ =       m  -- | Generate JS AST for bindings.-genAST :: Config -> ModuleName -> [StgBinding] -> [(S.Set J.Name, AST Stm)]+genAST :: Config -> String -> [StgBinding] -> [(S.Set J.Name, Stm)] genAST cfg modname binds =     binds'   where     binds' =-      map (depsAndCode . genJS cfg myModName . uncurry (genBind True))+      map (depsAndCode . genJS cfg modname . uncurry (genBind True))       $ concatMap unRec       $ binds-    myModName = moduleNameString modname-    depsAndCode (_, ds, locs, stm) = (ds S.\\ locs, stm nullRet)+    depsAndCode (_, ds, locs, stm) = (ds S.\\ locs, stm stop)  -- | Check for builtins that should generate inlined code. At this point only --   w2i and i2w.-genInlinedBuiltin :: Var.Var -> [StgArg] -> JSGen Config (Maybe (AST Exp))+genInlinedBuiltin :: GHC.Var -> [StgArg] -> JSGen Config (Maybe Exp) genInlinedBuiltin f [x] = do     x' <- genArg x     return $ case (modname, varname) of@@ -93,14 +77,14 @@       _ ->         Nothing   where-    modname = moduleNameString . moduleName <$> nameModule_maybe (Var.varName f)-    varname = occNameString $ nameOccName $ Var.varName f+    modname = moduleNameString . moduleName <$> nameModule_maybe (GHC.varName f)+    varname = occNameString $ nameOccName $ GHC.varName f genInlinedBuiltin _ _ =   return Nothing   -- | Generate code for an STG expression.-genEx :: StgExpr -> JSGen Config (AST Exp)+genEx :: StgExpr -> JSGen Config Exp genEx (StgApp f xs) = do   mex <- genInlinedBuiltin f xs   case mex of@@ -119,7 +103,7 @@       (tag, stricts) <- genDataCon con       (args', stricts') <- genArgsPair $ zip args stricts       -- Don't create unboxed tuples with a single element.-      case (isUnboxedTupleCon con, args') of+      case (isNewtypeLikeCon con || isUnboxedTupleCon con, args') of         (True, [arg]) -> return $ evaluate arg (head stricts')         _             -> mkCon tag args' stricts'   where@@ -139,17 +123,14 @@   cfg <- getCfg   let theOp = case op of         StgPrimOp op' ->-          maybeTrace cfg (showOutputable cfg op') args' <$> genOp cfg op' args'+          maybeTrace cfg opstr args' <$> genOp cfg op' args'+          where opstr = BS.fromString $ showOutputable cfg op'         StgPrimCallOp (PrimCall f _) ->           Right $ maybeTrace cfg fs args' $ callForeign fs args'-          where fs = unpackFS f-#if __GLASGOW_HASKELL__ >= 706+          where fs = BS.fromString $ unpackFS f         StgFCallOp (CCall (CCallSpec (StaticTarget f _ _) _ _)) _t ->-#else-        StgFCallOp (CCall (CCallSpec (StaticTarget f _) _ _)) _t ->-#endif           Right $ maybeTrace cfg fs args' $ callForeign fs args'-          where fs = unpackFS f+          where fs = BS.fromString $ unpackFS f         _ ->           error $ "Tried to generate unsupported dynamic foreign call!"   case theOp of@@ -163,19 +144,21 @@   genEx ex genEx (StgCase ex _ _ bndr _ t alts) = do   genCase t ex bndr alts+  +#if __GLASGOW_HASKELL__ < 710+-- StgSCC is gone in 7.10, and StgTick has an argument less. genEx (StgSCC _ _ _ ex) = do   genEx ex genEx (StgTick _ _ ex) = do+#else+genEx (StgTick _ ex) = do+#endif   genEx ex-#if __GLASGOW_HASKELL__ >= 706+ genEx (StgLam _ _) = do   error "StgLam caught during code generation - that's impossible!"-#else-genEx (StgLam _ _ _) = do-  error "StgLam caught during code generation - that's impossible!"-#endif -- | Trace the given expression, if tracing is on.-maybeTrace :: Config -> String -> [AST Exp] -> AST Exp -> AST Exp+maybeTrace :: Config -> BS.ByteString -> [Exp] -> Exp -> Exp maybeTrace cfg msg args ex =   if tracePrimops cfg     then callForeign "__h_trace" [lit msg, array args, ex]@@ -204,14 +187,12 @@     addLocal v'   expr <- genRhs (isJust funsInRecGroup) rhs   popBind-  opt <- optimize `fmap` getCfg-  let expr' = if opt then optimizeFun v' expr else expr-  continue $ newVar True v' expr'+  continue $ newVar True v' expr genBind _ _ (StgRec _) =   error $  "genBind got recursive bindings!"  -- | Generate the RHS of a binding.-genRhs :: Bool -> StgRhs -> JSGen Config (AST Exp)+genRhs :: Bool -> StgRhs -> JSGen Config Exp genRhs recursive (StgRhsCon _ con args) = do   -- Constructors are never partially applied, and we have arguments, so this   -- is obviously a full application.@@ -227,10 +208,10 @@                then thunk' upd (body' $ thunkRet retExp)                else fun args' (body' $ ret retExp)   where-    thunk' _ (AST (Return l@(Lit _)) js) = AST l js-    thunk' Updatable stm                 = thunk True stm-    thunk' ReEntrant stm                 = thunk True stm-    thunk' SingleEntry stm               = thunk False stm+    thunk' _ (Return l@(J.Lit _)) = l+    thunk' Updatable stm          = thunk True stm+    thunk' ReEntrant stm          = thunk True stm+    thunk' SingleEntry stm        = thunk False stm  -- | Turn a recursive binding into a list of non-recursive ones, together with --   information about whether they came from a recursive group or not.@@ -245,51 +226,64 @@ --   Lists of vars are often accompanied by lists of strictness or usage --   annotations, which need to be filtered for types without representation --   as well.-genArgVarsPair :: [(Var.Var, a)] -> JSGen Config ([J.Var], [a])+genArgVarsPair :: [(GHC.Var, a)] -> JSGen Config ([J.Var], [a]) genArgVarsPair vps = do     vs' <- mapM genVar vs     return (vs', xs)   where     (vs, xs) = unzip $ filter (hasRepresentation . fst) vps -genCase :: AltType -> StgExpr -> Id -> [StgAlt] -> JSGen Config (AST Exp)+genCase :: AltType -> StgExpr -> Id -> [StgAlt] -> JSGen Config Exp genCase t ex scrut alts = do   ex' <- genEx ex+  -- Return a scrutinee variable and a function to replace all occurrences of+  -- the STG scrutinee with our JS one, if needed.+  (scrut', withScrutinee) <- case ex' of+    Eval (J.Var v) -> do+      continue $ assignVar (reorderableType scrut) v ex'+      oldscrut <- genVar scrut+      return (v, rename oldscrut v)+    _ -> do+      scrut' <- genVar scrut+      addLocal scrut'+      continue $ newVar (reorderableType scrut) scrut' ex'+      return (scrut', id)   -- If we have a unary unboxed tuple, we want to eliminate the case   -- entirely (modulo evaluation), so just generate the expression in the   -- sole alternative.-  case (isUnaryUnboxedTuple scrut, alts) of-    (True, [(_, as, _, expr)]) | [arg] <- filter hasRepresentation as -> do-      scrut' <- genVar scrut-      arg' <- genVar arg-      addLocal [scrut', arg']-      continue (newVar (reorderableType scrut) scrut' ex')-      continue (newVar (reorderableType scrut) arg' (varExp scrut'))-      genEx expr-    (True, _) -> do+  withScrutinee $ do+    case (isNewtypeLike scrut, isUnaryUnboxedTuple scrut, alts) of+      (_, True, [(_, as, _, expr)]) | [arg] <- filter hasRepresentation as -> do+        arg' <- genVar arg+        addLocal arg'+        continue $ newVar (reorderableType scrut) arg' (varExp scrut')+        genEx expr+      (True, _, [(_, [arg], _, expr)]) -> do+        arg' <- genVar arg+        addLocal arg'+        continue $ newVar (reorderableType scrut) arg' (varExp scrut')+        genEx expr+      (_, True, _) -> do         error "Case on unary unboxed tuple with more than one alt! WTF?!"-    _ -> do-      -- Generate scrutinee and result vars-      scrut' <- genVar scrut-      res <- genResultVar scrut-      addLocal [scrut', res]-      -- Split alts into default and general, and generate code for them-      let (defAlt, otherAlts) = splitAlts alts-          scrutinee = cmp (varExp scrut')-      (_, defAlt') <- genAlt scrut' res defAlt-      alts' <- mapM (genAlt scrut' res) otherAlts-      -- Use the ternary operator where possible.-      useSloppyTCE <- sloppyTCE `fmap` getCfg-      self <- if useSloppyTCE then return blackHoleVar else getCurrentBinding-      case tryTernary self scrutinee (varExp res) defAlt' alts' of-        Just ifEx -> do-          continue $ newVar (reorderableType scrut) scrut' ex'-          continue $ newVar True res ifEx-          return (varExp res)-        _ -> do-          continue $ newVar (reorderableType scrut) scrut' ex'-          continue $ case_ scrutinee defAlt' alts'-          return (varExp res)+      _ -> do+        -- Generate scrutinee and result vars+        res <- genResultVar scrut+        addLocal res+        -- Split alts into default and general, and generate code for them+        let (defAlt, otherAlts) = splitAlts alts+            scrutinee = cmp (varExp scrut')+        (_, defAlt') <- genAlt scrut' res defAlt+        alts' <- mapM (genAlt scrut' res) otherAlts+        -- Use the ternary operator where possible.+        useSloppyTCE <- sloppyTCE `fmap` getCfg+        self <- if useSloppyTCE then return blackHoleVar else getCurrentBinding+        case tryTernary self scrutinee (varExp res) defAlt' alts' of+          Just ifEx -> do+            continue $ newVar True res ifEx+            return (varExp res)+          _ -> do+            continue $ case_ scrutinee defAlt' alts'+            return (varExp res)   where     getTag s = index s (litN 0)     cmp = case t of@@ -311,7 +305,7 @@     isDefault (DEFAULT, _, _, _) = True     isDefault _                  = False -genAlt :: J.Var -> J.Var -> StgAlt -> JSGen Config (AST Exp,AST Stm -> AST Stm)+genAlt :: J.Var -> J.Var -> StgAlt -> JSGen Config (Exp, Stm -> Stm) genAlt scrut res (con, args, used, body) = do   construct <- case con of     -- undefined is intentional here - the first element is never touched.@@ -324,73 +318,71 @@   (_, body') <- isolate $ do     continue $ foldr (.) id binds     retEx <- genEx body-    continue $ newVar True res retEx+    continue $ newVar False res retEx   return $ construct body'   where     bindVar v ix = newVar True v (index (varExp scrut) (litN ix))  -- | Generate a result variable for the given scrutinee variable.-genResultVar :: Var.Var -> JSGen Config J.Var+genResultVar :: GHC.Var -> JSGen Config J.Var genResultVar v = do-  cfg <- getCfg-  (\mn -> toJSVar cfg mn v (Just "#result")) <$> getModName+  v' <- genVar v >>= getActualName+  case v' of+    Foreign n ->+      return $ Internal (Name (BS.append n "#result") Nothing) "" True+    Internal (Name n mp) _ _ ->+      return $ Internal (Name (BS.append n "#result") mp) "" True  -- | Generate a new variable and add a dependency on it to the function --   currently being generated.-genVar :: Var.Var -> JSGen Config J.Var+genVar :: GHC.Var -> JSGen Config J.Var genVar v | hasRepresentation v = do   case toBuiltin v of     Just v' -> return v'     _       -> do       mymod <- getModName-      cfg <- getCfg-      v' <- return $ toJSVar cfg mymod v Nothing+      v' <- getActualName $ toJSVar mymod v       dependOn v'       return v' genVar _ = do   return $ foreignVar "_"  -- | Extracts the name of a foreign var.-foreignName :: ForeignCall -> String-#if __GLASGOW_HASKELL__ >= 706+foreignName :: ForeignCall -> BS.ByteString foreignName (CCall (CCallSpec (StaticTarget str _ _) _ _)) =-  unpackFS str-#else-foreignName (CCall (CCallSpec (StaticTarget str _) _ _)) =-  unpackFS str-#endif+  BS.fromString $ unpackFS str foreignName _ =   error "Dynamic foreign calls not supported!" -toJSVar :: Config -> String -> Var.Var -> Maybe String -> J.Var-toJSVar c thisMod v msuffix =+-- | Turn a 'GHC.Var' into a 'J.Var'. Falls back to a default module name,+--   typically the name of the current module under compilation, if the given+--   Var isn't qualified.+toJSVar :: String -> GHC.Var -> J.Var+toJSVar thisMod v =   case idDetails v of     FCallId fc -> foreignVar (foreignName fc)     _       | isLocalId v && not hasMod ->-        internalVar (name (unique ++ suffix) (Just (myPkg, myMod))) ""+        internalVar (name unique (Just (myPkg, myMod))) ""       | isGlobalId v || hasMod ->-        internalVar (name (extern ++ suffix) (Just (myPkg, myMod))) comment+        internalVar (name extern (Just (myPkg, myMod))) comment     _ ->       error $ "Var is not local, global or external!"   where-    comment = myMod ++ "." ++ extern ++ suffix-    suffix = case msuffix of-               Just s -> s-               _      -> ""-    vname  = Var.varName v+    comment = BS.concat [myMod, ".", extern]+    vname  = GHC.varName v     hasMod = case nameModule_maybe vname of                Nothing -> False                _       -> True-    myMod =-      maybe thisMod (moduleNameString . moduleName) (nameModule_maybe vname)-    myPkg =-      maybe "main" (showOutputable c . modulePackageId) (nameModule_maybe vname)-    extern = occNameString $ nameOccName vname-    unique = show $ nameUnique vname+    myMod = BS.fromString $ maybe thisMod (moduleNameString . moduleName)+                                          (nameModule_maybe vname)+    myPkg = BS.fromString $ maybe "main" (pkgKeyString . modulePkgKey)+                                         (nameModule_maybe vname)+    extern = BS.fromString $ occNameString $ nameOccName vname+    unique = BS.fromString $ show $ nameUnique vname  -- | Generate an argument list. Any arguments of type State# a are filtered out.-genArgs :: [StgArg] -> JSGen Config [AST Exp]+genArgs :: [StgArg] -> JSGen Config [Exp] genArgs = mapM genArg . filter hasRep   where     hasRep (StgVarArg v) = hasRepresentation v@@ -399,7 +391,7 @@ -- | Filter out args without representation, along with their accompanying --   pair element, then generate code for the args. --   Se `genArgVarsPair` for more information.-genArgsPair :: [(StgArg, a)] -> JSGen Config ([AST Exp], [a])+genArgsPair :: [(StgArg, a)] -> JSGen Config ([Exp], [a]) genArgsPair aps = do     args' <- mapM genArg args     return (args', xs)@@ -410,7 +402,7 @@  -- | Returns True if the given var actually has a representation. --   Currently, only values of type State# a are considered representationless.-hasRepresentation :: Var.Var -> Bool+hasRepresentation :: GHC.Var -> Bool hasRepresentation = typeHasRep . varType  typeHasRep :: Type -> Bool@@ -419,18 +411,13 @@     Just (tc, _) -> tc /= statePrimTyCon     _            -> True -genArg :: StgArg -> JSGen Config (AST Exp)+genArg :: StgArg -> JSGen Config Exp genArg (StgVarArg v)  = varExp <$> genVar v genArg (StgLitArg l)  = genLit l-#if __GLASGOW_HASKELL__ < 706-genArg (StgTypeArg t) = do-  warn Normal "Generated StgTypeArg as 0!"-  return (litN 0)-#endif  -- | Generate code for data constructor creation. Returns a pair of --   (constructor, field strictness annotations).-genDataCon :: DataCon -> JSGen Config (AST Exp, [Bool])+genDataCon :: DataCon -> JSGen Config (Exp, [Bool]) genDataCon dc = do   if isEnumerationDataCon dc     then return (tagexp, [])@@ -445,7 +432,7 @@ -- --   IMPORTANT: remember to update the RTS if any changes are made to the --              constructor tag values!-genDataConTag :: DataCon -> AST Exp+genDataConTag :: DataCon -> Exp genDataConTag d =   case dataConNameModule d of     ("True", "GHC.Types")  -> lit True@@ -461,14 +448,10 @@   -- | Generate literals.-genLit :: L.Literal -> JSGen Config (AST Exp)+genLit :: GHC.Literal -> JSGen Config Exp genLit l = do   case l of-#if __GLASGOW_HASKELL__ >= 707-    MachStr s           -> return . lit $ B.toString s-#else-    MachStr s           -> return . lit $ unpackFS s-#endif+    MachStr s           -> return $ lit s     MachInt n       | n > 2147483647 ||         n < -2147483648 -> do warn Verbose (constFail "Int" n)@@ -485,7 +468,8 @@     MachNullAddr        -> return $ litN 0     MachInt64 n         -> return $ int64 n     LitInteger n _      -> return $ lit n-    MachLabel _ _ _     -> return $ lit ":(" -- Labels point to machine code - ignore!+    -- Labels point to machine code - ignore!+    MachLabel _ _ _     -> return $ litS ":("   where     constFail t n = t ++ " literal " ++ show n ++ " doesn't fit in 32 bits;"                     ++ " truncating!"@@ -501,7 +485,7 @@         hi = n `shiftR` 32  -- | Generate a function application.-genApp :: Var.Var -> [StgArg] -> JSGen Config (AST Exp)+genApp :: GHC.Var -> [StgArg] -> JSGen Config Exp genApp f xs = do     f' <- varExp <$> genVar f     xs' <- mapM genArg xs@@ -515,9 +499,31 @@ isEnumerationDataCon :: DataCon -> Bool isEnumerationDataCon = isEnumerationTyCon . dataConTyCon +-- | Does this data constructor create a newtype-like value? That is, a value+--   of a type with a single data constructor having a single argument?+isNewtypeLikeCon :: DataCon -> Bool+isNewtypeLikeCon c =+  case tyConDataCons (dataConTyCon c) of+    [_] -> case dataConRepArgTys c of+      [t] -> isUnLiftedType t+      _   -> False+    _   -> False++-- | Does this data constructor create a newtype-like value? That is, a value+--   of a type with a single data constructor having a single unlifted+--   argument?+isNewtypeLike :: GHC.Var -> Bool+isNewtypeLike v = maybe False id $ do+  (tycon, _) <- splitTyConApp_maybe (varType v)+  case tyConDataCons tycon of+    [c] -> case dataConRepArgTys c of+      [t] -> return (isUnLiftedType t)+      _   -> return False+    _   -> return False+ -- | Returns True if the given Var is an unboxed tuple with a single element --   after any represenationless elements are discarded.-isUnaryUnboxedTuple :: Var.Var -> Bool+isUnaryUnboxedTuple :: GHC.Var -> Bool isUnaryUnboxedTuple v = maybe False id $ do     (_, args) <- splitTyConApp_maybe t     case filter typeHasRep args of@@ -527,7 +533,7 @@     t = varType v  -- | Is it safe to reorder values of the given type?-reorderableType :: Var.Var -> Bool+reorderableType :: GHC.Var -> Bool reorderableType v =     case splitTyConApp_maybe t of       Just (_, args) -> length (filter typeHasRep args) == length args
src/Haste/Config.hs view
@@ -15,9 +15,10 @@  stdJSLibs :: [FilePath] stdJSLibs = map (jsDir </>)  [-    "rts.js", "stdlib.js", "MVar.js", "StableName.js", "Integer.js", "Int64.js",-    "md5.js", "array.js", "pointers.js", "cheap-unicode.js", "Canvas.js",-    "Handle.js", "Weak.js"+    "rts.js", "floatdecode.js", "stdlib.js", "jsstring.js", "endian.js",+    "MVar.js", "StableName.js", "Integer.js", "Int64.js", "md5.js", "array.js",+    "pointers.js", "cheap-unicode.js", "Canvas.js", "Handle.js", "Weak.js",+    "Foreign.js"   ]  debugLib :: FilePath@@ -52,68 +53,93 @@   "window.onload = " <> mainSym <> ";"  -- | Int op wrapper for strictly 32 bit (|0).-strictly32Bits :: AST Exp -> AST Exp+strictly32Bits :: Exp -> Exp strictly32Bits = flip (binOp BitOr) (litN 0)  -- | Safe Int multiplication.-safeMultiply :: AST Exp -> AST Exp -> AST Exp+safeMultiply :: Exp -> Exp -> Exp safeMultiply a b = callForeign "imul" [a, b]  -- | Fast but unsafe Int multiplication.-fastMultiply :: AST Exp -> AST Exp -> AST Exp+fastMultiply :: Exp -> Exp -> Exp fastMultiply = binOp Mul  -- | Compiler configuration. data Config = Config {     -- | Runtime files to dump into the JS blob.     rtsLibs :: [FilePath],+     -- | Path to directory where system jsmods are located.     libPaths :: [FilePath],+     -- | Write all jsmods to this path.     targetLibPath :: FilePath,+     -- | A function that takes the main symbol as its input and outputs the     --   code that starts the program.     appStart :: AppStart,+     -- | Wrap the program in its own namespace?     wrapProg :: Bool,+     -- | Options to the pretty printer.     ppOpts :: PPOpts,+     -- | A function that takes the name of the a target as its input and     --   outputs the name of the file its JS blob should be written to.     outFile :: Config -> String -> String,+     -- | Link the program?     performLink :: Bool,+     -- | A function to call on each Int arithmetic primop.-    wrapIntMath :: AST Exp -> AST Exp,+    wrapIntMath :: Exp -> Exp,+     -- | Operation to use for Int multiplication.-    multiplyIntOp :: AST Exp -> AST Exp -> AST Exp,+    multiplyIntOp :: Exp -> Exp -> Exp,+     -- | Be verbose about warnings, etc.?     verbose :: Bool,+     -- | Perform optimizations over the whole program at link time?     wholeProgramOpts :: Bool,+     -- | Allow the possibility that some tail recursion may not be optimized     --   in order to gain slightly smaller code?     sloppyTCE :: Bool,+     -- | Turn on run-time tracing of primops?     tracePrimops :: Bool,+     -- | Run the entire thing through Google Closure when done?     useGoogleClosure :: Maybe FilePath,+     -- | Extra flags for Google Closure to take?     useGoogleClosureFlags :: [String],+     -- | Any external Javascript to link into the JS bundle.     jsExternals :: [FilePath],+     -- | Produce a skeleton HTML file containing the program rather than a     --   JS file.     outputHTML :: Bool,+     -- | GHC DynFlags used for STG generation.     --   Currently only used for printing StgSyn values.     showOutputable :: forall a. Outputable a => a -> String,+     -- | Which module contains the program's main function?     --   Defaults to Just ("main", "Main")     mainMod :: Maybe (String, String),+     -- | Perform optimizations.     --   Defaults to True.-    optimize :: Bool+    optimize :: Bool,++    -- | Emit @"use strict";@ declaration. Does not affect minification, but+    --   *does* affect any external JS.+    --   Defaults to True.+    useStrict :: Bool   }  -- | Default compiler configuration.@@ -142,7 +168,8 @@     outputHTML       = False,     showOutputable   = const "No showOutputable defined in config!",     mainMod          = Just ("main", "Main"),-    optimize         = True+    optimize         = True,+    useStrict        = True   }  instance Default Config where
src/Haste/Environment.hs view
@@ -1,13 +1,14 @@ {-# LANGUAGE CPP #-} -- | Paths, host bitness and other environmental information about Haste. module Haste.Environment (-    hasteSysDir, jsmodSysDir, hasteInstSysDir, pkgSysDir, pkgSysLibDir, jsDir,-    hasteUserDir, jsmodUserDir, hasteInstUserDir, pkgUserDir, pkgUserLibDir,-    hostWordSize, ghcLibDir,-    ghcBinary, ghcPkgBinary,-    hasteBinary, hastePkgBinary, hasteInstHisBinary, hasteInstBinary,+    hasteSysDir, jsmodSysDir, hasteCabalSysDir, pkgSysDir, pkgSysLibDir, jsDir,+    hasteUserDir, jsmodUserDir, hasteCabalUserDir, pkgUserDir, pkgUserLibDir,+    hasteGhcLibDir,+    hostWordSize,+    ghcPkgBinary, ghcBinary,+    hasteBinary, hastePkgBinary, hasteInstHisBinary, hasteCabalBinary,     hasteCopyPkgBinary, closureCompiler, portableHaste,-    needsReboot, bootFile+    hasteNeedsReboot, hasteCabalNeedsReboot, bootFile   ) where import System.IO.Unsafe import Data.Bits@@ -15,10 +16,27 @@ import Control.Shell hiding (hClose) import Paths_haste_compiler import System.IO-import System.Environment (getExecutablePath)-import Haste.GHCPaths (ghcBinary, ghcPkgBinary, ghcLibDir)+import Haste.GHCPaths (ghcPkgBinary, ghcBinary) import Haste.Version+#if defined(PORTABLE)+import System.Environment (getExecutablePath)+#endif +-- | Directory to search for GHC settings. Always equal to 'hasteSysDir'+--   except on Windows where we rely on a working Haskell Platform for GCC and+--   other needed tools.+hasteGhcLibDir :: FilePath+#ifdef mingw32_HOST_OS+hasteGhcLibDir = unsafePerformIO $ do+  eout <- shell $ run ghcBinary ["--print-libdir"] ""+  case eout of+    Right out -> return $ init out+    _         -> error $ "This Haste build requires a working " +++                         "Haskell Platform install!"+#else+hasteGhcLibDir = hasteSysDir+#endif+ #if defined(PORTABLE) -- | Was Haste built in portable mode or not? portableHaste :: Bool@@ -68,33 +86,33 @@ jsmodSysDir :: FilePath jsmodSysDir = hasteSysDir </> "jsmods" --- | Base directory for haste-inst; system packages.-hasteInstSysDir :: FilePath-hasteInstSysDir = hasteSysDir </> "libraries"+-- | Base directory for haste-cabal; system packages.+hasteCabalSysDir :: FilePath+hasteCabalSysDir = hasteSysDir </> "libraries"  -- | Base directory for Haste's system libraries. pkgSysLibDir :: FilePath-pkgSysLibDir = hasteInstSysDir </> "lib"+pkgSysLibDir = hasteCabalSysDir </> "lib"  -- | Directory housing package information. pkgSysDir :: FilePath-pkgSysDir = hasteSysDir </> "packages"+pkgSysDir = hasteSysDir </> "package.conf.d"  -- | Directory where user .jsmod files are stored. jsmodUserDir :: FilePath jsmodUserDir = hasteUserDir </> "jsmods" --- | Base directory for haste-inst.-hasteInstUserDir :: FilePath-hasteInstUserDir = hasteUserDir </> "libraries"+-- | Base directory for haste-cabal.+hasteCabalUserDir :: FilePath+hasteCabalUserDir = hasteUserDir </> "libraries"  -- | Directory containing library information. pkgUserLibDir :: FilePath-pkgUserLibDir = hasteInstUserDir </> "lib"+pkgUserLibDir = hasteCabalUserDir </> "lib"  -- | Directory housing package information. pkgUserDir :: FilePath-pkgUserDir = hasteUserDir </> "packages"+pkgUserDir = hasteUserDir </> "package.conf.d"  -- | Host word size in bits. hostWordSize :: Int@@ -117,8 +135,8 @@ hasteCopyPkgBinary = hasteBinDir </> "haste-copy-pkg"  -- | Binary for haste-pkg.-hasteInstBinary :: FilePath-hasteInstBinary = hasteBinDir </> "haste-inst"+hasteCabalBinary :: FilePath+hasteCabalBinary = hasteBinDir </> "haste-cabal"  -- | Binary for haste-install-his. hasteInstHisBinary :: FilePath@@ -126,17 +144,25 @@  -- | JAR for Closure compiler. closureCompiler :: FilePath-closureCompiler = hasteBinDir </> "compiler.jar"+closureCompiler = hasteSysDir </> "compiler.jar"  -- | File indicating whether Haste is booted or not, and for which Haste+GHC --   version combo. bootFile :: FilePath-bootFile = hasteSysDir </> "booted"+bootFile = hasteUserDir </> "booted"  -- | Returns which parts of Haste need rebooting. A change in the boot file --   format triggers a full reboot.-needsReboot :: Bool-needsReboot = unsafePerformIO $ do+hasteNeedsReboot :: Bool+#ifdef PORTABLE+hasteNeedsReboot = False+#else+hasteNeedsReboot = hasteCabalNeedsReboot+#endif++-- | Does haste-cabal possibly needs rebooting?+hasteCabalNeedsReboot :: Bool+hasteCabalNeedsReboot = unsafePerformIO $ do   exists <- shell $ isFile bootFile   case exists of     Right True -> do
src/Haste/Errors.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | Functions for warning about and causing runtime errors. module Haste.Errors (runtimeError, warn, WarnLevel(..)) where import System.IO.Unsafe@@ -9,7 +10,7 @@ data WarnLevel = Normal | Verbose deriving Eq  -- | Produce a runtime error whenever this expression gets evaluated.-runtimeError :: String -> AST Exp+runtimeError :: String -> Exp runtimeError s = callForeign "die" [lit s]  -- | Produce a warning message. This function is horrible and should be
src/Haste/GHCPaths.hs view
@@ -1,6 +1,5 @@ -- | Paths to GHC binaries and directories. module Haste.GHCPaths where-import Control.Shell import System.IO.Unsafe import System.Directory (findExecutable) import Config (cProjectVersion)@@ -27,9 +26,3 @@     _       -> error $  "No appropriate ghc-pkg executable in search path!\n"                      ++ "Are you sure you have GHC " ++ cProjectVersion                      ++ " installed?"---- | GHC library directory.-ghcLibDir :: FilePath-ghcLibDir = unsafePerformIO $ do-  Right out <- shell $ run ghcBinary ["--print-libdir"] ""-  return $ init out
src/Haste/Linker.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses,+             FlexibleContexts, OverloadedStrings #-} module Haste.Linker (link) where import Haste.Config import Haste.Module@@ -6,9 +7,10 @@ import qualified Data.Set as S import Control.Monad.State.Strict import Control.Monad.Trans.Either-import Control.Applicative import Data.JSTarget import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString as BS+import Data.ByteString.UTF8 (toString, fromString) import Data.ByteString.Builder import Data.Monoid import System.IO (hPutStrLn, stderr)@@ -20,11 +22,12 @@ mainSym = name "main" (Just ("main", ":Main"))  -- | Link a program using the given config and input file name.-link :: Config -> String -> FilePath -> IO ()+link :: Config -> BS.ByteString -> FilePath -> IO () link cfg pkgid target = do-  let mainmod = case mainMod cfg of-                 Just mm -> mm-                 _       -> error "Haste.Linker.link called without main sym!"+  let mainmod =+        case mainMod cfg of+         Just (m, p) -> (fromString m, fromString p)+         _           -> error "Haste.Linker.link called without main sym!"   ds <- getAllDefs cfg (targetLibPath cfg : libPaths cfg) mainmod pkgid mainSym   let myDefs = if wholeProgramOpts cfg then topLevelInline ds else ds       (progText, myMain') = prettyProg (ppOpts cfg) mainSym myDefs@@ -40,13 +43,15 @@     assembleProg True extlibs rtslibs progText callMain launchApp =       stringUtf8 (unlines extlibs)       <> stringUtf8 "var hasteMain = function() {"+      <> (if useStrict cfg then stringUtf8 "\n\"use strict\";\n" else mempty)       <> stringUtf8 (unlines rtslibs)       <> progText       <> callMain       <> stringUtf8 "};\n"       <> launchApp     assembleProg _ extlibs rtslibs progText callMain launchApp =-      stringUtf8 (unlines extlibs)+      (if useStrict cfg then stringUtf8 "\"use strict\";\n" else mempty)+      <> stringUtf8 (unlines extlibs)       <> stringUtf8 (unlines rtslibs)       <> progText       <> stringUtf8 "\nvar hasteMain = function() {" <> callMain@@ -60,26 +65,26 @@ -- | Generate a sequence of all assignments needed to run Main.main. getAllDefs :: Config            -> [FilePath]-           -> (String, String)-           -> String+           -> (BS.ByteString, BS.ByteString)+           -> BS.ByteString            -> Name-           -> IO (AST Stm)+           -> IO Stm getAllDefs cfg libpaths mainmod pkgid mainsym =   runDep cfg mainmod $ addDef libpaths pkgid mainsym  data DepState = DepState {-    mainmod     :: !(String, String),-    defs        :: !(AST Stm -> AST Stm),+    mainModule  :: !(BS.ByteString, BS.ByteString),+    defs        :: !(Stm -> Stm),     alreadySeen :: !(S.Set Name),-    modules     :: !(M.Map String Module),+    modules     :: !(M.Map BS.ByteString Module),     infoLogger  :: String -> IO ()   }  type DepM a = EitherT Name (StateT DepState IO) a -initState :: Config -> (String, String) -> DepState+initState :: Config -> (BS.ByteString, BS.ByteString) -> DepState initState cfg m = DepState {-    mainmod     = m,+    mainModule  = m,     defs        = id,     alreadySeen = S.empty,     modules     = M.empty,@@ -93,14 +98,16 @@   liftIO $ infoLogger st s  -- | Run a dependency resolution computation.-runDep :: Config -> (String, String) -> DepM a -> IO (AST Stm)+runDep :: Show a => Config -> (BS.ByteString,BS.ByteString) -> DepM a -> IO Stm runDep cfg mainmod m = do     res <- runStateT (runEitherT m) (initState cfg mainmod)     case res of       (Right _, st) ->-        return $ defs st nullRet-      (Left (Name f (Just (p, m))), _) -> do-        error $ msg m f+        return $ defs st stop+      (Left (Name f (Just (_, modul))), _) -> do+        error $ msg (toString modul) (toString f)+      (r, _) -> do+        error $ "Impossible result in runDep: " ++ show r   where     msg "Main" "main" =       "Unable to locate a main function.\n" ++@@ -109,8 +116,8 @@       "for instance, `-main-is MyModule.myMain'.\n" ++       "If your progam intentionally has no main function," ++       " please use `--dont-link' to avoid this error."-    msg m f =-      "Unable to locate function `" ++ f ++ "' in module `" ++ m ++ "'!"+    msg s f =+      "Unable to locate function `" ++ f ++ "' in module `" ++ s ++ "'!"  -- | Return the module the given variable resides in. getModuleOf :: [FilePath] -> Name -> DepM Module@@ -120,7 +127,7 @@     Just ""         -> return foreignModule     Nothing         -> return foreignModule     Just ":Main"    -> do-      (p, m) <- mainmod `fmap` get+      (p, m) <- mainModule `fmap` get       getModuleOf libpaths (Name n (Just (p, m)))     Just m          -> do       mm <- getModule libpaths (maybe "main" id $ pkgOf v) m@@ -130,18 +137,18 @@  -- | Return the module at the given path, loading it into cache if it's not --   already there.-getModule :: [FilePath] -> String -> String -> DepM (Maybe Module)+getModule :: [FilePath] -> BS.ByteString -> BS.ByteString -> DepM (Maybe Module) getModule libpaths pkgid modname = do     st <- get     case M.lookup modname (modules st) of       Just m -> do         return $ Just m       _ -> do-        info $ "Linking " ++ modname+        info $ "Linking " ++ toString modname         go libpaths   where     go (libpath:lps) = do-      mm <- liftIO $ readModule libpath pkgid modname+      mm <- liftIO $ readModule libpath (toString pkgid) (toString modname)       case mm of         Just m -> do           st <- get@@ -154,7 +161,7 @@  -- | Add a new definition and its dependencies. If the given identifier has --   already been added, it's just ignored.-addDef :: [FilePath] -> String -> Name -> DepM ()+addDef :: [FilePath] -> BS.ByteString -> Name -> DepM () addDef libpaths pkgid v = do   st <- get   when (not $ v `S.member` alreadySeen st) $ do
src/Haste/Module.hs view
@@ -6,6 +6,7 @@ import Control.Applicative import Data.JSTarget import Data.Binary+import qualified Data.ByteString.UTF8 as BS  -- | The file extension to use for modules. jsmodExt :: Bool -> String@@ -27,7 +28,8 @@     mkdir True (takeDirectory path)     liftIO $ B.writeFile path (encode m)   where-    path = moduleFilePath basepath pkgid modname boot+    path =+      moduleFilePath basepath (BS.toString pkgid) (BS.toString modname) boot  -- | Read a module from file. If the module is not found at the specified path, --   libpath/path is tried instead. Returns Nothing is the module is not found
src/Haste/Monad.hs view
@@ -1,30 +1,42 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances,+             MultiParamTypeClasses #-} module Haste.Monad (     JSGen, genJS, dependOn, getModName, addLocal, getCfg, continue, isolate,-    pushBind, popBind, getCurrentBinding, whenCfg+    pushBind, popBind, getCurrentBinding, whenCfg, rename, getActualName   ) where-import Control.Monad.State+import Control.Monad.State.Strict import Data.JSTarget as J hiding (modName) import qualified Data.Set as S import Control.Applicative+import qualified Data.Map as M  data GenState cfg = GenState {-    deps         :: !(S.Set Name),-    locals       :: !(S.Set Name),-    continuation :: !(AST Stm -> AST Stm),+    -- | Dependencies in current context.+    deps         :: ![Name],+    -- | Local variables in current context.+    locals       :: ![Name],+    -- | The current continuation. Code is generated by appending to this+    --   continuation.+    continuation :: !(Stm -> Stm),+    -- | The stack of nested lambdas we've traversed.     bindStack    :: ![Var],+    -- | Name of the module being compiled.     modName      :: !String,-    config       :: !cfg+    -- | Current compiler configuration.+    config       :: !cfg,+    -- | Mapping of variable renamings.+    renames      :: !(M.Map Var Var)   }  initialState :: cfg -> GenState cfg initialState cfg = GenState {-    deps         = S.empty,-    locals       = S.empty,+    deps         = [],+    locals       = [],     continuation = id,     bindStack    = [],     modName      = "",-    config       = cfg+    config       = cfg,+    renames      = M.empty   }  newtype JSGen cfg a =@@ -38,20 +50,25 @@   addLocal :: a -> JSGen cfg ()  instance Dependency J.Name where+  {-# INLINE dependOn #-}   dependOn v = JSGen $ do     st <- get-    put st {deps = S.insert v (deps st)}+    put st {deps = v : deps st} +  {-# INLINE addLocal #-}   addLocal v = JSGen $ do     st <- get-    put st {locals = S.insert v (locals st)}+    put st {locals = v : locals st}  instance Dependency J.Var where-  dependOn (Foreign _)    = return ()-  dependOn (Internal n _) = dependOn n-  addLocal (Foreign _)    = return ()-  addLocal (Internal n _) = addLocal n+  {-# INLINE dependOn #-}+  dependOn (Foreign _)      = return ()+  dependOn (Internal n _ _) = dependOn n +  {-# INLINE addLocal #-}+  addLocal (Foreign _)      = return ()+  addLocal (Internal n _ _) = addLocal n+ instance Dependency a => Dependency [a] where   dependOn = mapM_ dependOn   addLocal = mapM_ addLocal@@ -63,11 +80,11 @@ genJS :: cfg         -- ^ Config to use for code generation.       -> String      -- ^ Name of the module being compiled.       -> JSGen cfg a -- ^ The code generation computation.-      -> (a, S.Set J.Name, S.Set J.Name, AST Stm -> AST Stm)+      -> (a, S.Set J.Name, S.Set J.Name, Stm -> Stm) genJS cfg myModName (JSGen gen) =   case runState gen (initialState cfg) {modName = myModName} of-    (a, GenState dependencies loc cont _ _ _) ->-      (a, dependencies, loc, cont)+    (a, GenState dependencies loc cont _ _ _ _) ->+      (a, S.fromList dependencies, S.fromList loc, cont)  getModName :: JSGen cfg String getModName = JSGen $ modName <$> get@@ -86,20 +103,27 @@ getCurrentBinding = JSGen $ fmap (head . bindStack) get  -- | Add a new continuation onto the current one.-continue :: (AST Stm -> AST Stm) -> JSGen cfg ()+continue :: (Stm -> Stm) -> JSGen cfg () continue cont = JSGen $ do   st <- get   put st {continuation = continuation st . cont}  -- | Run a GenJS computation in isolation, returning its results rather than --   writing them to the output stream. Dependencies and locals are still---   updated, however.-isolate :: JSGen cfg a -> JSGen cfg (a, AST Stm -> AST Stm)+--   updated, however, and any enclosing renames are still visible within+--   the isolated computation.+isolate :: JSGen cfg a -> JSGen cfg (a, Stm -> Stm) isolate gen = do   myMod <- getModName   cfg <- getCfg   b <- getCurrentBinding-  let (x, dep, loc, cont) = genJS cfg myMod (pushBind b >> gen)+  rns <- renames <$> JSGen get+  let (x, dep, loc, cont) = genJS cfg myMod $ do+        pushBind b+        JSGen $ do+          st <- get+          put st {renames = rns}+        gen   dependOn dep   addLocal loc   return (x, cont)@@ -111,3 +135,20 @@ whenCfg p act = do   cfg <- getCfg   when (p cfg) act++-- | Run a computation with the given renaming added to its context.+rename :: Var -> Var -> JSGen cfg a -> JSGen cfg a+rename from to m = do+  st <- JSGen get+  JSGen $ put st {renames = M.insert from to $ renames st}+  x <- m+  st' <- JSGen get+  JSGen $ put st' {renames = renames st}+  return x++-- | Get the actual name of a variable, recursing through multiple renamings+--   if necessary.+getActualName :: Var -> JSGen cfg Var+getActualName v = do+  rns <- renames <$> JSGen get+  maybe (return v) getActualName $ M.lookup v rns
src/Haste/Opts.hs view
@@ -40,6 +40,11 @@                                  performLink = False}) $            "Install .jsmod files into the user's library. " ++            "Implies --dont-link.",+    Option "" ["no-use-strict"]+           (NoArg $ \cfg -> cfg {useStrict = False}) $+           "Do not emit '\"use strict\";' declaration. Does not affect " +++           "minifier behavior, but *does* affect any external JS included " +++           "with --with-js.",     Option "" ["onexec"]            (NoArg $ \cfg -> cfg {appStart = startCustom "onexec"}) $            "Launch application immediately when the JS file is loaded. " ++@@ -57,9 +62,8 @@            "Enable all optimizations, safe and unsafe. Equivalent to " ++            "--opt-all --opt-unsafe-ints",     Option "" ["opt-minify"]-           (OptArg updateClosureCfg "PATH") $-           "Minify JS output using Google Closure compiler. " ++-           "Optionally, use the Closure compiler located at PATH.",+           (NoArg updateClosureCfg) $+           "Minify JS output using Google Closure compiler.",     Option "" ["opt-minify-flag"]            (ReqArg updateClosureFlags "FLAG") $            "Pass a flag to Closure. " ++@@ -98,6 +102,11 @@            (NoArg $ \cfg -> cfg {outputHTML = True}) $            "Write the JS output to an HTML file together with a simple " ++            "HTML skeleton.",+    Option "" ["own-namespace"]+           (NoArg $ \cfg -> cfg {wrapProg = True}) $+           "Wrap the whole program in a closure to avoid polluting the " +++           "global namespace. Incurs a performance hit, and makes " +++           "minification slightly less effective.",     Option "" ["start"]            (ReqArg (\start cfg -> cfg {appStart = startCustom start})                    "CODE") $@@ -106,6 +115,11 @@            "--start='$(\"foo\").onclick($HASTE_MAIN);' " ++            "will use jQuery to launch the application whenever the element " ++            "with the id \"foo\" is clicked.",+    Option "" ["output-jsflow"]+           (NoArg enableJSFlow) $+           "Output code for use with the JSFlow interpreter. Note that " +++           "this may leave your code crippled, since JSFlow doesn't " +++           "all of Haste's needs.",     Option "v" ["verbose"]            (NoArg $ \cfg -> cfg {verbose = True}) $            "Display even the most obnoxious warnings and messages.",@@ -161,14 +175,11 @@  -- | Enable all safe optimizations. optAllSafe :: Config -> Config-optAllSafe = enableWholeProgramOpts . updateClosureCfg Nothing+optAllSafe = enableWholeProgramOpts . updateClosureCfg  -- | Set the path to the Closure compiler.jar to use.-updateClosureCfg :: Maybe FilePath -> Config -> Config-updateClosureCfg (Just fp) cfg =-  cfg {useGoogleClosure = Just fp}-updateClosureCfg _ cfg =-  cfg {useGoogleClosure = Just closureCompiler}+updateClosureCfg :: Config -> Config+updateClosureCfg cfg = cfg {useGoogleClosure = Just closureCompiler}  -- | Add flags for Google Closure to use updateClosureFlags :: String -> Config -> Config@@ -178,6 +189,17 @@ -- | Enable optimizations over the entire program. enableWholeProgramOpts :: Config -> Config enableWholeProgramOpts cfg = cfg {wholeProgramOpts = True}++-- | Produce output for the JSFlow interpreter.+enableJSFlow :: Config -> Config+enableJSFlow cfg = cfg {+    rtsLibs = [libfile |+               libfile <- rtsLibs cfg,+               not $ any (`isSuffixOf` libfile) jsflowIncompatible] +++              [jsDir </> "jsflow.js"]+  }+  where+    jsflowIncompatible = ["floatdecode.js", "endian.js"]  -- | Save some space and performance by using degenerate implementations of --   the Unicode functions.
src/Haste/PrimOps.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, OverloadedStrings #-} module Haste.PrimOps (genOp) where import Prelude hiding (LT, GT) import PrimOp@@ -6,7 +6,7 @@ import Haste.Config  -- | Dummy State# RealWorld value for where one is needed.-defState :: AST Exp+defState :: Exp defState = litN 0  -- | Generate primops.@@ -15,7 +15,7 @@ --   on the evaluation operation in the RTS being able to handle plain values --   as though they were thunks. If this were to change, all those ops MUST --   be changed to return thunks!-genOp :: Config -> PrimOp -> [AST Exp] -> Either String (AST Exp)+genOp :: Config -> PrimOp -> [Exp] -> Either String (Exp) genOp cfg op xs =   case op of     -- negations@@ -59,9 +59,7 @@     -- FIXME: this is correct but slow!     IntMulMayOfloOp -> intMath $ Right $ multiplyIntOp cfg (xs !! 0) (xs !! 1)     IntQuotOp ->       callF "quot"-#if __GLASGOW_HASKELL__ >= 706     IntQuotRemOp ->    callF "quotRemI"-#endif     IntRemOp ->        bOp Mod -- JS % operator is actually rem, not mod!     IntAddCOp -> callF "addC"     IntSubCOp -> callF "subC"@@ -80,9 +78,7 @@     WordSubOp ->  wordMath $ bOp Sub     WordMulOp ->  wordMath $ callF "imul"     WordQuotOp -> callF "quot"-#if __GLASGOW_HASKELL__ >= 706     WordQuotRemOp -> callF "quotRemI"-#endif     WordRemOp ->  bOp Mod     AndOp ->      wordMath $ bOp BitAnd     OrOp ->       wordMath $ bOp BitOr@@ -154,8 +150,8 @@     ReadArrayOp -> Right $ index arr ix     WriteArrayOp -> Right $ assignEx (index arr ix) rhs       where (_arr:_ix:rhs:_) = xs-    SizeofArrayOp -> Right $ index (head xs) (lit "length")-    SizeofMutableArrayOp -> Right $ index (head xs) (lit "length")+    SizeofArrayOp -> Right $ index (head xs) (litS "length")+    SizeofMutableArrayOp -> Right $ index (head xs) (litS "length")     IndexArrayOp -> Right $ index arr ix     UnsafeFreezeArrayOp -> Right $ head xs     UnsafeThawArrayOp -> Right $ head xs@@ -204,8 +200,8 @@     WriteByteArrayOp_Float   -> writeArr xs "f32"     WriteByteArrayOp_Double  -> writeArr xs "f64"     -    SizeofByteArrayOp        -> Right $ index (head xs) (lit "byteLength")-    SizeofMutableByteArrayOp -> Right $ index (head xs) (lit "byteLength")+    SizeofByteArrayOp        -> Right $ index (head xs) (litS "byteLength")+    SizeofMutableByteArrayOp -> Right $ index (head xs) (litS "byteLength")     NewAlignedPinnedByteArrayOp_Char -> Right $ callForeign "newByteArr" [xs!!0]     UnsafeFreezeByteArrayOp  -> Right $ head xs     ByteArrayContents_Char   -> Right $ head xs@@ -267,7 +263,7 @@         Right $ binOp Sub (litN 0) $ callForeign "addrLT" [a, b]       where (a:b:_) = xs     Addr2IntOp             ->-        Right $ index x (lit "off")+        Right $ index x (litS "off")       where         (x:_) = xs @@ -324,23 +320,23 @@   where     (arr:ix:_) = xs     -    writeArr (a:i:rhs:_) elemtype =-      Right $ assignEx (index (index (index a (lit "v")) (lit elemtype)) i) rhs+    writeArr (a:i:rhs:_) etype =+      Right $ assignEx (index (index (index a (litS "v")) (litS etype)) i) rhs     writeArr _ _ =       error "writeArray primop with too few arguments!"      readArr (a:i:_) elemtype =-      Right $ index (index (index a (lit "v")) (lit elemtype)) i+      Right $ index (index (index a (litS "v")) (litS elemtype)) i     readArr _ _ =       error "writeArray primop with too few arguments!"      writeOffAddr (addr:off:rhs:_) etype esize =-      Right $ callForeign "writeOffAddr" [lit etype, litN esize, addr, off, rhs]+      Right $ callForeign "writeOffAddr" [litS etype,litN esize,addr,off,rhs]     writeOffAddr _ _ _ =       error "writeOffAddr primop with too few arguments!"          readOffAddr (addr:off:_) etype esize =-      Right $ callForeign "readOffAddr" [lit etype, litN esize, addr, off]+      Right $ callForeign "readOffAddr" [litS etype,litN esize,addr,off]     readOffAddr _ _ _ =       error "readOffAddr primop with too few arguments!" 
src/Haste/Version.hs view
@@ -2,19 +2,17 @@ module Haste.Version (     BootVer (..),     hasteVersion, intVersion, ghcVersion, bootVersion,-    showBootVersion, parseBootVersion+    showBootVersion, parseBootVersion,+    showVersion   ) where-import System.IO.Unsafe-import Control.Shell (shell, run) import Data.Version import Config (cProjectVersion)-import Haste.GHCPaths (ghcBinary) import Text.ParserCombinators.ReadP import Data.Maybe (listToMaybe)  -- | Current Haste version. hasteVersion :: Version-hasteVersion = Version [0, 4, 4, 4] []+hasteVersion = Version [0, 5, 0] []  -- | Current Haste version as an Int. The format of this version number is --   MAJOR*10 000 + MINOR*100 + MICRO.@@ -28,11 +26,7 @@ ghcVersion =     fst $ head $ filter (\(_,s) -> null s) parses   where-    parses = readP_to_S parseVersion . unsafePerformIO $ do-      res <- shell $ run ghcBinary ["--numeric-version"] ""-      case res of-        Right ver -> return $ init ver -- remove trailing newline-        _         -> return cProjectVersion+    parses = readP_to_S parseVersion cProjectVersion  -- | Haste + GHC version combo. bootVersion :: BootVer
− src/Main.hs
@@ -1,267 +0,0 @@-{-# LANGUAGE CPP #-}-module Main (main) where-import GHC-import HscMain-import Outputable (showPpr)-import DynFlags-import TidyPgm-import CorePrep-import CoreToStg-import StgSyn (StgBinding)-import HscTypes-import GhcMonad-import Module (packageIdString)-import System.Environment (getArgs)-import Control.Monad (when)-import Haste-import Haste.Args-import Haste.Opts-import Haste.Environment-import Haste.Version-import System.IO-import System.Exit (exitFailure)-import Data.Version-import Data.List-import qualified Control.Shell as Sh--logStr :: Config -> String -> IO ()-logStr cfg = when (verbose cfg) . hPutStrLn stderr--rebootMsg :: String-rebootMsg = "Haste needs to be rebooted; please run haste-boot"--printInfo :: IO ()-printInfo = do-  ghc <- runGhc (Just ghcLibDir) getSessionDynFlags-  putStrLn $ formatInfo $ compilerInfo ghc-  where-    formatInfo = ('[' :) . tail . unlines . (++ ["]"]) . map ((',' :) . show)---- | Check for arguments concerning version info and the like, and act on them.---   Return True if the compiler should run afterwards.-preArgs :: [String] -> IO Bool-preArgs args-  | "--numeric-version" `elem` args =-    putStrLn (showVersion ghcVersion) >> return False-  | "--info" `elem` args =-    printInfo >> return False-  | "--print-libdir" `elem` args =-    putStrLn ghcLibDir >> return False-  | "--version" `elem` args =-    putStrLn (showVersion hasteVersion) >> return False-  | "--supported-extensions" `elem` args =-    (putStrLn $ unlines $ supportedLanguagesAndExtensions) >> return False-  | "--supported-languages" `elem` args =-    (putStrLn $ unlines $ supportedLanguagesAndExtensions) >> return False-  | otherwise =-    return True--main :: IO ()-main = do-    initUserPkgDB-    args <- fmap (++ packageDBArgs) getArgs-    runCompiler <- preArgs args-    when (runCompiler) $ do-      if allSupported args-        then hasteMain args-        else callVanillaGHC args-  where-#if __GLASGOW_HASKELL__ >= 706-    packageDBArgs = ["-no-global-package-db",-                     "-no-user-package-db",-                     "-package-db " ++ pkgSysDir,-                     "-package-db " ++ pkgUserDir ]-#else-    packageDBArgs = ["-no-user-package-conf",-                     "-package-conf " ++ pkgSysDir]-#endif--- | Call vanilla GHC; used for boot files and the like.-callVanillaGHC :: [String] -> IO ()-callVanillaGHC args = do-  _ <- Sh.shell $ Sh.run_ ghcBinary (filter noHasteArgs args) ""-  return ()-  where-    noHasteArgs x =-      x /= "--libinstall" &&-      x /= "--unbooted"--initUserPkgDB :: IO ()-initUserPkgDB = do-  _ <- Sh.shell $ do-    pkgDirExists <- Sh.isDirectory pkgUserDir-    when (not pkgDirExists) $ do-      Sh.mkdir True pkgUserLibDir-      Sh.runInteractive ghcPkgBinary ["init", pkgUserDir]-  return ()---- | Run the compiler if everything's satisfactorily booted, otherwise whine---   and exit.-hasteMain :: [String] -> IO ()-hasteMain args-  | not needsReboot =-    compiler False ("-O2" : args)-  | otherwise = do-    if "--unbooted" `elem` args-      then compiler True (filter (/= "--unbooted") ("-O2" : args))-      else fail rebootMsg---- | Determine whether all given args are handled by Haste, or if we need to---   ship them off to vanilla GHC instead.-allSupported :: [String] -> Bool-allSupported args =-  and args'-  where-    args' = [not $ any (`isSuffixOf` a) someoneElsesProblems | a <- args]-    someoneElsesProblems = [".c", ".cmm"]---- | The main compiler driver.-compiler :: Bool -> [String] -> IO ()-compiler unbooted cmdargs = do-  let argRes = parseArgs (hasteOpts unbooted) helpHeader cmdargs-      usedGhcMode = if "-c" `elem` cmdargs then OneShot else CompManager--  case argRes of-    -- We got --help as an argument - display help and exit.-    Left help -> putStr help--    -- We got a config and a set of arguments for GHC; let's compile!-    Right (mkConfig, ghcargs) -> do-      let config = mkConfig def--      -- Parse static flags, but ignore profiling.-      (ghcargs', _) <- parseStaticFlags [noLoc a | a <- ghcargs, a /= "-prof"]--      runGhc (Just ghcLibDir) $ do-        -- Handle dynamic GHC flags. Make sure __HASTE__ is #defined.-        let hastever = "-D__HASTE__=" ++ show intVersion-            args = hastever : map unLoc ghcargs'-            justDie = const $ liftIO exitFailure-        dynflags <- getSessionDynFlags-        defaultCleanupHandler dynflags $ handleSourceError justDie $ do-          (dynflags', files, _) <- parseDynamicFlags dynflags (map noLoc args)-          _ <- setSessionDynFlags dynflags' {ghcLink = NoLink,-                                             ghcMode = usedGhcMode}--          -- Prepare and compile all needed targets.-          let files' = map unLoc files-              printErrorAndDie e = printException e >> liftIO exitFailure-          deps <- handleSourceError printErrorAndDie $ do-            ts <- mapM (flip guessTarget Nothing) files'-            setTargets ts-            _ <- load LoadAllTargets-            depanal [] False-          let cfg = fillLinkerConfig dynflags' config-          mapM_ (compile cfg dynflags') deps--          -- Link everything together into a .js file.-          when (performLink cfg) $ liftIO $ do-            flip mapM_ files' $ \file -> do-              let outfile = outFile cfg cfg file-              logStr cfg $ "Linking program " ++ outfile-#if __GLASGOW_HASKELL__ >= 706-              let pkgid = showPpr dynflags $ thisPackage dynflags'-#else-              let pkgid = showPpr $ thisPackage dynflags'-#endif-              link cfg pkgid file-              case useGoogleClosure cfg of-                Just clopath -> closurize cfg clopath outfile-                _            -> return ()-              when (outputHTML cfg) $ do-                res <- Sh.shell $ Sh.withCustomTempFile "." $ \tmp h -> do-                  prog <- Sh.file outfile-                  Sh.hPutStrLn h (htmlSkeleton outfile prog)-                  Sh.liftIO $ hClose h-                  Sh.mv tmp outfile-                case res of-                  Right () -> return ()-                  Left err -> error $ "Couldn't output HTML file: " ++ err---- | Produce an HTML skeleton with an embedded JS program.-htmlSkeleton :: FilePath -> String -> String-htmlSkeleton filename prog = concat [-  "<!DOCTYPE HTML>",-  "<html><head>",-  "<title>", filename , "</title>",-  "<meta charset=\"UTF-8\">",-  "<script type=\"text/javascript\">", prog, "</script>",-  "</head><body></body></html>"]---- | Do everything required to get a list of STG bindings out of a module.-prepare :: (GhcMonad m) => DynFlags -> ModSummary -> m ([StgBinding], ModuleName)-prepare dynflags theMod = do-  env <- getSession-  let name = moduleName $ ms_mod theMod-  pgm <- parseModule theMod-    >>= typecheckModule-    >>= desugarModule-    >>= liftIO . hscSimplify env . coreModule-    >>= liftIO . tidyProgram env-    >>= prepPgm env . fst-#if __GLASGOW_HASKELL__ >= 707-    >>= liftIO . coreToStg dynflags (ms_mod theMod)-#else-    >>= liftIO . coreToStg dynflags-#endif-  return (pgm, name)-  where-    prepPgm env tidy = liftIO $ do-#if __GLASGOW_HASKELL__ >= 706-      prepd <- corePrepPgm dynflags env (cg_binds tidy) (cg_tycons tidy)-#else-      prepd <- corePrepPgm dynflags (cg_binds tidy) (cg_tycons tidy)-#endif-      return prepd---- | Run Google Closure on a file.-closurize :: Config -> FilePath -> FilePath -> IO ()-closurize cfg cloPath f = do-  let arguments = useGoogleClosureFlags cfg-  logStr cfg $ "Running the Google Closure compiler on " ++ f ++ "..."-  let cloFile = f `Sh.addExtension` ".clo"-  res <- Sh.shell $ do-    str <- Sh.run "java"-      (["-jar", cloPath,-        "--compilation_level", "ADVANCED_OPTIMIZATIONS",-        "--jscomp_off", "globalThis", f]-       ++ arguments) ""-    Sh.file cloFile str :: Sh.Shell ()-    Sh.mv cloFile f-  case res of-    Left e  -> fail $ "Couldn't execute Google Closure compiler: " ++ e-    Right _ -> return ()---- | Compile a module into a .jsmod intermediate file.-compile :: (GhcMonad m) => Config -> DynFlags -> ModSummary -> m ()-compile cfg dynflags modSummary = do-    let boot = case ms_hsc_src modSummary of-                 HsBootFile -> True-                 _          -> False-    (pgm, name) <- prepare dynflags modSummary-#if __GLASGOW_HASKELL__ >= 706-    let pkgid = showPpr dynflags $ modulePackageId $ ms_mod modSummary-        cfg' = cfg {showOutputable = showPpr dynflags}-#else-    let pkgid = showPpr $ modulePackageId $ ms_mod modSummary-        cfg' = cfg {showOutputable = showPpr}-#endif-        theCode = generate cfg' pkgid name pgm-    liftIO $ logStr cfg $ "Compiling " ++ myName boot ++ " into " ++ targetpath-    liftIO $ writeModule targetpath theCode boot-  where-    myName False = moduleNameString $ moduleName $ ms_mod modSummary-    myName True = myName False ++ " [boot]"-    targetpath = targetLibPath cfg---- | Fill in linkage info, such as whether to link at all and what the program---   entry point is.-fillLinkerConfig :: DynFlags -> Config -> Config-fillLinkerConfig df cfg =-    cfg {-        mainMod = mainmod,-        performLink = maybe False (const $ performLink cfg) mainmod-      }-  where-    mainmod =-      Just (packageIdString $ modulePackageId (mainModIs df),-            moduleNameString $ moduleName (mainModIs df))
src/haste-boot.hs view
@@ -19,10 +19,10 @@ import Haste.Args import System.Console.GetOpt -#if __GLASGOW_HASKELL__ >= 708-baseDir = "base-ghc-7.8"+#if __GLASGOW_HASKELL__ >= 710+libDir = "ghc-7.10" #else-baseDir = "base-ghc-7.6"+libDir = "ghc-7.8" #endif  downloadFile :: String -> Shell BS.ByteString@@ -45,18 +45,32 @@     useLocalLibs          :: Bool,     tracePrimops          :: Bool,     forceBoot             :: Bool,-    populateSetupExeCache :: Bool+    populateSetupExeCache :: Bool,+    initialPortableBoot   :: Bool   }  defCfg :: Cfg+#ifdef PORTABLE defCfg = Cfg {-    getLibs = True,-    getClosure = True,-    useLocalLibs = False,-    tracePrimops = False,-    forceBoot = False,-    populateSetupExeCache = True+    getLibs               = True,+    getClosure            = False,+    useLocalLibs          = False,+    tracePrimops          = False,+    forceBoot             = False,+    populateSetupExeCache = True,+    initialPortableBoot   = False   }+#else+defCfg = Cfg {+    getLibs               = True,+    getClosure            = True,+    useLocalLibs          = False,+    tracePrimops          = False,+    forceBoot             = False,+    populateSetupExeCache = True,+    initialPortableBoot   = False+  }+#endif  devBoot :: Cfg -> Cfg devBoot cfg = cfg {@@ -66,42 +80,61 @@     populateSetupExeCache = False   } +setInitialPortableBoot :: Cfg -> Cfg+setInitialPortableBoot cfg = cfg {+    getLibs             = True,+    useLocalLibs        = True,+    forceBoot           = True,+    getClosure          = True,+    initialPortableBoot = True+  }+ specs :: [OptDescr (Cfg -> Cfg)] specs = [-    Option "" ["dev"]+#ifndef PORTABLE+      Option "" ["dev"]            (NoArg devBoot) $            "Boot Haste for development. Implies --force " ++-           "--local --no-closure --no-populate-setup-exe-cache",-    Option "" ["force"]+           "--local --no-closure --no-populate-setup-exe-cache"+    , Option "" ["force"]+#else+      Option "" ["force"]+#endif            (NoArg $ \cfg -> cfg {forceBoot = True}) $-           "Re-boot Haste even if already properly booted.",-    Option "" ["local"]+           "Re-boot Haste even if already properly booted."+    , Option "" ["initial"]+           (NoArg setInitialPortableBoot) $+           "Prepare boot files for binary distribution. Should only ever " +++           "be called by the release build scripts, never by users."+#ifndef PORTABLE+    , Option "" ["local"]            (NoArg $ \cfg -> cfg {useLocalLibs = True}) $            "Use libraries from source repository rather than " ++            "downloading a matching set from the Internet. " ++            "This is nearly always necessary when installing " ++            "Haste from Git rather than from Hackage. " ++            "When using --local, your current working directory " ++-           "must be the root of the Haste source tree.",-    Option "" ["no-closure"]+           "must be the root of the Haste source tree."+    , Option "" ["no-closure"]            (NoArg $ \cfg -> cfg {getClosure = False}) $            "Don't download Closure compiler. You won't be able " ++            "to use --opt-minify, unless you manually " ++-           "give hastec the path to compiler.jar.",-    Option "" ["no-libs"]+           "give hastec the path to compiler.jar."+    , Option "" ["no-libs"]            (NoArg $ \cfg -> cfg {getLibs = False}) $            "Don't install any libraries. This is probably not " ++-           "what you want.",-    Option "" ["no-populate-setup-exe-cache"]+           "what you want."+    , Option "" ["no-populate-setup-exe-cache"]            (NoArg $ \cfg -> cfg {populateSetupExeCache = False}) $            "Don't populate Cabal's setup-exe-cache. Speeds up boot, " ++            "but vill fail spectacularly unless your setup-exe-cache " ++-           "is already populated.",-    Option "" ["trace-primops"]+           "is already populated."+    , Option "" ["trace-primops"]            (NoArg $ \cfg -> cfg {tracePrimops = True}) $            "Build standard libs for tracing of primitive " ++            "operations. Only use if you're debugging the code " ++            "generator."+#endif   ]  hdr :: String@@ -113,7 +146,7 @@   case parseArgs specs hdr args of     Right (mkConfig, _) -> do       let cfg = mkConfig defCfg-      when (needsReboot || forceBoot cfg) $ do+      when (hasteNeedsReboot || hasteCabalNeedsReboot || forceBoot cfg) $ do         res <- shell $ if useLocalLibs cfg                          then bootHaste cfg "."                          else withTempDirectory "haste" $ bootHaste cfg@@ -128,23 +161,30 @@   removeBootFile <- isFile bootFile   when removeBootFile $ rm bootFile   when (getLibs cfg) $ do+    when (not $ useLocalLibs cfg) $ do+      fetchLibs tmpdir     when (populateSetupExeCache cfg) $ do       void $ run "cabal" ["update"] ""       void $ run "cabal" ["install", "-j", "populate-setup-exe-cache"] ""+      inDirectory "popcache" . void $ run "cabal" ["install", "-j"] ""+      void $ run "ghc-pkg" ["unregister", "haste-populate-configure"] ""       void $ run "ghc-pkg" ["unregister", "populate-setup-exe-cache"] ""-    when (not $ useLocalLibs cfg) $ do-      fetchLibs tmpdir-    mapM_ clearDir [hasteInstUserDir, jsmodUserDir, pkgUserDir,-                    hasteInstSysDir, jsmodSysDir, pkgSysDir]-    buildLibs cfg-    when (portableHaste) $ do++    when (not portableHaste || initialPortableBoot cfg) $ do+      mapM_ clearDir [hasteCabalUserDir, jsmodUserDir, pkgUserDir,+                      hasteCabalSysDir, jsmodSysDir, pkgSysDir]+      void $ run hastePkgBinary ["init", pkgSysDir] ""+      buildLibs cfg++    when (initialPortableBoot cfg) $ do       mapM_ relocate ["array", "bytestring", "containers", "data-default",                       "data-default-class", "data-default-instances-base",                       "data-default-instances-containers",                       "data-default-instances-dlist",                       "data-default-instances-old-locale",                       "deepseq", "dlist", "haste-lib", "integer-gmp",-                      "monads-tf", "old-locale", "transformers"]+                      "monads-tf", "old-locale", "transformers", "time"]+   when (getClosure cfg) $ do     installClosure   file bootFile (showBootVersion bootVersion)@@ -184,40 +224,46 @@     mkdir True $ pkgSysLibDir     cpDir "include" hasteSysDir     run_ hastePkgBinary ["update", "--global", "libraries" </> "rts.pkg"] ""-    +     inDirectory "libraries" $ do-      -- Install ghc-prim-      inDirectory "ghc-prim" $ do-        hasteInst ["configure", "--solver", "topdown"]-        hasteInst $ ["build", "--install-jsmods"] ++ ghcOpts-        run_ hasteInstHisBinary ["ghc-prim-0.3.0.0", "dist" </> "build"] ""-        run_ hastePkgBinary ["update", "--global", "packageconfig"] ""-      -      -- Install integer-gmp; double install shouldn't be needed anymore.-      run_ hasteCopyPkgBinary ["Cabal"] ""-      inDirectory "integer-gmp" $ do-        hasteInst ("install" : "--solver" : "topdown" : ghcOpts)-      -      -- Install base-      inDirectory baseDir $ do-        basever <- file "base.cabal" >>= return-          . dropWhile (not . isDigit)-          . head-          . filter (not . null)-          . filter (and . zipWith (==) "version")-          . lines-        hasteInst ["configure", "--solver", "topdown"]-        hasteInst $ ["build", "--install-jsmods"] ++ ghcOpts-        let base = "base-" ++ basever-            pkgdb = "--package-db=dist" </> "package.conf.inplace"-        run_ hasteInstHisBinary [base, "dist" </> "build"] ""-        run_ hasteCopyPkgBinary [base, pkgdb] ""-        forEachFile "include" $ \f -> cp f (hasteSysDir </> "include")-      -      -- Install array and haste-lib-      forM_ ["array", "haste-lib"] $ \pkg -> do-        inDirectory pkg $ hasteInst ("install" : ghcOpts)+      inDirectory libDir $ do+        -- Install ghc-prim+        inDirectory "ghc-prim" $ do+          hasteCabal ["configure", "--solver", "topdown"]+          hasteCabal $ ["build", "--install-jsmods"] ++ ghcOpts+          run_ hasteInstHisBinary ["ghc-prim-0.3.0.0", "dist" </> "build"] ""+          run_ hastePkgBinary ["update", "--global", "packageconfig"] "" +        -- Install integer-gmp; double install shouldn't be needed anymore.+        run_ hasteCopyPkgBinary ["Cabal"] ""+        inDirectory "integer-gmp" $ do+          hasteCabal ("install" : "--solver" : "topdown" : ghcOpts)++        -- Install base+        inDirectory "base" $ do+          basever <- file "base.cabal" >>= return+            . dropWhile (not . isDigit)+            . head+            . filter (not . null)+            . filter (and . zipWith (==) "version")+            . lines+          hasteCabal ["configure", "--solver", "topdown"]+          hasteCabal $ ["build", "--install-jsmods"] ++ ghcOpts+          let base = "base-" ++ basever+              pkgdb = "--package-db=dist" </> "package.conf.inplace"+          run_ hasteInstHisBinary [base, "dist" </> "build"] ""+          run_ hasteCopyPkgBinary [base, pkgdb] ""+          forEachFile "include" $ \f -> cp f (hasteSysDir </> "include")++        -- Install array+        inDirectory "array" $ hasteCabal ("install" : ghcOpts)++      -- Install haste-lib+      inDirectory "haste-lib" $ hasteCabal ("install" : ghcOpts)++      -- Install time+      inDirectory "time" $ hasteCabal ("install" : ghcOpts)+       -- Export monads-tf; it seems to be hidden by default       run_ hastePkgBinary ["expose", "monads-tf"] ""   where@@ -225,8 +271,8 @@         if tracePrimops cfg then ["--ghc-option=-debug"] else [],         ["--ghc-option=-DHASTE_HOST_WORD_SIZE_IN_BITS=" ++ show hostWordSize]       ]-    hasteInst args =-      run_ hasteInstBinary ("--install-global" : "--unbooted" : args) ""+    hasteCabal args =+      run_ hasteCabalBinary ("--install-global" : "--unbooted" : args) ""  relocate :: String -> Shell () relocate pkg = run_ hastePkgBinary ["relocate", pkg] ""
+ src/haste-cabal.hs view
@@ -0,0 +1,59 @@+-- | haste-cabal - Haste wrapper for cabal.+module Main where+import System.Environment+import System.Exit+import Haste.Environment+import Control.Shell+import Control.Monad (when)+import Data.List++type Match = (String -> Bool, [String] -> [String])++cabal :: [String] -> IO ()+cabal args = do+  res <- shell $ run_ "cabal" (hasteargs ++ args') ""+  case res of+    Left _ -> exitFailure+    _      -> exitSuccess+  where+    args' = [arg | arg <- args, arg /= "--install-global", arg /= "--global"]+    hasteargs+      | "update" `elem` args =+        []+      | "build" `elem` args =+        ["--with-ghc=" ++ hasteBinary]+      | otherwise =+        ["--with-compiler=" ++ hasteBinary,+         "--with-hc-pkg=" ++ hastePkgBinary,+         "--with-hsc2hs=hsc2hs",+         "-fhaste-cabal"] +++        if "--install-global" `elem` args || "--global" `elem` args+           then ["--prefix=" ++ hasteCabalSysDir,+                 "--package-db=" ++ pkgSysDir]+           else ["--prefix=" ++ hasteCabalUserDir,+                 "--package-db=" ++ pkgSysDir,+                 "--package-db=" ++ pkgUserDir]+++main :: IO ()+main = do+  as <- getArgs+  when (hasteCabalNeedsReboot && not ("--unbooted" `elem` as)) $ do+    putStrLn "WARNING: haste-cabal has not been properly booted."+    putStrLn "If you experience problems installing packages, or simply want to"+    putStrLn "get rid of this message, please run 'haste-boot'."++  if "update" `elem` as+    then do+      cabal as+    else do+      as <- return $ if "--install-jsmods" `elem` as || not ("build" `elem` as)+                       then libinstall : filter (/= "--install-jsmods") as+                       else as+      as <- return $ if "--unbooted" `elem` as+                       then unbooted : filter (/= "--unbooted") as+                       else as+      cabal as+  where+    libinstall = "--ghc-option=--libinstall"+    unbooted   = "--ghc-option=--unbooted"
src/haste-cat.hs view
@@ -1,3 +1,5 @@++{-# LANGUAGE OverloadedStrings #-} module Main where import System.Environment import Haste.Module@@ -6,7 +8,8 @@ import Data.JSTarget.PP import Data.Maybe import qualified Data.Map as M-import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BSL+import qualified Data.ByteString.Char8 as BS  main = do   as <- getArgs@@ -23,11 +26,11 @@   mapM_ printDef $ M.toList $ modDefs mod  printDef (name, def) = do-  putStrLn $ niceName name-  BS.putStrLn $ pretty debugPPOpts def+  BS.putStrLn $ niceName name+  BSL.putStrLn $ pretty debugPPOpts def   putStrLn ""  niceName (Name n (Just (pkg, m))) =-  pkg ++ ":" ++ m ++ "." ++ n+  BS.concat [pkg, ":", m, ".", n] niceName (Name n _) =   n
src/haste-copy-pkg.hs view
@@ -11,11 +11,7 @@ main = do   args <- getArgs   let (dbs, pkgs) = partition ("--package-db=" `isPrefixOf`) args-#if __GLASGOW_HASKELL__ < 706-      pkgdbs = map (("--package-conf" ++) . drop 12) dbs-#else       pkgdbs = dbs-#endif   if null args     then do       putStrLn "Usage: haste-copy-pkg [--package-db=foo.conf] <packages>"
− src/haste-inst.hs
@@ -1,47 +0,0 @@--- | haste-inst - Haste wrapper for cabal.-module Main where-import System.Environment-import System.Exit-import Haste.Environment-import Control.Shell-import Data.List--type Match = (String -> Bool, [String] -> [String])--cabal :: [String] -> IO ()-cabal args = do-  res <- shell $ run_ "cabal" (hasteargs ++ args') ""-  case res of-    Left _ -> exitFailure-    _      -> exitSuccess-  where-    args' = [arg | arg <- args, arg /= "--install-global", arg /= "--global"]-    hasteargs -      | "build" `elem` args =-        ["--with-ghc=" ++ hasteBinary]-      | otherwise =-        ["--with-compiler=" ++ hasteBinary,-         "--with-hc-pkg=" ++ hastePkgBinary,-         "--with-hsc2hs=hsc2hs",-         "-fhaste-inst"] ++-        if "--install-global" `elem` args || "--global" `elem` args-           then ["--prefix=" ++ hasteInstSysDir,-                 "--package-db=" ++ pkgSysDir]-           else ["--prefix=" ++ hasteInstUserDir,-                 "--package-db=" ++ pkgSysDir,-                 "--package-db=" ++ pkgUserDir]---main :: IO ()-main = do-  as <- getArgs-  as <- return $ if "--install-jsmods" `elem` as || not ("build" `elem` as)-                   then libinstall : filter (/= "--install-jsmods") as-                   else as-  as <- return $ if "--unbooted" `elem` as-                   then unbooted : filter (/= "--unbooted") as-                   else as-  cabal as-  where-    libinstall = "--ghc-option=--libinstall"-    unbooted   = "--ghc-option=--unbooted"
− src/haste-pkg.hs
@@ -1,72 +0,0 @@-{-# LANGUAGE CPP #-}--- | haste-pkg; wrapper for ghc-pkg.-module Main where-import Control.Monad-import System.Environment (getArgs)-import Haste.Environment-import Control.Shell-import System.Info (os)--main = shell $ do-  args <- liftIO getArgs-  case args of-    ["relocate", pkg] -> relocate packages pkg-    _                 -> ghcPkg packages args-  where-#if __GLASGOW_HASKELL__ >= 706-    packages = ["--global-package-db=" ++ pkgSysDir,-                "--package-db=" ++ pkgSysDir,-                "--package-db=" ++ pkgUserDir]-#else-    packages = ["--no-user-package-conf",-                "--global-conf=" ++ pkgUserDir]-#endif--ghcPkg :: [String] -> [String] -> Shell ()-ghcPkg packages args = do-  pkgDirExists <- isDirectory pkgUserDir-  when (not pkgDirExists) $ do-    mkdir True pkgUserLibDir-    runInteractive ghcPkgBinary ["init", pkgUserDir]-  pkgDirExists <- isDirectory pkgSysDir-  when (not pkgDirExists) $ do-    mkdir True pkgSysLibDir-    runInteractive ghcPkgBinary ["init", pkgSysDir]-  runInteractive ghcPkgBinary (packages ++ args)---- | Only global packages may be marked as relocatable!---   May break horribly for general use, only reliable for Haste base packages.-relocate :: [String] -> String -> Shell ()-relocate packages pkg = do-    pi <- run ghcPkgBinary (packages ++ ["describe", pkg]) ""-    run_ ghcPkgBinary (packages++["update","-","--force","--global"]) (reloc pi)-  where-    reloc = unlines . map fixPath . lines--    fixPath s-      | isKey "library-dirs: " s       = prefix s "library-dirs" importDir-      | isKey "import-dirs: " s        = prefix s "import-dirs" importDir-      | isKey "haddock-interfaces: " s = prefix s "haddock-interfaces" importDir-      | isKey "haddock-html: " s       = prefix s "haddock-html" importDir-      | isKey "include-dirs: " s       = "include-dirs: " ++ includeDir-      | otherwise                      = s--    prefix s pfx path = pfx ++ ": " ++ path </> stripPrefix s--    stripPrefix s-      | os == "darwin" =-        case take 3 $ reverse $ splitPath s of-          [third, second, first] -> first </> second </> third-      | otherwise =-        case take 2 $ reverse $ splitPath s of-          [second, first] -> first </> second--    isKey _ "" =-      False-    isKey key str =-      and $ zipWith (==) key str--    importDir-      | os == "linux"  = "${pkgroot}" </> "libraries" </> "lib"-      | otherwise      = "${pkgroot}" </> "libraries"-    includeDir = "${pkgroot}" </> "include"
+ src/hastec.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE CPP #-}+-- | Haste's main compiler driver.+module Main where+import Language.Haskell.GHC.Simple+import GHC+import Outputable (showPpr)++import System.Environment (getArgs)+import System.Exit+import Data.List+import System.IO.Unsafe+import System.IO+import Control.Monad+import qualified Control.Shell as Sh+import qualified Data.ByteString.UTF8 as BS++import Haste.Opts+import Haste.Args+import Haste.Config+import Haste.Environment+import Haste.Version+import Haste.Module+import Haste.CodeGen+import Haste.Linker++logStr :: Config -> String -> IO ()+logStr cfg = when (verbose cfg) . hPutStrLn stderr++main :: IO ()+main = do+    initUserPkgDB+    as <- getArgs+    let args = "-O2":concat [as,packageDBArgs,["-D__HASTE__="++show intVersion]]+    case parseHasteFlags args of+      Left act             -> act+      Right (fs, mkConfig) -> do+        let ghcconfig = mkGhcCfg fs args+        (dfs, _) <- getDynFlagsForConfig ghcconfig+        let cfg = mkLinkerCfg dfs . setShowOutputable dfs $ mkConfig def+        res <- compileFold ghcconfig (compJS cfg) [] []+        case res of+          Failure _ _         -> do+            exitFailure+          Success targets _ _ -> do+            when (performLink cfg) $ do+              mapM_ (uncurry $ linkAndMinify cfg) targets+  where+    compJS cfg targets m = do+      compJSMod cfg m+      let infile = maybe (modInterfaceFile m) id (modSourceFile m)+      if modIsTarget m+        then return $ (modPackageKey m, infile) : targets+        else return targets++    mkGhcCfg fs args = defaultConfig {+        cfgGhcFlags = fs,+        cfgGhcLibDir = Just hasteGhcLibDir,+        cfgUseTargetsFromFlags = True,+        cfgUseGhcErrorLogger = True,+        cfgUpdateDynFlags = \dfs -> dfs {+            ghcLink = NoLink,+            ghcMode = if "-c" `elem` args+                        then OneShot+                        else CompManager+          }++      }+    mkLinkerCfg dfs cfg = cfg {+        mainMod = Just (pkgKeyString $ modulePkgKey (mainModIs dfs),+                        moduleNameString $ moduleName (mainModIs dfs))+      }+    setShowOutputable dfs cfg = cfg {showOutputable = showPpr dfs}+    -- TODO: this breaks sandboxes and must be fixed+    packageDBArgs = ["-no-global-package-db",+                     "-no-user-package-db",+                     "-package-db " ++ pkgSysDir,+                     "-package-db " ++ pkgUserDir ]++-- | Compile an STG module into a JS module and write it to its appropriate+--   location according to the given config.+compJSMod :: Config -> StgModule -> IO ()+compJSMod cfg stg = do+    logStr cfg $ "Compiling " ++ myName ++ " into " ++ targetpath+    writeModule targetpath (generate cfg stg) boot+  where+    boot = modSourceIsHsBoot stg+    myName = modName stg ++ if boot then " [boot]" else ""+    targetpath = targetLibPath cfg++-- | Link a program starting from the 'mainMod' symbol of the given 'Config'.+--   Minify the result if indicated by the config.+linkAndMinify :: Config -> String -> FilePath -> IO ()+linkAndMinify cfg pkgkey infile = do+    logStr cfg $ "Linking target " ++ outfile+    link cfg (BS.fromString pkgkey) infile+    case useGoogleClosure cfg of+      Just clopath -> closurize cfg clopath outfile+      _            -> return ()+    when (outputHTML cfg) $ do+      res <- Sh.shell $ Sh.withCustomTempFile "." $ \tmp h -> do+        prog <- Sh.file outfile+        Sh.hPutStrLn h (htmlSkeleton outfile prog)+        Sh.liftIO $ hClose h+        Sh.mv tmp outfile+      case res of+        Right () -> return ()+        Left err -> error $ "Couldn't output HTML file: " ++ err+  where+    outfile = outFile cfg cfg infile++-- | Produce an HTML skeleton with an embedded JS program.+htmlSkeleton :: FilePath -> String -> String+htmlSkeleton filename prog = concat [+  "<!DOCTYPE HTML>",+  "<html><head>",+  "<title>", filename , "</title>",+  "<meta charset=\"UTF-8\">",+  "<script type=\"text/javascript\">", prog, "</script>",+  "</head><body></body></html>"]++-- | Run Google Closure on a file.+closurize :: Config -> FilePath -> FilePath -> IO ()+closurize cfg cloPath f = do+  let arguments = useGoogleClosureFlags cfg+  logStr cfg $ "Minifying " ++ f ++ "..."+  let cloFile = f `Sh.addExtension` ".clo"+  res <- Sh.shell $ do+    str <- Sh.run "java"+      (["-jar", cloPath,+        "--compilation_level", "ADVANCED_OPTIMIZATIONS",+        "--jscomp_off", "globalThis", f]+       ++ arguments) ""+    Sh.file cloFile str :: Sh.Shell ()+    Sh.mv cloFile f+  case res of+    Left e  -> fail $ "Couldn't execute Google Closure compiler: " ++ e+    Right _ -> return ()++-- | Call vanilla GHC; used for C files and the like.+callVanillaGHC :: [String] -> IO ()+callVanillaGHC args = do+    _ <- Sh.shell $ Sh.run_ ghcBinary ghcArgs ""+    return ()+  where+    Right (_, ghcArgs) = parseArgs (hasteOpts False) "" args++-- | Initialize the Haste package database, unless it already exists.+initUserPkgDB :: IO ()+initUserPkgDB = do+  _ <- Sh.shell $ do+    pkgDirExists <- Sh.isDirectory pkgUserDir+    when (not pkgDirExists) $ do+      Sh.mkdir True pkgUserLibDir+      Sh.runInteractive hastePkgBinary ["init", pkgUserDir]+  return ()++type Message = String+data BootMode = Booted | Unbooted deriving (Show, Eq)+data Compiler = Haste BootMode | GHC deriving (Show, Eq)+data RunMode = Run !Compiler | DontRun !Message deriving (Show, Eq)++rebootMsg :: Message+rebootMsg = "Haste needs to be rebooted; please run haste-boot"++-- | How should we run the compiler for this command line?+runMode :: [String] -> RunMode+runMode args+  | "--help" `elem` args                 = Run (Haste Booted)+  | "--info" `elem` args                 = DontRun ghcInfo+  | "--print-libdir" `elem` args         = DontRun hasteGhcLibDir+  | "--version" `elem` args              = DontRun $ showVersion hasteVersion+  | "--numeric-version" `elem` args      = DontRun $ showVersion ghcVersion+  | "--supported-extensions" `elem` args = DontRun exts+  | "--supported-languages" `elem` args  = DontRun exts+  | "--unbooted" `elem` args             = Run (chooseCompilerFor args Unbooted)+  | hasteNeedsReboot                     = DontRun rebootMsg+  | otherwise                            = Run (chooseCompilerFor args Booted)+  where+    exts = unlines supportedLanguagesAndExtensions+    ghcInfo = unsafePerformIO $ do+      dfs <- runGhc (Just hasteGhcLibDir) $ getSessionDynFlags+      return $ formatInfo $ compilerInfo dfs+    formatInfo = ('[' :) . tail . unlines . (++ ["]"]) . map ((',' :) . show)++-- | Parse Haste and static GHC flags, returning either an action to be taken+--   before promptly exiting, or a Haste config and a list of flags for GHC.+parseHasteFlags :: [String] -> Either (IO ()) ([String], Config -> Config)+parseHasteFlags args = do+  case runMode args of+   DontRun msg    -> Left $ putStrLn msg+   Run GHC        -> Left $ callVanillaGHC args+   Run (Haste bm) -> do+     case parseArgs (hasteOpts $ bm == Unbooted) helpHeader args of+       Left msg          -> Left $ putStrLn msg+       Right (cfg, rest) -> Right (filter (/= "-prof") rest, cfg)++-- | Use Haste or GHC for this command line?+chooseCompilerFor :: [String] -> BootMode -> Compiler+chooseCompilerFor args bm+  | all hasteOK args  = Haste bm+  | otherwise         = GHC+  where+    hasteOK f = not $ any (`isSuffixOf` f) [".c",".cmm",".cc"]
+ utils/haste-pkg/CRT_noglob.c view
@@ -0,0 +1,4 @@+// Turns off globbing for MingW, this is the same as that+// CRT_noglob.o, but avoids having to locate CRT_nogob.o in the+// filesystem.+unsigned long _CRT_glob = 0;
+ utils/haste-pkg/haste-pkg.hs view
@@ -0,0 +1,1831 @@+{-# LANGUAGE PatternGuards, CPP, ForeignFunctionInterface #-}+-----------------------------------------------------------------------------+--+-- (c) The University of Glasgow 2004-2009.+--+-- Package management tool+--+-----------------------------------------------------------------------------++module Main (main) where++import Distribution.InstalledPackageInfo.Binary()+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.ModuleName hiding (main)+import Distribution.InstalledPackageInfo+import Distribution.Compat.ReadP+import Distribution.ParseUtils+import Distribution.Package hiding (depends)+import Distribution.Text+import Distribution.Version+import System.FilePath as FilePath+import qualified System.FilePath.Posix as FilePath.Posix+import System.Process+import System.Directory ( getAppUserDataDirectory, createDirectoryIfMissing,+                          getModificationTime )+import Text.Printf++import Prelude++import System.Console.GetOpt+import qualified Control.Exception as Exception+import Data.Maybe++import Data.Char ( isSpace, toLower )+import Data.Ord (comparing)+import Control.Applicative (Applicative(..))+import Control.Monad+import System.Directory ( doesDirectoryExist, getDirectoryContents,+                          doesFileExist, renameFile, removeFile,+                          getCurrentDirectory )+import System.Exit ( exitWith, ExitCode(..) )+import System.Environment ( getArgs, getProgName, getEnv )+import System.IO+import System.IO.Error+import Data.List+import Control.Concurrent++import qualified Data.ByteString.Lazy as B+import qualified Data.Binary as Bin+import qualified Data.Binary.Get as Bin++-- Haste-specific+import Haste.Environment+import Haste.Version+import System.Info (os)+import qualified Control.Shell as Sh++#if defined(mingw32_HOST_OS)+-- mingw32 needs these for getExecDir+import Foreign+import Foreign.C+#endif++#ifdef mingw32_HOST_OS+import GHC.ConsoleHandler+#else+import System.Posix hiding (fdToHandle)+#endif++#if defined(GLOB)+import qualified System.Info(os)+#endif++#if !defined(mingw32_HOST_OS) && !defined(BOOTSTRAPPING)+import System.Console.Terminfo as Terminfo+#endif++#ifdef mingw32_HOST_OS+# if defined(i386_HOST_ARCH)+#  define WINDOWS_CCONV stdcall+# elif defined(x86_64_HOST_ARCH)+#  define WINDOWS_CCONV ccall+# else+#  error Unknown mingw32 arch+# endif+#endif++-- -----------------------------------------------------------------------------+-- Entry point++main :: IO ()+main = do+  args <- getArgs++  case args of+    ["relocate", pkg] -> do+      Sh.shell (relocate packages pkg) >> exitWith ExitSuccess+    _ ->+      return ()++  case getOpt Permute (flags ++ deprecFlags) args of+        (cli,_,[]) | FlagHelp `elem` cli -> do+           prog <- getProgramName+           bye (usageInfo (usageHeader prog) flags)+        (cli,_,[]) | FlagVersion `elem` cli ->+           bye ourCopyright+        (cli,nonopts,[]) ->+           case getVerbosity Normal cli of+           Right v -> runit v cli nonopts+           Left err -> die err+        (_,_,errors) -> do+           prog <- getProgramName+           die (concat errors ++ shortUsage prog)+  where+    packages = ["--global-package-db=" ++ pkgSysDir,+                "--package-db=" ++ pkgSysDir,+                "--package-db=" ++ pkgUserDir]++-- -----------------------------------------------------------------------------+-- Command-line syntax++data Flag+  = FlagUser+  | FlagGlobal+  | FlagHelp+  | FlagVersion+  | FlagConfig FilePath+  | FlagGlobalConfig FilePath+  | FlagForce+  | FlagForceFiles+  | FlagAutoGHCiLibs+  | FlagExpandEnvVars+  | FlagExpandPkgroot+  | FlagNoExpandPkgroot+  | FlagSimpleOutput+  | FlagNamesOnly+  | FlagIgnoreCase+  | FlagNoUserDb+  | FlagVerbosity (Maybe String)+  deriving Eq++flags :: [OptDescr Flag]+flags = [+  Option [] ["user"] (NoArg FlagUser)+        "use the current user's package database",+  Option [] ["global"] (NoArg FlagGlobal)+        "use the global package database",+  Option ['f'] ["package-db"] (ReqArg FlagConfig "FILE/DIR")+        "use the specified package database",+  Option [] ["package-conf"] (ReqArg FlagConfig "FILE/DIR")+        "use the specified package database (DEPRECATED)",+  Option [] ["global-package-db"] (ReqArg FlagGlobalConfig "DIR")+        "location of the global package database",+  Option [] ["no-user-package-db"] (NoArg FlagNoUserDb)+        "never read the user package database",+  Option [] ["no-user-package-conf"] (NoArg FlagNoUserDb)+        "never read the user package database (DEPRECATED)",+  Option [] ["force"] (NoArg FlagForce)+         "ignore missing dependencies, directories, and libraries",+  Option [] ["force-files"] (NoArg FlagForceFiles)+         "ignore missing directories and libraries only",+  Option ['g'] ["auto-ghci-libs"] (NoArg FlagAutoGHCiLibs)+        "automatically build libs for GHCi (with register)",+  Option [] ["expand-env-vars"] (NoArg FlagExpandEnvVars)+        "expand environment variables (${name}-style) in input package descriptions",+  Option [] ["expand-pkgroot"] (NoArg FlagExpandPkgroot)+        "expand ${pkgroot}-relative paths to absolute in output package descriptions",+  Option [] ["no-expand-pkgroot"] (NoArg FlagNoExpandPkgroot)+        "preserve ${pkgroot}-relative paths in output package descriptions",+  Option ['?'] ["help"] (NoArg FlagHelp)+        "display this help and exit",+  Option ['V'] ["version"] (NoArg FlagVersion)+        "output version information and exit",+  Option [] ["simple-output"] (NoArg FlagSimpleOutput)+        "print output in easy-to-parse format for some commands",+  Option [] ["names-only"] (NoArg FlagNamesOnly)+        "only print package names, not versions; can only be used with list --simple-output",+  Option [] ["ignore-case"] (NoArg FlagIgnoreCase)+        "ignore case for substring matching",+  Option ['v'] ["verbose"] (OptArg FlagVerbosity "Verbosity")+        "verbosity level (0-2, default 1)"+  ]++data Verbosity = Silent | Normal | Verbose+    deriving (Show, Eq, Ord)++getVerbosity :: Verbosity -> [Flag] -> Either String Verbosity+getVerbosity v [] = Right v+getVerbosity _ (FlagVerbosity Nothing    : fs) = getVerbosity Verbose fs+getVerbosity _ (FlagVerbosity (Just "0") : fs) = getVerbosity Silent  fs+getVerbosity _ (FlagVerbosity (Just "1") : fs) = getVerbosity Normal  fs+getVerbosity _ (FlagVerbosity (Just "2") : fs) = getVerbosity Verbose fs+getVerbosity _ (FlagVerbosity v : _) = Left ("Bad verbosity: " ++ show v)+getVerbosity v (_ : fs) = getVerbosity v fs++deprecFlags :: [OptDescr Flag]+deprecFlags = [+        -- put deprecated flags here+  ]++ourCopyright :: String+ourCopyright = "Haste package manager version " ++ showVersion ghcVersion ++ "\n"++shortUsage :: String -> String+shortUsage prog = "For usage information see '" ++ prog ++ " --help'."++usageHeader :: String -> String+usageHeader prog = substProg prog $+  "Usage:\n" +++  "  $p init {path}\n" +++  "    Create and initialise a package database at the location {path}.\n" +++  "    Packages can be registered in the new database using the register\n" +++  "    command with --package-db={path}.  To use the new database with GHC,\n" +++  "    use GHC's -package-db flag.\n" +++  "\n" +++  "  $p register {filename | -}\n" +++  "    Register the package using the specified installed package\n" +++  "    description. The syntax for the latter is given in the $p\n" +++  "    documentation.  The input file should be encoded in UTF-8.\n" +++  "\n" +++  "  $p update {filename | -}\n" +++  "    Register the package, overwriting any other package with the\n" +++  "    same name. The input file should be encoded in UTF-8.\n" +++  "\n" +++  "  $p unregister {pkg-id}\n" +++  "    Unregister the specified package.\n" +++  "\n" +++  "  $p expose {pkg-id}\n" +++  "    Expose the specified package.\n" +++  "\n" +++  "  $p hide {pkg-id}\n" +++  "    Hide the specified package.\n" +++  "\n" +++  "  $p trust {pkg-id}\n" +++  "    Trust the specified package.\n" +++  "\n" +++  "  $p distrust {pkg-id}\n" +++  "    Distrust the specified package.\n" +++  "\n" +++  "  $p list [pkg]\n" +++  "    List registered packages in the global database, and also the\n" +++  "    user database if --user is given. If a package name is given\n" +++  "    all the registered versions will be listed in ascending order.\n" +++  "    Accepts the --simple-output flag.\n" +++  "\n" +++  "  $p dot\n" +++  "    Generate a graph of the package dependencies in a form suitable\n" +++  "    for input for the graphviz tools.  For example, to generate a PDF" +++  "    of the dependency graph: ghc-pkg dot | tred | dot -Tpdf >pkgs.pdf" +++  "\n" +++  "  $p find-module {module}\n" +++  "    List registered packages exposing module {module} in the global\n" +++  "    database, and also the user database if --user is given.\n" +++  "    All the registered versions will be listed in ascending order.\n" +++  "    Accepts the --simple-output flag.\n" +++  "\n" +++  "  $p latest {pkg-id}\n" +++  "    Prints the highest registered version of a package.\n" +++  "\n" +++  "  $p check\n" +++  "    Check the consistency of package dependencies and list broken packages.\n" +++  "    Accepts the --simple-output flag.\n" +++  "\n" +++  "  $p describe {pkg}\n" +++  "    Give the registered description for the specified package. The\n" +++  "    description is returned in precisely the syntax required by $p\n" +++  "    register.\n" +++  "\n" +++  "  $p field {pkg} {field}\n" +++  "    Extract the specified field of the package description for the\n" +++  "    specified package. Accepts comma-separated multiple fields.\n" +++  "\n" +++  "  $p dump\n" +++  "    Dump the registered description for every package.  This is like\n" +++  "    \"ghc-pkg describe '*'\", except that it is intended to be used\n" +++  "    by tools that parse the results, rather than humans.  The output is\n" +++  "    always encoded in UTF-8, regardless of the current locale.\n" +++  "\n" +++  "  $p recache\n" +++  "    Regenerate the package database cache.  This command should only be\n" +++  "    necessary if you added a package to the database by dropping a file\n" +++  "    into the database directory manually.  By default, the global DB\n" +++  "    is recached; to recache a different DB use --user or --package-db\n" +++  "    as appropriate.\n" +++  "\n" +++  " Substring matching is supported for {module} in find-module and\n" +++  " for {pkg} in list, describe, and field, where a '*' indicates\n" +++  " open substring ends (prefix*, *suffix, *infix*).\n" +++  "\n" +++  "  When asked to modify a database (register, unregister, update,\n"+++  "  hide, expose, and also check), ghc-pkg modifies the global database by\n"+++  "  default.  Specifying --user causes it to act on the user database,\n"+++  "  or --package-db can be used to act on another database\n"+++  "  entirely. When multiple of these options are given, the rightmost\n"+++  "  one is used as the database to act upon.\n"+++  "\n"+++  "  Commands that query the package database (list, tree, latest, describe,\n"+++  "  field) operate on the list of databases specified by the flags\n"+++  "  --user, --global, and --package-db.  If none of these flags are\n"+++  "  given, the default is --global --user.\n"+++  "\n" +++  " The following optional flags are also accepted:\n"++substProg :: String -> String -> String+substProg _ [] = []+substProg prog ('$':'p':xs) = prog ++ substProg prog xs+substProg prog (c:xs) = c : substProg prog xs++-- -----------------------------------------------------------------------------+-- Do the business++data Force = NoForce | ForceFiles | ForceAll | CannotForce+  deriving (Eq,Ord)++data PackageArg = Id PackageIdentifier | Substring String (String->Bool)++runit :: Verbosity -> [Flag] -> [String] -> IO ()+runit verbosity cli nonopts = do+  installSignalHandlers -- catch ^C and clean up+  prog <- getProgramName+  let+        force+          | FlagForce `elem` cli        = ForceAll+          | FlagForceFiles `elem` cli   = ForceFiles+          | otherwise                   = NoForce+        auto_ghci_libs = FlagAutoGHCiLibs `elem` cli+        expand_env_vars= FlagExpandEnvVars `elem` cli+        mexpand_pkgroot= foldl' accumExpandPkgroot Nothing cli+          where accumExpandPkgroot _ FlagExpandPkgroot   = Just True+                accumExpandPkgroot _ FlagNoExpandPkgroot = Just False+                accumExpandPkgroot x _                   = x+                +        splitFields fields = unfoldr splitComma (',':fields)+          where splitComma "" = Nothing+                splitComma fs = Just $ break (==',') (tail fs)++        substringCheck :: String -> Maybe (String -> Bool)+        substringCheck ""    = Nothing+        substringCheck "*"   = Just (const True)+        substringCheck [_]   = Nothing+        substringCheck (h:t) =+          case (h, init t, last t) of+            ('*',s,'*') -> Just (isInfixOf (f s) . f)+            ('*',_, _ ) -> Just (isSuffixOf (f t) . f)+            ( _ ,s,'*') -> Just (isPrefixOf (f (h:s)) . f)+            _           -> Nothing+          where f | FlagIgnoreCase `elem` cli = map toLower+                  | otherwise                 = id+#if defined(GLOB)+        glob x | System.Info.os=="mingw32" = do+          -- glob echoes its argument, after win32 filename globbing+          (_,o,_,_) <- runInteractiveCommand ("glob "++x)+          txt <- hGetContents o+          return (read txt)+        glob x | otherwise = return [x]+#endif+  --+  -- first, parse the command+  case nonopts of+#if defined(GLOB)+    -- dummy command to demonstrate usage and permit testing+    -- without messing things up; use glob to selectively enable+    -- windows filename globbing for file parameters+    -- register, update, FlagGlobalConfig, FlagConfig; others?+    ["glob", filename] -> do+        print filename+        glob filename >>= print+#endif+    ["init", filename] ->+        initPackageDB filename verbosity cli+    ["register", filename] ->+        registerPackage filename verbosity cli+                        auto_ghci_libs expand_env_vars False force+    ["update", filename] ->+        registerPackage filename verbosity cli+                        auto_ghci_libs expand_env_vars True force+    ["unregister", pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        unregisterPackage pkgid verbosity cli force+    ["expose", pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        exposePackage pkgid verbosity cli force+    ["hide",   pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        hidePackage pkgid verbosity cli force+    ["trust",    pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        trustPackage pkgid verbosity cli force+    ["distrust", pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        distrustPackage pkgid verbosity cli force+    ["list"] -> do+        listPackages verbosity cli Nothing Nothing+    ["list", pkgid_str] ->+        case substringCheck pkgid_str of+          Nothing -> do pkgid <- readGlobPkgId pkgid_str+                        listPackages verbosity cli (Just (Id pkgid)) Nothing+          Just m -> listPackages verbosity cli (Just (Substring pkgid_str m)) Nothing+    ["dot"] -> do+        showPackageDot verbosity cli+    ["find-module", moduleName] -> do+        let match = maybe (==moduleName) id (substringCheck moduleName)+        listPackages verbosity cli Nothing (Just match)+    ["latest", pkgid_str] -> do+        pkgid <- readGlobPkgId pkgid_str+        latestPackage verbosity cli pkgid+    ["describe", pkgid_str] -> do+        pkgarg <- case substringCheck pkgid_str of+          Nothing -> liftM Id (readGlobPkgId pkgid_str)+          Just m  -> return (Substring pkgid_str m)+        describePackage verbosity cli pkgarg (fromMaybe False mexpand_pkgroot)+        +    ["field", pkgid_str, fields] -> do+        pkgarg <- case substringCheck pkgid_str of+          Nothing -> liftM Id (readGlobPkgId pkgid_str)+          Just m  -> return (Substring pkgid_str m)+        describeField verbosity cli pkgarg+                      (splitFields fields) (fromMaybe True mexpand_pkgroot)++    ["check"] -> do+        checkConsistency verbosity cli++    ["dump"] -> do+        dumpPackages verbosity cli (fromMaybe False mexpand_pkgroot)++    ["recache"] -> do+        recache verbosity cli++    [] -> do+        die ("missing command\n" ++ shortUsage prog)+    (_cmd:_) -> do+        die ("command-line syntax error\n" ++ shortUsage prog)++parseCheck :: ReadP a a -> String -> String -> IO a+parseCheck parser str what =+  case [ x | (x,ys) <- readP_to_S parser str, all isSpace ys ] of+    [x] -> return x+    _ -> die ("cannot parse \'" ++ str ++ "\' as a " ++ what)++readGlobPkgId :: String -> IO PackageIdentifier+readGlobPkgId str = parseCheck parseGlobPackageId str "package identifier"++parseGlobPackageId :: ReadP r PackageIdentifier+parseGlobPackageId =+  parse+     ++++  (do n <- parse+      _ <- string "-*"+      return (PackageIdentifier{ pkgName = n, pkgVersion = globVersion }))++-- globVersion means "all versions"+globVersion :: Version+globVersion = Version{ versionBranch=[], versionTags=["*"] }++-- -----------------------------------------------------------------------------+-- Package databases++-- Some commands operate on a single database:+--      register, unregister, expose, hide, trust, distrust+-- however these commands also check the union of the available databases+-- in order to check consistency.  For example, register will check that+-- dependencies exist before registering a package.+--+-- Some commands operate  on multiple databases, with overlapping semantics:+--      list, describe, field++data PackageDB +  = PackageDB {+      location, locationAbsolute :: !FilePath,+      -- We need both possibly-relative and definately-absolute package+      -- db locations. This is because the relative location is used as+      -- an identifier for the db, so it is important we do not modify it.+      -- On the other hand we need the absolute path in a few places+      -- particularly in relation to the ${pkgroot} stuff.+      +      packages :: [InstalledPackageInfo]+    }++type PackageDBStack = [PackageDB]+        -- A stack of package databases.  Convention: head is the topmost+        -- in the stack.++allPackagesInStack :: PackageDBStack -> [InstalledPackageInfo]+allPackagesInStack = concatMap packages++getPkgDatabases :: Verbosity+                -> Bool    -- we are modifying, not reading+                -> Bool    -- read caches, if available+                -> Bool    -- expand vars, like ${pkgroot} and $topdir+                -> [Flag]+                -> IO (PackageDBStack, +                          -- the real package DB stack: [global,user] ++ +                          -- DBs specified on the command line with -f.+                       Maybe FilePath,+                          -- which one to modify, if any+                       PackageDBStack)+                          -- the package DBs specified on the command+                          -- line, or [global,user] otherwise.  This+                          -- is used as the list of package DBs for+                          -- commands that just read the DB, such as 'list'.++getPkgDatabases verbosity modify use_cache expand_vars my_flags = do+  -- first we determine the location of the global package config.  On Windows,+  -- this is found relative to the ghc-pkg.exe binary, whereas on Unix the+  -- location is passed to the binary using the --global-package-db flag by the+  -- wrapper script.+  let err_msg = "missing --global-package-db option, location of global package database unknown\n"+  global_conf <-+     case [ f | FlagGlobalConfig f <- my_flags ] of+        [] -> do mb_dir <- getLibDir+                 case mb_dir of+                   Nothing  -> die err_msg+                   Just dir -> do+                     r <- lookForPackageDBIn dir+                     case r of+                       Nothing -> die ("Can't find package database in " ++ dir)+                       Just path -> return path+        fs -> return (last fs)++  -- The value of the $topdir variable used in some package descriptions+  -- Note that the way we calculate this is slightly different to how it+  -- is done in ghc itself. We rely on the convention that the global+  -- package db lives in ghc's libdir.+  top_dir <- absolutePath (takeDirectory global_conf)++  let no_user_db = FlagNoUserDb `elem` my_flags++  mb_user_conf <-+     if no_user_db then return Nothing else do+         r <- lookForPackageDBIn hasteUserDir+         case r of+           Nothing -> return (Just (pkgUserDir, False))+           Just f  -> return (Just (f, True))++  -- If the user database doesn't exist, and this command isn't a+  -- "modify" command, then we won't attempt to create or use it.+  let sys_databases+        | Just (user_conf,user_exists) <- mb_user_conf,+          modify || user_exists = [user_conf, global_conf]+        | otherwise             = [global_conf]++  e_pkg_path <- tryIO (System.Environment.getEnv "GHC_PACKAGE_PATH")+  let env_stack =+        case e_pkg_path of+                Left  _ -> sys_databases+                Right path+                  | last cs == ""  -> init cs ++ sys_databases+                  | otherwise      -> cs+                  where cs = parseSearchPath path++        -- The "global" database is always the one at the bottom of the stack.+        -- This is the database we modify by default.+      virt_global_conf = last env_stack++  let db_flags = [ f | Just f <- map is_db_flag my_flags ]+         where is_db_flag FlagUser+                      | Just (user_conf, _user_exists) <- mb_user_conf +                      = Just user_conf+               is_db_flag FlagGlobal     = Just virt_global_conf+               is_db_flag (FlagConfig f) = Just f+               is_db_flag _              = Nothing++  let flag_db_names | null db_flags = env_stack+                    | otherwise     = reverse (nub db_flags)++  -- For a "modify" command, treat all the databases as+  -- a stack, where we are modifying the top one, but it+  -- can refer to packages in databases further down the+  -- stack.++  -- -f flags on the command line add to the database+  -- stack, unless any of them are present in the stack+  -- already.+  let final_stack = filter (`notElem` env_stack)+                     [ f | FlagConfig f <- reverse my_flags ]+                     ++ env_stack++  -- the database we actually modify is the one mentioned+  -- rightmost on the command-line.+  let to_modify+        | not modify    = Nothing+        | null db_flags = Just virt_global_conf+        | otherwise     = Just (last db_flags)++  db_stack  <- sequence+    [ do db <- readParseDatabase verbosity mb_user_conf use_cache db_path+         if expand_vars then return (mungePackageDBPaths top_dir db)+                        else return db+    | db_path <- final_stack ]++  let flag_db_stack = [ db | db_name <- flag_db_names,+                        db <- db_stack, location db == db_name ]++  return (db_stack, to_modify, flag_db_stack)+++lookForPackageDBIn :: FilePath -> IO (Maybe FilePath)+lookForPackageDBIn dir = do+  let path_dir = dir </> "package.conf.d"+  exists_dir <- doesDirectoryExist path_dir+  if exists_dir then return (Just path_dir) else do+    let path_file = dir </> "package.conf"+    exists_file <- doesFileExist path_file+    if exists_file then return (Just path_file) else return Nothing++readParseDatabase :: Verbosity+                  -> Maybe (FilePath,Bool)+                  -> Bool -- use cache+                  -> FilePath+                  -> IO PackageDB++readParseDatabase verbosity mb_user_conf use_cache path+  -- the user database (only) is allowed to be non-existent+  | Just (user_conf,False) <- mb_user_conf, path == user_conf+  = mkPackageDB []+  | otherwise+  = do e <- tryIO $ getDirectoryContents path+       case e of+         Left _   -> do+              pkgs <- parseMultiPackageConf verbosity path+              mkPackageDB pkgs+         Right fs+           | not use_cache -> ignore_cache (const $ return ())+           | otherwise -> do+              let cache = path </> cachefilename+              tdir     <- getModificationTime path+              e_tcache <- tryIO $ getModificationTime cache+              case e_tcache of+                Left ex -> do+                     when (verbosity > Normal) $+                        warn ("warning: cannot read cache file " ++ cache ++ ": " ++ show ex)+                     ignore_cache (const $ return ())+                Right tcache -> do+                  let compareTimestampToCache file =+                          when (verbosity >= Verbose) $ do+                              tFile <- getModificationTime file+                              compareTimestampToCache' file tFile+                      compareTimestampToCache' file tFile = do+                          let rel = case tcache `compare` tFile of+                                    LT -> " (NEWER than cache)"+                                    GT -> " (older than cache)"+                                    EQ -> " (same as cache)"+                          warn ("Timestamp " ++ show tFile+                             ++ " for " ++ file ++ rel)+                  when (verbosity >= Verbose) $ do+                      warn ("Timestamp " ++ show tcache ++ " for " ++ cache)+                      compareTimestampToCache' path tdir+                  if tcache >= tdir+                      then do+                          when (verbosity > Normal) $+                             infoLn ("using cache: " ++ cache)+                          pkgs <- myReadBinPackageDB cache+                          let pkgs' = map convertPackageInfoIn pkgs+                          mkPackageDB pkgs'+                      else do+                          when (verbosity >= Normal) $ do+                              warn ("WARNING: cache is out of date: "+                                 ++ cache)+                              warn "Use 'ghc-pkg recache' to fix."+                          ignore_cache compareTimestampToCache+            where+                 ignore_cache :: (FilePath -> IO ()) -> IO PackageDB+                 ignore_cache checkTime = do+                     let confs = filter (".conf" `isSuffixOf`) fs+                         doFile f = do checkTime f+                                       parseSingletonPackageConf verbosity f+                     pkgs <- mapM doFile $ map (path </>) confs+                     mkPackageDB pkgs+  where+    mkPackageDB pkgs = do+      path_abs <- absolutePath path+      return PackageDB {+        location = path,+        locationAbsolute = path_abs,+        packages = pkgs+      }++-- read the package.cache file strictly, to work around a problem with+-- bytestring 0.9.0.x (fixed in 0.9.1.x) where the file wasn't closed+-- after it has been completely read, leading to a sharing violation+-- later.+myReadBinPackageDB :: FilePath -> IO [InstalledPackageInfoString]+myReadBinPackageDB filepath = do+  h <- openBinaryFile filepath ReadMode+  sz <- hFileSize h+  b <- B.hGet h (fromIntegral sz)+  hClose h+  return $ Bin.runGet Bin.get b++parseMultiPackageConf :: Verbosity -> FilePath -> IO [InstalledPackageInfo]+parseMultiPackageConf verbosity file = do+  when (verbosity > Normal) $ infoLn ("reading package database: " ++ file)+  str <- readUTF8File file+  let pkgs = map convertPackageInfoIn $ read str+  Exception.evaluate pkgs+    `catchError` \e->+       die ("error while parsing " ++ file ++ ": " ++ show e)+  +parseSingletonPackageConf :: Verbosity -> FilePath -> IO InstalledPackageInfo+parseSingletonPackageConf verbosity file = do+  when (verbosity > Normal) $ infoLn ("reading package config: " ++ file)+  readUTF8File file >>= fmap fst . parsePackageInfo++cachefilename :: FilePath+cachefilename = "package.cache"++mungePackageDBPaths :: FilePath -> PackageDB -> PackageDB+mungePackageDBPaths top_dir db@PackageDB { packages = pkgs } =+    db { packages = map (mungePackagePaths top_dir pkgroot) pkgs }+  where+    pkgroot = takeDirectory (locationAbsolute db)    +    -- It so happens that for both styles of package db ("package.conf"+    -- files and "package.conf.d" dirs) the pkgroot is the parent directory+    -- ${pkgroot}/package.conf  or  ${pkgroot}/package.conf.d/++-- TODO: This code is duplicated in compiler/main/Packages.lhs+mungePackagePaths :: FilePath -> FilePath+                  -> InstalledPackageInfo -> InstalledPackageInfo+-- Perform path/URL variable substitution as per the Cabal ${pkgroot} spec+-- (http://www.haskell.org/pipermail/libraries/2009-May/011772.html)+-- Paths/URLs can be relative to ${pkgroot} or ${pkgrooturl}.+-- The "pkgroot" is the directory containing the package database.+--+-- Also perform a similar substitution for the older GHC-specific+-- "$topdir" variable. The "topdir" is the location of the ghc+-- installation (obtained from the -B option).+mungePackagePaths top_dir pkgroot pkg =+    pkg {+      importDirs  = munge_paths (importDirs pkg),+      includeDirs = munge_paths (includeDirs pkg),+      libraryDirs = munge_paths (libraryDirs pkg),+      frameworkDirs = munge_paths (frameworkDirs pkg),+      haddockInterfaces = munge_paths (haddockInterfaces pkg),+                     -- haddock-html is allowed to be either a URL or a file+      haddockHTMLs = munge_paths (munge_urls (haddockHTMLs pkg))+    }+  where+    munge_paths = map munge_path+    munge_urls  = map munge_url++    munge_path p+      | Just p' <- stripVarPrefix "${pkgroot}" p = pkgroot ++ p'+      | Just p' <- stripVarPrefix "$topdir"    p = top_dir ++ p'+      | otherwise                                = p++    munge_url p+      | Just p' <- stripVarPrefix "${pkgrooturl}" p = toUrlPath pkgroot p'+      | Just p' <- stripVarPrefix "$httptopdir"   p = toUrlPath top_dir p'+      | otherwise                                   = p++    toUrlPath r p = "file:///"+                 -- URLs always use posix style '/' separators:+                 ++ FilePath.Posix.joinPath+                        (r : -- We need to drop a leading "/" or "\\"+                             -- if there is one:+                             dropWhile (all isPathSeparator)+                                       (FilePath.splitDirectories p))++    -- We could drop the separator here, and then use </> above. However,+    -- by leaving it in and using ++ we keep the same path separator+    -- rather than letting FilePath change it to use \ as the separator+    stripVarPrefix var path = case stripPrefix var path of+                              Just [] -> Just []+                              Just cs@(c : _) | isPathSeparator c -> Just cs+                              _ -> Nothing+++-- -----------------------------------------------------------------------------+-- Creating a new package DB++initPackageDB :: FilePath -> Verbosity -> [Flag] -> IO ()+initPackageDB filename verbosity _flags = do+  let eexist = die ("cannot create: " ++ filename ++ " already exists")+  b1 <- doesFileExist filename+  when b1 eexist+  b2 <- doesDirectoryExist filename+  when b2 eexist+  filename_abs <- absolutePath filename+  changeDB verbosity [] PackageDB {+                          location = filename, locationAbsolute = filename_abs,+                          packages = []+                        }++-- -----------------------------------------------------------------------------+-- Registering++registerPackage :: FilePath+                -> Verbosity+                -> [Flag]+                -> Bool              -- auto_ghci_libs+                -> Bool              -- expand_env_vars+                -> Bool              -- update+                -> Force+                -> IO ()+registerPackage input verbosity my_flags auto_ghci_libs expand_env_vars update force = do+  (db_stack, Just to_modify, _flag_dbs) <- +      getPkgDatabases verbosity True True False{-expand vars-} my_flags++  let+        db_to_operate_on = my_head "register" $+                           filter ((== to_modify).location) db_stack+  --+  when (auto_ghci_libs && verbosity >= Silent) $+    warn "Warning: --auto-ghci-libs is deprecated and will be removed in GHC 7.4"+  --+  s <-+    case input of+      "-" -> do+        when (verbosity >= Normal) $+            info "Reading package info from stdin ... "+        -- fix the encoding to UTF-8, since this is an interchange format+        hSetEncoding stdin utf8+        getContents+      f   -> do+        when (verbosity >= Normal) $+            info ("Reading package info from " ++ show f ++ " ... ")+        readUTF8File f++  expanded <- if expand_env_vars then expandEnvVars s force+                                 else return s++  (pkg, ws) <- parsePackageInfo expanded+  when (verbosity >= Normal) $+      infoLn "done."++  -- report any warnings from the parse phase+  _ <- reportValidateErrors [] ws+         (display (sourcePackageId pkg) ++ ": Warning: ") Nothing++  -- validate the expanded pkg, but register the unexpanded+  pkgroot <- absolutePath (takeDirectory to_modify)+  let top_dir = takeDirectory (location (last db_stack))+      pkg_expanded = mungePackagePaths top_dir pkgroot pkg++  let truncated_stack = dropWhile ((/= to_modify).location) db_stack+  -- truncate the stack for validation, because we don't allow+  -- packages lower in the stack to refer to those higher up.+  validatePackageConfig pkg_expanded verbosity truncated_stack auto_ghci_libs update force+  let +     removes = [ RemovePackage p+               | p <- packages db_to_operate_on,+                 sourcePackageId p == sourcePackageId pkg ]+  --+  changeDB verbosity (removes ++ [AddPackage pkg]) db_to_operate_on++parsePackageInfo+        :: String+        -> IO (InstalledPackageInfo, [ValidateWarning])+parsePackageInfo str =+  case parseInstalledPackageInfo str of+    ParseOk warnings ok -> return (ok, ws)+      where+        ws = [ msg | PWarning msg <- warnings+                   , not ("Unrecognized field pkgroot" `isPrefixOf` msg) ]+    ParseFailed err -> case locatedErrorMsg err of+                           (Nothing, s) -> die s+                           (Just l, s) -> die (show l ++ ": " ++ s)++-- -----------------------------------------------------------------------------+-- Making changes to a package database++data DBOp = RemovePackage InstalledPackageInfo+          | AddPackage    InstalledPackageInfo+          | ModifyPackage InstalledPackageInfo++changeDB :: Verbosity -> [DBOp] -> PackageDB -> IO ()+changeDB verbosity cmds db = do+  let db' = updateInternalDB db cmds+  isfile <- doesFileExist (location db)+  if isfile+     then writeNewConfig verbosity (location db') (packages db')+     else do+       createDirectoryIfMissing True (location db)+       changeDBDir verbosity cmds db'++updateInternalDB :: PackageDB -> [DBOp] -> PackageDB+updateInternalDB db cmds = db{ packages = foldl do_cmd (packages db) cmds }+ where+  do_cmd pkgs (RemovePackage p) = +    filter ((/= installedPackageId p) . installedPackageId) pkgs+  do_cmd pkgs (AddPackage p) = p : pkgs+  do_cmd pkgs (ModifyPackage p) = +    do_cmd (do_cmd pkgs (RemovePackage p)) (AddPackage p)+    ++changeDBDir :: Verbosity -> [DBOp] -> PackageDB -> IO ()+changeDBDir verbosity cmds db = do+  mapM_ do_cmd cmds+  updateDBCache verbosity db+ where+  do_cmd (RemovePackage p) = do+    let file = location db </> display (installedPackageId p) <.> "conf"+    when (verbosity > Normal) $ infoLn ("removing " ++ file)+    removeFileSafe file+  do_cmd (AddPackage p) = do+    let file = location db </> display (installedPackageId p) <.> "conf"+    when (verbosity > Normal) $ infoLn ("writing " ++ file)+    writeFileUtf8Atomic file (showInstalledPackageInfo p)+  do_cmd (ModifyPackage p) = +    do_cmd (AddPackage p)++updateDBCache :: Verbosity -> PackageDB -> IO ()+updateDBCache verbosity db = do+  let filename = location db </> cachefilename+  when (verbosity > Normal) $+      infoLn ("writing cache " ++ filename)+  writeBinaryFileAtomic filename (map convertPackageInfoOut (packages db))+    `catchIO` \e ->+      if isPermissionError e+      then die (filename ++ ": you don't have permission to modify this file")+      else ioError e+#ifndef mingw32_HOST_OS+  status <- getFileStatus filename+  setFileTimes (location db) (accessTime status) (modificationTime status)+#endif++-- -----------------------------------------------------------------------------+-- Exposing, Hiding, Trusting, Distrusting, Unregistering are all similar++exposePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()+exposePackage = modifyPackage (\p -> ModifyPackage p{exposed=True})++hidePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()+hidePackage = modifyPackage (\p -> ModifyPackage p{exposed=False})++trustPackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()+trustPackage = modifyPackage (\p -> ModifyPackage p{trusted=True})++distrustPackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()+distrustPackage = modifyPackage (\p -> ModifyPackage p{trusted=False})++unregisterPackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()+unregisterPackage = modifyPackage RemovePackage++modifyPackage+  :: (InstalledPackageInfo -> DBOp)+  -> PackageIdentifier+  -> Verbosity+  -> [Flag]+  -> Force+  -> IO ()+modifyPackage fn pkgid verbosity my_flags force = do+  (db_stack, Just _to_modify, _flag_dbs) <- +      getPkgDatabases verbosity True{-modify-} True{-use cache-} False{-expand vars-} my_flags++  (db, ps) <- fmap head $ findPackagesByDB db_stack (Id pkgid)+  let +      db_name = location db+      pkgs    = packages db++      pids = map sourcePackageId ps++      cmds = [ fn pkg | pkg <- pkgs, sourcePackageId pkg `elem` pids ]+      new_db = updateInternalDB db cmds++      old_broken = brokenPackages (allPackagesInStack db_stack)+      rest_of_stack = filter ((/= db_name) . location) db_stack+      new_stack = new_db : rest_of_stack+      new_broken = map sourcePackageId (brokenPackages (allPackagesInStack new_stack))+      newly_broken = filter (`notElem` map sourcePackageId old_broken) new_broken+  --+  when (not (null newly_broken)) $+      dieOrForceAll force ("unregistering " ++ display pkgid +++           " would break the following packages: "+              ++ unwords (map display newly_broken))++  changeDB verbosity cmds db++recache :: Verbosity -> [Flag] -> IO ()+recache verbosity my_flags = do+  (db_stack, Just to_modify, _flag_dbs) <- +     getPkgDatabases verbosity True{-modify-} False{-no cache-} False{-expand vars-} my_flags+  let+        db_to_operate_on = my_head "recache" $+                           filter ((== to_modify).location) db_stack+  --+  changeDB verbosity [] db_to_operate_on++-- -----------------------------------------------------------------------------+-- Listing packages++listPackages ::  Verbosity -> [Flag] -> Maybe PackageArg+             -> Maybe (String->Bool)+             -> IO ()+listPackages verbosity my_flags mPackageName mModuleName = do+  let simple_output = FlagSimpleOutput `elem` my_flags+  (db_stack, _, flag_db_stack) <- +     getPkgDatabases verbosity False True{-use cache-} False{-expand vars-} my_flags++  let db_stack_filtered -- if a package is given, filter out all other packages+        | Just this <- mPackageName =+            [ db{ packages = filter (this `matchesPkg`) (packages db) }+            | db <- flag_db_stack ]+        | Just match <- mModuleName = -- packages which expose mModuleName+            [ db{ packages = filter (match `exposedInPkg`) (packages db) }+            | db <- flag_db_stack ]+        | otherwise = flag_db_stack++      db_stack_sorted+          = [ db{ packages = sort_pkgs (packages db) }+            | db <- db_stack_filtered ]+          where sort_pkgs = sortBy cmpPkgIds+                cmpPkgIds pkg1 pkg2 =+                   case pkgName p1 `compare` pkgName p2 of+                        LT -> LT+                        GT -> GT+                        EQ -> pkgVersion p1 `compare` pkgVersion p2+                   where (p1,p2) = (sourcePackageId pkg1, sourcePackageId pkg2)++      stack = reverse db_stack_sorted++      match `exposedInPkg` pkg = any match (map display $ exposedModules pkg)++      pkg_map = allPackagesInStack db_stack+      broken = map sourcePackageId (brokenPackages pkg_map)++      show_normal PackageDB{ location = db_name, packages = pkg_confs } =+          do hPutStrLn stdout (db_name ++ ":")+             if null pp_pkgs+                 then hPutStrLn stdout "    (no packages)"+                 else hPutStrLn stdout $ unlines (map ("    " ++) pp_pkgs)+           where+                 -- Sort using instance Ord PackageId+                 pp_pkgs = map pp_pkg . sortBy (comparing installedPackageId) $ pkg_confs+                 pp_pkg p+                   | sourcePackageId p `elem` broken = printf "{%s}" doc+                   | exposed p = doc+                   | otherwise = printf "(%s)" doc+                   where doc | verbosity >= Verbose = printf "%s (%s)" pkg ipid+                             | otherwise            = pkg+                          where+                          InstalledPackageId ipid = installedPackageId p+                          pkg = display (sourcePackageId p)++      show_simple = simplePackageList my_flags . allPackagesInStack++  when (not (null broken) && not simple_output && verbosity /= Silent) $ do+     prog <- getProgramName+     warn ("WARNING: there are broken packages.  Run '" ++ prog ++ " check' for more details.")++  if simple_output then show_simple stack else do++#if defined(mingw32_HOST_OS) || defined(BOOTSTRAPPING)+    mapM_ show_normal stack+#else+    let+       show_colour withF db =+           mconcat $ map (<#> termText "\n") $+               (termText (location db) :+                  map (termText "   " <#>) (map pp_pkg (packages db)))+          where+                   pp_pkg p+                     | sourcePackageId p `elem` broken = withF Red  doc+                     | exposed p                       = doc+                     | otherwise                       = withF Blue doc+                     where doc | verbosity >= Verbose+                               = termText (printf "%s (%s)" pkg ipid)+                               | otherwise+                               = termText pkg+                            where+                            InstalledPackageId ipid = installedPackageId p+                            pkg = display (sourcePackageId p)++    is_tty <- hIsTerminalDevice stdout+    if not is_tty+       then mapM_ show_normal stack+       else do tty <- Terminfo.setupTermFromEnv+               case Terminfo.getCapability tty withForegroundColor of+                   Nothing -> mapM_ show_normal stack+                   Just w  -> runTermOutput tty $ mconcat $+                                                  map (show_colour w) stack+#endif++simplePackageList :: [Flag] -> [InstalledPackageInfo] -> IO ()+simplePackageList my_flags pkgs = do+   let showPkg = if FlagNamesOnly `elem` my_flags then display . pkgName+                                                  else display+       -- Sort using instance Ord PackageId+       strs = map showPkg $ sort $ map sourcePackageId pkgs+   when (not (null pkgs)) $+      hPutStrLn stdout $ concat $ intersperse " " strs++showPackageDot :: Verbosity -> [Flag] -> IO ()+showPackageDot verbosity myflags = do+  (_, _, flag_db_stack) <- +      getPkgDatabases verbosity False True{-use cache-} False{-expand vars-} myflags++  let all_pkgs = allPackagesInStack flag_db_stack+      ipix  = PackageIndex.fromList all_pkgs++  putStrLn "digraph {"+  let quote s = '"':s ++ "\""+  mapM_ putStrLn [ quote from ++ " -> " ++ quote to+                 | p <- all_pkgs,+                   let from = display (sourcePackageId p),+                   depid <- depends p,+                   Just dep <- [PackageIndex.lookupInstalledPackageId ipix depid],+                   let to = display (sourcePackageId dep)+                 ]+  putStrLn "}"++-- -----------------------------------------------------------------------------+-- Prints the highest (hidden or exposed) version of a package++latestPackage ::  Verbosity -> [Flag] -> PackageIdentifier -> IO ()+latestPackage verbosity my_flags pkgid = do+  (_, _, flag_db_stack) <- +     getPkgDatabases verbosity False True{-use cache-} False{-expand vars-} my_flags++  ps <- findPackages flag_db_stack (Id pkgid)+  case ps of+    [] -> die "no matches"+    _  -> show_pkg . maximum . map sourcePackageId $ ps+  where+    show_pkg pid = hPutStrLn stdout (display pid)++-- -----------------------------------------------------------------------------+-- Describe++describePackage :: Verbosity -> [Flag] -> PackageArg -> Bool -> IO ()+describePackage verbosity my_flags pkgarg expand_pkgroot = do+  (_, _, flag_db_stack) <- +      getPkgDatabases verbosity False True{-use cache-} expand_pkgroot my_flags+  dbs <- findPackagesByDB flag_db_stack pkgarg+  doDump expand_pkgroot [ (pkg, locationAbsolute db)+                        | (db, pkgs) <- dbs, pkg <- pkgs ]++dumpPackages :: Verbosity -> [Flag] -> Bool -> IO ()+dumpPackages verbosity my_flags expand_pkgroot = do+  (_, _, flag_db_stack) <- +     getPkgDatabases verbosity False True{-use cache-} expand_pkgroot my_flags+  doDump expand_pkgroot [ (pkg, locationAbsolute db)+                        | db <- flag_db_stack, pkg <- packages db ]++doDump :: Bool -> [(InstalledPackageInfo, FilePath)] -> IO ()+doDump expand_pkgroot pkgs = do+  -- fix the encoding to UTF-8, since this is an interchange format+  hSetEncoding stdout utf8+  putStrLn $+    intercalate "---\n"+    [ if expand_pkgroot+        then showInstalledPackageInfo pkg+        else showInstalledPackageInfo pkg ++ pkgrootField+    | (pkg, pkgloc) <- pkgs+    , let pkgroot      = takeDirectory pkgloc+          pkgrootField = "pkgroot: " ++ show pkgroot ++ "\n" ]++-- PackageId is can have globVersion for the version+findPackages :: PackageDBStack -> PackageArg -> IO [InstalledPackageInfo]+findPackages db_stack pkgarg+  = fmap (concatMap snd) $ findPackagesByDB db_stack pkgarg++findPackagesByDB :: PackageDBStack -> PackageArg+                 -> IO [(PackageDB, [InstalledPackageInfo])]+findPackagesByDB db_stack pkgarg+  = case [ (db, matched)+         | db <- db_stack,+           let matched = filter (pkgarg `matchesPkg`) (packages db),+           not (null matched) ] of+        [] -> die ("cannot find package " ++ pkg_msg pkgarg)+        ps -> return ps+  where+        pkg_msg (Id pkgid)           = display pkgid+        pkg_msg (Substring pkgpat _) = "matching " ++ pkgpat++matches :: PackageIdentifier -> PackageIdentifier -> Bool+pid `matches` pid'+  = (pkgName pid == pkgName pid')+    && (pkgVersion pid == pkgVersion pid' || not (realVersion pid))++realVersion :: PackageIdentifier -> Bool+realVersion pkgid = versionBranch (pkgVersion pkgid) /= []+  -- when versionBranch == [], this is a glob++matchesPkg :: PackageArg -> InstalledPackageInfo -> Bool+(Id pid)        `matchesPkg` pkg = pid `matches` sourcePackageId pkg+(Substring _ m) `matchesPkg` pkg = m (display (sourcePackageId pkg))++-- -----------------------------------------------------------------------------+-- Field++describeField :: Verbosity -> [Flag] -> PackageArg -> [String] -> Bool -> IO ()+describeField verbosity my_flags pkgarg fields expand_pkgroot = do+  (_, _, flag_db_stack) <- +      getPkgDatabases verbosity False True{-use cache-} expand_pkgroot my_flags+  fns <- mapM toField fields+  ps <- findPackages flag_db_stack pkgarg+  mapM_ (selectFields fns) ps+  where showFun = if FlagSimpleOutput `elem` my_flags+                  then showSimpleInstalledPackageInfoField+                  else showInstalledPackageInfoField+        toField f = case showFun f of+                    Nothing -> die ("unknown field: " ++ f)+                    Just fn -> return fn+        selectFields fns pinfo = mapM_ (\fn->putStrLn (fn pinfo)) fns+++-- -----------------------------------------------------------------------------+-- Check: Check consistency of installed packages++checkConsistency :: Verbosity -> [Flag] -> IO ()+checkConsistency verbosity my_flags = do+  (db_stack, _, _) <- +         getPkgDatabases verbosity True True{-use cache-} True{-expand vars-} my_flags+         -- check behaves like modify for the purposes of deciding which+         -- databases to use, because ordering is important.++  let simple_output = FlagSimpleOutput `elem` my_flags++  let pkgs = allPackagesInStack db_stack++      checkPackage p = do+         (_,es,ws) <- runValidate $ checkPackageConfig p verbosity db_stack False True+         if null es+            then do when (not simple_output) $ do+                      _ <- reportValidateErrors [] ws "" Nothing+                      return ()+                    return []+            else do+              when (not simple_output) $ do+                  reportError ("There are problems in package " ++ display (sourcePackageId p) ++ ":")+                  _ <- reportValidateErrors es ws "  " Nothing+                  return ()+              return [p]++  broken_pkgs <- concat `fmap` mapM checkPackage pkgs++  let filterOut pkgs1 pkgs2 = filter not_in pkgs2+        where not_in p = sourcePackageId p `notElem` all_ps+              all_ps = map sourcePackageId pkgs1++  let not_broken_pkgs = filterOut broken_pkgs pkgs+      (_, trans_broken_pkgs) = closure [] not_broken_pkgs+      all_broken_pkgs = broken_pkgs ++ trans_broken_pkgs++  when (not (null all_broken_pkgs)) $ do+    if simple_output+      then simplePackageList my_flags all_broken_pkgs+      else do+       reportError ("\nThe following packages are broken, either because they have a problem\n"+++                "listed above, or because they depend on a broken package.")+       mapM_ (hPutStrLn stderr . display . sourcePackageId) all_broken_pkgs++  when (not (null all_broken_pkgs)) $ exitWith (ExitFailure 1)+++closure :: [InstalledPackageInfo] -> [InstalledPackageInfo]+        -> ([InstalledPackageInfo], [InstalledPackageInfo])+closure pkgs db_stack = go pkgs db_stack+ where+   go avail not_avail =+     case partition (depsAvailable avail) not_avail of+        ([],        not_avail') -> (avail, not_avail')+        (new_avail, not_avail') -> go (new_avail ++ avail) not_avail'++   depsAvailable :: [InstalledPackageInfo] -> InstalledPackageInfo+                 -> Bool+   depsAvailable pkgs_ok pkg = null dangling+        where dangling = filter (`notElem` pids) (depends pkg)+              pids = map installedPackageId pkgs_ok++        -- we want mutually recursive groups of package to show up+        -- as broken. (#1750)++brokenPackages :: [InstalledPackageInfo] -> [InstalledPackageInfo]+brokenPackages pkgs = snd (closure [] pkgs)++-- -----------------------------------------------------------------------------+-- Manipulating package.conf files++type InstalledPackageInfoString = InstalledPackageInfo_ String++convertPackageInfoOut :: InstalledPackageInfo -> InstalledPackageInfoString+convertPackageInfoOut+    (pkgconf@(InstalledPackageInfo { exposedModules = e,+                                     hiddenModules = h })) =+        pkgconf{ exposedModules = map display e,+                 hiddenModules  = map display h }++convertPackageInfoIn :: InstalledPackageInfoString -> InstalledPackageInfo+convertPackageInfoIn+    (pkgconf@(InstalledPackageInfo { exposedModules = e,+                                     hiddenModules = h })) =+        pkgconf{ exposedModules = map convert e,+                 hiddenModules  = map convert h }+    where convert = fromJust . simpleParse++writeNewConfig :: Verbosity -> FilePath -> [InstalledPackageInfo] -> IO ()+writeNewConfig verbosity filename ipis = do+  when (verbosity >= Normal) $+      info "Writing new package config file... "+  createDirectoryIfMissing True $ takeDirectory filename+  let shown = concat $ intersperse ",\n "+                     $ map (show . convertPackageInfoOut) ipis+      fileContents = "[" ++ shown ++ "\n]"+  writeFileUtf8Atomic filename fileContents+    `catchIO` \e ->+      if isPermissionError e+      then die (filename ++ ": you don't have permission to modify this file")+      else ioError e+  when (verbosity >= Normal) $+      infoLn "done."++-----------------------------------------------------------------------------+-- Sanity-check a new package config, and automatically build GHCi libs+-- if requested.++type ValidateError   = (Force,String)+type ValidateWarning = String++newtype Validate a = V { runValidate :: IO (a, [ValidateError],[ValidateWarning]) }++instance Functor Validate where+    fmap = liftM++instance Applicative Validate where+    pure = return+    (<*>) = ap++instance Monad Validate where+   return a = V $ return (a, [], [])+   m >>= k = V $ do+      (a, es, ws) <- runValidate m+      (b, es', ws') <- runValidate (k a)+      return (b,es++es',ws++ws')++verror :: Force -> String -> Validate ()+verror f s = V (return ((),[(f,s)],[]))++vwarn :: String -> Validate ()+vwarn s = V (return ((),[],["Warning: " ++ s]))++liftIO :: IO a -> Validate a+liftIO k = V (k >>= \a -> return (a,[],[]))++-- returns False if we should die+reportValidateErrors :: [ValidateError] -> [ValidateWarning]+                     -> String -> Maybe Force -> IO Bool+reportValidateErrors es ws prefix mb_force = do+  mapM_ (warn . (prefix++)) ws+  oks <- mapM report es+  return (and oks)+  where+    report (f,s)+      | Just force <- mb_force+      = if (force >= f)+           then do reportError (prefix ++ s ++ " (ignoring)")+                   return True+           else if f < CannotForce+                   then do reportError (prefix ++ s ++ " (use --force to override)")+                           return False+                   else do reportError err+                           return False+      | otherwise = do reportError err+                       return False+      where+             err = prefix ++ s++validatePackageConfig :: InstalledPackageInfo+                      -> Verbosity+                      -> PackageDBStack+                      -> Bool   -- auto-ghc-libs+                      -> Bool   -- update, or check+                      -> Force+                      -> IO ()+validatePackageConfig pkg verbosity db_stack auto_ghci_libs update force = do+  (_,es,ws) <- runValidate $ checkPackageConfig pkg verbosity db_stack auto_ghci_libs update+  ok <- reportValidateErrors es ws (display (sourcePackageId pkg) ++ ": ") (Just force)+  when (not ok) $ exitWith (ExitFailure 1)++checkPackageConfig :: InstalledPackageInfo+                      -> Verbosity+                      -> PackageDBStack+                      -> Bool   -- auto-ghc-libs+                      -> Bool   -- update, or check+                      -> Validate ()+checkPackageConfig pkg verbosity db_stack auto_ghci_libs update = do+  checkInstalledPackageId pkg db_stack update+  checkPackageId pkg+  checkDuplicates db_stack pkg update+  mapM_ (checkDep db_stack) (depends pkg)+  checkDuplicateDepends (depends pkg)+  mapM_ (checkDir False "import-dirs")  (importDirs pkg)+  mapM_ (checkDir True  "library-dirs") (libraryDirs pkg)+  mapM_ (checkDir True  "include-dirs") (includeDirs pkg)+  mapM_ (checkDir True  "framework-dirs") (frameworkDirs pkg)+  mapM_ (checkFile   True "haddock-interfaces") (haddockInterfaces pkg)+  mapM_ (checkDirURL True "haddock-html")       (haddockHTMLs pkg)+  checkModules pkg+  mapM_ (checkHSLib verbosity (libraryDirs pkg) auto_ghci_libs) (hsLibraries pkg)+  -- ToDo: check these somehow?+  --    extra_libraries :: [String],+  --    c_includes      :: [String],++checkInstalledPackageId :: InstalledPackageInfo -> PackageDBStack -> Bool +                        -> Validate ()+checkInstalledPackageId ipi db_stack update = do+  let ipid@(InstalledPackageId str) = installedPackageId ipi+  when (null str) $ verror CannotForce "missing id field"+  let dups = [ p | p <- allPackagesInStack db_stack, +                   installedPackageId p == ipid ]+  when (not update && not (null dups)) $+    verror CannotForce $+        "package(s) with this id already exist: " ++ +         unwords (map (display.packageId) dups)++-- When the package name and version are put together, sometimes we can+-- end up with a package id that cannot be parsed.  This will lead to+-- difficulties when the user wants to refer to the package later, so+-- we check that the package id can be parsed properly here.+checkPackageId :: InstalledPackageInfo -> Validate ()+checkPackageId ipi =+  let str = display (sourcePackageId ipi) in+  case [ x :: PackageIdentifier | (x,ys) <- readP_to_S parse str, all isSpace ys ] of+    [_] -> return ()+    []  -> verror CannotForce ("invalid package identifier: " ++ str)+    _   -> verror CannotForce ("ambiguous package identifier: " ++ str)++checkDuplicates :: PackageDBStack -> InstalledPackageInfo -> Bool -> Validate ()+checkDuplicates db_stack pkg update = do+  let+        pkgid = sourcePackageId pkg+        pkgs  = packages (head db_stack)+  --+  -- Check whether this package id already exists in this DB+  --+  when (not update && (pkgid `elem` map sourcePackageId pkgs)) $+       verror CannotForce $+          "package " ++ display pkgid ++ " is already installed"++  let+        uncasep = map toLower . display+        dups = filter ((== uncasep pkgid) . uncasep) (map sourcePackageId pkgs)++  when (not update && not (null dups)) $ verror ForceAll $+        "Package names may be treated case-insensitively in the future.\n"+++        "Package " ++ display pkgid +++        " overlaps with: " ++ unwords (map display dups)++checkDir, checkFile, checkDirURL :: Bool -> String -> FilePath -> Validate ()+checkDir  = checkPath False True+checkFile = checkPath False False+checkDirURL = checkPath True True++checkPath :: Bool -> Bool -> Bool -> String -> FilePath -> Validate ()+checkPath url_ok is_dir warn_only thisfield d+ | url_ok && ("http://"  `isPrefixOf` d+           || "https://" `isPrefixOf` d) = return ()++ | url_ok+ , Just d' <- stripPrefix "file://" d+ = checkPath False is_dir warn_only thisfield d'++   -- Note: we don't check for $topdir/${pkgroot} here. We rely on these+   -- variables having been expanded already, see mungePackagePaths.++ | isRelative d = verror ForceFiles $+                     thisfield ++ ": " ++ d ++ " is a relative path which "+                  ++ "makes no sense (as there is nothing for it to be "+                  ++ "relative to). You can make paths relative to the "+                  ++ "package database itself by using ${pkgroot}."+        -- relative paths don't make any sense; #4134+ | otherwise = do+   there <- liftIO $ if is_dir then doesDirectoryExist d else doesFileExist d+   when (not there) $+       let msg = thisfield ++ ": " ++ d ++ " doesn't exist or isn't a "+                                        ++ if is_dir then "directory" else "file"+       in+       if warn_only +          then vwarn msg+          else verror ForceFiles msg++checkDep :: PackageDBStack -> InstalledPackageId -> Validate ()+checkDep db_stack pkgid+  | pkgid `elem` pkgids = return ()+  | otherwise = verror ForceAll ("dependency \"" ++ display pkgid+                                 ++ "\" doesn't exist")+  where+        all_pkgs = allPackagesInStack db_stack+        pkgids = map installedPackageId all_pkgs++checkDuplicateDepends :: [InstalledPackageId] -> Validate ()+checkDuplicateDepends deps+  | null dups = return ()+  | otherwise = verror ForceAll ("package has duplicate dependencies: " +++                                     unwords (map display dups))+  where+       dups = [ p | (p:_:_) <- group (sort deps) ]++checkHSLib :: Verbosity -> [String] -> Bool -> String -> Validate ()+checkHSLib verbosity dirs auto_ghci_libs lib = do+  let batch_lib_file = "lib" ++ lib ++ ".a"+      filenames = ["lib" ++ lib ++ ".a",+                   "lib" ++ lib ++ ".p_a",+                   "lib" ++ lib ++ "-ghc" ++ showVersion ghcVersion ++ ".so",+                   "lib" ++ lib ++ "-ghc" ++ showVersion ghcVersion ++ ".dylib",+                            lib ++ "-ghc" ++ showVersion ghcVersion ++ ".dll"]+  m <- liftIO $ doesFileExistOnPath filenames dirs+  case m of+    Nothing -> verror ForceFiles ("cannot find any of " ++ show filenames +++                                  " on library path")+    Just dir -> liftIO $ checkGHCiLib verbosity dir batch_lib_file lib auto_ghci_libs++doesFileExistOnPath :: [FilePath] -> [FilePath] -> IO (Maybe FilePath)+doesFileExistOnPath filenames paths = go fullFilenames+  where fullFilenames = [ (path, path </> filename)+                        | filename <- filenames+                        , path <- paths ]+        go []             = return Nothing+        go ((p, fp) : xs) = do b <- doesFileExist fp+                               if b then return (Just p) else go xs++checkModules :: InstalledPackageInfo -> Validate ()+checkModules pkg = do+  mapM_ findModule (exposedModules pkg ++ hiddenModules pkg)+  where+    findModule modl =+      -- there's no interface file for GHC.Prim+      unless (modl == fromString "GHC.Prim") $ do+      let files = [ toFilePath modl <.> extension+                  | extension <- ["hi", "p_hi", "dyn_hi" ] ]+      m <- liftIO $ doesFileExistOnPath files (importDirs pkg)+      when (isNothing m) $+         verror ForceFiles ("cannot find any of " ++ show files)++checkGHCiLib :: Verbosity -> String -> String -> String -> Bool -> IO ()+checkGHCiLib verbosity batch_lib_dir batch_lib_file lib auto_build+  | auto_build = autoBuildGHCiLib verbosity batch_lib_dir batch_lib_file ghci_lib_file+  | otherwise  = return ()+ where+    ghci_lib_file = lib <.> "o"++-- automatically build the GHCi version of a batch lib,+-- using ld --whole-archive.++autoBuildGHCiLib :: Verbosity -> String -> String -> String -> IO ()+autoBuildGHCiLib verbosity dir batch_file ghci_file = do+  let ghci_lib_file  = dir ++ '/':ghci_file+      batch_lib_file = dir ++ '/':batch_file+  when (verbosity >= Normal) $+    info ("building GHCi library " ++ ghci_lib_file ++ "...")+#if defined(darwin_HOST_OS)+  r <- rawSystem "ld" ["-r","-x","-o",ghci_lib_file,"-all_load",batch_lib_file]+#elif defined(mingw32_HOST_OS)+  execDir <- getLibDir+  r <- rawSystem (maybe "" (++"/gcc-lib/") execDir++"ld") ["-r","-x","-o",ghci_lib_file,"--whole-archive",batch_lib_file]+#else+  r <- rawSystem "ld" ["-r","-x","-o",ghci_lib_file,"--whole-archive",batch_lib_file]+#endif+  when (r /= ExitSuccess) $ exitWith r+  when (verbosity >= Normal) $+    infoLn (" done.")++-- -----------------------------------------------------------------------------+-- Searching for modules++#if not_yet++findModules :: [FilePath] -> IO [String]+findModules paths =+  mms <- mapM searchDir paths+  return (concat mms)++searchDir path prefix = do+  fs <- getDirectoryEntries path `catchIO` \_ -> return []+  searchEntries path prefix fs++searchEntries path prefix [] = return []+searchEntries path prefix (f:fs)+  | looks_like_a_module  =  do+        ms <- searchEntries path prefix fs+        return (prefix `joinModule` f : ms)+  | looks_like_a_component  =  do+        ms <- searchDir (path </> f) (prefix `joinModule` f)+        ms' <- searchEntries path prefix fs+        return (ms ++ ms')+  | otherwise+        searchEntries path prefix fs++  where+        (base,suffix) = splitFileExt f+        looks_like_a_module =+                suffix `elem` haskell_suffixes &&+                all okInModuleName base+        looks_like_a_component =+                null suffix && all okInModuleName base++okInModuleName c++#endif++-- ---------------------------------------------------------------------------+-- expanding environment variables in the package configuration++expandEnvVars :: String -> Force -> IO String+expandEnvVars str0 force = go str0 ""+ where+   go "" acc = return $! reverse acc+   go ('$':'{':str) acc | (var, '}':rest) <- break close str+        = do value <- lookupEnvVar var+             go rest (reverse value ++ acc)+        where close c = c == '}' || c == '\n' -- don't span newlines+   go (c:str) acc+        = go str (c:acc)++   lookupEnvVar :: String -> IO String+   lookupEnvVar "pkgroot"    = return "${pkgroot}"    -- these two are special,+   lookupEnvVar "pkgrooturl" = return "${pkgrooturl}" -- we don't expand them+   lookupEnvVar nm =+        catchIO (System.Environment.getEnv nm)+           (\ _ -> do dieOrForceAll force ("Unable to expand variable " +++                                        show nm)+                      return "")++-----------------------------------------------------------------------------++getProgramName :: IO String+getProgramName = liftM (`withoutSuffix` ".bin") getProgName+   where str `withoutSuffix` suff+            | suff `isSuffixOf` str = take (length str - length suff) str+            | otherwise             = str++bye :: String -> IO a+bye s = putStr s >> exitWith ExitSuccess++die :: String -> IO a+die = dieWith 1++dieWith :: Int -> String -> IO a+dieWith ec s = do+  prog <- getProgramName+  reportError (prog ++ ": " ++ s)+  exitWith (ExitFailure ec)++dieOrForceAll :: Force -> String -> IO ()+dieOrForceAll ForceAll s = ignoreError s+dieOrForceAll _other s   = dieForcible s++warn :: String -> IO ()+warn = reportError++-- send info messages to stdout+infoLn :: String -> IO ()+infoLn = putStrLn++info :: String -> IO ()+info = putStr++ignoreError :: String -> IO ()+ignoreError s = reportError (s ++ " (ignoring)")++reportError :: String -> IO ()+reportError s = do hFlush stdout; hPutStrLn stderr s++dieForcible :: String -> IO ()+dieForcible s = die (s ++ " (use --force to override)")++my_head :: String -> [a] -> a+my_head s []      = error s+my_head _ (x : _) = x++getLibDir :: IO (Maybe String)+getLibDir = return $ Just hasteSysDir++-----------------------------------------+-- Adapted from ghc/compiler/utils/Panic++installSignalHandlers :: IO ()+installSignalHandlers = do+  threadid <- myThreadId+  let+      interrupt = Exception.throwTo threadid+                                    (Exception.ErrorCall "interrupted")+  --+#if !defined(mingw32_HOST_OS)+  _ <- installHandler sigQUIT (Catch interrupt) Nothing+  _ <- installHandler sigINT  (Catch interrupt) Nothing+  return ()+#else+  -- GHC 6.3+ has support for console events on Windows+  -- NOTE: running GHCi under a bash shell for some reason requires+  -- you to press Ctrl-Break rather than Ctrl-C to provoke+  -- an interrupt.  Ctrl-C is getting blocked somewhere, I don't know+  -- why --SDM 17/12/2004+  let sig_handler ControlC = interrupt+      sig_handler Break    = interrupt+      sig_handler _        = return ()++  _ <- installHandler (Catch sig_handler)+  return ()+#endif++#if mingw32_HOST_OS || mingw32_TARGET_OS+throwIOIO :: Exception.IOException -> IO a+throwIOIO = Exception.throwIO+#endif++catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+catchIO = Exception.catch++catchError :: IO a -> (String -> IO a) -> IO a+catchError io handler = io `Exception.catch` handler'+    where handler' (Exception.ErrorCall err) = handler err++tryIO :: IO a -> IO (Either Exception.IOException a)+tryIO = Exception.try++writeBinaryFileAtomic :: Bin.Binary a => FilePath -> a -> IO ()+writeBinaryFileAtomic targetFile obj =+  withFileAtomic targetFile $ \h -> do+     hSetBinaryMode h True+     B.hPutStr h (Bin.encode obj)++writeFileUtf8Atomic :: FilePath -> String -> IO ()+writeFileUtf8Atomic targetFile content =+  withFileAtomic targetFile $ \h -> do+     hSetEncoding h utf8+     hPutStr h content++-- copied from Cabal's Distribution.Simple.Utils, except that we want+-- to use text files here, rather than binary files.+withFileAtomic :: FilePath -> (Handle -> IO ()) -> IO ()+withFileAtomic targetFile write_content = do+  (newFile, newHandle) <- openNewFile targetDir template+  do  write_content newHandle+      hClose newHandle+#if mingw32_HOST_OS || mingw32_TARGET_OS+      renameFile newFile targetFile+        -- If the targetFile exists then renameFile will fail+        `catchIO` \err -> do+          exists <- doesFileExist targetFile+          if exists+            then do removeFileSafe targetFile+                    -- Big fat hairy race condition+                    renameFile newFile targetFile+                    -- If the removeFile succeeds and the renameFile fails+                    -- then we've lost the atomic property.+            else throwIOIO err+#else+      renameFile newFile targetFile+#endif+   `Exception.onException` do hClose newHandle+                              removeFileSafe newFile+  where+    template = targetName <.> "tmp"+    targetDir | null targetDir_ = "."+              | otherwise       = targetDir_+    --TODO: remove this when takeDirectory/splitFileName is fixed+    --      to always return a valid dir+    (targetDir_,targetName) = splitFileName targetFile++openNewFile :: FilePath -> String -> IO (FilePath, Handle)+openNewFile dir template = do+  -- this was added to System.IO in 6.12.1+  -- we must use this version because the version below opens the file+  -- in binary mode.+  openTempFileWithDefaultPermissions dir template++-- | The function splits the given string to substrings+-- using 'isSearchPathSeparator'.+parseSearchPath :: String -> [FilePath]+parseSearchPath path = split path+  where+    split :: String -> [String]+    split s =+      case rest' of+        []     -> [chunk]+        _:rest -> chunk : split rest+      where+        chunk =+          case chunk' of+#ifdef mingw32_HOST_OS+            ('\"':xs@(_:_)) | last xs == '\"' -> init xs+#endif+            _                                 -> chunk'++        (chunk', rest') = break isSearchPathSeparator s++readUTF8File :: FilePath -> IO String+readUTF8File file = do+  h <- openFile file ReadMode+  -- fix the encoding to UTF-8+  hSetEncoding h utf8+  hGetContents h++-- removeFileSave doesn't throw an exceptions, if the file is already deleted+removeFileSafe :: FilePath -> IO ()+removeFileSafe fn =+  removeFile fn `catchIO` \ e ->+    when (not $ isDoesNotExistError e) $ ioError e++absolutePath :: FilePath -> IO FilePath+absolutePath path = return . normalise . (</> path) =<< getCurrentDirectory++-- | Only global packages may be marked as relocatable!+--   May break horribly for general use, only reliable for Haste base packages.+relocate :: [String] -> String -> Sh.Shell ()+relocate packages pkg = do+    pi <- Sh.run hastePkgBinary (packages ++ ["describe", pkg]) ""+    Sh.run_ hastePkgBinary (packages ++ ["update", "-", "--force", "--global"])+                           (reloc pi)+  where+    reloc = unlines . map fixPath . lines++    fixPath s+      | isKey "library-dirs: " s       = prefix s "library-dirs" importDir+      | isKey "import-dirs: " s        = prefix s "import-dirs" importDir+      | isKey "haddock-interfaces: " s = prefix s "haddock-interfaces" importDir+      | isKey "haddock-html: " s       = prefix s "haddock-html" importDir+      | isKey "include-dirs: " s       = "include-dirs: " ++ includeDir+      | otherwise                      = s++    prefix s pfx path = pfx ++ ": " ++ path </> stripPrefix s++    stripPrefix s+      | os == "darwin" =+        case take 3 $ reverse $ splitPath s of+          [third, second, first] -> first </> second </> third+      | otherwise =+        case take 2 $ reverse $ splitPath s of+          [second, first] -> first </> second++    isKey _ "" =+      False+    isKey key str =+      and $ zipWith (==) key str++    importDir+      | os == "linux"  = "${pkgroot}" </> "libraries" </> "lib"+      | otherwise      = "${pkgroot}" </> "libraries"+    includeDir = "${pkgroot}" </> "include"