Open sanketr opened 7 years ago
JavaScript files can be loaded into the context using eval with something like liftIO (readFile "something.js") >>= eval
.
You might want to embed the JS file into your executable with file-embed instead of using readFile (file-embed uses template haskell so it does not work yet on GHC cross compiling to iOS and Android yet).
Thank you!
One question about eval
:
we get JSM JSVal
out of it. Let us say, we evaluated a script which defines a function testfn
. How do we retrieve testfn
from JSVal
object we got from the eval
of that script? Surely, the function needs to be pulled out using some kind of indexing (I am guessing, with a Maybe
type signature in case it is not found) - so, lens indexing makes sense. I am not very familiar with conjoined type of lens indexing used here. That is why when I looked at jsaddle-hello
functions like js
and jsg
, I couldn't figure out how to pass testfn
to them. So, pointers will be very helpful.
You can create a single function with eval and then use call to call it like this:
f <- eval "function(x) {console.log(x)}"
call f (toJSVal "Helllo")
For a JavaScript library that defines functions in the JavaScript global scope use something like:
eval "var test = function(x) {console.log(x)}"
jsg1 "test" (toJSVal "Helllo") -- Like JavaScript test("Hello");
Note: in jsg1
the g
means it is in the global scope 1
means it is a function to be called with one argument.
If the library defines the functions inside some other object you might need to do something like:
eval "var test = { f: function(x) {console.log(x)} }"
jsg "test" ^. js1 "f" (toJSVal "Helllo") -- Like JavaScript test.f("Hello");
Without the number jsg "test"
becomes a getter for the attribute. You can think of ^.
in this code as being like the .
in JavaScript.
I wrote a small test along the lines you suggested, but am getting JSException
- what will be a good way to debug it to find the root cause? Here is the code - I can see JSException
in the debug output but can't figure out what I might be doing wrong with FFI. I did sanity check of the js code in node
to make sure the js code works fine.
Test module that simulates a js function loaded through library:
{-# LANGUAGE ScopedTypeVariables #-}
module Test ( main ) where
import Control.Monad.IO.Class (MonadIO(..))
import Control.Concurrent (forkIO)
import Control.Lens ((^.))
import Language.Javascript.JSaddle
(call,jsg1, js1, jss, fun, syncPoint, toJSVal,fromJSVal)
import Language.Javascript.JSaddle.Evaluate (eval)
import Language.Javascript.JSaddle.Run (enableLogging)
main = do
enableLogging True
eval "var test = function(x) {return x.length;}"
(res :: Maybe Int) <- jsg1 "test" (toJSVal "Helllo") >>= fromJSVal
liftIO $ print $ "String length: " ++ show res
return ()
WKWebView wrapper around test module:
module Main ( main ) where
import qualified Test (main) import Language.Javascript.JSaddle.WKWebView (run)
main = run Test.main
When compiled with `ghc 8.0.2` (with `-dynamic -threaded` option) on `mac sierra`, I get this when running the executable - btw, executable is named `wkwebmain` here:
Sync M ?? CB 0 (5,Right (ValueToNumber (JSValueForSend (-4)))) A JavaScript exception was thrown! (may not reach Haskell code) wkwebmain: JSException Sync M ?? CB 0 (1,Left (FreeRef "ThreadId 12" (JSValueForSend (-1)))) Sync M ?? CB 0 (4,Left (FreeRef "ThreadId 15" (JSValueForSend (-4)))) Sync M ?? CB 0 (1,Left (FreeRefs "ThreadId 15"))
Sorry, I forgot that eval
does not run in the global scope. Try "test = function(x) {return x.length;}"
.
To debug this I used the jsaddle-warp
runner. You can right click and get an inspector with the other runners, but often it is too late by then. With jsaddle-warp
you can load a browser and have the inspector window open before you connect it. I set it to break on exceptions then connected and it stopped when calling .apply
on the a undefined
. A quick look for window.test
showed it did not exist, but I did see test
in the local scope.
Very helpful pointers, thank you! Now, I am able to fix the error, as well as run the debugger using jsaddle-warp
- this also resolves the mystery of putMVar
getting stuck on askJSM
(cribbed your testJSaddle
function). Apparently, the browser needs to be fired up and warp server needs to be hit, for the page to be served, and hence, context to kick in. I forgot about this detail, and was waiting for the browser to pop up automatically. When you laid out debugging steps, I realized my mistake. Now, let me see if I can write a simple test, and then submit a pull request for inclusion in hello example.
@hamishmack I'm currently trying to implement bluetooth.requestDevice
using JSaddle. The goal is to structure a function that would resemble the following.
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* ... */ })
.catch(error => { console.log(error); });
Here is a rough idea of what I have so far:
listBluetoothDevices = do
nav <- liftJSM $ jsg ("navigator" :: Text)
let handleBluetoothFunc = eval (textToStr ("(function(){device => { console.log(1); }).catch(error => { console.log(error); });" :: Text))
bluetooth = nav
^. JSaddle.js ("bluetooth" :: Text)
^. js1 ("requestDevice" :: Text) ("{ acceptAllDevices: true }" :: Text)
bluetoothResults <- liftJSM $ JSaddle.call bluetooth handleBluetoothFunc ()
What are the best practices for creating a Promise
in JSaddle?
Lastly, what is the best way to obj.func().then().catch()
?
It will be helpful to show how to load the third-party libraries that are referenced in JS FFI. It can be just a simple js library/script defining, say an add function. The example can then show how to load that JS library (basically, how to link to the library referenced in FFI) and use the function referenced there.
I know how to do it through ghcjs (easy to script tag in index.html that ghcjs generates), but not how to do this through ghc. Will also appreciate pointers in comments because this will be very helpful in immediately moving forward with jsaddle, and third party JS libraries - I plan to use jsaddle, wkwebview and ghc along the lines of the hello example demonstrated here, but with third-party JS libraries also included.