summaryrefslogtreecommitdiff
path: root/libraries/base/jsbits
diff options
context:
space:
mode:
authorSylvain Henry <sylvain@haskus.fr>2022-02-10 08:24:24 +0000
committerSylvain Henry <sylvain@haskus.fr>2022-11-29 09:44:31 +0100
commitcc25d52e0f65d54c052908c7d91d5946342ab88a (patch)
tree0f35764ee3b9b0451ac999b64d2db9fa074fa3dd /libraries/base/jsbits
parentdef47dd32491311289bff26230b664c895f178cc (diff)
downloadhaskell-cc25d52e0f65d54c052908c7d91d5946342ab88a.tar.gz
Add Javascript backend
Add JS backend adapted from the GHCJS project by Luite Stegeman. Some features haven't been ported or implemented yet. Tests for these features have been disabled with an associated gitlab ticket. Bump array submodule Work funded by IOG. Co-authored-by: Jeffrey Young <jeffrey.young@iohk.io> Co-authored-by: Luite Stegeman <stegeman@gmail.com> Co-authored-by: Josh Meredith <joshmeredith2008@gmail.com>
Diffstat (limited to 'libraries/base/jsbits')
-rw-r--r--libraries/base/jsbits/base.js839
-rw-r--r--libraries/base/jsbits/errno.js102
-rw-r--r--libraries/base/jsbits/platform.js111
3 files changed, 1052 insertions, 0 deletions
diff --git a/libraries/base/jsbits/base.js b/libraries/base/jsbits/base.js
new file mode 100644
index 0000000000..dd491bac00
--- /dev/null
+++ b/libraries/base/jsbits/base.js
@@ -0,0 +1,839 @@
+//#OPTIONS: CPP
+#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;
+}
+
+
+#define TWO_PWR_32_DBL_ 0x100000000
+#define TWO_PWR_63_DBL_ 0x8000000000000000
+#define CLOSEST_FLOAT_NUMBER(h,l) (((h)*TWO_PWR_32_DBL_) + ((l)>>>0))
+
+/**
+ * Returns a 64-bit represention of the given number.
+ * NaN will be returned as zero.
+ * Infinity is converted to max value and
+ * -Infinity to min value.
+ * @param {f} The number in question.
+ * @param {c} the continuation taking high and low bits
+ */
+function h$long_from_number(f,c) {
+ if (f > 0) {
+ if (f >= TWO_PWR_63_DBL_) {
+ // return max value
+ return c(0x7FFFFFFF,0xFFFFFFFF);
+ }
+ return c(f / TWO_PWR_32_DBL_, f);
+ } else if (f < 0) {
+ if (f <= -TWO_PWR_63_DBL_) {
+ // return min value
+ return c(0x80000000,0);
+ }
+ var h = -f / TWO_PWR_32_DBL_;
+ var l = -f;
+ // negate h l
+ var nl = (~l + 1) | 0;
+ var nh = (~h + !nl) | 0;
+ return c(nh,nl);
+ } else {
+ // NaN or 0.
+ return c(0,0);
+ }
+}
+
+function h$base_lseek(fd, pos_h, pos_l, whence, c) {
+ TRACE_IO("base_lseek")
+#ifndef GHCJS_BROWSER
+ if(h$isNode()) {
+ var p = CLOSEST_FLOAT_NUMBER(pos_h,pos_l);
+ var o = h$base_fds[fd];
+ if(!o) {
+ h$errno = CONST_BADF;
+ c(-1,-1);
+ } else {
+ switch(whence) {
+ case 0: /* SET */
+ o.pos = p;
+ c(pos_h, pos_l);
+ break;
+ case 1: /* CUR */
+ o.pos += p;
+ h$long_from_number(o.pos,c);
+ 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;
+ h$long_from_number(o.pos,c);
+ }
+ });
+ 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) {
+// fd: file descriptor number
+// buf: buffer to write
+// buf_off: offset in the buffer
+// n: number of bytes to write
+// c: continuation
+ 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_h, pos_l, c) {
+ TRACE_IO("base_ftruncate")
+#ifndef GHCJS_BROWSER
+ if(h$isNode()) {
+ h$fs.ftruncate(fd, CLOSEST_FLOAT_NUMBER(pos_h,pos_l), 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 {
+ h$long_from_number(fs.atime.getTime(), (h,l) => {
+ timbuf.i3[0] = h;
+ timbuf.i3[1] = l;
+ });
+ h$long_from_number(fs.mtime.getTime(), (h,l) => {
+ timbuf.i3[2] = h;
+ timbuf.i3[3] = l;
+ });
+ h$long_from_number(fs.ctime.getTime(), (h,l) => {
+ timbuf.i3[4] = h;
+ timbuf.i3[5] = l;
+ });
+ 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;
+ h$long_from_number(fs.size, (h,l) => {
+ b.i3[o+1] = h;
+ b.i3[o+2] = l;
+ });
+
+ b.i3[o+3] = 0; // fixme
+ b.i3[o+4] = 0; // fixme
+ b.i3[o+5] = fs.dev;
+ h$long_from_number(fs.ino, (h,l) => {
+ b.i3[o+6] = h;
+ b.i3[o+7] = l;
+ });
+ 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;
+}
+
+const h$putchar_buf = h$newByteArray(1);
+
+function h$putchar(c) {
+ h$putchar_buf.u8[0] = c;
+ h$base_write(1, h$putchar_buf, 0, 1, null);
+ return h$errno;
+}
+
+function h$__hscore_set_errno(n) {
+ h$errno = n;
+}
+
+/*******************************************
+ * Directory API
+ *******************************************/
+
+function h$opendir(path) {
+ if(!h$isNode()) {
+ throw "h$opendir unsupported";
+ }
+
+ const d = fs.opendirSync(h$decodeUtf8z(path,0));
+ RETURN_UBX_TUP2(d,0);
+}
+
+function h$closedir(d,o) {
+ if(!h$isNode()) {
+ throw "h$closedir unsupported";
+ }
+ d.closeSync();
+ return 0;
+}
+
+function h$readdir(d,o) {
+ if(!h$isNode()) {
+ throw "h$readdir unsupported";
+ }
+ const c = d.readSync();
+ RETURN_UBX_TUP2(c,0);
+}
+
+function h$__hscore_readdir(d,o,dst_a,dst_o) {
+ if(!h$isNode()) {
+ throw "h$readdir unsupported";
+ }
+ const e = d.readSync();
+
+ if (!dst_a.arr) dst_a.arr = [];
+ dst_a.arr[dst_o*2] = [e,0];
+ return 0;
+}
+
+function h$__hscore_free_dirent(a,o) {
+}
+
+function h$__hscore_d_name(a,o) {
+ RETURN_UBX_TUP2(h$encodeModifiedUtf8(a.name),0);
+}
diff --git a/libraries/base/jsbits/errno.js b/libraries/base/jsbits/errno.js
new file mode 100644
index 0000000000..185f799186
--- /dev/null
+++ b/libraries/base/jsbits/errno.js
@@ -0,0 +1,102 @@
+//#OPTIONS: CPP
+
+#include "HsBaseConfig.h"
+
+#ifdef GHCJS_TRACE_ERRNO
+function h$logErrno() { h$log.apply(h$log,arguments); }
+#define TRACE_ERRNO(args...) h$logErrno(args)
+#else
+#define TRACE_ERRNO(args...)
+#endif
+
+var h$errno = 0;
+
+function h$__hscore_get_errno() {
+ TRACE_ERRNO("hscore_get_errno: " + h$errno);
+ return h$errno;
+}
+
+function h$unsupported(status, c) {
+ h$errno = 12456;
+ if(c) c(status);
+ return status;
+}
+
+function h$strerror(err) {
+ if(err === 12456) {
+ RETURN_UBX_TUP2(h$encodeUtf8("operation unsupported on this platform"), 0);
+ }
+#ifdef GHCJS_BROWSER
+ RETURN_UBX_TUP2(h$encodeUtf8("unknown error"), 0);
+#else
+ RETURN_UBX_TUP2(h$encodeUtf8(h$errorStrs[err] || "unknown error"), 0);
+#endif
+}
+
+#ifndef GHCJS_BROWSER
+function h$setErrno(e) {
+ TRACE_ERRNO("setErrno: " + e);
+ var es = e.toString();
+ var getErr = function() {
+ if(es.indexOf('ENOTDIR') !== -1) return CONST_ENOTDIR;
+ if(es.indexOf('EISDIR') !== -1) return CONST_EISDIR;
+ if(es.indexOf('ENOENT') !== -1) return CONST_ENOENT;
+ if(es.indexOf('EEXIST') !== -1) return CONST_EEXIST;
+ if(es.indexOf('ENETUNREACH') !== -1) return CONST_EINVAL; // fixme
+ if(es.indexOf('EPERM') !== -1) return CONST_EPERM;
+ if(es.indexOf('EMFILE') !== -1) return CONST_EMFILE;
+ if(es.indexOf('EPIPE') !== -1) return CONST_EPIPE;
+ if(es.indexOf('EAGAIN') !== -1) return CONST_EAGAIN;
+ if(es.indexOf('EINVAL') !== -1) return CONST_EINVAL;
+ if(es.indexOf('ESPIPE') !== -1) return CONST_ESPIPE;
+ if(es.indexOf('EBADF') !== -1) return CONST_EBADF;
+ if(es.indexOf('Bad argument') !== -1) return CONST_ENOENT; // fixme?
+ throw ("setErrno not yet implemented: " + e);
+
+ }
+ h$errno = getErr();
+}
+
+var h$errorStrs = { CONST_E2BIG: "Argument list too long"
+ , CONST_EACCESS: "Permission denied"
+ , CONST_EINVAL: "Invalid argument"
+ , CONST_EBADF: "Bad file descriptor"
+ , CONST_ENOTDIR: "Not a directory"
+ , CONST_EISDIR: "Illegal operation on a directory"
+ , CONST_ENOENT: "No such file or directory"
+ , CONST_EPERM: "Operation not permitted"
+ , CONST_EEXIST: "File exists"
+ , CONST_EMFILE: "Too many open files"
+ , CONST_EPIPE: "Broken pipe"
+ , CONST_EAGAIN: "Resource temporarily unavailable"
+ , CONST_ESPIPE: "Illegal seek"
+ }
+
+function h$handleErrno(r_err, f) {
+ try {
+ return f();
+ } catch(e) {
+ h$setErrno(e);
+ return r_err;
+ }
+}
+
+function h$handleErrnoS(r_err, r_success, f) {
+ try {
+ f();
+ return r_success;
+ } catch(e) {
+ h$setErrno(e);
+ return r_err;
+ }
+}
+
+function h$handleErrnoC(err, r_err, r_success, c) {
+ if(err) {
+ h$setErrno(err);
+ c(r_err);
+ } else {
+ c(r_success);
+ }
+}
+#endif
diff --git a/libraries/base/jsbits/platform.js b/libraries/base/jsbits/platform.js
new file mode 100644
index 0000000000..2ecdee2189
--- /dev/null
+++ b/libraries/base/jsbits/platform.js
@@ -0,0 +1,111 @@
+//#OPTIONS: CPP
+
+/* platform-specific setup */
+
+/*
+ if browser mode is active (GHCJS_BROWSER is defined), all the runtime platform
+ detection code should be removed by the preprocessor. The h$isPlatform variables
+ are undeclared.
+
+ in non-browser mode, use h$isNode, h$isJsShell, h$isBrowser to find the current
+ platform.
+
+ more platforms should be added here in the future
+*/
+#ifndef GHCJS_BROWSER
+var h$isNode_ = false; // runtime is node.js
+var h$isJvm_ = false; // runtime is JVM
+var h$isJsShell_ = false; // runtime is SpiderMonkey jsshell
+var h$isJsCore_ = false; // runtime is JavaScriptCore jsc
+var h$isBrowser_ = false; // running in browser or everything else
+
+var h$isGHCJSi_ = false; // Code is GHCJSi (browser or node)
+
+function h$isNode() {
+ return h$isNode_;
+}
+
+function h$isJvm() {
+ return h$isJvm_;
+}
+
+function h$isJsShell() {
+ return h$isJsShell_;
+}
+
+function h$isJsCore() {
+ return h$isJsCore_;
+}
+
+function h$isBrowser() {
+ return h$isBrowser_;
+}
+
+function h$isGHCJSi() {
+ return h$isGHCJSi_;
+}
+
+// load all required node.js modules
+if(typeof process !== 'undefined' && (typeof h$TH !== 'undefined' || (typeof require !== 'undefined' && typeof module !== 'undefined' && module.exports))) {
+ h$isNode_ = true;
+ // we have to use these names for the closure compiler externs to work
+ var fs = require('fs');
+ var path = require('path');
+ var os = require('os');
+ var child_process = require('child_process');
+ var h$fs = fs;
+ var h$path = path;
+ var h$os = os;
+ var h$child = child_process;
+ var h$process = process;
+ function h$getProcessConstants() {
+ // this is a non-public API, but we need these values for things like file access modes
+ var cs = process['binding']('constants');
+ if(typeof cs.os === 'object' && typeof cs.fs === 'object') {
+ return cs;
+ } else {
+ // earlier node.js versions (4.x and older) have all constants directly in the constants object
+ // construct something that resembles the hierarchy of the object in new versions:
+ return { 'fs': cs
+ , 'crypto': cs
+ , 'os': { 'UV_UDP_REUSEADDR': cs['UV_UDP_REUSEADDR']
+ , 'errno': cs
+ , 'signals': cs
+ }
+ };
+ }
+ }
+ var h$processConstants = h$getProcessConstants();
+} else if(typeof Java !== 'undefined') {
+ h$isJvm_ = true;
+ this.console = {
+ log: function(s) {
+ java.lang.System.out.print(s);
+ }
+ };
+} else if(typeof snarf !== 'undefined' && typeof print !== 'undefined' && typeof quit !== 'undefined') {
+ h$isJsShell_ = true;
+ this.console = { log: this.print };
+} else if(typeof numberOfDFGCompiles !== 'undefined' && typeof jscStack !== 'undefined') {
+ h$isJsCore_ = true;
+} else {
+ h$isBrowser_ = true;
+}
+if(typeof global !== 'undefined' && global.h$GHCJSi) {
+ h$isGHCJSi_ = true;
+}
+#endif
+
+function h$getGlobal(that) {
+ if(typeof global !== 'undefined') return global;
+ return that;
+}
+
+#ifdef GHCJS_BROWSER
+// IE 8 doesn't support Date.now(), shim it
+if (!Date.now) {
+ Date.now = function now() {
+ return +(new Date);
+ };
+}
+#endif