Coder Social home page Coder Social logo

ghcjs-base's Introduction

Introduction

GHCJS is a Haskell to JavaScript compiler that uses the GHC API.

Quick Start - Developing GHCJS

GHCJS contains a library, ghcjs, which contains the JavaScript code generator and a slightly customized variant of the ghc library, and several executable programs.

The repository has several submodules and some files must be generated before the package can be installed.

prerequisites

GHC

You need the same major version of GHC as the version of the GHCJS branch you're building.

cabal-install

cabal-install 3.0 is supported

emscripten emsdk

GHCJS uses a C toolchain, mostly for build system related tasks like the C preprocessor, Autoconf scripts and tools like hsc2hs. Direct support for using compiled foreign libraries from Haskell code may follow at a later date.

Please follow the installation instructions at https://emscripten.org/docs/getting_started/index.html

GHCJS requires the "upstream" emscripten backend, which is the default now. The earlier "fastcomp" backend will not work.

getting and preparing the source tree

$ git clone https://github.com/ghcjs/ghcjs.git
$ cd ghcjs
$ git submodule update --init --recursive

building the compiler

GHCJS depends on a few "local" packages in the source tree. You can use cabal-install and stack to set up a build environment that contains these packages.

Cabal new-install

After the source tree has been prepared, the package can be installed. You may want ensure that binaries of earlier versions are overwritten:

cabal v2-install --overwrite-policy=always --install-method=copy --installdir=inplace/bin

At the time of writing, cabal-install does not support creating symbolic links on Windows, even though this is the default installation method. A workaround is telling it to copy the executables instead:

cabal v1-install --prefix=inplace

v1 style Cabal sandbox

v1 style cabal sandboxes are also supported

if you want to build with a Cabal sandbox, use the makeSandbox.sh script to add the local packages.

$ cabal v1-sandbox init
$ cabal v1-install

stack

or you can use stack:

$ stack --system-ghc --skip-ghc-check install --local-bin-dir=inplace/bin

Booting GHCJS

The ghcjs-boot program builds the "boot" libraries, like ghc-prim, base and template-haskell with GHCJS. After booting, GHCJS can compile regular Haskell programs and packages.

ghcjs-boot needs to be able to find the emscripten toolchain, a nodejs executable. The easiest way to do this is by running the emsdk_env.sh script. After that, you can run ghcjs-boot by pointing it to the boot libraries (the directory containing the boot.yaml file)

$ source ~/emsdk/emsdk_env.sh
$ ./inplace/bin/ghcjs-boot -s ./lib/boot

GHCJS executables and library paths

After booting, you can add the directory containing the GHCJS binaries to your executable PATH. The ghcjs-boot program prints the location after finishing building the libraries.

You can also create a symbolic link for the ghcjs and ghcjs-pkg programs, or use the --with-compiler and --with-hc-pkg flags when using cabal-install

Generating a source distribution

if you work on boot packages that need some for an upstream library, make sure to update the patches in /lib/patches first

$ ./utils/updatePatches.sh

then regenerate the packages

$ ./utils/makePackages.sh

ghcjs-base's People

Contributors

alaendle avatar bergey avatar crocket avatar eryx67 avatar eugenen avatar getcontented avatar hamishmack avatar hsyl20 avatar jeremyschlatter avatar joshmeredith avatar kfigiela avatar louispan avatar luigy avatar luite avatar matthewbauer avatar mgsloan avatar mikelpr avatar ocharles avatar rimmington avatar sevanspowell avatar sgrb avatar tavisrudd avatar werehamster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ghcjs-base's Issues

Duplicated h$textFromString

  • There are 2 implementations of h$textFromString
  • It seems only one of them is used, but not sure which.
  • Might this issue cause problems when compiling with google's closure compiler? (My text strings containing \n trigger exception when optimized with the closure compiler, could this be related? ).
  • If there's a reason to have to h$textFromString, do you mind adding a comment explaining it?

Dereferencing an exported (0 :: Int) fails

Int prim type is represented as JavaScript integer, so this check in h$derefExport wrongfully fails:

if (!e.root) return null;

Same problem with anything that is represented in JavaScript as a falsy value (false, undefined, empty string).

Bring back ToJSString / FromJSString? (improved-base)

The definition in ghcjs-dom seems reasonable https://github.com/ghcjs/ghcjs-dom/blob/master/src/GHCJS/DOM/Types.hs#L984, though maybe instead of using empty typeclasses, they could instead be defined like this:

class ToJSString a where
    toJSString :: a -> JSString
    default toJSString :: (PToJSRef a, ToJSRef a) => a -> JSString
    toJSString = pFromJSRef . pToJSRef

Or maybe it's good that it forces you to make your toJSRef also be a conversion to string?

Capture `this` in callbacks

The current callbacks into Haskell from JavaScript don't provide a means of accessing the this value of the current scope. It's an implicit argument to every function, yet the current callback code doesn't recognize this. I wrote some code to kind of brute-force bring this in as an argument for a Callback, so it's not impossible to work around, but first-class support in ghcjs-base would better reflect the reality of how JavaScript libraries often work.

For reference, here's a way to grab this:

function captureThis(f) {
  return function() {
    var args = Array.from(arguments);
    args.unshift(this);
    f.apply(this, args);
  };
}
-- we don't want to treat the wrapped version as a callback on the Haskell side
-- because there shouldn't be an attempt to release it.
foreign import javascript unsafe "captureThis($1)" captureThis :: Callback a -> JSVal

h$sendXHR is not defined

I'm getting the following error:

JavaScript exception: ReferenceError: h$sendXHR is not defined

Is this because /jsbits/xhr.js is not included in the js-sources field in the cabal file?

improved-base wrapping problems

When returning an unboxed tuple in the javascript FFI, wrapping is expected from the JS code, even for JSRef. This is not done correctly everywhere, and is a source for confusion.

I'd like to change the convention to the following:

  • Allow unboxed tuple returns of all ccall types, JSRef and Any
  • wrapping is performed like for non-tuple returns
  • Any is to be wrapped by the callee, it's safe to unsafeCoerce to the actual type (including pre-wrapped JSRef)

This will probably lead to temporary breakage between ghcjs and ghcjs-base, but the current implementation is also buggy so it's better to make the change now, rather then when unboxed tuples are used more in the wild.

fromByteString produces a null Buffer when the ByteString is empty

