summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Henry <sylvain@haskus.fr>2022-06-15 15:57:20 +0200
committerSylvain Henry <sylvain@haskus.fr>2022-06-15 16:00:04 +0200
commit4fc40885f73c0aa256b041df16c114f7ce569c09 (patch)
tree89f5532e0010181e7765e4f7f80cdbea4c42d552
parent006c3f4ca577238d485c716bb042ff93cc43191a (diff)
downloadhaskell-4fc40885f73c0aa256b041df16c114f7ce569c09.tar.gz
Temporarily wire-in base's shim
Use JS_BASE_PATH env var to set base's shim directory (js_base for now) Also minor other changes
-rw-r--r--compiler/GHC/StgToJS/Linker/Linker.hs50
-rw-r--r--compiler/GHC/StgToJS/Rts/Rts.hs10
-rw-r--r--js_base/base.js.pp731
3 files changed, 768 insertions, 23 deletions
diff --git a/compiler/GHC/StgToJS/Linker/Linker.hs b/compiler/GHC/StgToJS/Linker/Linker.hs
index a09f15717d..4f2ef2de5b 100644
--- a/compiler/GHC/StgToJS/Linker/Linker.hs
+++ b/compiler/GHC/StgToJS/Linker/Linker.hs
@@ -124,7 +124,7 @@ import System.Directory ( createDirectoryIfMissing
, listDirectory
)
-import GHC.Driver.Session (targetWays_, DynFlags(..), addGlobalInclude)
+import GHC.Driver.Session (targetWays_, DynFlags(..))
import GHC.Unit.Module.Name
import GHC.Unit.Module (moduleStableString)
import GHC.Utils.Logger (Logger)
@@ -132,7 +132,7 @@ import GHC.Utils.TmpFs (TmpFs)
import GHC.Linker.Static.Utils (exeFileName)
-newtype LinkerStats = LinkerStats
+newtype LinkerStats = LinkerStats
{ bytesPerModule :: Map Module Word64 -- ^ number of bytes linked per module
}
@@ -180,6 +180,8 @@ link env lc_cfg cfg logger tmpfs dflags unit_env out include pkgs objFiles jsFil
| otherwise = "js"
createDirectoryIfMissing False out
B.writeFile (out </> "out" <.> jsExt) (linkOut link_res)
+
+ -- dump foreign references file (.frefs)
unless (lcOnlyOut lc_cfg) $ do
let frefsFile = if genBase then "out.base.frefs" else "out.frefs"
-- FIXME: Jeff (2022,03): GHCJS used Aeson to encode Foreign
@@ -194,29 +196,38 @@ link env lc_cfg cfg logger tmpfs dflags unit_env out include pkgs objFiles jsFil
BL.writeFile (out </> frefsFile <.> "json") jsonFrefs
BL.writeFile (out </> frefsFile <.> "js")
("h$checkForeignRefs(" <> jsonFrefs <> ");")
+
+ -- dump stats
unless (lcNoStats lc_cfg) $ do
let statsFile = if genBase then "out.base.stats" else "out.stats"
let stats = linkerStats (linkOutMetaSize link_res) (linkOutStats link_res)
writeFile (out </> statsFile) stats
+
+ -- Sylvain (2022-06): find RTS js files (shims) via an environment variable...
+ -- Remove when all files are located via Cabal's js-sources
+ let is_js_file f = "js" `isExtensionOf` f || "pp" `isExtensionOf` f
+ let find_env_shims env_var = do
+ lookupEnv env_var >>= \case
+ Nothing -> error (env_var ++ " env var not set!")
+ Just dir -> do
+ (fmap (dir </>) . filter is_js_file) <$> listDirectory dir
+
+ -- link with the RTS
unless (lcNoRts lc_cfg) $ do
- -- Sylvain (2022-06): find RTS js files (shims) via an environment variable...
- -- Remove when all files are located via Cabal's js-sources
- let is_js_file f = "js" `isExtensionOf` f || "pp" `isExtensionOf` f
- static_rts_files <- lookupEnv "JS_RTS_PATH" >>= \case
- Nothing -> error "JS_RTS_PATH env var not set: can't link the RTS!"
- Just dir -> (fmap (dir </>) . filter is_js_file) <$> listDirectory dir
-
- let all_rts_js = linkLibRTS link_res ++ static_rts_files
-
- withRts <- mapM (tryReadShimFile logger tmpfs dflags unit_env) all_rts_js
- BL.writeFile (out </> "rts.js") (BLC.pack (T.unpack rtsDeclsText)
- <> BL.fromChunks withRts
- <> BLC.pack (T.unpack $ rtsText cfg))
- -- FIXME (Sylvain, 2022-05): disable shims
- -- lla' <- mapM (tryReadShimFile logger tmpfs dflags unit_env) (linkLibA link_res)
+ static_rts_files <- find_env_shims "JS_RTS_PATH"
+ let all_rts_js = linkLibRTS link_res ++ static_rts_files
+
+ rts_js_bss <- mapM (tryReadShimFile logger tmpfs dflags unit_env) all_rts_js
+ BL.writeFile (out </> "rts.js") (BLC.pack rtsDeclsText
+ <> BL.fromChunks rts_js_bss
+ <> BLC.pack (rtsText cfg))
+
+ static_base_files <- find_env_shims "JS_BASE_PATH"
+ let all_lib_js = linkLibA link_res ++ static_base_files
+ lla' <- mapM (tryReadShimFile logger tmpfs dflags unit_env) all_lib_js
-- llarch' <- mapM (readShimsArchive dflags) (linkLibArch link_res)
-- let lib_js = BL.fromChunks $ llarch' ++ lla'
- let lib_js = BL.empty
+ let lib_js = BL.fromChunks lla'
BL.writeFile (out </> "lib" <.> jsExt) lib_js
if genBase
@@ -280,6 +291,7 @@ link' env lc_cfg cfg dflags logger unit_env target _include pkgs objFiles _jsFil
-- c <- newMVar M.empty
let preload_units = preloadUnits (ue_units unit_env)
+ -- FIXME (Sylvain 2022-06): what are these "@rts" units?
let rtsPkgs = map stringToUnitId ["@rts", "@rts_" ++ waysTag (targetWays_ $ dflags)]
pkgs' :: [UnitId]
pkgs' = nub (rtsPkgs ++ preload_units ++ rdPkgs ++ reverse objPkgs ++ reverse pkgs)
@@ -294,6 +306,7 @@ link' env lc_cfg cfg dflags logger unit_env target _include pkgs objFiles _jsFil
logInfo logger $ hang (text "Linking with archives:") 2 (vcat (fmap text pkgArchs))
-- compute dependencies
+ -- FIXME (Sylvain 2022-06): why are we appending the home unit here?
let dep_units = pkgs' ++ [homeUnitId (ue_unsafeHomeUnit $ unit_env)] -- FIXME: dont use unsafe
dep_map = objDepsMap `M.union` archsDepsMap
excluded_units = baseUnits base -- already linked units
@@ -315,7 +328,6 @@ link' env lc_cfg cfg dflags logger unit_env target _include pkgs objFiles _jsFil
(all_deps `S.union` baseUnits base)
-- FIXME: (Sylvain, 2022-05): disabled because it comes from shims.
- -- Just delete?
-- (alreadyLinkedBefore, alreadyLinkedAfter) <- getShims [] (filter (isAlreadyLinked base) pkgs')
-- (shimsBefore, shimsAfter) <- getShims jsFiles pkgs''
return $ LinkResult
diff --git a/compiler/GHC/StgToJS/Rts/Rts.hs b/compiler/GHC/StgToJS/Rts/Rts.hs
index cd5917ec02..40151e4f1d 100644
--- a/compiler/GHC/StgToJS/Rts/Rts.hs
+++ b/compiler/GHC/StgToJS/Rts/Rts.hs
@@ -382,11 +382,13 @@ rtsDecls = jsSaturate (Just "h$RTSD") $
, declRegs
, declRets]
-rtsText :: StgToJSConfig -> T.ShortText
-rtsText = T.pack . show . pretty . rts
+-- FIXME (Sylvain 2022-06): don't use String
+rtsText :: StgToJSConfig -> String
+rtsText = show . pretty . rts
-rtsDeclsText :: T.ShortText
-rtsDeclsText = T.pack . show . pretty $ rtsDecls
+-- FIXME (Sylvain 2022-06): don't use String
+rtsDeclsText :: String
+rtsDeclsText = show . pretty $ rtsDecls
rts :: StgToJSConfig -> JStat
rts = jsSaturate (Just "h$RTS") . rts'
diff --git a/js_base/base.js.pp b/js_base/base.js.pp
new file mode 100644
index 0000000000..21a53ddca8
--- /dev/null
+++ b/js_base/base.js.pp
@@ -0,0 +1,731 @@
+#include "HsBaseConfig.h"
+
+// #define GHCJS_TRACE_IO 1
+
+#ifdef GHCJS_TRACE_IO
+function h$logIO() { h$log.apply(h$log, arguments); }
+#define TRACE_IO(args...) h$logIO(args)
+#else
+#define TRACE_IO(args...)
+#endif
+
+function h$base_access(file, file_off, mode, c) {
+ TRACE_IO("base_access");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.stat(fd, function(err, fs) {
+ if(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ } else {
+ c(mode & fs.mode); // fixme is this ok?
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+
+function h$base_chmod(file, file_off, mode, c) {
+ TRACE_IO("base_chmod");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.chmod(h$decodeUtf8z(file, file_off), mode, function(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+
+function h$base_close(fd, c) {
+ TRACE_IO("base_close fd: " + fd);
+ var fdo = h$base_fds[fd];
+ if(fdo) {
+ delete h$base_fds[fd];
+ if(--fdo.refs < 1) {
+ TRACE_IO("base_close: closing underlying fd");
+ if(fdo.close) {
+ fdo.close(fd, fdo, c);
+ } else {
+ c(0);
+ }
+ } else {
+ TRACE_IO("base_close: remaining references, not closing underlying fd");
+ c(0);
+ }
+ } else {
+ TRACE_IO("base_close: file descriptor not found, already closed?");
+ h$errno = CONST_EINVAL;
+ c(-1);
+ }
+}
+
+function h$base_dup(fd, c) {
+ // h$log("h$base_dup al: " + arguments.length);
+ h$base_dup2(fd, h$base_fdN--, c);
+}
+
+function h$base_dup2(fd, new_fd, c) {
+ TRACE_IO("base_dup2 " + fd + " " + new_fd);
+ // if(new_fd >= 0 && new_fd <= 2) {
+
+ // }
+ // h$log("h$base_dup2 al: " + arguments.length);
+ // if(fd >= 0 && fd < 2) {
+ // h$errno = CONST_EINVAL;
+ // c(-1);
+ // fixme make sure it can't be called again!
+ // return;
+ // } // && new_fd )
+
+ /* Fixme:
+ The two descriptors do not share file descriptor flags
+ (the close-on-exec flag). The close-on-exec flag
+ (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor is off.
+ */
+ var fdo = h$base_fds[fd];
+ if(!fdo) {
+ TRACE_IO("file descriptor not found");
+ h$errno = CONST_EINVAL;
+ c(-1);
+ } else {
+ var new_fdo = h$base_fds[new_fd];
+ function f() {
+ h$base_fds[new_fd] = fdo;
+ fdo.refs++;
+ c(new_fd);
+ }
+ if(new_fdo) {
+ TRACE_IO("closing existing fd");
+ h$base_close(new_fd, f);
+ } else {
+ f();
+ } // h$new_fdo.close();
+ }
+}
+
+function h$base_fstat(fd, stat, stat_off, c) {
+ TRACE_IO("base_stat");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.fstat(fd, function(err, fs) {
+ if(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ } else {
+ h$base_fillStat(fs, stat, stat_off);
+ c(0);
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+
+function h$base_isatty(fd) {
+ TRACE_IO("base_isatty " + fd);
+ // return 1; // fixme debug
+ var fdo = h$base_fds[fd];
+ if(fdo && typeof fdo.isatty !== 'undefined') {
+ if(typeof fdo.isatty === 'function') return fdo.isatty() ? 1 : 0;
+ return fdo.isatty ? 1 : 0;
+ }
+ return 0;
+}
+
+function h$base_lseek(fd, pos_1, pos_2, whence, c) {
+ TRACE_IO("base_lseek");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ var p = goog.math.Long.fromBits(pos_2, pos_1), p1;
+ var o = h$base_fds[fd];
+ if(!o) {
+ h$errno = CONST_BADF;
+ c(-1,-1);
+ } else {
+ switch(whence) {
+ case 0: /* SET */
+ o.pos = p.toNumber();
+ c(p.getHighBits(), p.getLowBits());
+ break;
+ case 1: /* CUR */
+ o.pos += p.toNumber();
+ p1 = goog.math.Long.fromNumber(o.pos);
+ c(p1.getHighBits(), p1.getLowBits());
+ break;
+ case 2: /* END */
+ h$fs.fstat(fd, function(err, fs) {
+ if(err) {
+ h$setErrno(err);
+ c(-1,-1);
+ } else {
+ o.pos = fs.size + p.toNumber();
+ p1 = goog.math.Long.fromNumber(o.pos);
+ c(p1.getHighBits(), p1.getLowBits());
+ }
+ });
+ break;
+ default:
+ h$errno = CONST_EINVAL;
+ c(-1,-1);
+ }
+ }
+ } else {
+#endif
+ h$unsupported();
+ c(-1, -1);
+#ifndef GHCJS_BROWSER
+ }
+#endif
+}
+
+function h$base_lstat(file, file_off, stat, stat_off, c) {
+ TRACE_IO("base_lstat");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.lstat(h$decodeUtf8z(file, file_off), function(err, fs) {
+ if(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ } else {
+ h$base_fillStat(fs, stat, stat_off);
+ c(0);
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_open(file, file_off, how, mode, c) {
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ var flags, off;
+ var fp = h$decodeUtf8z(file, file_off);
+ TRACE_IO("base_open: " + fp);
+ var acc = how & h$base_o_accmode;
+ // passing a number lets node.js use it directly as the flags (undocumented)
+ if(acc === h$base_o_rdonly) {
+ flags = h$processConstants['fs']['O_RDONLY'];
+ } else if(acc === h$base_o_wronly) {
+ flags = h$processConstants['fs']['O_WRONLY'];
+ } else { // r+w
+ flags = h$processConstants['fs']['O_RDWR'];
+ }
+ off = (how & h$base_o_append) ? -1 : 0;
+ flags = flags | ((how & h$base_o_trunc) ? h$processConstants['fs']['O_TRUNC'] : 0)
+ | ((how & h$base_o_creat) ? h$processConstants['fs']['O_CREAT'] : 0)
+ | ((how & h$base_o_excl) ? h$processConstants['fs']['O_EXCL'] : 0)
+ | ((how & h$base_o_append) ? h$processConstants['fs']['O_APPEND'] : 0);
+ h$fs.open(fp, flags, mode, function(err, fd) {
+ if(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ } else {
+ var f = function(p) {
+ h$base_fds[fd] = { read: h$base_readFile
+ , write: h$base_writeFile
+ , close: h$base_closeFile
+ , fd: fd
+ , pos: p
+ , refs: 1
+ };
+ TRACE_IO("base_open: " + fp + " -> " + fd);
+ c(fd);
+ }
+ if(off === -1) {
+ h$fs.stat(fp, function(err, fs) {
+ if(err) h$handleErrnoC(err, -1, 0, c); else f(fs.size);
+ });
+ } else {
+ f(0);
+ }
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_read(fd, buf, buf_off, n, c) {
+ TRACE_IO("base_read: " + fd);
+ var fdo = h$base_fds[fd];
+ if(fdo && fdo.read) {
+ fdo.read(fd, fdo, buf, buf_off, n, c);
+ } else {
+ h$fs.read(fd, buf.u8, buf_off, n, null, function(err, bytesRead, buf0) {
+ h$handleErrnoC(err, -1, bytesRead, c);
+ });
+ }
+}
+function h$base_stat(file, file_off, stat, stat_off, c) {
+ TRACE_IO("base_stat");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.stat(h$decodeUtf8z(file, file_off), function(err, fs) {
+ if(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ } else {
+ h$base_fillStat(fs, stat, stat_off);
+ c(0);
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_umask(mode) {
+ TRACE_IO("base_umask: " + mode);
+#ifndef GHCJS_BROWSER
+ if(h$isNode) return process.umask(mode);
+#endif
+ return 0;
+}
+
+function h$base_write(fd, buf, buf_off, n, c) {
+ TRACE_IO("base_write: " + fd);
+ var fdo = h$base_fds[fd];
+ if(fdo && fdo.write) {
+ fdo.write(fd, fdo, buf, buf_off, n, c);
+ } else {
+ h$fs.write(fd, buf.u8, buf_off, n, function(err, bytesWritten, buf0) {
+ h$handleErrnoC(err, -1, bytesWritten, c);
+ });
+ }
+}
+
+function h$base_ftruncate(fd, pos_1, pos_2, c) {
+ TRACE_IO("base_ftruncate");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.ftruncate(fd, goog.math.Long.fromBits(pos_2, pos_1).toNumber(), function(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_unlink(file, file_off, c) {
+ TRACE_IO("base_unlink");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.unlink(h$decodeUtf8z(file, file_off), function(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_getpid() {
+ TRACE_IO("base_getpid");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) return process.pid;
+#endif
+ return 0;
+}
+function h$base_link(file1, file1_off, file2, file2_off, c) {
+ TRACE_IO("base_link");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.link(h$decodeUtf8z(file1, file1_off), h$decodeUtf8z(file2, file2_off), function(err) {
+ h$handleErrnoC(err, -1, 0, c);
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_mkfifo(file, file_off, mode, c) {
+ throw "h$base_mkfifo";
+}
+function h$base_sigemptyset(sigset, sigset_off) {
+ return 0;
+ // throw "h$base_sigemptyset";
+}
+function h$base_sigaddset(sigset, sigset_off, sig) {
+ return 0;
+ // throw "h$base_sigaddset";
+}
+function h$base_sigprocmask(sig, sigset1, sigset1_off, sigset2, sigset2_off) {
+ return 0;
+ // throw "h$base_sigprocmask";
+}
+function h$base_tcgetattr(attr, termios, termios_off) {
+ return 0;
+}
+function h$base_tcsetattr(attr, val, termios, termios_off) {
+ return 0;
+}
+function h$base_utime(file, file_off, timbuf, timbuf_off, c) {
+ TRACE_IO("base_utime");
+#ifndef GHCJS_BROWSER
+ if(h$isNode) {
+ h$fs.fstat(h$decodeUtf8z(file, file_off), function(err, fs) {
+ if(err) {
+ h$handleErrnoC(err, 0, -1, c); // fixme
+ } else {
+ var atime = goog.math.Long.fromNumber(fs.atime.getTime());
+ var mtime = goog.math.Long.fromNumber(fs.mtime.getTime());
+ var ctime = goog.math.Long.fromNumber(fs.ctime.getTime());
+ timbuf.i3[0] = atime.getHighBits();
+ timbuf.i3[1] = atime.getLowBits();
+ timbuf.i3[2] = mtime.getHighBits();
+ timbuf.i3[3] = mtime.getLowBits();
+ timbuf.i3[4] = ctime.getHighBits();
+ timbuf.i3[5] = ctime.getLowBits();
+ c(0);
+ }
+ });
+ } else
+#endif
+ h$unsupported(-1, c);
+}
+function h$base_waitpid(pid, stat, stat_off, options, c) {
+ throw "h$base_waitpid";
+}
+/** @const */ var h$base_o_rdonly = 0x00000;
+/** @const */ var h$base_o_wronly = 0x00001;
+/** @const */ var h$base_o_rdwr = 0x00002;
+/** @const */ var h$base_o_accmode = 0x00003;
+/** @const */ var h$base_o_append = 0x00008;
+/** @const */ var h$base_o_creat = 0x00200;
+/** @const */ var h$base_o_trunc = 0x00400;
+/** @const */ var h$base_o_excl = 0x00800;
+/** @const */ var h$base_o_noctty = 0x20000;
+/** @const */ var h$base_o_nonblock = 0x00004;
+/** @const */ var h$base_o_binary = 0x00000;
+
+function h$base_c_s_isreg(mode) {
+ return 1;
+}
+function h$base_c_s_ischr(mode) {
+ return 0;
+}
+function h$base_c_s_isblk(mode) {
+ return 0;
+}
+function h$base_c_s_isdir(mode) {
+ return 0; // fixme
+}
+function h$base_c_s_isfifo(mode) {
+ return 0;
+}
+
+#ifndef GHCJS_BROWSER
+function h$base_fillStat(fs, b, off) {
+ if(off%4) throw "h$base_fillStat: not aligned";
+ var o = off>>2;
+ b.i3[o+0] = fs.mode;
+ var s = goog.math.Long.fromNumber(fs.size);
+ b.i3[o+1] = s.getHighBits();
+ b.i3[o+2] = s.getLowBits();
+ b.i3[o+3] = 0; // fixme
+ b.i3[o+4] = 0; // fixme
+ b.i3[o+5] = fs.dev;
+ var i = goog.math.Long.fromNumber(fs.ino);
+ b.i3[o+6] = i.getHighBits();
+ b.i3[o+7] = i.getLowBits();
+ b.i3[o+8] = fs.uid;
+ b.i3[o+9] = fs.gid;
+}
+#endif
+
+// [mode,size1,size2,mtime1,mtime2,dev,ino1,ino2,uid,gid] all 32 bit
+/** @const */ var h$base_sizeof_stat = 40;
+
+function h$base_st_mtime(stat, stat_off) {
+ RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+3], stat.i3[(stat_off>>2)+4]);
+}
+
+function h$base_st_size(stat, stat_off) {
+ RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+1], stat.i3[(stat_off>>2)+2]);
+}
+
+function h$base_st_mode(stat, stat_off) {
+ return stat.i3[stat_off>>2];
+}
+
+function h$base_st_dev(stat, stat_off) {
+ return stat.i3[(stat_off>>2)+5];
+}
+
+function h$base_st_ino(stat, stat_off) {
+ RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+6], stat.i3[(stat_off>>2)+7]);
+}
+
+/** @const */ var h$base_echo = 1;
+/** @const */ var h$base_tcsanow = 2;
+/** @const */ var h$base_icanon = 4;
+/** @const */ var h$base_vmin = 8;
+/** @const */ var h$base_vtime = 16;
+/** @const */ var h$base_sigttou = 0;
+/** @const */ var h$base_sig_block = 0;
+/** @const */ var h$base_sig_setmask = 0;
+/** @const */ var h$base_f_getfl = 0;
+/** @const */ var h$base_f_setfl = 0;
+/** @const */ var h$base_f_setfd = 0;
+/** @const */ var h$base_fd_cloexec = 0;
+/** @const */ var h$base_sizeof_termios = 4;
+/** @const */ var h$base_sizeof_sigset_t = 4;
+
+function h$base_lflag(termios, termios_off) {
+ return 0;
+}
+
+function h$base_poke_lflag(termios, termios_off, flag) {
+ return 0;
+}
+
+function h$base_ptr_c_cc(termios, termios_off) {
+ RETURN_UBX_TUP2(h$newByteArray(8), 0);
+}
+
+/** @const */ var h$base_default_buffer_size = 32768;
+
+function h$base_c_s_issock(mode) {
+ return 0; // fixme
+}
+
+/** @const */ var h$base_SEEK_SET = 0;
+/** @const */ var h$base_SEEK_CUR = 1;
+/** @const */ var h$base_SEEK_END = 2;
+
+function h$base_set_saved_termios(a, b, c) {
+ RETURN_UBX_TUP2(null, 0);
+}
+
+function h$base_get_saved_termios(r) {
+ RETURN_UBX_TUP2(null, 0);
+}
+
+// fixme
+function h$lockFile(fd, dev, ino, for_writing) {
+ TRACE_IO("lockFile:" + fd);
+ return 0;
+}
+function h$unlockFile(fd) {
+ TRACE_IO("unlockFile:" + fd);
+ return 0;
+}
+
+
+
+// engine-dependent setup
+var h$base_readStdin , h$base_writeStderr, h$base_writeStdout;
+var h$base_isattyStdin = false, h$base_isattyStdout = false, h$base_isattyStderr = false;
+var h$base_closeStdin = null, h$base_closeStderr = null, h$base_closeStdout = null;
+var h$base_readFile, h$base_writeFile, h$base_closeFile;
+#ifndef GHCJS_BROWSER
+var h$base_stdin_waiting = new h$Queue();
+var h$base_stdin_chunk = { buf: null
+ , pos: 0
+ , processing: false
+ };
+var h$base_stdin_eof = false;
+var h$base_process_stdin = function() {
+ var c = h$base_stdin_chunk;
+ var q = h$base_stdin_waiting;
+ if(!q.length() || c.processing) return;
+ c.processing = true;
+ if(!c.buf) { c.pos = 0; c.buf = process.stdin.read(); }
+ while(c.buf && q.length()) {
+ var x = q.dequeue();
+ var n = Math.min(c.buf.length - c.pos, x.n);
+ for(var i=0;i<n;i++) {
+ x.buf.u8[i+x.off] = c.buf[c.pos+i];
+ }
+ c.pos += n;
+ x.c(n);
+ if(c.pos >= c.buf.length) c.buf = null;
+ if(!c.buf && q.length()) { c.pos = 0; c.buf = process.stdin.read(); }
+ }
+ while(h$base_stdin_eof && q.length()) q.dequeue().c(0);
+ c.processing = false;
+}
+
+if(h$isNode) {
+ h$base_closeFile = function(fd, fdo, c) {
+ TRACE_IO("base_closeFile: " + fd + " (" + fdo.fd + ")");
+ var real_fd = typeof fdo.fd === 'number' ? fdo.fd : fd;
+ h$fs.close(real_fd, function(err) {
+ delete h$base_fds[fd];
+ h$handleErrnoC(err, -1, 0, c);
+ });
+ }
+
+ h$base_readFile = function(fd, fdo, buf, buf_offset, n, c) {
+ var pos = typeof fdo.pos === 'number' ? fdo.pos : null;
+ TRACE_IO("base_readFile: " + fd + " (" + fdo.fd + ") " + pos + " " + buf_offset + " " + n);
+ var real_fd = typeof fdo.fd === 'number' ? fdo.fd : fd;
+ h$fs.read(real_fd, Buffer.alloc(n), 0, n, pos, function(err, bytesRead, nbuf) {
+ if(err) {
+ h$setErrno(err);
+ c(-1);
+ } else {
+ for(var i=bytesRead-1;i>=0;i--) buf.u8[buf_offset+i] = nbuf[i];
+ if(typeof fdo.pos === 'number') fdo.pos += bytesRead;
+ c(bytesRead);
+ }
+ });
+ }
+
+ h$base_readStdin = function(fd, fdo, buf, buf_offset, n, c) {
+ TRACE_IO("read stdin");
+ h$base_stdin_waiting.enqueue({buf: buf, off: buf_offset, n: n, c: c});
+ h$base_process_stdin();
+ }
+
+ h$base_closeStdin = function(fd, fdo, c) {
+ TRACE_IO("close stdin");
+ // process.stdin.close(); fixme
+ c(0);
+ }
+
+ h$base_writeFile = function(fd, fdo, buf, buf_offset, n, c) {
+ var pos = typeof fdo.pos === 'number' ? fdo.pos : null;
+ TRACE_IO("base_writeFile: " + fd + " (" + fdo.fd + ") " + pos + " " + buf_offset + " " + n);
+ var nbuf = Buffer.alloc(n);
+ for(var i=0;i<n;i++) nbuf[i] = buf.u8[i+buf_offset];
+ var real_fd = typeof fdo.fd === 'number' ? fdo.fd : fd;
+ if(typeof fdo.pos === 'number') fdo.pos += n;
+ h$fs.write(real_fd, nbuf, 0, n, pos, function(err, bytesWritten) {
+ TRACE_IO("written file: " + fd + " (" + fdo.fd + ")");
+ if(err) {
+ h$setErrno(err);
+ if(typeof fdo.pos === 'number') fdo.pos -= n;
+ if(h$errno === CONST_EAGAIN)
+ setTimeout(function() { h$base_writeFile(fd, fdo, buf, buf_offset, n, c); }, 20);
+ else c(-1);
+ } else {
+ if(typeof fdo.pos === 'number') fdo.pos += bytesWritten - n;
+ c(bytesWritten);
+ }
+ });
+ }
+
+ h$base_writeStdout = function(fd, fdo, buf, buf_offset, n, c) {
+ TRACE_IO("write stdout");
+ h$base_writeFile(1, fdo, buf, buf_offset, n, c);
+ }
+
+ h$base_closeStdout = function(fd, fdo, c) {
+ TRACE_IO("close stdout");
+ // not actually closed, fixme?
+ c(0);
+ }
+
+ h$base_writeStderr = function(fd, fdo, buf, buf_offset, n, c) {
+ TRACE_IO("write stderr");
+ h$base_writeFile(2, fdo, buf, buf_offset, n, c);
+ }
+
+ h$base_closeStderr = function(fd, fdo, c) {
+ TRACE_IO("close stderr");
+ // not actually closed, fixme?
+ c(0);
+ }
+
+ process.stdin.on('readable', h$base_process_stdin);
+ process.stdin.on('end', function() { h$base_stdin_eof = true; h$base_process_stdin(); });
+
+ h$base_isattyStdin = function() { return process.stdin.isTTY; };
+ h$base_isattyStdout = function() { return process.stdout.isTTY; };
+ h$base_isattyStderr = function() { return process.stderr.isTTY; };
+
+} else if (h$isJsShell) {
+ h$base_readStdin = function(fd, fdo, buf, buf_offset, n, c) {
+ c(0);
+ }
+ h$base_writeStdout = function(fd, fdo, buf, buf_offset, n, c) {
+ putstr(h$decodeUtf8(buf, n, buf_offset));
+ c(n);
+ }
+ h$base_writeStderr = function(fd, fdo, buf, buf_offset, n, c) {
+ printErr(h$decodeUtf8(buf, n, buf_offset));
+ c(n);
+ }
+} else if(h$isJsCore) {
+ h$base_readStdin = function(fd, fdo, buf, buf_offset, n, c) {
+ c(0);
+ }
+ var h$base_stdoutLeftover = { f: print, val: null };
+ var h$base_stderrLeftover = { f: debug, val: null };
+ var h$base_writeWithLeftover = function(buf, n, buf_offset, c, lo) {
+ var lines = h$decodeUtf8(buf, n, buf_offset).split(/\r?\n/);
+ if(lines.length === 1) {
+ if(lines[0].length) {
+ if(lo.val !== null) lo.val += lines[0];
+ else lo.val = lines[0];
+ }
+ } else {
+ lo.f(((lo.val !== null) ? lo.val : '') + lines[0]);
+ for(var i=1;i<lines.length-1;i++) lo.f(lines[i]);
+ if(lines[lines.length-1].length) lo.val = lines[lines.length-1];
+ else lo.val = null;
+ }
+ c(n);
+ }
+ h$base_writeStdout = function(fd, fdo, buf, buf_offset, n, c) {
+ h$base_writeWithLeftover(buf, n, buf_offset, c, h$base_stdoutLeftover);
+ }
+ h$base_writeStderr = function(fd, fdo, buf, buf_offset, n, c) {
+ // writing to stderr not supported, write to stdout
+ h$base_writeWithLeftover(buf, n, buf_offset, c, h$base_stderrLeftover);
+ }
+} else { // browser / fallback
+#endif
+ h$base_readStdin = function(fd, fdo, buf, buf_offset, n, c) {
+ c(0);
+ }
+ h$base_writeStdout = function(fd, fdo, buf, buf_offset, n, c) {
+ console.log(h$decodeUtf8(buf, n, buf_offset));
+ c(n);
+ }
+ h$base_writeStderr = function(fd, fdo, buf, buf_offset, n, c) {
+ console.log(h$decodeUtf8(buf, n, buf_offset));
+ c(n);
+ }
+#ifndef GHCJS_BROWSER
+}
+#endif
+
+var h$base_stdin_fd =
+ { read: h$base_readStdin
+ , close: h$base_closeStdin
+ , isatty: h$base_isattyStdin
+ , refs: 1
+ };
+var h$base_stdout_fd =
+ { write: h$base_writeStdout
+ , close: h$base_closeStdout
+ , isatty: h$base_isattyStdout
+ , refs: 1
+ };
+var h$base_stderr_fd =
+ { write: h$base_writeStderr
+ , close: h$base_closeStderr
+ , isatty: h$base_isattyStderr
+ , refs: 1
+ };
+
+var h$base_fdN = -2; // negative file descriptors are 'virtual', -1 is already used to indicated error
+var h$base_fds = [h$base_stdin_fd, h$base_stdout_fd, h$base_stderr_fd];
+
+function h$shutdownHaskellAndExit(code, fast) {
+#ifndef GHCJS_BROWSER
+#ifdef GHCJS_LOG_BUFFER
+ if(h$isNode) console.log(h$logBuffer);
+ if(h$isJsShell || h$isJsCore) print(h$logBuffer);
+#endif
+#endif
+ h$exitProcess(code);
+}
+
+// RAND_MAX = 32767
+function h$rand() {
+ return (32768 * Math.random()) & 32767;
+}
+
+// SIGUSR1, SIGTERM, SIGINT, SIGPIPE, SIGHUP, SIGTERM, SIGINT
+// SIGBREAK, SIGWINCH, SIGKILL, SIGSTOP, SIGBUS, SIGFPE
+// SIGSEGV, SIGILL
+
+// returns old action code
+function h$stg_sig_install(sigNo, actionCode, sigSet_d, sigSet_o) {
+ // XXX dummy implementation
+ return 0;
+}