This program:

{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}

import qualified Data.ByteString as BS
import GHCJS.Types
import GHCJS.Buffer

main :: IO ()
main = do
  let (b, _, _) = fromByteString BS.empty
  consoleLog $ jsval $ getArrayBuffer b

foreign import javascript safe "console.log($1)" consoleLog :: JSVal -> IO ()

produces the following error:

uncaught exception in Haskell main thread: TypeError: Cannot read property 'buf' of null
TypeError: Cannot read property 'buf' of null
    at h$$k4 (/home/ryan/try-reflex22/fromByteStringIssue.jsexe/all.js:37401:57)
    at h$runThreadSlice (/home/ryan/try-reflex22/fromByteStringIssue.jsexe/all.js:9790:11)
    at h$runThreadSliceCatch (/home/ryan/try-reflex22/fromByteStringIssue.jsexe/all.js:9741:12)
    at Immediate.h$mainLoop (/home/ryan/try-reflex22/fromByteStringIssue.jsexe/all.js:9736:9)
    at processImmediate [as _immediateCallback] (timers.js:374:17)

But, when the BS.empty is replaced with a non-empty ByteString, it outputs

ArrayBuffer {}

as expected.

using Data.Time.getZonedTime causes exception, undefined symbol

The following program:

import Data.Time
main = getZonedTime >>= print

Generates this error in console:

uncaught exception in Haskell main thread: ReferenceError: h$get_current_timezone_seconds is not defined
ReferenceError: h$get_current_timezone_seconds is not defined
at h$$q1 (out.js:18651)
at h$mainLoop (rts.js:9519)
at rts.js:3632
at runIfPresent (rts.js:3650)
at onGlobalMessage (rts.js:3691)

$ ghcjs --version
The Glorious Glasgow Haskell Compilation System for JavaScript, version 0.1.0 (GHC 7.8.3.20141119)

Fix compiler warnings.

When I compiled ghcjs-base with ghcjs-0.2.0.9006021_ghc-7.10.3 from http://ghcjs.tolysz.org/lts-6.21-9006021.tar.gz, ghcjs gave the following warnings.

The warnings are sorted.

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Generic.hs:2:45: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:2:54: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:111:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STUint8Array s) = Word8

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:112:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STUint8ClampedArray s) = Word8

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:113:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STUint16Array s) = Word16

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:114:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STUint32Array s) = Word

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:115:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STInt8Array s) = Int8

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:116:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STInt16Array s) = Int16

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:117:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STInt32Array s) = Int

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:118:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STFloat32Array s) = Double

/home/crocket/repos/other/ghcjs-base/JavaScript/TypedArray/Internal/Types.hs:119:5: Warning:
    Overlapped type family instance equation:
    forall (k :: BOX) (s :: k). Elem (STFloat64Array s) = Double
/home/crocket/repos/other/ghcjs-base/GHCJS/Foreign/Internal.hs:93:1: Warning:
    Module ‘Foreign.ForeignPtr.Safe’ is deprecated:
      Safe is now the default, please use Foreign.ForeignPtr instead
/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:38:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:39:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:40:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:41:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:42:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:43:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:44:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:45:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:46:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:47:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:48:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘decimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:132:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:133:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:134:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:135:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:136:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:137:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:138:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:139:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:140:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:141:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/Int.hs:142:1: Warning:
    SPECIALISE pragma on INLINE function probably won't fire: ‘hexadecimal’

/home/crocket/repos/other/ghcjs-base/Data/JSString/RealFloat.hs:29:1: Warning:
    Ignoring useless SPECIALISE pragma for NOINLINE function: ‘realFloat’

/home/crocket/repos/other/ghcjs-base/Data/JSString/RealFloat.hs:30:1: Warning:
    Ignoring useless SPECIALISE pragma for NOINLINE function: ‘realFloat’

/home/crocket/repos/other/ghcjs-base/Data/JSString/RealFloat.hs:41:1: Warning:
    Ignoring useless SPECIALISE pragma for NOINLINE function: ‘formatRealFloat’

/home/crocket/repos/other/ghcjs-base/Data/JSString/RealFloat.hs:42:1: Warning:
    Ignoring useless SPECIALISE pragma for NOINLINE function: ‘formatRealFloat’
/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Internal.hs:11:7: Warning:
    ‘MutableValue’ is exported by ‘MutableValue’ and ‘MutableValue’

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Internal.hs:11:21: Warning:
    ‘MutableValue'’ is exported by ‘MutableValue'’ and ‘MutableValue'’
/home/crocket/repos/other/ghcjs-base/GHCJS/Marshal.hs:28:1: Warning:
    Module ‘Data.Attoparsec.Number’ is deprecated:
      This module will be removed in the next major release.

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:71:1: Warning:
    Module ‘Data.Attoparsec.Number’ is deprecated:
      This module will be removed in the next major release.

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:1067:37: Warning:
    In the use of type constructor or class ‘Number’
    (imported from Data.Attoparsec.Number):
    Deprecated: "This module will be removed in the next major release."

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:1069:19: Warning:
    In the use of data constructor ‘D’
    (imported from Data.Attoparsec.Number):
    Deprecated: "This module will be removed in the next major release."

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:1070:19: Warning:
    In the use of data constructor ‘I’
    (imported from Data.Attoparsec.Number):
    Deprecated: "This module will be removed in the next major release."
/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:494:17: Warning:
    In the use of ‘parseTime’
    (imported from Data.Time.Format, but defined in time-1.5.0.1:Data.Time.Format.Parse):
    Deprecated: "use "parseTimeM True" instead"

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:516:16: Warning:
    In the use of ‘parseTime’
    (imported from Data.Time.Format, but defined in time-1.5.0.1:Data.Time.Format.Parse):
    Deprecated: "use "parseTimeM True" instead"

/home/crocket/repos/other/ghcjs-base/JavaScript/JSON/Types/Instances.hs:539:14: Warning:
    In the use of ‘parseTime’
    (imported from Data.Time.Format, but defined in time-1.5.0.1:Data.Time.Format.Parse):
    Deprecated: "use "parseTimeM True" instead"

Fix `js_indexOf1` in `Data/JSString/Raw.hs`

Here's the diff.

modified   Data/JSString/Raw.hs
@@ -140,7 +140,7 @@ foreign import javascript unsafe
 foreign import javascript unsafe
   "$3.indexOf($1,$2)" js_indexOf :: JSString -> Int# -> JSString -> Int#
 foreign import javascript unsafe
-  "$3.indexOf($1)" js_indexOf1 :: JSString -> JSString -> Int#
+  "$2.indexOf($1)" js_indexOf1 :: JSString -> JSString -> Int#
 foreign import javascript unsafe
   "$2.charCodeAt($1)" js_charCodeAt :: Int# -> JSString -> Int#
 foreign import javascript unsafe

Export constructor for JSString

Is there any reason to not export JSString constructor? It is implemented as newtype around JSVal and not exported constructor cause to use unsafeCoerce :: JSVal -> JSString and vise versa instead of coerce or explicit packing/unpacking.

[Questions]

@luite, I still uncertain is it good to post questions on GitHub, because it could be distracting, so please let me know if there is some better place to ask questions.
I still toil with web socket connection realisation. But my first question is about types.
Consider following working code:

foreign import javascript safe
    "$r = (function(){if (window.config) return window.config.gameMaker else return undefined}())"
    js_get_gmConfig :: IO (JSRef a)

-- ...
    where
        read :: IO (Maybe ModuleConfig)
        read = do
            jc <- js_get_gmConfig
            cv <- fromJSRef jc
            return $ cv >>= parseMaybe parseJSON

This works correct, here I write configuration on server side, then on client side after page loaded I try to read configuration, which may be stored in global variable config. If configuration does not exists my foreign function will return undefined value, in this case fromJSRef will return Nothing.
Code above compiles and work as expected.
But following code rejected by compiler:

foreign import javascript safe "$r = (function () {\
        \var conn = new WebSocket($1);\
        \conn.onmessage = $2;\
        \conn.onopen = $3;\
        \conn.onclose = $4;\
        \return conn;})()"
    js_newWebSocket :: JSString
                    -> JSFun (JSRef a -> IO ())
                    -> JSFun (IO ())
                    -> JSFun (IO ())
                    -> IO WebSocket
configuredWebSocketConnection :: String -> (MessageEvent -> IO ()) -> IO () -> IO () -> IO WebSocket
configuredWebSocketConnection url onms onop oncl = do
    onmscb <- syncCallback1 AlwaysRetain False (readMsg onms) -- :: (JSRef a -> IO b)
    onopcb <- syncCallback AlwaysRetain False onop
    onclcb <- syncCallback AlwaysRetain False oncl
    js_newWebSocket (toJSString url) onmscb onopcb onclcb
    where
        readMsg :: (MessageEvent -> IO()) -> JSRef a -> IO () -- line 56
        readMsg f jsm = do
            mv <- fromJSRef jsm
            case mv >>= parseMaybe parseJSON of  -- line 59
                Just evt -> f evt
                _        -> reportError "Unexpected message from web socket recieved."

Here is error message:

GDom/WebSockets.hs:59:36:
    Couldn't match type ‘a’ with ‘Value’
      ‘a’ is a rigid type variable bound by
          the type signature for
            readMsg :: (MessageEvent -> IO ()) -> JSRef a -> IO ()
          at GDom/WebSockets.hs:56:20
    Expected type: a
                   -> aeson-0.8.0.0:Data.Aeson.Types.Internal.Parser MessageEvent
      Actual type: Value
                   -> aeson-0.8.0.0:Data.Aeson.Types.Internal.Parser MessageEvent
    Relevant bindings include
      mv :: Maybe a (bound at GDom/WebSockets.hs:58:13)
      jsm :: JSRef a (bound at GDom/WebSockets.hs:57:19)
      readMsg :: (MessageEvent -> IO ()) -> JSRef a -> IO ()
        (bound at GDom/WebSockets.hs:57:9)
    In the first argument of ‘parseMaybe’, namely ‘parseJSON’
    In the second argument of ‘(>>=)’, namely ‘parseMaybe parseJSON’

The question is why first time I have no issue with treating JSRef a with JSRef Value, but second time compiler abuses?
Sorry, this question is more about Haskell in general rather than about GHCJS.
Update: I guess I've understood why its happening. Just closing this one, sorry for noise. I will ask second question in ghcjs's main repository.

Should JavaScript.Web.AnimationFrame re-export OnBlocked?

Using the AnimationFrame things requires an import of the GHCJS.Foreign.Callback to use the OnBlocked data type, exported by the seemingly unrelated (in hiearchy) module GHCJS.Foreign.Callback.

There's also the same data type in GHCJS.Concurrent, which I think is really the place that it should be in (or at least canonically exported from).

Todo:

  • Remove OnBlocked from GHCJS.Foreign.Callback, use the one from GHCJS.Concurrent instead
  • Decide whether GHCJS.Foreign.Callback and JavaScript.Web.AnimationFrame should re-export OnBlocked.

Add multi argument callback helpers

We should add something like this to GHCJS.Foreign.Callback (based on function from jsaddle):

syncCallbackMulti :: OnBlocked -> (JSVal -> [JSVal] -> IO ())
                               -> IO (Callback (JSVal -> JSVal -> IO ()), Object)
syncCallbackMulti onBlocked f = do
    callback <- syncCallback2 onBlocked $ \this args -> do
        rargs <- Array.toListIO (coerce args)
        f this rargs
    (callback,) <$> makeMultiArgCallback callback

foreign import javascript unsafe "$r = function () { $1(this, arguments); }"
    makeMultiArgCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object

Returns two values. The first is just for use with releaseCallback, the second is to pass on to JavaScript. We should change it to something more meaningful than Object.

JavaScript.Web.MessageEvent.getData broken

getData seems to have 2 == BlobData and 3 == ArrayBuffer:

getData :: MessageEvent -> MessageEventData
getData me = case js_getData me of
               (# 1#, r #) -> StringData      (JSString r)
               (# 2#, r #) -> BlobData        (SomeBlob r)
               (# 3#, r #) -> ArrayBufferData (SomeArrayBuffer r)
{-# INLINE getData #-}

but the js_getData FFI import seems to have 2 == ArrayBuffer and 3 == BlobData

foreign import javascript unsafe
  "$r2 = $1.data;\
  \$r1 = typeof $r2 === 'string' ? 1 : ($r2 instanceof ArrayBuffer ? 2 : 3)"
  js_getData :: MessageEvent -> (# Int#, JSVal #)

https://github.com/ghcjs/ghcjs-base/blob/master/JavaScript/Web/MessageEvent.hs

Submit ghcjs-base to hackage

@hamishmack confirmed that ghcjs-boot libraries do not depend on ghcjs-base, so it should be safe to use any version of ghcjs-base from hackage.

I think I can handle this one after #77 is merged.

I'll try to add anyone, who is enthusiastic and doesn't look malicious or crazy to me, to the maintainer list of ghcjs-base on hackage.

h$isObject is implemented incorrectly in `jsbits/utils.js`.

Let's have a look at

function h$isObject(o) {
return typeof(o) === 'object';
}

function h$isObject(o) {
    return typeof(o) === 'object';
}

typeof(null) is 'object', thus you should replace that with something like

function h$isObject(o) {
    return (o instanceof Object);
}

I don't know if instanceof Object is totally accurate yet. I'll add comments on this.

Add return value for synchronous callbacks

calling into Haskell from JS is a common task, and often a return value is needed in some way. Synchronous callbacks don't return a value currently, but they could. This would require converting the signalling mechanism for blocking to an exception.

This should probably be added to the ghcjs-base library, with some documentation on the limitations of synchronous tasks.

MVarListener dies in few seconds

GHC version 7.8.0.20140228
GHCJS version 0.1.0 (GHC 7.8.0.20140228)

I'm facing problem with h$makeMVarListener. The listener dies in few seconds after run.

Consider following example code:

{-# LANGUAGE JavaScriptFFI #-}
module Main where

import Control.Concurrent( forkIO )
import Control.Concurrent.MVar( MVar, newEmptyMVar, takeMVar )
import Control.Monad( forever )

import GHCJS.Foreign
import GHCJS.Types
import FRP.Sodium

data DOMElements_ = DOM_Button
                  | DOM_Div
                  | DOM_A
instance Show DOMElements_ where
    show DOM_Button = "button"
    show DOM_Div    = "div"
    show DOM_A      = "a"
type DOMElements     = JSRef DOMElements_

data DOMEvent_       = DOMEvent_
type DOMEvent        = JSRef DOMEvent_

data DOMEventSource_ = DOMEventSource_
type DOMEventSource  = JSRef DOMEventSource_

foreign import javascript unsafe "$r = document.body;"
    js_DocBody :: IO DOMEventSource

foreign import javascript unsafe "$r = document.createElement($1);"
    js_DocCreateElement :: JSString -> IO DOMEventSource

foreign import javascript unsafe "document.body.appendChild($1);"
    js_AppendBodyChild :: DOMEventSource -> IO ()

foreign import javascript unsafe "$1.addEventListener($2, h$makeMVarListener($3, false, false, $4));"
    js_MVarListener :: DOMEventSource -> JSString -> JSObject (MVar DOMEvent) -> JSBool -> IO ()

mkElement :: DOMElements_ -> IO DOMEventSource
mkElement = js_DocCreateElement . toJSString . show

appendToBody :: DOMEventSource -> IO DOMEventSource
appendToBody des = js_AppendBodyChild des >> return des

mkMVarListener_ :: DOMEventSource -> String -> (DOMEvent -> IO ()) -> Bool -> IO ()
mkMVarListener_ de et hnd prDef = do
    mv <- newEmptyMVar :: IO (MVar DOMEvent)
    forkIO (forever (takeMVar mv >>= hnd))
    js_MVarListener de (toJSString et) (mvarRef mv) (toJSBool prDef)

mkMVarListener  :: DOMEventSource -> String -> (DOMEvent -> IO ()) -> IO ()
mkMVarListener  de et hnd = mkMVarListener_ de et hnd False
mkMVarListener' :: DOMEventSource -> String -> (DOMEvent -> IO ()) -> IO ()
mkMVarListener' de et hnd = mkMVarListener_ de et hnd True

mkDom :: IO DOMEventSource
mkDom = do
   mkElement DOM_Button >>= appendToBody

bindHandler :: DOMEventSource -> IO ()
bindHandler des = mkMVarListener des "click" (\_ -> print "Hello!")

runTest :: IO ()
runTest = mkDom >>= bindHandler

main :: IO ()
main = runTest

Expose GHCJS.Types phantom types

I asked about this in the #ghcjs IRC channel a while ago but couldn't stick around long enough to check for an answer, so please do tell me if I'm mistaken about how this should work.

GHCJS.Types defines phantom types and wrappers that use them like so:

data JSString_
type JSString = JSRef JSString_

GHCJS.Foreign contains lines like the following, which implies that JSArray and JSRef should be parametrized by the same type of thing:

foreign import javascript safe "$2[$1]" js_index :: Int -> JSArray a -> IO (JSRef a)

This means that if I want to write an import with an argument that is an array of strings, what I need is a JSArray JSString_. I can't do that currently because JSString_ and its ilk are not exported from GHCJS.Types.

I know that they're all just phantom types and I could use GHCJS.Types.castRef to get around it, but I'd rather do less casting if possible.

Some kind of mind-map of the existing types on GHCJS, and how to convert to/from JS?

I'm trying to convert Haskell's Data.Vector.Unboxed.Vector Word32 to JavaScript's Uint32Array. I'm sure there is probably some short, one-line, perhaps even O(1) way to do it. The problem is finding it. Browsing through GHCJS's source code, I find a ton of different possibly related types:

SomeBuffer, SomeArrayBuffer, I.SomeInt32Array, ByteArray#, Buffer, ByteString, 
JSArray, MutableArrayBuffer, ArrayBuffer, STArrayBuffer, SomeSTTypedArray, 
SomeUint32Array, IOUint32Array, Uint32Array (...)

And that's not even 10% of the thing. I have an impression it is very well organized, and most of those types are probably necessary and I see a very clear relationship between them, JS arrays, and JS TypedArrays. Yet, the whole is still huge and it is not clear enough what each thing is for. Some kind of mind-map (even if in a written form), connecting all those pieces together, would come very hand for me and anyone else trying to understand GHCJS's source code. Is anyone willing to do the dirty job?

Use informative exceptions instead of Maybe for fromJSRef

Currently, the type of improved-base fromJSRef is fromJSRef :: FromJSRef a => JSRef -> Maybe a. Having a Maybe type for marshaling failures isn't very informative, and in practice, I find that I always want the Nothing case to be a failure. By using the Monad instance for Maybe, we lose info about why and where marshaling failed. Each time I use fromJSRef, I need to write my own error message saying that overall marshaling failed.

How can we resolve this? My proposal is to instead throw an exception. Here's why:

  1. It's more efficient than plumbing Maybe everywhere.
  2. fromJSRef already lives in IO.
  3. I almost always want marshaling failure to be an exception.
  4. In the cases where I want to do something upon marshaling exception, I can catch the exception.

I think the following is a reasonable type for exceptions during marshaling:

newtype FromJSRefException a = FromJSRefException a
  deriving (Typeable, Show)

instance (Show a, Typeable a) => Exception (FromJSRefException a)

Ideally, there would be a convention of always throwing exceptions wrapped in this, to provide a uniform way of catching marshaling issues. It may also be worth considering passing through a path like [(TypeRep, String)], so that it's clear where in the structure the error is occurring. It might be good to make this optional, though, as there could be quite a bit of overhead.

It's not possible to call `app.on('certificate-error', callback)` on electron.

I'm trying to find a good way to wrap https://github.com/electron/electron/blob/master/docs/api/app.md#event-certificate-error

newtype AppBlahCallback = AppBlahCallback JSVal

foreign import javascript interruptible
  "app.on('blah', function (a, b, c, callback) { $c(a, b, c, callback); })"
  waitAppBlah :: IO (JSVal, JSVal, JSVal, AppBlahCallback)

foreign import javascript unsafe "$1($2)"
  appBlahCallback :: AppBlahCallback -> Bool -> IO ()

main :: IO ()
main = do
  forkIO $ do
    (_, _, _, cb) <- waitAppBlah
    appBlahCallback cb True

I cannot retrieve IO (JSVal, JSVal, JSVal, AppBlahCallback) from waitAppBlah more than once, but the event can call the callback multiple times.

If I retrieved

IO (JSVal, JSVal, JSVal, AppBlahCallback)

from waitAppBlah twice,

app.on('blah', function (a, b, c, callback) { $c(a, b, c, callback); })

would just be called twice. This doesn't make sense.

Since no function on GHCJS/Foreign/Callback.hs generates a callback of 4 or more parameters, it's not possible to write the following snippet, either.

newtype AppBlahCallback = AppBlahCallback JSVal

foreign import javascript unsafe
  "app.on('blah', $1)"
  onAppBlah :: Callback (JSVal -> JSVal -> JSVal -> AppBlahCallback -> IO ()) -> IO ()

foreign import javascript unsafe "$1($2)"
  appBlahCallback :: AppBlahCallback -> Bool -> IO ()

main :: IO ()
main = do
  onAppBlah $ asyncCallback4 $ \_ _ _ cb -> appBlahCallback cb True

Does ghcjs-base need modification before I become able to write a wrapper for https://github.com/electron/electron/blob/master/docs/api/app.md#event-certificate-error?

Perhaps, do we need the following functions?

syncCallbackN :: Int -> OnBlocked -> a -> IO (Callback a)

asyncCallbackN :: Int -> a -> IO (Callback a)

Or, how about

asyncCallback ::
  (JSVal -> JSVal -> ... -> JSVal -> IO a) ->
  IO (Callback (JSVal -> JSVal -> ... -> JSVal -> IO a))

How do I convert a JSVal to an Object?

I'm exposing an API to JavaScript that takes a JavaScript Object and sets some properties on it. syncCallback2 gives me the JSVals passed by the caller, but I cannot figure out how to convert it to a JavaScript.Object.Object so I can pass it to setProp. Seems every useful conversion function is hidden in an internal model. I ended up simply using unsafeCoerce, which is not very satisfactory of course.

JSArray.fromListIO is missing a JSRef constructor

Here is the simple test program

module Main where

import GHCJS.Marshal
import GHCJS.Types
import JavaScript.Array (fromListIO, JSArray)

foreign import javascript unsafe
    "console.log($1)"
    js_log :: JSArray -> IO ()

main :: IO ()
main = do
    e <- fromListIO =<< mapM toJSRef ([1, 15, 25] :: [Int])
    js_log e

When I run it with node, I get the error

uncaught exception in Haskell main thread: TypeError: Cannot read property 't' of undefined
TypeError: Cannot read property 't' of undefined
    at h$e (/home/wuzzeb/projects/react-flux/dist/build/test/test.jsexe/all.js:20397:20)
    at h$$lG (/home/wuzzeb/projects/react-flux/dist/build/test/test.jsexe/all.js:37675:10)
    at Immediate.h$mainLoop (/home/wuzzeb/projects/react-flux/dist/build/test/test.jsexe/all.js:9798:25)
    at processImmediate [as _immediateCallback] (timers.js:367:17)

The functions look like

function h$$lG()
{
  var a = h$stack[(h$sp - 1)];
  h$sp -= 2;
  var b = h$fromHsListJSRef(a);
  h$p1(h$$lH);
  return h$e(b);
};

....


function h$e(h$RTS_578)
{
  h$r1 = h$RTS_578;
  if((typeof h$RTS_578 !== "object"))
  {
    return h$stack[h$sp];
  };
  var h$RTS_579 = h$RTS_578.f;
  if((h$RTS_579 === h$unbox_e))
  {
    h$r1 = h$RTS_578.d1;
    return h$stack[h$sp];
  };
  if (!h$RTS_579) { return h$RTS_579; }
  switch (h$RTS_579.t)
  {

...

I added a console.log to h$$lG, and the variable b inside it the javascript list [1, 15, 25]. The function h$e goes on to access the f property of this list which is undefined.

What I guess is the error is that fromListIO is defined as

fromListIO xs = IO (\s -> rnf xs `seq` js_toJSArray (unsafeCoerce xs) s)

foreign import javascript unsafe "h$fromHsListJSRef($1)"
  js_toJSArray :: Exts.Any -> State# s -> (# State# s, SomeJSArray m #)

But h$fromHsListJSRef returns a javascript array and we never put a JSRef constructor on this javascript array. I suspect that js_toJSArray should change to

foreign import javascript unsafe "h$fromHsListJSRef($1)"
 js_toJSArray :: Exts.Any -> IO (SomeJSArray m)

But I don't know why js_toJSArray was defined to expose the State# in the first place.

Non-exhaustive in GFromJSArr instance

Getting the below:

Progress: 1/2
GHCJS/Marshal/Internal.hs:(219,5)-(224,40): Non-exhaustive patterns in case

On generic serialization with a product type that contains a list. There is a partial pattern match here that I believe might be responsible.

Just (a',an) -> do

I believe a Nothing case just needs to be added, if this is correct I can submit a patch.

Document / export IsJSRef?

I was perusing the code in improved-base, and noticed IsJSRef:

class IsJSRef a where
  jsref_ :: a -> JSRef

  default jsref_ :: Coercible a JSRef => a -> JSRef
  jsref_ = coerce
  {-# INLINE jsref_ #-}

jsref :: IsJSRef a => a -> JSRef
jsref = jsref_
{-# INLINE jsref #-}

(what's up with having both jsref and jsref_?)

I was curious as to why this is the definition instead of

type IsJSRef a = Coercible a JSRef

But as I was writing this issue, I came up with a few reasons / remembered prior discussions:

Pros of typeclass:

  • Constraint can be used even if the constructor isn't imported (Coercible only works if the newtype constructor is in scope).
  • Less potential for scary type errors.

Pros of constraint synonym:

  • No need to define instance IsJSRef Thing for all the datatypes.

Cannot set TypedArrayData in XMLHttpRequest.RequestData

This code:

import           JavaScript.TypedArray
import qualified JavaScript.Web.XMLHttpRequest as XHR

setData :: Uint8Array -> XHR.Request -> XHR.Request
setData u8 req = req { XHR.reqData = XHR.TypedArrayData u8 }

Produces this error message:

Couldn't match kind ‘AnyK’ with ‘*’
Expected type: ghcjs-base-0.2.0.0:JavaScript.TypedArray.Internal.Types.SomeTypedArray
                 e 'ghcjs-base-0.2.0.0:GHCJS.Internal.Types.Immutable
  Actual type: Uint8Array
In the first argument of ‘XHR.TypedArrayData’, namely ‘u8’
In the ‘XHR.reqData’ field of a record

As far as I can tell, this is the intended purpose of XHR.TypedArrayData, so I'm not sure why it doesn't work.

Converting `undefined` value to Text

I faced some issue when tried to convert an undefined javascript value to Text with fromJSRef_fromJSString.
Consider following code:

foreign import javascript safe
    "$r = (function(){\
    \    if (window.config) { return window.config[$1]; }\
    \    else { return undefined; }\
    \}())"
    js_get_config_of :: JSString -> IO (JSRef a)
configRefByName :: ToJSString a => a -> IO (JSRef b)
configRefByName = js_get_config_of . toJSString

This is quite simple code to read globally stored configuration, if there is no configuration, undefined value will be returned.
Converting undefined value to String works, but a bit strange:

do
    undefRef <- configRefByName "not defined"
    str <- fromJSRef_fromJSString undefRef :: IO (Maybe String)
    print str

This prints Just "", though I supposed to see Nothing in this case. At least it works.
If I change result type to IO (Maybe Text) I'm facing runtime exception:

uncaught exception in Haskell main thread: TypeError: Cannot read property 'length' of undefined

Obviously, marshall code does not check if value actually valid and just treats it like array of chars (or something), and tries to read length of it.

FromJSVal Aeson instance broken w.r.t. objects.

Program to reproduce

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}

module Main where

import qualified "aeson" Data.Aeson as AE
import           "ghcjs-base" GHCJS.Types ( jsval )
import           "ghcjs-base" GHCJS.Marshal ( fromJSValUnchecked )
import           "ghcjs-base" GHCJS.Foreign ( jsTrue )
import qualified "ghcjs-base" JavaScript.Object as Obj


main :: IO ()
main = do
  putStrLn "Creating object"
  obj <- Obj.create

  Obj.setProp "foo" jsTrue obj

  putStrLn "Object -> JSVal"
  let jsv = jsval obj

  putStrLn "Going to Aeson"
  x <- (fromJSValUnchecked jsv) :: IO AE.Value -- <-- Error here

  putStrLn $ seq x "Evaluated x"

Output

Creating object

lib.js:4616 Object -> JSVal

lib.js:4616 Going to Aeson

rts.js:7062 uncaught exception in Haskell main thread: TypeError: Cannot read property 't' of undefined
rts.js:7062 TypeError: Cannot read property 't' of undefined
    at h$e (rts.js:18673)
    at h$$Bz (out.js:28148)
    at h$runThreadSlice (rts.js:8012)
    at h$runThreadSliceCatch (rts.js:7968)
    at h$mainLoop (rts.js:7963)
    at rts.js:2460
    at runIfPresent (rts.js:2478)
    at onGlobalMessage (rts.js:2512)
rts.js:8313 Uncaught h$ThreadAbortedError {code: 0}
h$exitProcess @ rts.js:8313
h$doneMain @ rts.js:8298
h$doneMain_e @ rts.js:12230
h$runThreadSliceCatch @ rts.js:7975
h$mainLoop @ rts.js:7963
(anonymous) @ rts.js:2460
runIfPresent @ rts.js:2478
onGlobalMessage @ rts.js:2512

ghcjs version:
The Glorious Glasgow Haskell Compilation System for JavaScript, version 0.2.1 (GHC 8.0.2)
ghcjs-0.2.020161101

Package versions:

    aeson-0.11.2.0
    array-0.5.1.1
    attoparsec-0.13.0.2
    base-4.9.0.0
    binary-0.8.3.0
    bytestring-0.10.8.1
    containers-0.5.7.1
    deepseq-1.4.2.0
    directory-1.2.6.2
    dlist-0.7.1.2
    fail-4.9.0.0
    filepath-1.4.1.0
    ghc-8.0.1
    ghc-boot-8.0.1
    ghc-boot-th-8.0.1
    ghc-prim-0.5.0.0
    ghci-8.0.1
    ghcjs-base-0.2.0.0
    ghcjs-dom-0.7.0.4
    ghcjs-dom-jsffi-0.7.0.4
    ghcjs-ffiqq-0.1.0.0
    ghcjs-prim-0.1.0.0
    ghcjs-th-0.1.0.0
    hashable-1.2.4.0
    integer-gmp-1.0.0.1
    mtl-2.2.2
    pretty-1.1.3.3
    primitive-0.6.1.0
    process-1.4.2.0
    rts-1.0
    scientific-0.3.4.7
    split-0.2.3.1
    syb-0.6
    tagged-0.8.5
    template-haskell-2.11.0.0
    text-1.2.2.1
    time-1.6.0.1
    transformers-0.5.2.0
    transformers-compat-0.5.1.4
    unix-2.7.2.0
    unordered-containers-0.2.7.0
    vector-0.11.0.0

[Question] Kinds of Retentions

I have a question about retention. Consider following code:

foreign import javascript safe "$r = (function () {\
        \var conn = new WebSocket($1);\
        \conn.onmessage = $2;\
        \conn.onopen = $3;\
        \conn.onclose = $4;\
        \return conn;})()"
    js_newWebSocket :: JSString
                    -> JSFun (JSRef a -> IO ())
                    -> JSFun (IO ())
                    -> JSFun (IO ())
                    -> IO WebSocket
configuredWebSocketConnection :: String -> (MessageEvent -> IO ()) -> IO () -> IO () -> IO WebSocket
configuredWebSocketConnection url onms onop oncl = do
    onmscb <- syncCallback1 NeverRetain False (readMsg onms) -- :: (JSRef a -> IO b)
    onopcb <- syncCallback NeverRetain False onop
    onclcb <- syncCallback NeverRetain False oncl
    js_newWebSocket (toJSString url) onmscb onopcb onclcb
    where
        readMsg :: (MessageEvent -> IO()) -> JSRef a -> IO ()
        readMsg f eref = do
            let vref = castRef eref :: JSRef Value
            mv <- fromJSRef vref
            print mv
            case mv >>= parseMaybe parseJSON of
                Just evt -> f evt
                _        -> reportError "Unexpected message from web socket recieved."

I have faced retention issue. First when I just tested the concept itself, I've used AlwaysRetain to create on—message callback (onmscb), at that moment there was no any parsing logic, it just printed "Message!" and it worked. Finally, I've introduced conversion from JSRef to MessageEvent, leaving retention as is. Due to this entire page became completely inaccessible, crashing in short amount of time. This «dead—lock» was resolved by changing retention kind to NeverRetain.
So why in first case printing worked, but parsing does not?
And small question, related to retention: what is CAFs (they are mentioned in retain description)?

chang type for retention

When making callbacks, change the retention option to something other than Bool, to make it more clear how to use

export roundtrip throws internal exception

I was expecting this to print Just 424242424242. Instead it throws a RTS exception.

import GHCJS.Foreign.Export
main = do
    withExport (424242424242 :: Int) $ \r -> do
        v <- derefExport r
        print (v :: Maybe Int)
uncaught exception in Haskell main thread: TypeError: Cannot read property 't' of undefined
rts.js:10133 TypeError: Cannot read property 't' of undefined
    at h$ap_1_0_fast (rts.js:18020)
    at h$$nm (out.js:14090)
    at h$mainLoop (rts.js:11317)
    at rts.js:3765
    at runIfPresent (rts.js:3784)
    at onGlobalMessage (rts.js:3832)

The problem appears to be that the heap object root is the integer, while h$ap_1_0_fast expects it to be an object with a f property.

Enable a isInstance method in JavaScript.Cast

Hi, I'm having trouble using Javascript.Cast for more complex types like React's SyntheticEvent

AFAIK there is no prototype exported by React for SyntheticEvent, so my logic for detecting if a javascript value is a SyntheticEvent is e && e.nativeEvent && e.nativeEvent instanceof Event

Unfortunately, this means I cannot use Javascript.Cast since the logic for detecting instance is hard-coded to use js_checkcast, which is:

foreign import javascript unsafe 
  "$1 instanceof $2" js_checkCast :: JSVal -> JSVal -> Bool

Would it be possible to invert the hard-coding and make the Cast class as follows?

class InstanceRef a where
  instanceRef :: a -> JSVal

class Cast a where
  unsafeWrap  :: JSVal -> a
  isInstance :: JSVal -> Maybe a
  default isInstance :: InstanceRef a => JSVal -> Maybe a
  isInstance x = js_checkCast x (instanceRef (undefined :: a))

cast :: forall a. Cast a => JSVal -> Maybe a
cast x | isInstance x = Just (unsafeWrap x)
           | otherwise     = Nothing
{-# INLINE cast #-}

How does a `FromJSVal` instance affect FFI?

According to my experiments, any Type with a PFromJSVal instance can be used as Nullable Type in FFI signatures.

I conjecture that FromJSVal instances affect FFI in a similar way.

Does anyone know about this?

Below is a full demonstration of how PFromJSVal affects FFI.

{-# LANGUAGE JavaScriptFFI, OverloadedStrings #-}
module Main where

import qualified Data.JSString as S
import qualified GHCJS.Types as T
import qualified GHCJS.Marshal as M
import qualified GHCJS.Marshal.Pure as MP
import qualified JavaScript.Object as O
import System.IO
import Unsafe.Coerce
import Control.Monad (forever)
import Data.Maybe (fromJust)
import qualified Data.Aeson as AE
import GHCJS.Nullable
import qualified Data.Time.Format as TF
import qualified Data.Time.Clock as TC

foreign import javascript interruptible
  "require('fs').stat($1, function (err, stat) { $c(err,stat) });"
  js_fsStat :: S.JSString -> IO (T.JSVal, Nullable FsStat)
foreign import javascript unsafe
  "$2[$1]" js_getProp :: S.JSString -> O.Object -> T.JSVal

newtype Time = Time TC.UTCTime deriving Show

foreign import javascript unsafe
  "$r=$1.toUTCString();" js_Date_toUTCString :: T.JSVal -> S.JSString

instance MP.PFromJSVal Time where
  pFromJSVal jsval = Time $ timeFunc utcDateString
    where utcDateString = S.unpack $ js_Date_toUTCString jsval
          timeFunc =
            TF.parseTimeOrError False TF.defaultTimeLocale TF.rfc822DateFormat

data FsStat = FsStat
  { dev :: Int , ino :: Int , mode :: Int
  , nlink :: Int , uid :: Int , gid :: Int
  , rdev :: Int , size :: Int , blksize :: Int
  , blocks :: Int , atime :: Time, mtime :: Time
  , ctime :: Time , birthtime :: Time } deriving Show

instance MP.PFromJSVal FsStat where
  pFromJSVal jsval =
    let obj = unsafeCoerce jsval
        dev' = MP.pFromJSVal $ js_getProp "dev" obj
        ino' = MP.pFromJSVal $ js_getProp "ino" obj
        mode' = MP.pFromJSVal $ js_getProp "mode" obj
        nlink' = MP.pFromJSVal $ js_getProp "nlink" obj
        uid' = MP.pFromJSVal $ js_getProp "uid" obj
        gid' = MP.pFromJSVal $ js_getProp "gid" obj
        rdev' = MP.pFromJSVal $ js_getProp "rdev" obj
        size' = MP.pFromJSVal $ js_getProp "size" obj
        blksize' = MP.pFromJSVal $ js_getProp "blksize" obj
        blocks' = MP.pFromJSVal $ js_getProp "blocks" obj
        atime' = MP.pFromJSVal $ js_getProp "atime" obj
        mtime' = MP.pFromJSVal $ js_getProp "mtime" obj
        ctime' = MP.pFromJSVal $ js_getProp "ctime" obj
        birthtime' = MP.pFromJSVal $ js_getProp "birthtime" obj
    in FsStat { dev = dev', ino = ino', mode=mode', nlink=nlink',
                uid=uid', gid=gid', rdev=rdev', size=size',
                blksize=blksize', blocks=blocks', atime=atime',
                mtime=mtime', ctime=ctime', birthtime=birthtime'}

fsStat :: String -> IO (AE.Value, Maybe FsStat)
fsStat f = do
  (err, stats) <- js_fsStat $ S.pack f
  err' <- fromJust <$> M.fromJSVal err
  return (err', nullableToMaybe stats)

prompt :: String -> IO String
prompt text = putStr text >> hFlush stdout >> getLine

main :: IO ()
main = forever $ do
  file <- prompt "Enter file name: "
  (err, fsstat) <- fsStat file
  case err of
    AE.Null -> putStrLn "No Error"
    AE.Object obj -> putStrLn $ show obj
    _ -> putStrLn $ "Unexpected data : " ++ (show err)
  putStrLn "-------------------------------------"
  case fsstat of
    Nothing -> putStrLn "No file stat"
    Just s -> putStrLn $ show s

`js-sources` is ignored on GHCJS REPL in ghcjs-base.

Any ghcjs-base function that relies on a javascript function from js-sources in ghcjs-base.cabal fails to find the javascript function on GHCJS REPL in ghcjs-base.

> syncCallbackMulti ContinueAsync $ \_ -> putStrLn "ok"
uncaught exception in Haskell main thread: ReferenceError: h$makeCallbackMulti is not defined
ReferenceError: h$makeCallbackMulti is not defined
    at h$$mainZCGHCJSziForeignziCallback_7 (eval at h$loadCodeStr (/home/crocket/.ghcjs/x86_64-linux-0.2.0.9006021-7.10.3/ghcjs/irunner.js:185:8), <anonymous>:76424:11)
    at h$runThreadSlice (eval at h$loadCodeStr (/home/crocket/.ghcjs/x86_64-linux-0.2.0.9006021-7.10.3/ghcjs/irunner.js:185:8), <anonymous>:9867:11)
    at h$runThreadSliceCatch (eval at h$loadCodeStr (/home/crocket/.ghcjs/x86_64-linux-0.2.0.9006021-7.10.3/ghcjs/irunner.js:185:8), <anonymous>:9823:12)
    at Immediate.h$mainLoop [as _callback] (eval at h$loadCodeStr (/home/crocket/.ghcjs/x86_64-linux-0.2.0.9006021-7.10.3/ghcjs/irunner.js:185:8), <anonymous>:9818:9)
    at runCallback (timers.js:637:20)
    at tryOnImmediate (timers.js:610:5)
    at processImmediate [as _immediateCallback] (timers.js:582:5)

Remove instance PFromJSVal a => PFromJSVal (Maybe a)

Please, remove instance PFromJSVal a => PFromJSVal (Maybe a) from GHCJS.Marshal.Pure

This is especially important for enum types. Let say, I want map a string property of JS object onto dedicated Haskell data type:

data X = XoneOption | XuselessThing | Xanother
instance PFromJSVal (Maybe X) where
    pFromJSVal js = if not (isTruthy js)
        then Nothing
        else case pFromJSVal js :: JSString of
            "XoneOption"    -> Just XoneOption
            "XuselessThing" -> Just XuselessThing
            "Xanother"      -> Just Xanother
            _               -> Nothing

But current implementation does not make a lot of sense, because if a is an instance of PFromJSVal, one should probably take into account that underlying JS object may be null.

public haddocks

It would be nice to have a public location for the ghcjs-base haddocks like other packages have on hackage.

add conversion between JS ArrayBuffer and `ByteArray#`

The IO implementation already uses the RTS function h$wrapBuffer to do some conversions, we should make this available from Haskell. These functions can go in GHCJS.Foreign and can be used by higher level functions to convert things that use these under the hood (Vector, UArray)

Proposed signatures:

fromArrayBuffer :: JSRef a -> IO ByteArray#
toArrayBuffer :: ByteArray# -> IO (JSRef a)

Should there be extra functions or arguments to:

  • optionally copy the buffer before converting
  • slice the buffer
  • other things like alsignment?

Error handling: fromArrayBuffer throws an exception if the JSRef is not an ArrayBuffer

ByteString <=> ArrayBuffer conversion seems extremely difficult

Libraries like aeson use ByteString and things like JavaScript.Web.WebSocket use ArrayBuffer. Converting from one to the other seems unduly difficult.

For example, I can used, fromByteString :: ByteString -> (Buffer, Int, Int) to get a Buffer from a ByteString and getArrayBuffer :: SomeBuffer any -> SomeArrayBuffer any to convert the Buffer to an ArrayBuffer. Except, that is not good enough because it does not use the offset and length values that fromByteString returns. And I can not seem to find a way to use them. I thought maybe I could use ArrayBuffer.slice except it is not exported.

It seems to me that ByteString <=> ArrayBuffer conversion might common enough that helper functions should provided that do it in a single step?

Not able to run the tests

I use stack, ghcjs from the git repo and lts-3.11 snapshot. I get lots of compiler errors of this form:

[snip]/ghcjs-base/test/Tests/Properties.hs:672:9:
No instance for (Show (Char -> Char -> Bool))
      (maybe you haven't applied enough arguments to a function?)
      arising from a use of ‘testProperty’
    In the expression: testProperty "j_groupBy" j_groupBy

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.