├── test ├── test-empty.js ├── fixtures │ ├── require_broken_dir │ │ ├── foo │ │ │ └── foo.c │ │ └── foo.js │ └── require_main │ │ ├── bar.js │ │ └── foo.js ├── test-shebang.js ├── helper7.js ├── shebangtest.js ├── helper6.js ├── test-require-main.js ├── helper5.js ├── test-shebang-require.js ├── helper1.js ├── test-os-cwd.js ├── helper4.js ├── helper2.js ├── helper8.js ├── helper3.js ├── test-io-poll.js ├── test-module-file.js ├── test-system-endianness.js ├── daemon.js ├── test-pwd.js ├── test-net-socket-bad-domain.js ├── test-io-select.js ├── test-io-select-stdout.js ├── test-os-fstat.js ├── test-time.js ├── test-path-normalize.js ├── test-hash-createhash.js ├── test-require-bad-dir.js ├── test-hash-incremental.js ├── test-os-scandir.js ├── test-io-poll-stdout.js ├── test-os-fork-exit.js ├── test-os-chdir.js ├── test-os-stat.js ├── test-random-interval.js ├── test-eval-require-main.js ├── test-system-stdio.js ├── test-net-create-socket.js ├── test-os-fork-execve-fail.js ├── test-os-setsid.js ├── test-os-getpid.js ├── test-require-main-set.js ├── test-os-fork-simple.js ├── test-random-seed.js ├── test-os-pipe.js ├── test-io-select-badargs.js ├── test-net-socketpair.js ├── test-os-urandom.js ├── test-os-fork-pipe.js ├── test-io-poll-badargs.js ├── test-net-socket-dgram.js ├── test-process-spawn.js ├── test-net-socket-dgram-connected.js ├── test-os-lowlevel-io.js ├── test-process-spawn-setuid.js ├── test-os-fork-execve.js ├── test-net-socket-dgram-buffers.js ├── test-net-socket-connection.js ├── test-os-fork-execve-args.js ├── test-os-fork-execve-env.js ├── test-io-file.js ├── test-os-fork-execv.js ├── test-process-spawn-stdio.js ├── test-net-socket-abstract.js ├── test-os-fork-execve-json.js ├── test-net-socket-buffers.js ├── test-random-mersenne.js ├── test-os-popen-sample.js ├── test-hash-vectors.js └── test-net-socket-options.js ├── docs └── source │ ├── .static │ ├── sjs.png │ ├── sjs.xcf │ ├── sjs-logo.png │ ├── sjs-logo-icon.png │ ├── sjs-libsjs-diagram.png │ ├── sjs-logo.svg │ └── sjs-libsjs-diagram.svg │ ├── modules │ ├── time.rst │ ├── errno.rst │ ├── codecs.rst │ ├── pwd.rst │ ├── path.rst │ ├── hash.rst │ ├── uuid.rst │ ├── random.rst │ ├── assert.rst │ ├── system.rst │ ├── console.rst │ ├── process.rst │ └── io.rst │ ├── modules.rst │ ├── features.rst │ ├── sphinx-plugins │ └── manpage.py │ ├── faq.rst │ ├── index.rst │ ├── design.rst │ ├── module_system.rst │ └── usage.rst ├── AUTHORS ├── modules ├── pwd.js ├── utils.js ├── time.js ├── errno.js ├── _io_poll.js ├── _utils_unicode.js ├── _io_select.js ├── path.js ├── codecs.js ├── system.js ├── random.js ├── hash.js ├── console.js ├── io.js └── uuid.js ├── include └── sjs │ ├── sjs.h │ ├── version.h.in │ ├── build.h.in │ └── vm.h ├── .gitignore ├── src ├── embedjs.h ├── bindings │ ├── hash │ │ ├── includes.h │ │ ├── md5.h │ │ ├── sha1.h │ │ ├── hash.c │ │ ├── sha1.c │ │ └── sha2.h │ ├── random │ │ ├── test.c │ │ ├── random.c │ │ └── mt19937ar.h │ ├── path.c │ ├── poll.c │ ├── select.c │ ├── pwd.c │ └── gai.c ├── bootstrap.js ├── platform │ ├── linux.c │ ├── darwin.c │ └── common.c ├── cli │ ├── greet.h │ ├── greet.js │ └── linenoise.h ├── polyfill.h ├── bootstrap.h ├── duk_module_node.h ├── version.c ├── binding.h ├── internal.h ├── util.c ├── binding.c └── builtins.c ├── examples └── hello.js ├── tools ├── generrno.py ├── build_duktape │ ├── build.sh │ └── config.yaml └── test.py ├── .editorconfig ├── CONTRIBUTING.md ├── Makefile ├── LICENSE ├── .travis.yml └── README.md /test/test-empty.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/require_broken_dir/foo/foo.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/require_broken_dir/foo.js: -------------------------------------------------------------------------------- 1 | 2 | exports.foo = 42; 3 | -------------------------------------------------------------------------------- /test/test-shebang.js: -------------------------------------------------------------------------------- 1 | #!/bin/env sjs 2 | 3 | console.log('hello'); 4 | -------------------------------------------------------------------------------- /docs/source/.static/sjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saghul/sjs/HEAD/docs/source/.static/sjs.png -------------------------------------------------------------------------------- /docs/source/.static/sjs.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saghul/sjs/HEAD/docs/source/.static/sjs.xcf -------------------------------------------------------------------------------- /docs/source/.static/sjs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saghul/sjs/HEAD/docs/source/.static/sjs-logo.png -------------------------------------------------------------------------------- /test/helper7.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | assert(require.main !== module); 6 | -------------------------------------------------------------------------------- /docs/source/.static/sjs-logo-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saghul/sjs/HEAD/docs/source/.static/sjs-logo-icon.png -------------------------------------------------------------------------------- /test/shebangtest.js: -------------------------------------------------------------------------------- 1 | #!/bin/env sjs 2 | 3 | 'use strict'; 4 | 5 | exports.foo = function() { 6 | return 42; 7 | } 8 | -------------------------------------------------------------------------------- /docs/source/.static/sjs-libsjs-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saghul/sjs/HEAD/docs/source/.static/sjs-libsjs-diagram.png -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors ordered by first contribution 2 | 3 | Saúl Ibarra Corretgé 4 | Joegen Baclor 5 | -------------------------------------------------------------------------------- /test/helper6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sys = require('system'); 4 | 5 | 6 | sys.stdout.writeLine(JSON.stringify(sys.argv.slice(2))); 7 | -------------------------------------------------------------------------------- /test/test-require-main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | 6 | assert(require.main === module); 7 | require('./helper7') 8 | -------------------------------------------------------------------------------- /modules/pwd.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _pwd = _system.binding('pwd'); 4 | 5 | 6 | exports.getpwnam = _pwd.getpwnam; 7 | exports.getpwuid = _pwd.getpwuid; 8 | -------------------------------------------------------------------------------- /include/sjs/sjs.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_INCLUDE_H 3 | #define SJS_INCLUDE_H 4 | 5 | #include "duktape.h" 6 | #include "vm.h" 7 | #include "version.h" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /test/fixtures/require_main/bar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | 6 | assert(require.main !== undefined); 7 | assert(require.main !== module); 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | original/ 3 | tools/build_duktape/duktape/ 4 | tools/build_duktape/out/ 5 | *.swp 6 | *.swo 7 | *.pyc 8 | include/sjs/build.h 9 | include/sjs/version.h 10 | -------------------------------------------------------------------------------- /test/helper5.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const codecs = require('codecs'); 4 | const sys = require('system'); 5 | 6 | 7 | sys.stdout.write(JSON.stringify(sys.env)); 8 | sys.stdout.flush(); 9 | -------------------------------------------------------------------------------- /test/test-shebang-require.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const shebangtest = require('./shebangtest'); 5 | 6 | 7 | assert.equal(shebangtest.foo(), 42); 8 | -------------------------------------------------------------------------------- /test/helper1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('os'); 4 | const system = require('system'); 5 | 6 | 7 | var fd = Number.parseInt(system.env.PARENT_FD); 8 | os.write(fd, 'HELLO'); 9 | -------------------------------------------------------------------------------- /modules/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const object = require('_utils_object'); 4 | const unicode = require('_utils_unicode'); 5 | 6 | 7 | exports.object = object; 8 | exports.unicode = unicode; 9 | -------------------------------------------------------------------------------- /src/embedjs.h: -------------------------------------------------------------------------------- 1 | #ifndef SJS_EMBEDJS_H 2 | #define SJS_EMBEDJS_H 3 | 4 | #define INCBIN_PREFIX sjs__code_ 5 | #define INCBIN_STYLE INCBIN_STYLE_SNAKE 6 | 7 | #include "incbin.h" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /test/test-os-cwd.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | 7 | var cwd = os.cwd; 8 | assert(typeof cwd === 'string'); 9 | console.log(cwd); 10 | -------------------------------------------------------------------------------- /test/fixtures/require_main/foo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const bar = require('./bar'); 5 | 6 | 7 | assert(require.main !== undefined); 8 | assert(require.main === module); 9 | -------------------------------------------------------------------------------- /test/helper4.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('os'); 4 | const system = require('system'); 5 | 6 | 7 | var fd = Number.parseInt(system.env.PARENT_FD); 8 | os.write(fd, JSON.stringify(system.env)); 9 | -------------------------------------------------------------------------------- /src/bindings/hash/includes.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_HASH_H 3 | #define SJS_HASH_H 4 | 5 | #include 6 | 7 | 8 | #if !defined(__bounded__) 9 | # define __bounded__(x, y, z) 10 | #endif 11 | 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /test/helper2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('os'); 4 | const system = require('system'); 5 | 6 | 7 | var fd = Number.parseInt(system.env.PARENT_FD); 8 | os.write(fd, system.argv.slice(2).join(',')); 9 | 10 | -------------------------------------------------------------------------------- /test/helper8.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is used from test-os-fork-execv.js, which doesn't 4 | // add the modules directory into the path so use the builtin 5 | // "secret" print function. 6 | _system.print('HELLO SJS'); 7 | -------------------------------------------------------------------------------- /test/helper3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sys = require('system'); 4 | 5 | 6 | var data = sys.stdin.readLine(); 7 | sys.stdout.write(data); 8 | sys.stdout.flush(); 9 | sys.stderr.write(data); 10 | sys.stderr.flush(); 11 | -------------------------------------------------------------------------------- /examples/hello.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sys = require('system'); 4 | 5 | 6 | sys.stdout.write('What is your name? '); 7 | var name = sys.stdin.readLine(); 8 | sys.stdout.writeLine('Hello ' + name.trim() + ', nice to meet you!'); 9 | -------------------------------------------------------------------------------- /tools/generrno.py: -------------------------------------------------------------------------------- 1 | 2 | import errno 3 | 4 | for i in sorted(errno.errorcode.keys()): 5 | err = errno.errorcode[i] 6 | print('#ifdef %s' % err) 7 | print(' { "%s", %s },' % (err, err)) 8 | print('#endif') 9 | 10 | -------------------------------------------------------------------------------- /test/test-io-poll.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const poll = require('io').poll; 5 | 6 | 7 | var res = poll.poll([], 0); 8 | assert.equal(res.nevents, 0); 9 | assert.equal(res.fds.length, 0); 10 | 11 | -------------------------------------------------------------------------------- /test/test-module-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | 6 | const THIS_FILE = 'test-module-file.js'; 7 | 8 | 9 | assert.equal(path.basename(__filename), THIS_FILE); 10 | -------------------------------------------------------------------------------- /test/test-system-endianness.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const system = require('system'); 5 | 6 | 7 | var endian = system.endianness; 8 | assert.ok(endian === 'big' || endian === 'little'); 9 | 10 | -------------------------------------------------------------------------------- /test/daemon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const time = require('time'); 4 | const process = require('process'); 5 | 6 | 7 | // go into the background 8 | process.daemonize(); 9 | 10 | // wait forever 11 | for (;;) { 12 | time.sleep(1); 13 | } 14 | -------------------------------------------------------------------------------- /modules/time.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const select = require('io').select; 4 | 5 | 6 | function sleep(delay) { 7 | select.select([], [], [], delay); 8 | // TODO: handle EINTR? return remaining time? 9 | } 10 | 11 | 12 | exports.sleep = sleep; 13 | -------------------------------------------------------------------------------- /test/test-pwd.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const pwd = require('pwd'); 5 | const os = require('os'); 6 | 7 | 8 | var uid = os.getuid(); 9 | var p = pwd.getpwuid(uid); 10 | var p1 = pwd.getpwnam(p.name); 11 | assert.deepEqual(p, p1); 12 | -------------------------------------------------------------------------------- /test/test-net-socket-bad-domain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | function badSocketDomain() { 8 | var s = new net.Socket(1234, net.SOCK_STREAM); 9 | } 10 | 11 | assert.throws(badSocketDomain); 12 | 13 | -------------------------------------------------------------------------------- /test/test-io-select.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const select = require('io').select; 5 | 6 | 7 | var res = select.select([], [], [], 0); 8 | assert.equal(res.rfds.length, 0); 9 | assert.equal(res.wfds.length, 0); 10 | assert.equal(res.xfds.length, 0); 11 | -------------------------------------------------------------------------------- /test/test-io-select-stdout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const select = require('io').select; 5 | 6 | 7 | var res = select.select([], [1], [], 0); 8 | assert.equal(res.rfds.length, 0); 9 | assert.equal(res.wfds.length, 1); 10 | assert.equal(res.xfds.length, 0); 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | trim_trailing_whitespace = true 11 | 12 | [Makefile] 13 | indent_style = tab 14 | tab_width = 8 15 | -------------------------------------------------------------------------------- /test/test-os-fstat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const system = require('system'); 6 | 7 | 8 | const fd = os.open(system.executable, 'r'); 9 | const stat = os.fstat(fd); 10 | 11 | assert(stat); 12 | assert(stat.size); 13 | 14 | os.close(fd); 15 | -------------------------------------------------------------------------------- /test/test-time.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const time = require('time'); 5 | 6 | 7 | var t = performance.now(); 8 | assert.ok(t > 0); 9 | 10 | var t1 = performance.now(); 11 | time.sleep(0.5); 12 | var diff = performance.now() - t1; 13 | assert.ok(diff >= 500); 14 | 15 | -------------------------------------------------------------------------------- /test/test-path-normalize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | 6 | 7 | var p = path.normalize('~'); 8 | assert.notEqual(p, '~'); 9 | 10 | var bogusPath = '/this/path/wont/exist'; 11 | var p = path.normalize(bogusPath); 12 | assert.equal(p, bogusPath); 13 | -------------------------------------------------------------------------------- /test/test-hash-createhash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const hash = require('hash'); 5 | 6 | 7 | var h1 = hash.createHash('sha512').update('aaa').update('aaa'); 8 | var h2 = new hash.SHA512(); 9 | h2.update('aaaaaa'); 10 | 11 | assert.equal(h1.digest('hex'), h2.digest('hex')); 12 | -------------------------------------------------------------------------------- /test/test-require-bad-dir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | const system = require('system'); 6 | 7 | 8 | system.path.unshift(path.join(__dirname, 'fixtures', 'require_broken_dir')); 9 | const foo = require('foo'); 10 | 11 | assert.equal(foo.foo, 42); 12 | -------------------------------------------------------------------------------- /modules/errno.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _errno = _system.binding('errno'); 4 | 5 | 6 | exports.strerror = _errno.strerror; 7 | exports.map = new Map(); 8 | 9 | // extract constants 10 | for (var k in _errno.c) { 11 | var val = _errno.c[k]; 12 | exports[k] = val; 13 | exports.map.set(val, k); 14 | } 15 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | global.console = require('console'); 2 | global.system = require('system'); 3 | 4 | // These are not supported yet, but babel-polyfill injects some. 5 | delete global.Promise; 6 | delete global.setTimeout; 7 | delete global.clearTimeout; 8 | delete global.setImmediate; 9 | delete global.clearImmediate; 10 | -------------------------------------------------------------------------------- /test/test-hash-incremental.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const hash = require('hash'); 5 | 6 | 7 | var h1 = new hash.SHA1(); 8 | h1.update('aaaaaa'); 9 | 10 | var h2 = new hash.SHA1(); 11 | h2.update('aaa'); 12 | h2.digest(); 13 | h2.update('aaa'); 14 | 15 | assert.equal(h1.digest('hex'), h2.digest('hex')); 16 | -------------------------------------------------------------------------------- /test/test-os-scandir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const errno = require('errno'); 5 | const os = require('os'); 6 | 7 | 8 | var r = os.scandir('.'); 9 | assert(r.length > 0); 10 | 11 | try { 12 | os.scandir('hdu3gdb893dyu8o3ijddd'); 13 | } catch (e) { 14 | assert.equal(e.errno, errno.ENOENT) 15 | } 16 | -------------------------------------------------------------------------------- /test/test-io-poll-stdout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const poll = require('io').poll; 5 | 6 | 7 | var res = poll.poll([{fd: 1, events: poll.POLLOUT}], null); 8 | assert.equal(res.nevents, 1); 9 | assert.equal(res.fds.length, 1); 10 | assert.equal(res.fds[0].fd, 1); 11 | assert.equal(res.fds[0].revents, poll.POLLOUT); 12 | -------------------------------------------------------------------------------- /tools/build_duktape/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Prepare output directory 4 | mkdir -p out 5 | rm -f out/* 6 | 7 | # Build Duktape 8 | pushd duktape 9 | python2 tools/configure.py \ 10 | --output-directory ../out \ 11 | --option-file ../config.yaml \ 12 | --omit-removed-config-options \ 13 | --omit-deprecated-config-options 14 | popd 15 | -------------------------------------------------------------------------------- /src/platform/linux.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | void sjs__executable(char* buf, size_t size) { 8 | ssize_t n; 9 | 10 | n = size - 1; 11 | if (n > 0) { 12 | n = readlink("/proc/self/exe", buf, n); 13 | } 14 | 15 | if (n == -1) { 16 | return; 17 | } 18 | 19 | buf[n] = '\0'; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/cli/greet.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_GREET_H 3 | #define SJS_GREET_H 4 | 5 | #include "../embedjs.h" 6 | 7 | INCBIN(greet, "embed-js/greet.js"); 8 | 9 | /** 10 | * These are defined now: 11 | * 12 | * const unsigned char sjs__code_greet_data[]; 13 | * const unsigned char *const sjs__code_greet_end; 14 | * const unsigned int sjs__code_greet_size; 15 | * 16 | */ 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/polyfill.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_POLYFILL_H 3 | #define SJS_POLYFILL_H 4 | 5 | #include "embedjs.h" 6 | 7 | INCBIN(polyfill, "embed-js/polyfill.min.js"); 8 | 9 | /** 10 | * These are defined now: 11 | * 12 | * const unsigned char sjs__code_polyfill_data[]; 13 | * const unsigned char *const sjs__code_polyfill_end; 14 | * const unsigned int sjs__code_polyfill_size; 15 | * 16 | */ 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/sjs/version.h.in: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_VERSION_H 3 | #define SJS_VERSION_H 4 | 5 | #include "duktape.h" 6 | 7 | 8 | #define SJS_VERSION_MAJOR @SJS__VERSION_MAJOR@ 9 | #define SJS_VERSION_MINOR @SJS__VERSION_MINOR@ 10 | #define SJS_VERSION_PATCH @SJS__VERSION_PATCH@ 11 | #define SJS_VERSION_SUFFIX "@SJS__VERSION_SUFFIX@" 12 | 13 | DUK_EXTERNAL_DECL const char* sjs_version(void); 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /src/bootstrap.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_BOOTSTRAP_H 3 | #define SJS_BOOTSTRAP_H 4 | 5 | #include "embedjs.h" 6 | 7 | INCBIN(bootstrap, "embed-js/bootstrap.js"); 8 | 9 | /** 10 | * These are defined now: 11 | * 12 | * const unsigned char sjs__code_bootstrap_data[]; 13 | * const unsigned char *const sjs__code_bootstrap_end; 14 | * const unsigned int sjs__code_bootstrap_size; 15 | * 16 | */ 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /test/test-os-fork-exit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | 7 | var pid = os.fork(); 8 | 9 | if (pid == 0) { 10 | // child 11 | os.exit(42); 12 | } else { 13 | // parent 14 | var r = os.waitpid(pid); 15 | assert.equal(r.pid, pid); 16 | assert(os.WIFEXITED(r.status)); 17 | assert.equal(os.WEXITSTATUS(r.status), 42); 18 | } 19 | -------------------------------------------------------------------------------- /test/test-os-chdir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const system = require('system'); 6 | 7 | 8 | var tmpDir; 9 | if (system.platform === 'osx') { 10 | tmpDir = '/private/tmp'; 11 | } else { 12 | tmpDir = '/tmp'; 13 | } 14 | var cwd = os.cwd; 15 | os.chdir(tmpDir); 16 | assert.equal(os.cwd, tmpDir); 17 | os.chdir(cwd); 18 | assert.equal(os.cwd, cwd); 19 | -------------------------------------------------------------------------------- /src/duk_module_node.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_MODULE_NODE_H_INCLUDED) 2 | #define DUK_MODULE_NODE_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | 7 | extern void duk_module_node_init(duk_context *ctx); 8 | extern duk_ret_t duk_module_node_peval_file(duk_context *ctx, const char* filename, int main); 9 | extern void duk_module_node_register_builtin(duk_context *ctx, const char* id); 10 | 11 | #endif /* DUK_MODULE_NODE_H_INCLUDED */ 12 | -------------------------------------------------------------------------------- /test/test-os-stat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const system = require('system'); 6 | 7 | 8 | var stat = os.stat(system.executable); 9 | assert(stat); 10 | 11 | assert.throws(function() { os.stat('/this/does/not/exist'); }); 12 | 13 | assert(os.S_ISREG(stat.mode)); 14 | 15 | const mode = os.S_IMODE(stat.mode); 16 | assert.ok(mode === 0o0755 || mode === 0o0775); 17 | -------------------------------------------------------------------------------- /test/test-random-interval.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const random = require('random'); 5 | 6 | const N = 5000; 7 | 8 | var sources = [new random.Random(), new random.SystemRandom()]; 9 | for (var j = 0; j < sources.length; j++) { 10 | var src = sources[j]; 11 | for (var i = 0; i < N; i++) { 12 | var n = src.random(); 13 | assert(n >= 0.0 && n < 1.0); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/source/modules/time.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modtime: 3 | 4 | time 5 | ==== 6 | 7 | This module provides time related functionality. 8 | 9 | 10 | Functions 11 | --------- 12 | 13 | .. js:function:: time.sleep(secs) 14 | 15 | Suspends the execution of the program for the given amount of `seconds` or until a signal is received. 16 | 17 | .. note:: 18 | This function may return earlier than the given amount of delay. 19 | 20 | -------------------------------------------------------------------------------- /include/sjs/build.h.in: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_BUILD_H 3 | #define SJS_BUILD_H 4 | 5 | 6 | #define SJS_BUILD_COMPILER "@SJS__BUILD_COMPILER@" 7 | #define SJS_BUILD_COMPILER_VERSION "@SJS__BUILD_COMPILER_VERSION@" 8 | #define SJS_BUILD_SYSTEM "@SJS__BUILD_SYSTEM@" 9 | #define SJS_BUILD_CFLAGS "@SJS__BUILD_CFLAGS@" 10 | #define SJS_BUILD_TIMESTAMP "@SJS__BUILD_TIMESTAMP@" 11 | #define SJS_BUILD_TYPE "@SJS__BUILD_TYPE@" 12 | 13 | #endif 14 | 15 | -------------------------------------------------------------------------------- /src/bindings/random/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "mt19937ar.h" 4 | 5 | 6 | void main(void){ 7 | mt_t mt; 8 | uint32_t init[4] = {61731, 24903, 614, 42143}; 9 | unsigned int length = 4; 10 | 11 | mt_init_by_array(&mt, init, length); 12 | for (int i = 0; i < 2000; i++) { 13 | printf("%.16f ", mt_genrand_res53(&mt)); 14 | if (i%5==4) 15 | printf("\n"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/test-eval-require-main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const proc = require('process'); 5 | const sys = require('system'); 6 | 7 | 8 | var p; 9 | var r; 10 | 11 | p = proc.spawn([sys.executable, '-e', 'require(\'assert\')(require.main === undefined)'], 12 | {stdin: null, stdout: null, stderr: null}); 13 | r = p.wait(); 14 | assert.equal(r.exit_status, 0); 15 | assert.equal(r.term_signal, 0); 16 | -------------------------------------------------------------------------------- /test/test-system-stdio.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const sys = require('system'); 5 | 6 | 7 | sys.stdout.write('lalala'); 8 | sys.stdout.write(new Buffer('lalala')); 9 | sys.stderr.write('lalala'); 10 | sys.stderr.write(new Buffer('lalala')); 11 | 12 | assert.throws(function() {sys.stdin.write('aaa');}); 13 | assert.throws(function() {sys.stdout.read();}); 14 | assert.throws(function() {sys.stderr.read();}); 15 | -------------------------------------------------------------------------------- /test/test-net-create-socket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | var s1 = new net.Socket(net.AF_INET, net.SOCK_STREAM); 8 | assert.ok(s1); 9 | assert.notEqual(s1.fd, -1); 10 | s1.close(); 11 | assert.equal(s1.fd, -1); 12 | 13 | var s2 = new net.Socket(net.AF_INET, net.SOCK_STREAM); 14 | assert.ok(s2); 15 | assert.notEqual(s2.fd, -1); 16 | s2.close(); 17 | assert.equal(s2.fd, -1); 18 | 19 | -------------------------------------------------------------------------------- /test/test-os-fork-execve-fail.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | 7 | 8 | var pid = os.fork(); 9 | 10 | if (pid == 0) { 11 | // child 12 | os.execve('/broken/path/yeah'); 13 | } else { 14 | var r = os.waitpid(pid, 0); 15 | assert.equal(r.pid, pid); 16 | assert(os.WIFEXITED(r.status)); 17 | assert.equal(os.WEXITSTATUS(r.status), 1); 18 | } 19 | -------------------------------------------------------------------------------- /test/test-os-setsid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | 7 | os.setsid(); 8 | assert.throws(os.setsid); 9 | 10 | var pid = os.fork(); 11 | 12 | if (pid == 0) { 13 | // child 14 | os.setsid(); 15 | } else { 16 | // parent 17 | var r = os.waitpid(pid, 0); 18 | assert.equal(r.pid, pid); 19 | assert(os.WIFEXITED(r.status)); 20 | assert.equal(os.WEXITSTATUS(r.status), 0); 21 | } 22 | -------------------------------------------------------------------------------- /test/test-os-getpid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | const parentPid = os.getpid(); 7 | 8 | 9 | var pid = os.fork(); 10 | 11 | if (pid == 0) { 12 | // child 13 | assert.equal(parentPid, os.getppid()); 14 | } else { 15 | // parent 16 | var r = os.waitpid(pid, 0); 17 | assert.equal(r.pid, pid); 18 | assert(os.WIFEXITED(r.status)); 19 | assert.equal(os.WEXITSTATUS(r.status), 0); 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/greet.js: -------------------------------------------------------------------------------- 1 | console.log('Skookum JS ' + system.versions.sjs + ' running on ' + system.platform + ' ' + system.arch); 2 | if (system.build.type === 'Debug') { 3 | console.log('[Engine: Duktape ' + system.versions.duktape + ' (' + system.versions.duktapeCommit + ')]'); 4 | console.log('[Build: ' + system.build.type + ' on ' + system.build.timestamp + ']'); 5 | console.log('[' + system.build.compiler + ' ' + system.build.compilerVersion + ' on ' + system.build.system + ']'); 6 | } 7 | -------------------------------------------------------------------------------- /test/test-require-main-set.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | const proc = require('process'); 6 | const sys = require('system'); 7 | 8 | 9 | var p; 10 | var r; 11 | 12 | p = proc.spawn([sys.executable, path.join(__dirname, 'fixtures', 'require_main', 'foo.js')], 13 | {stdin: 'inherit', stdout: 'inherit', stderr: 'inherit'}); 14 | r = p.wait(); 15 | assert.equal(r.exit_status, 0); 16 | assert.equal(r.term_signal, 0); 17 | -------------------------------------------------------------------------------- /test/test-os-fork-simple.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | const sentinel = 4242; 7 | 8 | 9 | var pid = os.fork(); 10 | 11 | if (pid == 0) { 12 | // child 13 | assert.equal(sentinel, 4242); 14 | } else { 15 | // parent 16 | assert.equal(sentinel, 4242); 17 | var r = os.waitpid(pid, 0); 18 | assert.equal(r.pid, pid); 19 | assert(os.WIFEXITED(r.status)); 20 | assert.equal(os.WEXITSTATUS(r.status), 0); 21 | } 22 | -------------------------------------------------------------------------------- /test/test-random-seed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const random = require('random'); 5 | 6 | 7 | // some working seeds 8 | var seeds = [0, 1, 1234, [0, 1, 2], null, undefined]; 9 | for (var i = 0; i < seeds.length; i++) { 10 | random.seed(seeds[i]); 11 | } 12 | 13 | // some non-working seeds 14 | var seeds = [{}, NaN, Buffer(10), new Uint32Array(10)]; 15 | for (var i = 0; i < seeds.length; i++) { 16 | assert.throws(function() { random.seed(seeds[i]); }); 17 | } 18 | -------------------------------------------------------------------------------- /src/version.c: -------------------------------------------------------------------------------- 1 | 2 | #include "duktape.h" 3 | #include "version.h" 4 | 5 | 6 | #define SJS_STRINGIFY(v) SJS_STRINGIFY_HELPER(v) 7 | #define SJS_STRINGIFY_HELPER(v) #v 8 | 9 | #define SJS_VERSION_STRING SJS_STRINGIFY(SJS_VERSION_MAJOR) "." \ 10 | SJS_STRINGIFY(SJS_VERSION_MINOR) "." \ 11 | SJS_STRINGIFY(SJS_VERSION_PATCH) \ 12 | SJS_VERSION_SUFFIX 13 | 14 | 15 | DUK_EXTERNAL const char* sjs_version(void) { 16 | return SJS_VERSION_STRING; 17 | } 18 | -------------------------------------------------------------------------------- /test/test-os-pipe.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | const TEST_DATA = 'hello sjs!'; 7 | 8 | 9 | var fds = os.pipe(); 10 | var rfd = fds[0]; 11 | var wfd = fds[1]; 12 | 13 | assert.equal(fds.length, 2); 14 | assert.notEqual(rfd, -1); 15 | assert.notEqual(wfd, -1); 16 | 17 | os.write(wfd, TEST_DATA); 18 | var data = os.read(rfd); 19 | data = new TextDecoder().decode(data); 20 | assert.equal(data, TEST_DATA); 21 | 22 | os.close(rfd); 23 | os.close(wfd); 24 | -------------------------------------------------------------------------------- /test/test-io-select-badargs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const select = require('io').select; 5 | 6 | 7 | function badRfds() { 8 | return select.select({foo: 'bar'}, [], [], 0); 9 | } 10 | 11 | assert.throws(badRfds); 12 | 13 | function badTimeout(t) { 14 | return select.select([], [], [], t); 15 | } 16 | 17 | var timeouts = [NaN, -3, {foo: 'bar'}, 'sjs']; 18 | for (var i = 0, len = timeouts.length; i < len; i++) { 19 | assert.throws(badTimeout.bind(this, timeouts[i])); 20 | } 21 | -------------------------------------------------------------------------------- /test/test-net-socketpair.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | const TEST_DATA = 'PING'; 7 | 8 | 9 | var pair = net.socketpair(net.AF_UNIX, net.SOCK_STREAM); 10 | pair[0].send(TEST_DATA); 11 | var data = pair[1].recv(); 12 | data = Buffer(data.buffer); 13 | assert.equal(data, TEST_DATA); 14 | pair[1].send(TEST_DATA); 15 | var data = pair[0].recv(); 16 | data = Buffer(data.buffer); 17 | assert.equal(data, TEST_DATA); 18 | 19 | pair[0].close(); 20 | pair[1].close(); 21 | -------------------------------------------------------------------------------- /test/test-os-urandom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | 7 | var r; 8 | 9 | r = os.urandom(64); 10 | assert(r instanceof Buffer); 11 | assert.equal(r.length, 64); 12 | 13 | r = Buffer(128); 14 | os.urandom(r); 15 | 16 | var data = [null, NaN, 0, Buffer(0), new Uint8Array(16)]; 17 | for (var i = 0, len = data.length; i < len; i++) { 18 | assert.throws(os.urandom.bind(null, data[i])); 19 | } 20 | 21 | // test some large(r) value 22 | r = os.urandom(4096); 23 | assert(r instanceof Buffer); 24 | assert.equal(r.length, 4096); 25 | -------------------------------------------------------------------------------- /modules/_io_poll.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _poll = _system.binding('poll'); 4 | 5 | 6 | function poll(pfds, timeout) { 7 | if (timeout == null) { 8 | timeout = null; 9 | } else { 10 | timeout = parseFloat(timeout); 11 | if (isNaN(timeout) || timeout < 0) { 12 | throw new TypeError('timeout must be null or a positive number') 13 | } 14 | } 15 | return _poll.poll(pfds, timeout); 16 | } 17 | 18 | 19 | exports.poll = poll; 20 | 21 | // extract constants 22 | for (var k in _poll.c) { 23 | var val = _poll.c[k]; 24 | exports[k] = val; 25 | } 26 | -------------------------------------------------------------------------------- /test/test-os-fork-pipe.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | const sentinel = 4242; 7 | 8 | var p = os.pipe(); 9 | var rfd = p[0]; 10 | var wfd = p[1]; 11 | var pid = os.fork(); 12 | 13 | if (pid == 0) { 14 | // child 15 | os.write(wfd, "HELLO"); 16 | } else { 17 | // parent 18 | var data = os.read(rfd); 19 | data = new TextDecoder().decode(data); 20 | assert.equal(data, "HELLO"); 21 | var r = os.waitpid(pid); 22 | assert.equal(r.pid, pid); 23 | assert(os.WIFEXITED(r.status)); 24 | assert.equal(os.WEXITSTATUS(r.status), 0); 25 | } 26 | -------------------------------------------------------------------------------- /modules/_utils_unicode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const unorm = require('_utils_unicode_unorm'); 4 | 5 | 6 | function normalize(form, str) { 7 | form = form === undefined ? "NFC" : form; 8 | 9 | if (form === "NFC") { 10 | return unorm.nfc(str); 11 | } else if (form === "NFD") { 12 | return unorm.nfd(str); 13 | } else if (form === "NFKC") { 14 | return unorm.nfkc(str); 15 | } else if (form === "NFKD") { 16 | return unorm.nfkd(str); 17 | } else { 18 | throw new RangeError("Invalid normalization form: " + form); 19 | } 20 | } 21 | 22 | 23 | exports.normalize = normalize; 24 | -------------------------------------------------------------------------------- /test/test-io-poll-badargs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const poll = require('io').poll; 5 | 6 | 7 | function badPfds() { 8 | return poll.poll({foo: 'bar'}, 0); 9 | } 10 | 11 | function badPfds2() { 12 | return poll.poll([{fd: 0, events: poll.POLLIN}, {foo: 'bar'}], 0); 13 | } 14 | 15 | assert.throws(badPfds); 16 | assert.throws(badPfds2); 17 | 18 | function badTimeout(t) { 19 | return poll.poll([], t); 20 | } 21 | 22 | var timeouts = [NaN, {foo: 'bar'}, [], 'sjs']; 23 | for (var i = 0, len = timeouts.length; i < len; i++) { 24 | assert.throws(badTimeout.bind(this, timeouts[i])); 25 | } 26 | -------------------------------------------------------------------------------- /modules/_io_select.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _select = _system.binding('select'); 4 | 5 | 6 | function select(rfds, wfds, xfds, timeout) { 7 | if (timeout == null) { 8 | timeout = null; 9 | } else { 10 | timeout = parseFloat(timeout); 11 | if (isNaN(timeout) || timeout < 0) { 12 | throw new TypeError('timeout must be null or a positive number') 13 | } 14 | } 15 | // fds are sanitized inside the C module 16 | return _select.select(rfds || [], wfds || [], xfds || [], timeout); 17 | } 18 | 19 | 20 | exports.select = select; 21 | exports.FD_SETSIZE = _select.c.FD_SETSIZE; 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | Thinking about contributing to Skookum JS? That's awesome, thanks! 5 | 6 | Before you being to write any code, here are some simple guidelines which will save 7 | us some trouble down the road. 8 | 9 | * If you are fixing a small bug, just open a Pull Request 10 | * If you are adding a new feature or fixing a reasonably big bug, please open an issue 11 | first so we can discuss it beforehand 12 | * Style fix patches are not welcome, you like your style and I like mine 13 | * Make your code style look like mine 14 | * Any new feature should come accompanied by documentation and tests 15 | 16 | Once again, thanks for taking the time to contribute! 17 | -------------------------------------------------------------------------------- /test/test-net-socket-dgram.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | const TEST_ADDR = {host: '127.0.0.1', port: 0}; 8 | const TEST_DATA = 'PING'; 9 | 10 | 11 | var server = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 12 | server.bind(TEST_ADDR); 13 | TEST_ADDR.port = server.getsockname().port; 14 | 15 | var client = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 16 | client.bind({host: '127.0.0.1', port: 0}); 17 | 18 | client.sendto(TEST_DATA, TEST_ADDR); 19 | var res = server.recvfrom(); 20 | res.data = Buffer(res.data.buffer); 21 | assert.equal(res.data, TEST_DATA); 22 | assert.deepEqual(res.address, client.getsockname()); 23 | 24 | server.close(); 25 | client.close(); 26 | -------------------------------------------------------------------------------- /test/test-process-spawn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const proc = require('process'); 5 | 6 | 7 | var p; 8 | var r; 9 | 10 | p = proc.spawn(['ls', '-l']); 11 | r = p.wait(); 12 | assert.equal(r.exit_status, 0); 13 | assert.equal(r.term_signal, 0); 14 | 15 | p = proc.spawn('ls -l', {shell: true}); 16 | r = p.wait(); 17 | assert.equal(r.exit_status, 0); 18 | assert.equal(r.term_signal, 0); 19 | 20 | p = proc.spawn(['ls', '-l', '/bogus/path/1']); 21 | r = p.wait(); 22 | assert.notEqual(r.exit_status, 0); 23 | assert.equal(r.term_signal, 0); 24 | 25 | p = proc.spawn('ls -l /bogus/path/1', {shell: true}); 26 | r = p.wait(); 27 | assert.notEqual(r.exit_status, 0); 28 | assert.equal(r.term_signal, 0); 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/source/modules/errno.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _moderrno: 3 | 4 | errno 5 | ===== 6 | 7 | The `errno` module exposes all system error codes matching reason strings. 8 | 9 | .. note:: 10 | The exposed error codes may vary depending on the platform. 11 | 12 | 13 | .. js:data:: errno.map 14 | 15 | A ``Map`` mapping error codes to their string versions. Example: 16 | 17 | :: 18 | 19 | sjs> errno.map.get(1) 20 | = EPERM 21 | 22 | .. js:data:: errno.E* 23 | 24 | All errno constants available in the system as exposed as module level constants. Example: 25 | 26 | :: 27 | 28 | sjs> errno.EPERM 29 | = 1 30 | 31 | .. js:function:: errno.strerror(code) 32 | 33 | Get the string that describes the given error `code`. 34 | -------------------------------------------------------------------------------- /test/test-net-socket-dgram-connected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | const TEST_ADDR = {host: '127.0.0.1', port: 0}; 8 | const TEST_DATA = 'PING'; 9 | 10 | 11 | var server = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 12 | server.bind(TEST_ADDR); 13 | TEST_ADDR.port = server.getsockname().port; 14 | 15 | var client = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 16 | client.bind({host: '127.0.0.1', port: 0}); 17 | client.connect(TEST_ADDR); 18 | 19 | client.send(TEST_DATA); 20 | var res = server.recvfrom(); 21 | res.data = Buffer(res.data.buffer); 22 | assert.equal(res.data, TEST_DATA); 23 | assert.deepEqual(res.address, client.getsockname()); 24 | 25 | server.close(); 26 | client.close(); 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | MAKE?=make 3 | PYTHON?=python3 4 | 5 | CMAKE_MK=build/Makefile 6 | 7 | BUILDTYPE?=Debug 8 | PREFIX?=/usr/local 9 | VERBOSE?= 10 | 11 | all: build 12 | 13 | build: $(CMAKE_MK) 14 | @$(MAKE) -C build VERBOSE=$(VERBOSE) 15 | 16 | $(CMAKE_MK): 17 | @mkdir -p build; \ 18 | cd build; \ 19 | cmake ../ -DCMAKE_BUILD_TYPE=$(BUILDTYPE) -DCMAKE_INSTALL_PREFIX=$(PREFIX); \ 20 | cd .. 21 | 22 | install: $(CMAKE_MK) 23 | @$(MAKE) -C build install 24 | 25 | clean: 26 | @$(MAKE) -C build clean 27 | 28 | distclean: 29 | @rm -rf build 30 | 31 | test: build 32 | $(PYTHON) tools/test.py test/ 33 | 34 | .PHONY: all build install clean distclean run 35 | -------------------------------------------------------------------------------- /test/test-os-lowlevel-io.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | 6 | const TEST_FILE = 'test123'; 7 | const TEST_DATA = 'hello sjs!'; 8 | 9 | 10 | safeUnlink(TEST_FILE); 11 | 12 | var fd, r; 13 | fd = os.open(TEST_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC); 14 | assert(fd > -1); 15 | r = os.write(fd, TEST_DATA); 16 | assert.equal(r, TEST_DATA.length); 17 | os.close(fd); 18 | 19 | fd = os.open(TEST_FILE, 'r'); 20 | assert(fd > -1); 21 | r = os.read(fd); 22 | r = new TextDecoder().decode(r); 23 | assert.equal(r, TEST_DATA); 24 | os.close(fd); 25 | 26 | safeUnlink(TEST_FILE); 27 | 28 | 29 | function safeUnlink(path) { 30 | try { 31 | os.unlink(path); 32 | } catch (e) { 33 | // ignore 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/test-process-spawn-setuid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const errno = require('errno'); 5 | const os = require('os'); 6 | const proc = require('process'); 7 | const pwd = require('pwd'); 8 | 9 | 10 | var info = pwd.getpwnam('nobody'); 11 | if (info === null) { 12 | console.log('SKIP: there is no "nobody" user'); 13 | os.exit(0); 14 | } 15 | 16 | var p; 17 | var r; 18 | 19 | try { 20 | p = proc.spawn(['ls', '-l'], 21 | {cwd: '/', uid: info.uid, gid: info.gid}); 22 | } catch (e) { 23 | assert.equal(e.errno, errno.EPERM); 24 | console.log('SKIP: no permission to downgrade to user "nobody"'); 25 | os.exit(0); 26 | } 27 | r = p.wait(); 28 | assert.equal(r.exit_status, 0); 29 | assert.equal(r.term_signal, 0); 30 | -------------------------------------------------------------------------------- /test/test-os-fork-execve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | const system = require('system'); 7 | 8 | 9 | var p = os.pipe(); 10 | var rfd = p[0]; 11 | var wfd = p[1]; 12 | os.cloexec(wfd, false); 13 | var pid = os.fork(); 14 | 15 | if (pid == 0) { 16 | // child 17 | os.execve(system.executable, 18 | [system.executable, path.join(__dirname, 'helper1.js')], 19 | {PARENT_FD: wfd}); 20 | assert(false); 21 | } else { 22 | // parent 23 | var data = os.read(rfd); 24 | data = new TextDecoder().decode(data); 25 | assert.equal(data, "HELLO"); 26 | var r = os.waitpid(pid); 27 | assert.equal(r.pid, pid); 28 | assert(os.WIFEXITED(r.status)); 29 | assert.equal(os.WEXITSTATUS(r.status), 0); 30 | } 31 | -------------------------------------------------------------------------------- /test/test-net-socket-dgram-buffers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | const TEST_ADDR = {host: '127.0.0.1', port: 0}; 8 | const TEST_DATA = 'PING'; 9 | 10 | 11 | var server = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 12 | server.bind(TEST_ADDR); 13 | TEST_ADDR.port = server.getsockname().port; 14 | 15 | var client = new net.Socket(net.AF_INET, net.SOCK_DGRAM); 16 | client.bind({host: '127.0.0.1', port: 0}); 17 | 18 | client.sendto(new Buffer(TEST_DATA), TEST_ADDR); 19 | client.sendto(TEST_DATA, TEST_ADDR); 20 | var buffer = new Buffer(4096); 21 | var r = server.recvfrom(buffer); 22 | assert.equal(buffer.slice(0, r.nrecv).toString(), TEST_DATA); 23 | assert.equal(r.nrecv, TEST_DATA.length); 24 | assert.deepEqual(r.address, client.getsockname()); 25 | 26 | server.close(); 27 | client.close(); 28 | -------------------------------------------------------------------------------- /test/test-net-socket-connection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | const TEST_ADDR = {host: '127.0.0.1', port: 0}; 8 | const TEST_DATA = 'PING'; 9 | 10 | var server = new net.Socket(net.AF_INET, net.SOCK_STREAM); 11 | server.bind(TEST_ADDR); 12 | server.listen(128); 13 | TEST_ADDR.port = server.getsockname().port; 14 | 15 | var client = new net.Socket(net.AF_INET, net.SOCK_STREAM); 16 | client.connect(TEST_ADDR); 17 | 18 | var connection = server.accept(); 19 | 20 | connection.send(TEST_DATA); 21 | var data = client.recv(); 22 | data = Buffer(data.buffer); 23 | assert.equal(data, TEST_DATA); 24 | client.send(TEST_DATA); 25 | var data = connection.recv(); 26 | data = Buffer(data.buffer); 27 | assert.equal(data, TEST_DATA); 28 | 29 | server.close(); 30 | client.close(); 31 | connection.close(); 32 | 33 | -------------------------------------------------------------------------------- /tools/build_duktape/config.yaml: -------------------------------------------------------------------------------- 1 | # Duktape build configuration for SkookumJS 2 | # 3 | 4 | DUK_USE_FATAL_HANDLER: 5 | verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) do { const char *fatal_msg = (msg); fprintf(stderr, \"*** FATAL ERROR: %s\\n\", fatal_msg ? fatal_msg : \"no message\"); fflush(stderr); *((volatile unsigned int *) 0) = (unsigned int) 0xdeadbeefUL; abort(); } while(0)" 6 | DUK_USE_BYTECODE_DUMP_SUPPORT: false 7 | DUK_USE_COMMONJS_MODULES: false 8 | DUK_USE_COROUTINE_SUPPORT: false 9 | DUK_USE_DUKTAPE_BUILTIN: true 10 | DUK_USE_ENCODING_BUILTINS: true 11 | DUK_USE_ERRCREATE: false 12 | DUK_USE_ERRTHROW: false 13 | DUK_USE_FILE_IO: false 14 | DUK_USE_JC: false 15 | DUK_USE_JX: true 16 | DUK_USE_SYMBOL_BUILTIN: true 17 | DUK_USE_UNION_INITIALIZERS: true 18 | DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME: true 19 | DUK_USE_BROWSER_LIKE: false 20 | DUK_USE_GLOBAL_BINDING: true 21 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modules: 3 | 4 | Modules 5 | ======= 6 | 7 | `sjs` includes a builtin `Common JS `_ module loading system. 8 | See :ref:`module_system`. There is also a :ref:`stdlib` whith many modules. The goal is to provide a 9 | `kitchen-sink` collection of modules. 10 | 11 | Starting with version 0.4.0, `sjs` includes a module system more similar to Node. 12 | 13 | 14 | .. _stdlib: 15 | 16 | Standard library 17 | ---------------- 18 | 19 | .. toctree:: 20 | :maxdepth: 1 21 | 22 | modules/assert 23 | modules/codecs 24 | modules/console 25 | modules/errno 26 | modules/hash 27 | modules/io 28 | modules/net 29 | modules/os 30 | modules/path 31 | modules/process 32 | modules/pwd 33 | modules/random 34 | modules/system 35 | modules/time 36 | modules/utils 37 | modules/uuid 38 | 39 | -------------------------------------------------------------------------------- /test/test-os-fork-execve-args.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | const system = require('system'); 7 | 8 | const args = ['444', '555', '666']; 9 | 10 | 11 | var p = os.pipe(); 12 | var rfd = p[0]; 13 | var wfd = p[1]; 14 | os.cloexec(wfd, false); 15 | var pid = os.fork(); 16 | 17 | if (pid == 0) { 18 | // child 19 | os.execve(system.executable, 20 | [system.executable, path.join(__dirname, 'helper2.js')].concat(args), 21 | {PARENT_FD: wfd}); 22 | assert(false); 23 | } else { 24 | // parent 25 | var data = os.read(rfd); 26 | data = new TextDecoder().decode(data); 27 | assert.deepEqual(data.split(','), args); 28 | var r = os.waitpid(pid); 29 | assert.equal(r.pid, pid); 30 | assert(os.WIFEXITED(r.status)); 31 | assert.equal(os.WEXITSTATUS(r.status), 0); 32 | } 33 | -------------------------------------------------------------------------------- /test/test-os-fork-execve-env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | const system = require('system'); 7 | 8 | 9 | var p = os.pipe(); 10 | var rfd = p[0]; 11 | var wfd = p[1]; 12 | os.cloexec(wfd, false); 13 | 14 | var childEnv = {PARENT_FD: wfd, FOO: 'foo'}; 15 | 16 | 17 | var pid = os.fork(); 18 | 19 | if (pid == 0) { 20 | // child 21 | os.execve(system.executable, 22 | [system.executable, path.join(__dirname, 'helper4.js')], 23 | childEnv); 24 | assert(false); 25 | } else { 26 | // parent 27 | var data = os.read(rfd, 16834); 28 | data = new TextDecoder().decode(data); 29 | assert.deepEqual(JSON.parse(data), childEnv); 30 | var r = os.waitpid(pid); 31 | assert.equal(r.pid, pid); 32 | assert(os.WIFEXITED(r.status)); 33 | assert.equal(os.WEXITSTATUS(r.status), 0); 34 | } 35 | -------------------------------------------------------------------------------- /docs/source/modules/codecs.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modcodecs: 3 | 4 | codecs 5 | ====== 6 | 7 | The `codecs` module provides a consistent API for encoding strings (or objects) into different representations. 8 | The following encodeings are supported: 9 | 10 | - ``base64`` 11 | - ``hex`` 12 | - ``json`` 13 | - ``punycode`` 14 | - ``utf8`` 15 | 16 | 17 | .. js:function:: encode(encoding, data) 18 | 19 | Encodes the given `data` using the desired `encoding`. 20 | 21 | :param encoding: The desired encoding. (Must be one of the supported encodings) 22 | :param data: Value which will be encoded. 23 | :returns: A string with the encoded data. 24 | 25 | .. js:function:: decode(encoding, data) 26 | 27 | Decodes the given `data` using the desired `encoding`. 28 | 29 | :param encoding: The desired encoding. (Must be one of the supported encodings) 30 | :param data: Value which will be decoded. 31 | :returns: The decoded object. 32 | -------------------------------------------------------------------------------- /src/platform/darwin.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include /* _NSGetExecutablePath */ 5 | #include 6 | 7 | #include "../internal.h" 8 | 9 | 10 | void sjs__executable(char* buf, size_t size) { 11 | /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ 12 | char abspath[PATH_MAX * 2 + 1]; 13 | char exepath[PATH_MAX + 1]; 14 | uint32_t exepath_size; 15 | size_t abspath_size; 16 | 17 | exepath_size = sizeof(exepath); 18 | if (_NSGetExecutablePath(exepath, &exepath_size)) { 19 | return; 20 | } 21 | 22 | if (realpath(exepath, abspath) != abspath) { 23 | return; 24 | } 25 | 26 | abspath_size = strlen(abspath); 27 | if (abspath_size == 0) { 28 | return; 29 | } 30 | 31 | size -= 1; 32 | if (size > abspath_size) 33 | size = abspath_size; 34 | 35 | memcpy(buf, abspath, size); 36 | buf[size] = '\0'; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/binding.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_BINDING_H 3 | #define SJS_BINDING_H 4 | 5 | #include "internal.h" 6 | 7 | void sjs__setup_binding(duk_context* ctx); 8 | void sjs__register_binding(duk_context* ctx, 9 | const char* id, 10 | const duk_function_list_entry functions[], 11 | const duk_number_list_entry constants[]); 12 | 13 | /* current bindings */ 14 | void sjs__binding_errno_init(duk_context* ctx); 15 | void sjs__binding_gai_init(duk_context* ctx); 16 | void sjs__binding_hash_init(duk_context* ctx); 17 | void sjs__binding_io_init(duk_context* ctx); 18 | void sjs__binding_os_init(duk_context* ctx); 19 | void sjs__binding_path_init(duk_context* ctx); 20 | void sjs__binding_poll_init(duk_context* ctx); 21 | void sjs__binding_pwd_init(duk_context* ctx); 22 | void sjs__binding_random_init(duk_context* ctx); 23 | void sjs__binding_select_init(duk_context* ctx); 24 | void sjs__binding_socket_init(duk_context* ctx); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/internal.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_INTERNAL_H 3 | #define SJS_INTERNAL_H 4 | 5 | #include "duktape.h" 6 | 7 | #define SJS_PATH_MAX 4096 8 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 9 | 10 | 11 | void sjs__setup_system_module(duk_context* ctx); 12 | void sjs__setup_commonjs(duk_context* ctx); 13 | void sjs__register_bultins(duk_context* ctx); 14 | 15 | int sjs__path_expanduser(const char* path, char* normalized_path, size_t normalized_path_len); 16 | int sjs__path_normalize(const char* path, char* normalized_path, size_t normalized_path_len); 17 | ssize_t sjs__file_read(const char* path, char** data); 18 | 19 | /* platform */ 20 | void sjs__executable(char* buf, size_t size); 21 | int sjs__cloexec(int fd, int set); 22 | int sjs__nonblock(int fd, int set); 23 | int sjs__close(int fd); 24 | 25 | /* string functions borrowed from OpenBSD */ 26 | size_t sjs__strlcat(char *dst, const char *src, size_t dsize); 27 | size_t sjs__strlcpy(char *dst, const char *src, size_t dsize); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/platform/common.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int sjs__cloexec(int fd, int set) { 9 | int r; 10 | 11 | do 12 | r = ioctl(fd, set ? FIOCLEX : FIONCLEX); 13 | while (r == -1 && errno == EINTR); 14 | 15 | if (r) 16 | return -errno; 17 | 18 | return 0; 19 | } 20 | 21 | 22 | int sjs__nonblock(int fd, int set) { 23 | int r; 24 | 25 | do 26 | r = ioctl(fd, FIONBIO, &set); 27 | while (r == -1 && errno == EINTR); 28 | 29 | if (r) 30 | return -errno; 31 | 32 | return 0; 33 | } 34 | 35 | 36 | int sjs__close(int fd) { 37 | int saved_errno; 38 | int r; 39 | 40 | saved_errno = errno; 41 | r = close(fd); 42 | if (r == -1) { 43 | r = -errno; 44 | if (r == -EINTR || r == -EINPROGRESS) { 45 | r = 0; /* The close is in progress, not an error. */ 46 | } 47 | errno = saved_errno; 48 | } 49 | 50 | return r; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /test/test-io-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const io = require('io'); 5 | 6 | const TEST_FILE = 'test123'; 7 | const TEST_DATA = 'hello sjs!'; 8 | 9 | 10 | safeUnlink(TEST_FILE); 11 | var f = io.open(TEST_FILE, 'wb+'); 12 | assert(f.fd !== -1); 13 | assert(!f.closed); 14 | f.write(TEST_DATA); 15 | f.flush(); 16 | f.close(); 17 | assert.equal(f.fd, -1); 18 | assert(f.closed); 19 | 20 | f = io.open(TEST_FILE, 'rb'); 21 | var data = f.read(); 22 | f.close(); 23 | assert.equal(new TextDecoder().decode(data), TEST_DATA); 24 | 25 | f = io.open(TEST_FILE, 'rb'); 26 | var databuf = Buffer(4096); 27 | f.read(databuf); 28 | f.close(); 29 | assert.equal(databuf.slice(0, TEST_DATA.length).toString(), TEST_DATA); 30 | 31 | var data = io.readFile(TEST_FILE); 32 | assert.equal(new TextDecoder().decode(data), TEST_DATA); 33 | 34 | safeUnlink(TEST_FILE); 35 | 36 | function safeUnlink(path) { 37 | try { 38 | os.unlink(path); 39 | } catch (e) { 40 | // ignore 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/test-os-fork-execv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | const system = require('system'); 7 | 8 | 9 | var p = os.pipe(); 10 | var rfd = p[0]; 11 | var wfd = p[1]; 12 | 13 | 14 | var pid = os.fork(); 15 | 16 | if (pid == 0) { 17 | // child 18 | os.dup2(wfd, 1, false); 19 | os.execv(system.executable, 20 | [system.executable, path.join(__dirname, 'helper8.js')]); 21 | assert(false); 22 | } else { 23 | // parent 24 | os.close(wfd); 25 | var data = ''; 26 | for (;;) { 27 | var chunk = os.read(rfd, 16834); 28 | chunk = new TextDecoder().decode(chunk); 29 | if (chunk) { 30 | data += chunk; 31 | } else { 32 | break; 33 | } 34 | } 35 | assert.equal(data, 'HELLO SJS\n'); 36 | var r = os.waitpid(pid); 37 | assert.equal(r.pid, pid); 38 | assert(os.WIFEXITED(r.status)); 39 | assert.equal(os.WEXITSTATUS(r.status), 0); 40 | } 41 | -------------------------------------------------------------------------------- /test/test-process-spawn-stdio.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const proc = require('process'); 5 | const sys = require('system'); 6 | 7 | const TEXT = 'test123\n'; 8 | const TEST_ARGS = ['foo', 'bar', 'baz', '123']; 9 | 10 | 11 | var p; 12 | var r; 13 | 14 | p = proc.spawn([sys.executable, 'test/helper3.js'], 15 | {stdin: 'pipe', stdout: 'pipe', stderr: 'pipe'}); 16 | p.stdin.write(TEXT); 17 | assert.equal(p.stdout.readLine(), TEXT); 18 | assert.equal(p.stderr.readLine(), TEXT); 19 | p.stdin.close(); 20 | p.stdout.close(); 21 | p.stderr.close(); 22 | r = p.wait(); 23 | assert.equal(r.exit_status, 0); 24 | assert.equal(r.term_signal, 0); 25 | 26 | 27 | p = proc.spawn([sys.executable, 'test/helper6.js'].concat(TEST_ARGS), 28 | {stdin: null, stdout: 'pipe', stderr: 'inherit'}); 29 | var data = p.stdout.readLine(); 30 | assert.deepEqual(JSON.parse(data), TEST_ARGS); 31 | p.stdout.close(); 32 | r = p.wait(); 33 | assert.equal(r.exit_status, 0); 34 | assert.equal(r.term_signal, 0); 35 | -------------------------------------------------------------------------------- /test/test-net-socket-abstract.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | const os = require('os'); 6 | const system = require('system'); 7 | 8 | 9 | if (system.platform !== 'linux') { 10 | // the test only works on Linux 11 | os.exit(0); 12 | } 13 | 14 | 15 | const TEST_ADDR = '\0sjs-sock'; 16 | const TEST_DATA = 'PING'; 17 | 18 | var server = new net.Socket(net.AF_UNIX, net.SOCK_STREAM); 19 | server.bind(TEST_ADDR); 20 | assert.equal(server.getsockname(), TEST_ADDR); 21 | server.listen(128); 22 | 23 | var client = new net.Socket(net.AF_UNIX, net.SOCK_STREAM); 24 | client.connect(TEST_ADDR); 25 | 26 | var connection = server.accept(); 27 | 28 | connection.send(TEST_DATA); 29 | var data = client.recv(); 30 | data = Buffer(data.buffer); 31 | assert.equal(data, TEST_DATA); 32 | client.send(TEST_DATA); 33 | var data = connection.recv(); 34 | data = Buffer(data.buffer); 35 | assert.equal(data, TEST_DATA); 36 | 37 | server.close(); 38 | client.close(); 39 | connection.close(); 40 | 41 | -------------------------------------------------------------------------------- /docs/source/modules/pwd.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modpwd: 3 | 4 | pwd 5 | === 6 | 7 | This module provides functions to query the Unix password database. 8 | 9 | 10 | passwd object 11 | ------------- 12 | 13 | These objects are returned by the functions in this module and represent an entry in the password database. 14 | They are a thin wrapper around a ``struct passwd`` structure. 15 | 16 | Properties: 17 | 18 | * name: user name 19 | * passwd: user password (typically 'x', since it's encrypted) 20 | * uid: user ID 21 | * gid: group ID 22 | * gecos: user information / description 23 | * dir: user home directory 24 | * shell: user shell 25 | 26 | 27 | Functions 28 | --------- 29 | 30 | .. js:function:: pwd.getpwnam(name) 31 | 32 | Returns the passwd object mathing the given `name` in the Unix password database. 33 | 34 | .. seealso:: 35 | :man:`getpwnam(3)`. 36 | 37 | .. js:function:: pwd.getpwuid(uid) 38 | 39 | Returns the passwd object mathing the given `uid` in the Unix password database. 40 | 41 | .. seealso:: 42 | :man:`getpwuid(3)`. 43 | -------------------------------------------------------------------------------- /test/test-os-fork-execve-json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const os = require('os'); 5 | const path = require('path'); 6 | const system = require('system'); 7 | 8 | 9 | var p = os.pipe(); 10 | var rfd = p[0]; 11 | var wfd = p[1]; 12 | 13 | 14 | var pid = os.fork(); 15 | 16 | if (pid == 0) { 17 | // child 18 | os.dup2(wfd, 1, false); 19 | os.execve(system.executable, 20 | [system.executable, path.join(__dirname, 'helper5.js')], 21 | {FOO: 'foo'}); 22 | assert(false); 23 | } else { 24 | // parent 25 | os.close(wfd); 26 | var data = ''; 27 | for (;;) { 28 | var chunk = os.read(rfd, 16834); 29 | chunk = new TextDecoder().decode(chunk); 30 | if (chunk) { 31 | data += chunk; 32 | } else { 33 | break; 34 | } 35 | } 36 | data = JSON.parse(data); 37 | assert(data.FOO); 38 | var r = os.waitpid(pid); 39 | assert.equal(r.pid, pid); 40 | assert(os.WIFEXITED(r.status)); 41 | assert.equal(os.WEXITSTATUS(r.status), 0); 42 | } 43 | -------------------------------------------------------------------------------- /modules/path.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _path = _system.binding('path'); 4 | 5 | 6 | function join() { 7 | if (arguments.length === 0) 8 | return '.'; 9 | var joined; 10 | for (var i = 0; i < arguments.length; ++i) { 11 | var arg = arguments[i]; 12 | if (arg.length > 0) { 13 | if (joined === undefined) { 14 | joined = arg; 15 | } else { 16 | var sep = ''; 17 | if (arg.startsWith('/')) { 18 | arg = arg.substring(1); 19 | } 20 | if (!joined.endsWith('/')) { 21 | sep = '/'; 22 | } 23 | joined += sep + arg; 24 | } 25 | } 26 | } 27 | if (joined === undefined) 28 | return '.'; 29 | return joined; 30 | } 31 | 32 | 33 | function normalize(p) { 34 | try { 35 | return _path.normalize(p); 36 | } catch (e) { 37 | return p; 38 | } 39 | } 40 | 41 | 42 | exports.basename = _path.basename; 43 | exports.dirname = _path.dirname; 44 | exports.normalize = normalize; 45 | exports.join = join; 46 | -------------------------------------------------------------------------------- /modules/codecs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const punycode = require('_codecs_punycode'); 4 | const utf8 = require('_codecs_utf8'); 5 | 6 | 7 | var encoders = { 8 | 'base64' : Duktape.enc.bind(Duktape, 'base64'), 9 | 'hex' : Duktape.enc.bind(Duktape, 'hex'), 10 | 'json' : JSON.stringify, 11 | 'punycode': punycode.encode, 12 | 'utf8' : utf8.encode 13 | }; 14 | 15 | 16 | var decoders = { 17 | 'base64' : Duktape.dec.bind(Duktape, 'base64'), 18 | 'hex' : Duktape.dec.bind(Duktape, 'hex'), 19 | 'json' : JSON.parse, 20 | 'punycode': punycode.decode, 21 | 'utf8' : utf8.decode 22 | }; 23 | 24 | 25 | function encode(encoding, data) { 26 | const encoder = encoders[encoding]; 27 | if (!encoder) { 28 | throw new Error('encoder not found: ' + encoding); 29 | } 30 | return encoder(data); 31 | } 32 | 33 | 34 | function decode(encoding, data) { 35 | const decoder = decoders[encoding]; 36 | if (!decoder) { 37 | throw new Error('decoder not found: ' + encoding); 38 | } 39 | return decoder(data); 40 | } 41 | 42 | 43 | exports.encode = encode; 44 | exports.decode= decode; 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2016-present Saúl Ibarra Corretgé 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /test/test-net-socket-buffers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | const TEST_ADDR = {host: '127.0.0.1', port: 0}; 8 | const TEST_DATA = 'PING'; 9 | 10 | var server = new net.Socket(net.AF_INET, net.SOCK_STREAM); 11 | server.bind(TEST_ADDR); 12 | server.listen(128); 13 | TEST_ADDR.port = server.getsockname().port; 14 | 15 | var client = new net.Socket(net.AF_INET, net.SOCK_STREAM); 16 | client.connect(TEST_ADDR); 17 | 18 | var connection = server.accept(); 19 | 20 | connection.send(new Buffer(TEST_DATA)); 21 | var buffer = new Buffer(4096); 22 | var r = client.recv(buffer); 23 | assert.equal(buffer.slice(0, r).toString(), TEST_DATA); 24 | assert.equal(r, TEST_DATA.length); 25 | client.send(TEST_DATA); 26 | buffer.fill(0); 27 | r = connection.recv(buffer); 28 | assert.equal(buffer.slice(0, r).toString(), TEST_DATA); 29 | assert.equal(r, TEST_DATA.length); 30 | 31 | function recvData(data) { 32 | client.recv(data); 33 | } 34 | var data = ['aaaaa', {}, new Buffer(0), new Uint8Array(0)]; 35 | for (var i = 0, len = data.length; i < len; i++) { 36 | assert.throws(recvData.bind(this, data[i]), TypeError); 37 | } 38 | 39 | server.close(); 40 | client.close(); 41 | connection.close(); 42 | 43 | -------------------------------------------------------------------------------- /docs/source/features.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _features: 3 | 4 | Features 5 | ======== 6 | 7 | * Small footprint 8 | * `Ecmascript ES5/ES5.1 `_ compliant 9 | * Some post-ES5 features 10 | * `TypedArray `_ and 11 | `Buffer `_ support 12 | * Built-in regular expression engine 13 | * Built-in Unicode support 14 | * Tail call support 15 | * Combined reference counting and mark-and-sweep garbage collection with finalization 16 | * CommonJS-based module loading system 17 | * Support for native modules written in C 18 | * Rich standard library 19 | * Binary name 25% shorter than Node 20 | 21 | .. seealso:: 22 | Skookum JS gets most of its features through its engine: `Duktape `_. 23 | 24 | 25 | Post-ES5 features 26 | ----------------- 27 | 28 | Duktape (the JavaScript engine used by `sjs`) currently implements 29 | `some post-ES5 features `_. 30 | 31 | `sjs` expands on this by having `babel-polyfill `_ builtin. 32 | 33 | .. note:: 34 | Some of the polyfills depend on core dunctionality not currently present, such as ``setTimeout``. 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | matrix: 3 | include: 4 | - os: linux 5 | dist: xenial 6 | sudo: required 7 | compiler: gcc 8 | env: BUILDTYPE=Debug 9 | - os: linux 10 | dist: xenial 11 | sudo: required 12 | compiler: gcc 13 | env: BUILDTYPE=Release 14 | - os: linux 15 | dist: xenial 16 | sudo: required 17 | compiler: clang 18 | env: BUILDTYPE=Debug 19 | - os: linux 20 | dist: xenial 21 | sudo: required 22 | compiler: clang 23 | env: BUILDTYPE=Release 24 | - os: linux 25 | dist: xenial 26 | sudo: required 27 | addons: 28 | apt: 29 | update: true 30 | packages: 31 | - musl-tools 32 | env: BUILDTYPE=Debug MY_CC=musl-gcc MY_LINK=musl-gcc 33 | - os: osx 34 | osx_image: xcode9.3 35 | env: BUILDTYPE=Debug 36 | - os: osx 37 | osx_image: xcode9.3 38 | env: BUILDTYPE=Release 39 | before_install: 40 | - umask 022 41 | - test -n $MY_CC && export CC=$MY_CC 42 | - test -n $MY_CXX && export CXX=$MY_CXX 43 | - test -n $MY_LINK && export LINK=$MY_LINK 44 | install: 45 | - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew upgrade python; fi 46 | script: make && make test 47 | branches: 48 | only: 49 | - master 50 | -------------------------------------------------------------------------------- /docs/source/modules/path.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modpath: 3 | 4 | path 5 | ==== 6 | 7 | This module provides path manipulation related functions. 8 | 9 | 10 | Functions 11 | --------- 12 | 13 | .. js:function:: path.basename(p) 14 | 15 | Returns the last component from the pathname in `path`, deleting any trailing ``/`` characters. 16 | 17 | :: 18 | 19 | path.basename('foo/bar/baz.js'); 20 | = baz.js 21 | 22 | .. seealso:: 23 | :man:`basename(3)`. 24 | 25 | .. js:function:: path.dirname(p) 26 | 27 | The converse function of :js:func:`path.basename`. Returns the parent directory of the pathname in `path`. 28 | 29 | :: 30 | 31 | path.dirname('foo/bar/baz.js'); 32 | = foo/bar 33 | 34 | .. seealso:: 35 | :man:`dirname(3)`. 36 | 37 | .. js:function:: path.join(...) 38 | 39 | Joins the given list of partial paths into a single one using ``/`` as the separator. 40 | 41 | :: 42 | 43 | path.join('foo', 'bar', 'baz'); 44 | = foo/bar/baz 45 | 46 | .. js:function:: path.normalize(p) 47 | 48 | `Normalize` the given path by performing tilde expansion and then applying :man:`realpath(3)`. In case of error it 49 | returns the given path unchanged. 50 | 51 | :: 52 | 53 | path.normalize('~/src/sjs'); 54 | = /Users/saghul/src/sjs 55 | -------------------------------------------------------------------------------- /test/test-random-mersenne.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const random = require('random'); 5 | 6 | 7 | // Compare our implementation with results from the original 8 | // code. Create 2000 53-bit precision random floats. Compare only 9 | // the last ten entries to show that the independent implementations 10 | // are tracking. Here is the main() function needed to create the 11 | // list of expected random numbers: 12 | // void main(void){ 13 | // int i; 14 | // unsigned long init[4]={61731, 24903, 614, 42143}, length=4; 15 | // init_by_array(init, length); 16 | // for (i=0; i<2000; i++) { 17 | // printf("%.15f ", genrand_res53()); 18 | // if (i%5==4) printf("\n"); 19 | // } 20 | // } 21 | 22 | const expected = [0.4583980307371326, 23 | 0.8605781520197878, 24 | 0.9284833172678215, 25 | 0.3593268111978246, 26 | 0.08182349376244957, 27 | 0.1433222647016933, 28 | 0.08429782382352002, 29 | 0.5381486467183145, 30 | 0.0892150249119934, 31 | 0.7848619610537291]; 32 | 33 | 34 | random.seed([61731, 24903, 614, 42143]); 35 | 36 | var r = []; 37 | for (var i = 0; i < 2000; i++) { 38 | r.push(random.random()); 39 | } 40 | assert.deepEqual(r.slice(-10), expected); 41 | -------------------------------------------------------------------------------- /docs/source/sphinx-plugins/manpage.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # 4 | # Copyright (c) 2013 Dariusz Dwornikowski. All rights reserved. 5 | # 6 | # Adapted from https://github.com/tdi/sphinxcontrib-manpage 7 | # License: Apache 2 8 | # 9 | 10 | 11 | import re 12 | 13 | from docutils import nodes, utils 14 | from docutils.parsers.rst.roles import set_classes 15 | from string import Template 16 | 17 | 18 | def make_link_node(rawtext, app, name, manpage_num, options): 19 | ref = app.config.man_url_regex 20 | if not ref: 21 | ref = "http://linux.die.net/man/%s/%s" % (manpage_num, name) 22 | else: 23 | s = Template(ref) 24 | ref = s.substitute(num=manpage_num, topic=name) 25 | set_classes(options) 26 | node = nodes.reference(rawtext, "%s(%s)" % (name, manpage_num), refuri=ref, **options) 27 | return node 28 | 29 | 30 | def man_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 31 | app = inliner.document.settings.env.app 32 | p = re.compile("([a-zA-Z0-9_\.-_]+)\((\d)\)") 33 | m = p.match(text) 34 | 35 | manpage_num = m.group(2) 36 | name = m.group(1) 37 | node = make_link_node(rawtext, app, name, manpage_num, options) 38 | return [node], [] 39 | 40 | 41 | def setup(app): 42 | app.info('Initializing manpage plugin') 43 | app.add_role('man', man_role) 44 | app.add_config_value('man_url_regex', None, 'env') 45 | return 46 | 47 | -------------------------------------------------------------------------------- /test/test-os-popen-sample.js: -------------------------------------------------------------------------------- 1 | 'use trict'; 2 | 3 | const assert = require('assert'); 4 | const io = require('io'); 5 | const os = require('os'); 6 | const path = require('path'); 7 | const system = require('system'); 8 | 9 | const TEXT = 'hello sjs!\n'; 10 | 11 | 12 | function popen(filename) { 13 | var stdin = os.pipe(); 14 | var stdout = os.pipe(); 15 | var stderr = os.pipe(); 16 | 17 | var pid = os.fork(); 18 | if (pid === 0) { 19 | // child 20 | os.dup2(stdin[0], 0, false); 21 | os.dup2(stdout[1], 1, false); 22 | os.dup2(stderr[1], 2, false); 23 | os.execv(system.executable, [system.executable, filename]); 24 | assert(false); 25 | } else { 26 | // parent 27 | os.close(stdin[0]); 28 | os.close(stdout[1]); 29 | os.close(stderr[1]); 30 | var r = { 31 | pid: pid, 32 | stdin_fd: stdin[1], 33 | stdout_fd: stdout[0], 34 | stderr_fd: stderr[0] 35 | }; 36 | return r; 37 | } 38 | } 39 | 40 | var data; 41 | var r = popen(path.join(__dirname, 'helper3.js')); 42 | os.write(r.stdin_fd, TEXT); 43 | data = os.read(r.stdout_fd); 44 | data = new TextDecoder().decode(data); 45 | assert.equal(data, TEXT); 46 | data = os.read(r.stderr_fd); 47 | data = new TextDecoder().decode(data); 48 | assert.equal(data, TEXT); 49 | 50 | os.close(r.stdin_fd); 51 | os.close(r.stdout_fd); 52 | os.close(r.stderr_fd); 53 | 54 | var res = os.waitpid(r.pid); 55 | assert.equal(r.pid, res.pid); 56 | assert(os.WIFEXITED(res.status)); 57 | assert.equal(os.WEXITSTATUS(res.status), 0); 58 | -------------------------------------------------------------------------------- /docs/source/faq.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _faq: 3 | 4 | FAQ 5 | === 6 | 7 | Here are some questions and answers I came up with, which potentially answer some of the ones that 8 | will eventually come up. 9 | 10 | 11 | Q: WHY?! 12 | ^^^^^^^^ 13 | 14 | **A**: The motivation for this project was mainly experimentation. If you look at today's JavaScript runtimes they 15 | seem to have a similar model: on one hand we have browsers and on the server side we have Node, but both follow 16 | a similar event-driven model. Skookum JS follows the more "traditional" model of providing all the low level 17 | primitives required to build different kinds of abstractions, an event-driven model being just one of them. 18 | 19 | 20 | Q: Does it work on ? 21 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 22 | 23 | **A**: At the moment sjs works only on GNU/Linux and OSX. Adding support for other Unix platforms should be 24 | easy enough and contributions are more than welcome. Windows support is not currently planned in the short term. 25 | 26 | 27 | Q: Do Nodejs modules work with Skookum JS? 28 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 29 | 30 | **A**: Most likely not. Some of them might, but if a module uses any Node or browser specifics (such as ``process``) 31 | it won't. In addition, sjs does not read ``package.json`` files nor search for modules in 32 | ``node_mules`` directories. This is by design. 33 | 34 | 35 | Q: How can I install modules? 36 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 37 | 38 | **A**: At the moment there is no packaging system, so putting them in a directory in the sjs search path is the only 39 | option. Let's say ``cp`` is our current package manager :-) 40 | 41 | -------------------------------------------------------------------------------- /modules/system.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const io = require('io'); 4 | const os = require('os'); 5 | // _system is a builtin 6 | 7 | 8 | const staticProps = ['arch', 'endianness', 'executable', 'platform']; 9 | 10 | for (var i = 0; i < staticProps.length; i++) { 11 | var prop = staticProps[i]; 12 | Object.defineProperty(exports, prop, { 13 | configurable: false, 14 | enumerable: true, 15 | writable: false, 16 | value: _system[prop] 17 | }); 18 | } 19 | 20 | 21 | const frozenProps = ['build', 'env', 'versions']; 22 | 23 | for (var i = 0; i < frozenProps.length; i++) { 24 | var prop = frozenProps[i]; 25 | Object.defineProperty(exports, prop, { 26 | configurable: false, 27 | enumerable: true, 28 | writable: false, 29 | value: Object.freeze(_system[prop]) 30 | }); 31 | } 32 | 33 | 34 | Object.defineProperty(exports, 'path', { 35 | configurable: false, 36 | enumerable: true, 37 | get: function() { 38 | return _system.path; 39 | } 40 | }); 41 | 42 | 43 | var _argv = null; 44 | Object.defineProperty(exports, 'argv', { 45 | configurable: false, 46 | enumerable: true, 47 | get: function() { 48 | if (_argv === null) { 49 | _argv = Object.freeze(_system.argv); 50 | } 51 | return _argv; 52 | } 53 | }); 54 | 55 | 56 | Object.defineProperty(exports, 'args', { 57 | configurable: false, 58 | enumerable: true, 59 | get: function() { 60 | return (exports.argv || []).slice(1); 61 | } 62 | }); 63 | 64 | 65 | exports.stdin = io.fdopen(os.STDIN_FILENO, 'r', ''); 66 | exports.stdout = io.fdopen(os.STDOUT_FILENO, 'w', ''); 67 | exports.stderr = io.fdopen(os.STDERR_FILENO, 'w', ''); 68 | -------------------------------------------------------------------------------- /test/test-hash-vectors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const hash = require('hash'); 5 | 6 | 7 | var md5_vectors = { 8 | 'abc' : '900150983cd24fb0d6963f7d28e17f72', 9 | 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' : '8215ef0796a20bcaaae116d3876c664a', 10 | }; 11 | 12 | for (var k in md5_vectors) { 13 | var h = new hash.MD5(); 14 | h.update(k); 15 | assert.equal(md5_vectors[k], h.digest('hex')); 16 | } 17 | 18 | 19 | var sha1_vectors = { 20 | 'abc' : 'a9993e364706816aba3e25717850c26c9cd0d89d', 21 | 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' : '84983e441c3bd26ebaae4aa1f95129e5e54670f1', 22 | }; 23 | 24 | for (var k in sha1_vectors) { 25 | var h = new hash.SHA1(); 26 | h.update(k); 27 | assert.equal(sha1_vectors[k], h.digest('hex')); 28 | } 29 | 30 | 31 | var sha256_vectors = { 32 | 'abc' : 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', 33 | 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' : '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', 34 | }; 35 | 36 | for (var k in sha256_vectors) { 37 | var h = new hash.SHA256(); 38 | h.update(k); 39 | assert.equal(sha256_vectors[k], h.digest('hex')); 40 | } 41 | 42 | 43 | var sha512_vectors = { 44 | 'abc' : 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', 45 | 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' : '204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445', 46 | }; 47 | 48 | for (var k in sha512_vectors) { 49 | var h = new hash.SHA512(); 50 | h.update(k); 51 | assert.equal(sha512_vectors[k], h.digest('hex')); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/bindings/path.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "../binding.h" 5 | #include 6 | 7 | 8 | static duk_ret_t path_basename(duk_context* ctx) { 9 | const char* path; 10 | char* rpath; 11 | 12 | path = duk_require_string(ctx, 0); 13 | rpath = basename((char*) path); 14 | if (!rpath) { 15 | SJS_THROW_ERRNO_ERROR(); 16 | return -42; /* control never returns here */ 17 | } else { 18 | duk_push_string(ctx, rpath); 19 | } 20 | return 1; 21 | } 22 | 23 | 24 | static duk_ret_t path_dirname(duk_context* ctx) { 25 | const char* path; 26 | char* rpath; 27 | 28 | path = duk_require_string(ctx, 0); 29 | rpath = dirname((char*) path); 30 | if (!rpath) { 31 | SJS_THROW_ERRNO_ERROR(); 32 | return -42; /* control never returns here */ 33 | } else { 34 | duk_push_string(ctx, rpath); 35 | } 36 | return 1; 37 | } 38 | 39 | 40 | static duk_ret_t path_normalize(duk_context* ctx) { 41 | const char* path; 42 | char rpath[8192]; 43 | int r; 44 | 45 | path = duk_require_string(ctx, 0); 46 | r = sjs_path_normalize(path, rpath, sizeof(rpath)); 47 | if (r < 0) { 48 | /* 'r' contains the negated errno */ 49 | SJS_THROW_ERRNO_ERROR2(-r); 50 | return -42; /* control never returns here */ 51 | } else { 52 | duk_push_string(ctx, rpath); 53 | } 54 | return 1; 55 | } 56 | 57 | 58 | static const duk_function_list_entry module_funcs[] = { 59 | { "basename", path_basename, 1 /*nargs*/ }, 60 | { "dirname", path_dirname, 1 /*nargs*/ }, 61 | { "normalize", path_normalize, 1 /*nargs*/ }, 62 | { NULL, NULL, 0 } 63 | }; 64 | 65 | 66 | void sjs__binding_path_init(duk_context* ctx) { 67 | sjs__register_binding(ctx, "path", module_funcs, NULL); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /docs/source/modules/hash.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modhash: 3 | 4 | hash 5 | ==== 6 | 7 | This module provides facilities for creating varios hashes over data. 8 | 9 | 10 | Hash objects 11 | ------------ 12 | 13 | All objects defined in this module inherit from a ``HashBase`` base class and thus have the same API. 14 | 15 | .. js:class:: hash.HashBase 16 | 17 | Base class for all hash types. Currently the following classes are implemented: 18 | 19 | - ``MD5`` 20 | 21 | - ``SHA1`` 22 | 23 | - ``SHA256`` 24 | 25 | - ``SHA512`` 26 | 27 | .. js:function:: hash.HashBase.prototype.update(data) 28 | 29 | Update the hash object with the given data. This function can be called multiple times in order to incrementally 30 | update the data being hashed. 31 | 32 | .. js:function hash.HashBase.prototype.digest([encoding]) 33 | 34 | Get the digest of the hashed data. If `encoding` is specified it must be one of ``hex`` or ``base64``, which 35 | returns the digest encoded as requested, otherwise a string is returned. 36 | 37 | This function can be called multiple times and even in between calls to ``update()``. 38 | 39 | 40 | Functions 41 | --------- 42 | 43 | .. js:function:: hash.createHash(type) 44 | 45 | Helper function to create hash objects. ``type`` must be (a string) one of: 46 | 47 | - md5 48 | 49 | - sha1 50 | 51 | - sha256 52 | 53 | - sha512 54 | 55 | 56 | Examples 57 | -------- 58 | 59 | Using the helper function and chaining methods: 60 | 61 | :: 62 | 63 | hash.createHash('sha1').update('abc').digest('hex'); 64 | // "a9993e364706816aba3e25717850c26c9cd0d89d" 65 | 66 | Creating objects and calling methods individually: 67 | 68 | :: 69 | 70 | var h1 = new hash.SHA1(); 71 | h1.update('abc'); 72 | h1.digest('hex'); 73 | // "a9993e364706816aba3e25717850c26c9cd0d89d" 74 | 75 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "internal.h" 6 | 7 | 8 | /* 9 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 10 | * full size of dst, not space left). At most dsize-1 characters 11 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 12 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 13 | * If retval >= dsize, truncation occurred. 14 | */ 15 | size_t 16 | sjs__strlcat(char *dst, const char *src, size_t dsize) 17 | { 18 | const char *odst = dst; 19 | const char *osrc = src; 20 | size_t n = dsize; 21 | size_t dlen; 22 | 23 | /* Find the end of dst and adjust bytes left but don't go past end. */ 24 | while (n-- != 0 && *dst != '\0') 25 | dst++; 26 | dlen = dst - odst; 27 | n = dsize - dlen; 28 | 29 | if (n-- == 0) 30 | return(dlen + strlen(src)); 31 | while (*src != '\0') { 32 | if (n != 0) { 33 | *dst++ = *src; 34 | n--; 35 | } 36 | src++; 37 | } 38 | *dst = '\0'; 39 | 40 | return(dlen + (src - osrc)); /* count does not include NUL */ 41 | } 42 | 43 | 44 | /* 45 | * Copy string src to buffer dst of size dsize. At most dsize-1 46 | * chars will be copied. Always NUL terminates (unless dsize == 0). 47 | * Returns strlen(src); if retval >= dsize, truncation occurred. 48 | */ 49 | size_t 50 | sjs__strlcpy(char *dst, const char *src, size_t dsize) 51 | { 52 | const char *osrc = src; 53 | size_t nleft = dsize; 54 | 55 | /* Copy as many bytes as will fit. */ 56 | if (nleft != 0) { 57 | while (--nleft != 0) { 58 | if ((*dst++ = *src++) == '\0') 59 | break; 60 | } 61 | } 62 | 63 | /* Not enough room in dst, add NUL and traverse rest of src. */ 64 | if (nleft == 0) { 65 | if (dsize != 0) 66 | *dst = '\0'; /* NUL-terminate dst */ 67 | while (*src++) 68 | ; 69 | } 70 | 71 | return(src - osrc - 1); /* count does not include NUL */ 72 | } 73 | -------------------------------------------------------------------------------- /tools/test.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import subprocess 4 | import sys 5 | 6 | 7 | cmd = './build/sjs' 8 | 9 | results = {} 10 | 11 | 12 | def green(text): 13 | return '\033[32m{}\033[0m'.format(text) 14 | 15 | 16 | def red(text): 17 | return '\033[31m{}\033[0m'.format(text) 18 | 19 | 20 | def yellow(text): 21 | return '\033[33m{}\033[0m'.format(text) 22 | 23 | 24 | def run_test(f): 25 | test_file = os.path.basename(f) 26 | sys.stdout.write('Running {:50}'.format(test_file)) 27 | sys.stdout.flush() 28 | try: 29 | subprocess.check_output('%s %s' % (cmd, f), stderr=subprocess.STDOUT, shell=True, timeout=5) 30 | except subprocess.CalledProcessError as e: 31 | sys.stdout.write(red('ERROR')) 32 | sys.stdout.write('\n') 33 | results[test_file] = e 34 | except subprocess.TimeoutExpired as e: 35 | sys.stdout.write(yellow('TIMEOUT')) 36 | sys.stdout.write('\n') 37 | results[test_file] = e 38 | else: 39 | sys.stdout.write(green('OK')) 40 | sys.stdout.write('\n') 41 | results[test_file] = None 42 | 43 | 44 | if __name__ == "__main__": 45 | if len(sys.argv) != 2: 46 | raise RuntimeError('run as follows: tools/test.py test_directory') 47 | 48 | test_dir = sys.argv[1] 49 | for f in sorted(os.listdir(test_dir)): 50 | if f.startswith('test-') and f.endswith('.js'): 51 | run_test(os.path.join(test_dir, f)) 52 | 53 | sys.stdout.write('\n') 54 | 55 | if results and all(val is None for val in results.values()): 56 | print(green('All tests passed!')) 57 | sys.exit(0) 58 | else: 59 | print(red('Some tests failed :-(')) 60 | sys.stdout.write('\n') 61 | for test, result in results.items(): 62 | if result is not None: 63 | print('{}:\n{}\n'.format(test, result.output)) 64 | sys.exit(1) 65 | 66 | -------------------------------------------------------------------------------- /src/bindings/hash/md5.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: md5.h,v 1.17 2012/12/05 23:19:57 deraadt Exp $ */ 2 | 3 | /* 4 | * This code implements the MD5 message-digest algorithm. 5 | * The algorithm is due to Ron Rivest. This code was 6 | * written by Colin Plumb in 1993, no copyright is claimed. 7 | * This code is in the public domain; do with it what you wish. 8 | * 9 | * Equivalent code is available from RSA Data Security, Inc. 10 | * This code has been tested against that, and is equivalent, 11 | * except that you don't need to include two pages of legalese 12 | * with every copy. 13 | */ 14 | 15 | #ifndef _MD5_H_ 16 | #define _MD5_H_ 17 | 18 | #include "includes.h" 19 | 20 | #ifndef WITH_OPENSSL 21 | 22 | #define MD5_BLOCK_LENGTH 64 23 | #define MD5_DIGEST_LENGTH 16 24 | #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) 25 | 26 | typedef struct MD5Context { 27 | u_int32_t state[4]; /* state */ 28 | u_int64_t count; /* number of bits, mod 2^64 */ 29 | u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ 30 | } MD5_CTX; 31 | 32 | void MD5Init(MD5_CTX *); 33 | void MD5Update(MD5_CTX *, const u_int8_t *, size_t) 34 | __attribute__((__bounded__(__string__,2,3))); 35 | void MD5Pad(MD5_CTX *); 36 | void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *) 37 | __attribute__((__bounded__(__minbytes__,1,MD5_DIGEST_LENGTH))); 38 | void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]) 39 | __attribute__((__bounded__(__minbytes__,1,4))) 40 | __attribute__((__bounded__(__minbytes__,2,MD5_BLOCK_LENGTH))); 41 | char *MD5End(MD5_CTX *, char *) 42 | __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); 43 | char *MD5File(const char *, char *) 44 | __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); 45 | char *MD5FileChunk(const char *, char *, off_t, off_t) 46 | __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); 47 | char *MD5Data(const u_int8_t *, size_t, char *) 48 | __attribute__((__bounded__(__string__,1,2))) 49 | __attribute__((__bounded__(__minbytes__,3,MD5_DIGEST_STRING_LENGTH))); 50 | 51 | #endif /* !WITH_OPENSSL */ 52 | 53 | #endif /* _MD5_H_ */ 54 | -------------------------------------------------------------------------------- /modules/random.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _random = _system.binding('random'); 4 | 5 | const codecs = require('codecs'); 6 | const os = require('os'); 7 | const outil = require('utils').object; 8 | 9 | 10 | function Random() { 11 | this._state = _random.alloc(); 12 | 13 | // set finalizer 14 | outil.finalizer(this, randomDealloc); 15 | 16 | // initial seed 17 | this.seed(); 18 | } 19 | 20 | 21 | Random.prototype.seed = function(seed) { 22 | if (seed == null) { 23 | var seedBuf = os.urandom(2496); // 624 * 4, to cover the entire MT state 24 | var seedBufLen = seedBuf.length; 25 | seed = []; 26 | for (var i = 0; i < seedBufLen; i += 4) { 27 | seed.push(seedBuf.readUInt32LE(i)); 28 | } 29 | } else if ((typeof seed !== 'number' || Number.isNaN(seed)) && !Array.isArray(seed)) { 30 | throw new Error('seed may only be called with null, undefined, a number or an array of integers'); 31 | } 32 | _random.seed(this._state, seed); 33 | } 34 | 35 | 36 | Random.prototype.random = function() { 37 | return _random.random(this._state); 38 | } 39 | 40 | 41 | function SystemRandom() { 42 | } 43 | 44 | 45 | SystemRandom.prototype.random = function() { 46 | var num = uint53(); 47 | return num / 0x20000000000000; 48 | } 49 | 50 | 51 | // - helpers 52 | 53 | // get a 53 bit unsigned int from the system random source 54 | function uint53() { 55 | var high = os.urandom(4).readUInt32LE() & 0x1fffff; 56 | var low = os.urandom(4).readUInt32LE() >>> 0; 57 | return (high * 0x100000000) + low; 58 | } 59 | 60 | // the default Random object 61 | const default_random = new Random(); 62 | 63 | // finalizer: will get called when a Random instance is garbage collected 64 | function randomDealloc(random) { 65 | if (random._state !== null) { 66 | _random.dealloc(random.state); 67 | random._state = null; 68 | } 69 | } 70 | 71 | 72 | exports.Random = Random; 73 | exports.random = Random.prototype.random.bind(default_random); 74 | exports.seed = Random.prototype.seed.bind(default_random); 75 | exports.SystemRandom = SystemRandom; 76 | -------------------------------------------------------------------------------- /src/bindings/random/random.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | /*#include */ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../binding.h" 9 | #include 10 | #include "mt19937ar.h" 11 | 12 | 13 | static duk_ret_t random_alloc(duk_context* ctx) { 14 | mt_t* state; 15 | 16 | state = malloc(sizeof(*state)); 17 | if (state == NULL) { 18 | SJS_THROW_ERRNO_ERROR2(ENOMEM); 19 | return -42; /* control never returns here */ 20 | } 21 | 22 | duk_push_pointer(ctx, (void*) state); 23 | return 1; 24 | } 25 | 26 | 27 | static duk_ret_t random_dealloc(duk_context* ctx) { 28 | mt_t* state; 29 | 30 | state = duk_require_pointer(ctx, 0); 31 | free(state); 32 | 33 | duk_push_undefined(ctx); 34 | return 1; 35 | } 36 | 37 | 38 | static duk_ret_t random_seed(duk_context* ctx) { 39 | mt_t* state; 40 | 41 | state = duk_require_pointer(ctx, 0); 42 | 43 | if (duk_is_array(ctx, 1)) { 44 | size_t i, len; 45 | len = duk_get_length(ctx, 1); 46 | assert(len > 0); 47 | uint32_t seed[len]; 48 | for (i = 0; i< len; i++) { 49 | duk_get_prop_index(ctx, 1, i); 50 | seed[i] = duk_require_int(ctx, -1); 51 | duk_pop(ctx); 52 | } 53 | mt_init_by_array(state, seed, len); 54 | } else { 55 | uint32_t seed; 56 | seed = duk_require_uint(ctx, 1); 57 | mt_init(state, seed); 58 | } 59 | 60 | return 1; 61 | } 62 | 63 | 64 | static duk_ret_t random_random(duk_context* ctx) { 65 | mt_t* state; 66 | double d; 67 | 68 | state = duk_require_pointer(ctx, 0); 69 | d = mt_genrand_res53(state); 70 | 71 | duk_push_number(ctx, d); 72 | return 1; 73 | } 74 | 75 | 76 | static const duk_function_list_entry module_funcs[] = { 77 | { "alloc", random_alloc, 0 /*nargs*/ }, 78 | { "dealloc", random_dealloc, 1 /*nargs*/ }, 79 | { "seed", random_seed, 2 /*nargs*/ }, 80 | { "random", random_random, 1 /*nargs*/ }, 81 | { NULL, NULL, 0 } 82 | }; 83 | 84 | 85 | void sjs__binding_random_init(duk_context* ctx) { 86 | sjs__register_binding(ctx, "random", module_funcs, NULL); 87 | } 88 | 89 | -------------------------------------------------------------------------------- /docs/source/modules/uuid.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _moduuid: 3 | 4 | uuid 5 | ==== 6 | 7 | This module provides UUID generating and parsing functions. 8 | I was adaped from `this module `_. 9 | 10 | 11 | Functions 12 | --------- 13 | 14 | .. js:function:: uuid.v1([options [, buffer [, offset]]]) 15 | 16 | Generate and return a RFC4122 v1 (timestamp-based) UUID. 17 | 18 | :param options: Optional object with uuid state to apply. Properties may include: 19 | 20 | - node - (Array) Node id as Array of 6 bytes (per 4.1.6). Default: Randomly generated ID. See note 1. 21 | - clockseq - (Number between 0 - 0x3fff) RFC clock sequence. Default: An internally maintained clockseq is used. 22 | - msecs - (Number | Date) Time in milliseconds since unix Epoch. Default: The current time is used. 23 | - nsecs - (Number between 0-9999) additional time, in 100-nanosecond units. Ignored if msecs is unspecified. 24 | Default: internal uuid counter is used, as per 4.2.1.2. 25 | :param buffer: Array or buffer where UUID bytes are to be written. 26 | :param offset: Starting index in buffer at which to begin writing. 27 | :returns: The buffer, if specified, otherwise the string form of the UUID. 28 | 29 | .. note:: 30 | The randomly generated node id is only guaranteed to stay constant for the lifetime of the current JS 31 | runtime. 32 | 33 | 34 | .. js:function:: uuid.v4([options [, buffer [, offset]]]) 35 | 36 | Generate and return a RFC4122 v4 UUID. 37 | 38 | :param options: Optional object with uuid state to apply. Unused at the moment. 39 | :param buffer: Array or buffer where UUID bytes are to be written. 40 | :param offset: Starting index in buffer at which to begin writing. 41 | :returns: The buffer, if specified, otherwise the string form of the UUID. 42 | 43 | 44 | .. js:function:: uuid.parse(id[, buffer[, offset]]) 45 | .. js:function:: uuid.unparse(buffer[, offset]) 46 | 47 | Parse and unparse UUIDs. 48 | 49 | :param id: UUID(-like) string 50 | :param buffer: Array or buffer where UUID bytes are to be written. Default: A new Array or Buffer is used. 51 | :param offset: Starting index in buffer at which to begin writing. Default: 0. 52 | -------------------------------------------------------------------------------- /docs/source/modules/random.rst: -------------------------------------------------------------------------------- 1 | .. _modrandom: 2 | 3 | random 4 | ====== 5 | 6 | This module provides random number generation functions. 7 | 8 | The current random number generator is implemented as a 9 | `Mersenne Twister `_ PRNG which is 10 | automatically seeded on startup with data from :js:func:`os.urandom`. 11 | 12 | This module should be preferred to using ``Math.random`` since it uses a more roubust implementation. 13 | The implementation used my ``Math.random`` can be found `here `_. 14 | 15 | .. note:: 16 | This module could use some peer review. If you are competent to do so, please get in touch. 17 | 18 | 19 | Random objects 20 | -------------- 21 | 22 | .. js:class:: random.Random([seed]) 23 | 24 | Class implementing the Mersenne Twister PRNG. While users are free to create multiple instances 25 | of this class, the module exports the ``seed`` and ``random`` functions bound to a default 26 | instance which is seeded with the default seed. 27 | 28 | .. js:function:: random.Random.prototype.seed([seed]) 29 | 30 | Seed the PRNG. `seed` can be a number or an ``Array`` of numbers. If ``null`` or ``undefined`` is passed 31 | a default seed is obtained from :js:func:`os.urandom`. The default seed consists of 2496 bytes, enough to 32 | fill the Mersenne Twister state. 33 | 34 | .. js:function:: random.Random.prototype.random 35 | 36 | Return a random floating point number in the [0.0, 1.0) interval. 37 | 38 | 39 | SystemRandom objects 40 | -------------------- 41 | 42 | .. js:class:: random.SystemRandom 43 | 44 | Class implementing a similar interface to :js:class:`random.Random`, but using :js:func:`os.urandom` as the source 45 | for random numbers. 46 | 47 | .. js:function:: random.SystemRandom.prototype.random 48 | 49 | Return a random floating point number in the [0.0, 1.0) interval. 50 | 51 | 52 | Functions 53 | --------- 54 | 55 | .. js:function:: random.random 56 | 57 | Return a random floating point number in the [0.0, 1.0) interval. The default :js:class:`random.Random` instance 58 | is used. 59 | 60 | .. js:function:: random.seed([seed]) 61 | 62 | Seed the default :js:class:`random.Random` instance. 63 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Skookum JS 3 | ========== 4 | 5 | Skookum JS, or `sjs` for short, is a JavaScript runtime built on top of the `Duktape `_ engine. 6 | It provides a simple way to write applications using JavaScript with a traditional synchronous I/O model. It tries 7 | to minimize abstractions and give the developer as much control as possible over the platform and low level APIs. 8 | 9 | 10 | Quickstart 11 | ---------- 12 | 13 | .. code-block:: shell 14 | 15 | git clone https://github.com/saghul/sjs 16 | cd sjs 17 | make 18 | ./build/sjs 19 | 20 | 21 | Documentation 22 | ------------- 23 | 24 | .. toctree:: 25 | :maxdepth: 1 26 | 27 | features 28 | design 29 | usage 30 | module_system 31 | modules 32 | faq 33 | 34 | 35 | Building 36 | -------- 37 | 38 | Compiling `sjs` is easy, the only dependency is the `CMake `_ build system. 39 | 40 | Compile 41 | ^^^^^^^ 42 | 43 | For Debug builds (the default): 44 | 45 | .. code-block:: shell 46 | 47 | make 48 | 49 | For Release builds: 50 | 51 | .. code-block:: shell 52 | 53 | make BUILDTYPE=Release 54 | 55 | The installation prefix can be specified by setting ``PREFIX``, it defaults to ``/usr/local``. 56 | 57 | .. code-block:: shell 58 | 59 | make PREFIX=/usr 60 | 61 | Install 62 | ^^^^^^^ 63 | 64 | `sjs` consists of a single binary, so if all you care about is the CLI itself, you can copy it anywhere in 65 | your filesystem. The build system provides some standard way to do this: 66 | 67 | .. code-block:: shell 68 | 69 | make install 70 | 71 | By default `sjs` will be installed to the directory indicated by ``PREFIX`` (``/usr/local`` by default) with the 72 | following structure: 73 | 74 | * ``PREFIX``/bin: `sjs` binary 75 | * ``PREFIX``/lib: `libsjs` library 76 | 77 | The destination of the files can be further altered by setting ``DESTDIR``. This will be prepended to ``PREFIX``. 78 | Confusing, I know. 79 | 80 | .. code-block:: shell 81 | 82 | make DESTDIR=/tmp/testsjs install 83 | 84 | Run the test suite 85 | ^^^^^^^^^^^^^^^^^^ 86 | 87 | .. code-block:: shell 88 | 89 | make test 90 | 91 | Run the CLI without installing 92 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 93 | 94 | .. code-block:: shell 95 | 96 | ./build/sjs 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![sjs](https://raw.githubusercontent.com/saghul/sjs/master/docs/source/.static/sjs-logo.png) 3 | 4 | # Overview 5 | 6 | Skookum JS, or *sjs* for short, is a JavaScript runtime focused on providing 7 | comprehensive POSIX APIs. 8 | 9 | The motivation for this project comes from answering the question "how would a 10 | JavaScript runtime look like if there were no browsers?". 11 | 12 | # Features 13 | 14 | * Small footprint 15 | * [Ecmascript ES5/ES5.1](http://www.ecma-international.org/ecma-262/5.1/) compliant 16 | * Some post-ES5 features 17 | * Built-in Unicode support 18 | * Tail call support 19 | * CommonJS-based module loading system 20 | * Support for native modules written in C 21 | * Rich standard library 22 | * Binary name 25% shorter than Node 23 | 24 | See the full list [here](http://sjs.saghul.net/en/latest/features.html). 25 | 26 | # Documentation 27 | 28 | Check it out [here](http://sjs.saghul.net). 29 | 30 | [![Documentation Status](https://readthedocs.org/projects/sjs/badge/?version=latest)](http://sjs.readthedocs.io/en/latest/?badge=latest) 31 | 32 | # Building 33 | 34 | Skookum JS currently supports Linux and macOS. See compilation instructions [here](http://sjs.saghul.net/en/latest/#building). 35 | 36 | [![Build Status](https://travis-ci.org/saghul/sjs.svg?branch=master)](https://travis-ci.org/saghul/sjs) 37 | 38 | # License 39 | 40 | Check [the LICENSE file](https://github.com/saghul/sjs/blob/master/LICENSE). 41 | 42 | # Author 43 | 44 | Saúl Ibarra Corretgé ([**@saghul**](https://github.com/saghul)) 45 | 46 | # Contributing 47 | 48 | Please see [CONTRIBUTING](https://github.com/saghul/sjs/blob/master/CONTRIBUTING.md). 49 | 50 | # Acknowledgements 51 | 52 | sjs would not have been possible without some other projects existing. Some served 53 | as a source of inspiration, some as a source of actual code, and the author would like to thank them all. 54 | 55 | * [Duktape](http://duktape.org/): The engine that powers sjs. This project would not have been possible without Duktape. :hearteyes: 56 | * [Nodejs](https://nodejs.org): World's most well known JavaScript runtime, definitely an inspiration. 57 | * [libuv](https://github.com/libuv/libuv): A lot of the cross-platform code was borrowed from here. 58 | * [Dukluv](https://github.com/creationix/dukluv): Another JavaScript runtime combining Duktape with libuv. 59 | * [CPython](https://github.com/python/cpython/): Python's canonical interpreter was an inspiration, specially for the standard library components. 60 | -------------------------------------------------------------------------------- /docs/source/modules/assert.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modassert: 3 | 4 | assert 5 | ====== 6 | 7 | This module is primarily used to write tests. It originates from 8 | `this commonjs-assert module `_. 9 | 10 | 11 | Functions 12 | --------- 13 | 14 | .. js:function:: assert.fail(value, expected, message, operator) 15 | 16 | Throws an exception that displays the values for `value` and `expected` separated by the provided `operator`. 17 | If `message` is undefined, "value operator expected" is used. 18 | 19 | .. js:function:: assert.ok(value, [message]) 20 | 21 | Thests if `value` is truthy. 22 | 23 | .. js:function:: assert.equal(value, expected, [message]) 24 | 25 | Tests shallow, coercive equality with the equal comparison operator ( == ). 26 | 27 | .. js:function:: assert.notEqual(value, expected, [message]) 28 | 29 | Tests shallow, coercive non-equality with the not equal comparison operator ( != ). 30 | 31 | .. js:function:: assert.deepEqual(value, expected, [message]) 32 | 33 | Tests for deep equality. 34 | 35 | .. js:function:: assert.notDeepEqual(value, expected, [message]) 36 | 37 | Tests for any deep inequality. 38 | 39 | .. js:function:: assert.strictEqual(value, expected, [message]) 40 | 41 | Tests strict equality, as determined by the strict equality operator ( === ). 42 | 43 | .. js:function:: assert.notStrictEqual(value, expected, [message]) 44 | 45 | Tests strict non-equality, as determined by the strict not equal operator ( !== ). 46 | 47 | .. js:function:: assert.throws(block, [error], [message]) 48 | 49 | Expects `block` to throw an error. `error` can be constructor, RegExp or validation function. 50 | 51 | Validate instanceof using constructor: 52 | 53 | :: 54 | 55 | assert.throws(function() { throw new Error("Wrong value"); }, Error); 56 | 57 | Validate error message using RegExp: 58 | 59 | :: 60 | 61 | assert.throws(function() { throw new Error("Wrong value"); }, /value/); 62 | 63 | Custom error validation: 64 | 65 | :: 66 | 67 | assert.throws(function() { 68 | throw new Error("Wrong value"); 69 | }, function(err) { 70 | if ( (err instanceof Error) && /value/.test(err) ) { 71 | return true; 72 | } 73 | }, "unexpected error"); 74 | 75 | .. js:function:: assert.doesNotThrow(block, [message]) 76 | 77 | Expects `block` not to throw an error, see :js:func:`assert.throws` for details. 78 | -------------------------------------------------------------------------------- /src/bindings/hash/sha1.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: sha1.h,v 1.24 2012/12/05 23:19:57 deraadt Exp $ */ 2 | 3 | /* 4 | * SHA-1 in C 5 | * By Steve Reid 6 | * 100% Public Domain 7 | */ 8 | 9 | #ifndef _SHA1_H 10 | #define _SHA1_H 11 | 12 | #include "includes.h" 13 | 14 | #ifndef WITH_OPENSSL 15 | 16 | #define SHA1_BLOCK_LENGTH 64 17 | #define SHA1_DIGEST_LENGTH 20 18 | #define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1) 19 | 20 | typedef struct { 21 | u_int32_t state[5]; 22 | u_int64_t count; 23 | u_int8_t buffer[SHA1_BLOCK_LENGTH]; 24 | } SHA1_CTX; 25 | 26 | void SHA1Init(SHA1_CTX *); 27 | void SHA1Pad(SHA1_CTX *); 28 | void SHA1Transform(u_int32_t [5], const u_int8_t [SHA1_BLOCK_LENGTH]) 29 | __attribute__((__bounded__(__minbytes__,1,5))) 30 | __attribute__((__bounded__(__minbytes__,2,SHA1_BLOCK_LENGTH))); 31 | void SHA1Update(SHA1_CTX *, const u_int8_t *, size_t) 32 | __attribute__((__bounded__(__string__,2,3))); 33 | void SHA1Final(u_int8_t [SHA1_DIGEST_LENGTH], SHA1_CTX *) 34 | __attribute__((__bounded__(__minbytes__,1,SHA1_DIGEST_LENGTH))); 35 | char *SHA1End(SHA1_CTX *, char *) 36 | __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); 37 | char *SHA1File(const char *, char *) 38 | __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); 39 | char *SHA1FileChunk(const char *, char *, off_t, off_t) 40 | __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); 41 | char *SHA1Data(const u_int8_t *, size_t, char *) 42 | __attribute__((__bounded__(__string__,1,2))) 43 | __attribute__((__bounded__(__minbytes__,3,SHA1_DIGEST_STRING_LENGTH))); 44 | 45 | #define HTONDIGEST(x) do { \ 46 | x[0] = htonl(x[0]); \ 47 | x[1] = htonl(x[1]); \ 48 | x[2] = htonl(x[2]); \ 49 | x[3] = htonl(x[3]); \ 50 | x[4] = htonl(x[4]); } while (0) 51 | 52 | #define NTOHDIGEST(x) do { \ 53 | x[0] = ntohl(x[0]); \ 54 | x[1] = ntohl(x[1]); \ 55 | x[2] = ntohl(x[2]); \ 56 | x[3] = ntohl(x[3]); \ 57 | x[4] = ntohl(x[4]); } while (0) 58 | 59 | #endif /* !WITH_OPENSSL */ 60 | #endif /* _SHA1_H */ 61 | -------------------------------------------------------------------------------- /include/sjs/vm.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SJS_VM_H 3 | #define SJS_VM_H 4 | 5 | #include 6 | 7 | #include "duktape.h" 8 | 9 | 10 | /* opaque type */ 11 | typedef struct sjs_vm_t sjs_vm_t; 12 | 13 | DUK_EXTERNAL_DECL sjs_vm_t* sjs_vm_create(void); 14 | DUK_EXTERNAL_DECL void sjs_vm_destroy(sjs_vm_t* vm); 15 | DUK_EXTERNAL_DECL void sjs_vm_setup_args(sjs_vm_t* vm, int argc, char* argv[]); 16 | DUK_EXTERNAL_DECL duk_context* sjs_vm_get_duk_ctx(sjs_vm_t* vm); 17 | DUK_EXTERNAL_DECL sjs_vm_t* sjs_vm_get_vm(duk_context* ctx); 18 | DUK_EXTERNAL_DECL int sjs_vm_eval_code_global(const sjs_vm_t* vm, 19 | const char* filename, 20 | const char* code, 21 | size_t len, 22 | FILE* foutput, 23 | FILE* ferror); 24 | DUK_EXTERNAL_DECL int sjs_vm_eval_code(const sjs_vm_t* vm, 25 | const char* filename, 26 | const char* code, 27 | size_t len, 28 | FILE* foutput, 29 | FILE* ferror); 30 | DUK_EXTERNAL_DECL int sjs_vm_eval_file(const sjs_vm_t* vm, 31 | const char* filename, 32 | FILE* foutput, 33 | FILE* ferror); 34 | 35 | DUK_EXTERNAL_DECL int sjs_path_expanduser(const char* path, char* normalized_path, size_t normalized_path_len); 36 | DUK_EXTERNAL_DECL int sjs_path_normalize(const char* path, char* normalized_path, size_t normalized_path_len); 37 | 38 | 39 | #define SJS_THROW_ERRNO_ERROR2(x) \ 40 | do { \ 41 | int err = x; \ 42 | duk_push_error_object(ctx, DUK_ERR_ERROR, "[errno %d] %s", (err), strerror((err))); \ 43 | duk_push_int(ctx, (err)); \ 44 | duk_put_prop_string(ctx, -2, "errno"); \ 45 | duk_throw(ctx); \ 46 | } while (0) \ 47 | 48 | 49 | #define SJS_THROW_ERRNO_ERROR() \ 50 | SJS_THROW_ERRNO_ERROR2(errno) 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/binding.c: -------------------------------------------------------------------------------- 1 | 2 | #include "binding.h" 3 | 4 | 5 | static duk_bool_t get_cached_binding(duk_context *ctx, const char *id) { 6 | duk_push_global_stash(ctx); 7 | (void) duk_get_prop_string(ctx, -1, "\xff" "bindingCache"); 8 | if (duk_get_prop_string(ctx, -1, id)) { 9 | duk_remove(ctx, -2); 10 | duk_remove(ctx, -2); 11 | return 1; 12 | } else { 13 | duk_pop_3(ctx); 14 | return 0; 15 | } 16 | } 17 | 18 | 19 | static duk_ret_t binding_func(duk_context *ctx) { 20 | const char* id; 21 | 22 | id = duk_require_string(ctx, 0); 23 | if (get_cached_binding(ctx, id)) { 24 | /* use the cached module */ 25 | return 1; 26 | } 27 | 28 | return duk_error(ctx, DUK_ERR_ERROR, "Binding could not be loaded: %s", id); 29 | } 30 | 31 | 32 | void sjs__setup_binding(duk_context* ctx) { 33 | /* system.binding */ 34 | duk_get_global_string(ctx, "_system"); 35 | duk_push_string(ctx, "binding"); 36 | duk_push_c_function(ctx, binding_func, 1); 37 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); 38 | duk_pop(ctx); 39 | 40 | /* Initialize the bindings cache to a fresh object. */ 41 | duk_push_global_stash(ctx); 42 | duk_push_object(ctx); 43 | duk_put_prop_string(ctx, -2, "\xff" "bindingCache"); 44 | duk_pop(ctx); 45 | 46 | /* Initialize bindings. */ 47 | sjs__binding_errno_init(ctx); 48 | sjs__binding_gai_init(ctx); 49 | sjs__binding_hash_init(ctx); 50 | sjs__binding_io_init(ctx); 51 | sjs__binding_os_init(ctx); 52 | sjs__binding_path_init(ctx); 53 | sjs__binding_poll_init(ctx); 54 | sjs__binding_pwd_init(ctx); 55 | sjs__binding_random_init(ctx); 56 | sjs__binding_select_init(ctx); 57 | sjs__binding_socket_init(ctx); 58 | } 59 | 60 | 61 | void sjs__register_binding(duk_context* ctx, 62 | const char* id, 63 | const duk_function_list_entry functions[], 64 | const duk_number_list_entry constants[]) { 65 | /* setup binding object */ 66 | duk_push_object(ctx); 67 | if (functions) { 68 | duk_put_function_list(ctx, -1, functions); 69 | } 70 | if (constants) { 71 | duk_push_object(ctx); 72 | duk_put_number_list(ctx, -1, constants); 73 | duk_put_prop_string(ctx, -2, "c"); 74 | } 75 | 76 | /* [ ... binding ] */ 77 | 78 | duk_push_global_stash(ctx); 79 | (void) duk_get_prop_string(ctx, -1, "\xff" "bindingCache"); 80 | duk_dup(ctx, -3); 81 | 82 | /* [ ... binding stash cache binding ] */ 83 | 84 | duk_put_prop_string(ctx, -2, id); 85 | 86 | /* [ ... binding stash cache ] */ 87 | 88 | duk_pop_3(ctx); /* cleanup */ 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/cli/linenoise.h: -------------------------------------------------------------------------------- 1 | /* linenoise.h -- VERSION 1.0 2 | * 3 | * Guerrilla line editing library against the idea that a line editing lib 4 | * needs to be 20,000 lines of C code. 5 | * 6 | * See linenoise.c for more information. 7 | * 8 | * ------------------------------------------------------------------------ 9 | * 10 | * Copyright (c) 2010-2014, Salvatore Sanfilippo 11 | * Copyright (c) 2010-2013, Pieter Noordhuis 12 | * 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions are 17 | * met: 18 | * 19 | * * Redistributions of source code must retain the above copyright 20 | * notice, this list of conditions and the following disclaimer. 21 | * 22 | * * Redistributions in binary form must reproduce the above copyright 23 | * notice, this list of conditions and the following disclaimer in the 24 | * documentation and/or other materials provided with the distribution. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifndef __LINENOISE_H 40 | #define __LINENOISE_H 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | typedef struct linenoiseCompletions { 47 | size_t len; 48 | char **cvec; 49 | } linenoiseCompletions; 50 | 51 | typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); 52 | typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); 53 | typedef void(linenoiseFreeHintsCallback)(void *); 54 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); 55 | void linenoiseSetHintsCallback(linenoiseHintsCallback *); 56 | void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); 57 | void linenoiseAddCompletion(linenoiseCompletions *, const char *); 58 | 59 | char *linenoise(const char *prompt); 60 | void linenoiseFree(void *ptr); 61 | int linenoiseHistoryAdd(const char *line); 62 | int linenoiseHistorySetMaxLen(int len); 63 | int linenoiseHistorySave(const char *filename); 64 | int linenoiseHistoryLoad(const char *filename); 65 | void linenoiseClearScreen(void); 66 | void linenoiseSetMultiLine(int ml); 67 | void linenoisePrintKeyCodes(void); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif /* __LINENOISE_H */ 74 | -------------------------------------------------------------------------------- /docs/source/modules/system.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modsystem: 3 | 4 | system 5 | ====== 6 | 7 | This module provides information about the system where `sjs` is running as well as the 8 | environment. It implements `CommonJS System/1.0 `_ with some extras. 9 | 10 | 11 | Attributes 12 | ---------- 13 | 14 | .. js:data:: system.versions 15 | 16 | An object containing information about the `sjs` and embedded `Duktape` versions. 17 | 18 | :: 19 | 20 | sjs> print(JSON.stringify(system.versions, null, 4)) 21 | { 22 | "duktape": "v1.5.0", 23 | "duktapeCommit": "83d5577", 24 | "sjs": "0.1.0" 25 | } 26 | 27 | .. js:data:: system.env 28 | 29 | Array containing the current environment variables. 30 | 31 | .. js:data:: system.path 32 | 33 | Array containing the list of locations which are going to be used when searching for modules. It can be modified 34 | at runtime. 35 | 36 | A list of colon separated paths can also be specified with the ``SJS_PATH`` variable, which will be prepended 37 | to the default paths list. 38 | 39 | .. js:data:: system.arch 40 | 41 | System architecture (``x86``, ``x64`` or ``arm``). 42 | 43 | .. js:data:: system.platform 44 | 45 | String representing the running platform (``linux`` or ``osx``). 46 | 47 | .. js:data:: system.executable 48 | 49 | Absolute path of the executable that started the process. 50 | 51 | .. js:data:: system.argv 52 | 53 | Set of command line arguments that were given to the process. 54 | 55 | :: 56 | 57 | ./build/sjs t.js -i -o --verbose 58 | // [ './build/sjs', 't.js', '-i', '-o', '--verbose' ] 59 | 60 | .. js:data:: system.args 61 | 62 | Similar to ``argv``, except that the interpreter binary is not listed. This is to conform with 63 | `CommonJS System/1.0 `_. 64 | 65 | :: 66 | 67 | ./build/sjs t.js -i -o --verbose 68 | // [ 't.js', '-i', '-o', '--verbose' ] 69 | 70 | .. js:data:: system.build 71 | 72 | Object providing various information about the build and the system where it was produced: 73 | 74 | :: 75 | 76 | sjs> print(JSON.stringify(system.build, null, 4)) 77 | { 78 | "compiler": "GCC", 79 | "compilerVersion": "5.3.1", 80 | "system": "Linux-4.5.0-1-amd64", 81 | "cflags": "-pedantic -std=c99 -Wall -fstrict-aliasing -fno-omit-frame-pointer -Wextra -O0 -g3", 82 | "timestamp": "2016-05-07T17:37:46Z", 83 | "type": "Debug" 84 | } 85 | 86 | .. js:data:: system.endianness 87 | 88 | Returns ``big`` if the system is Big Engian, or ``little`` if the system is Little Endian. This is determined at runtime. 89 | 90 | .. js:data:: system.stdin 91 | 92 | Object of type :js:class:`io.File` representing the standard input. 93 | 94 | .. js:data:: system.stdout 95 | 96 | Object of type :js:class:`io.File` representing the standard output. 97 | 98 | .. js:data:: system.stderr 99 | 100 | Object of type :js:class:`io.File` representing the standard error. 101 | -------------------------------------------------------------------------------- /modules/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _hash = _system.binding('hash'); 4 | 5 | const codecs = require('codecs'); 6 | const outil = require('utils').object; 7 | 8 | 9 | function HashBase() { 10 | this._ctx = this._init(); 11 | 12 | // set finalizer 13 | outil.finalizer(this, hashDealloc); 14 | } 15 | 16 | HashBase.prototype.update = function(data) { 17 | this._update(data); 18 | return this; 19 | } 20 | 21 | HashBase.prototype.digest = function(encoding) { 22 | var digest = this._digest(); 23 | if (encoding === 'hex' || encoding === 'base64') { 24 | return codecs.encode(encoding, digest); 25 | } if (encoding === void 0) { 26 | return digest; 27 | } else { 28 | throw new RangeError('unsupported encoding: ' + encoding); 29 | } 30 | } 31 | 32 | 33 | function MD5() { 34 | HashBase.call(this); 35 | } 36 | 37 | outil.inherits(MD5, HashBase); 38 | 39 | MD5.prototype._init = function() { 40 | return _hash.md5_new(); 41 | } 42 | 43 | MD5.prototype._update = function(data) { 44 | _hash.md5_update(this._ctx, data); 45 | } 46 | 47 | MD5.prototype._digest = function() { 48 | return _hash.md5_digest(this._ctx); 49 | } 50 | 51 | 52 | function SHA1() { 53 | HashBase.call(this); 54 | } 55 | 56 | outil.inherits(SHA1, HashBase); 57 | 58 | SHA1.prototype._init = function() { 59 | return _hash.sha1_new(); 60 | } 61 | 62 | SHA1.prototype._update = function(data) { 63 | _hash.sha1_update(this._ctx, data); 64 | } 65 | 66 | SHA1.prototype._digest = function() { 67 | return _hash.sha1_digest(this._ctx); 68 | } 69 | 70 | 71 | function SHA256() { 72 | HashBase.call(this); 73 | } 74 | 75 | outil.inherits(SHA256, HashBase); 76 | 77 | SHA256.prototype._init = function() { 78 | return _hash.sha256_new(); 79 | } 80 | 81 | SHA256.prototype._update = function(data) { 82 | _hash.sha256_update(this._ctx, data); 83 | } 84 | 85 | SHA256.prototype._digest = function() { 86 | return _hash.sha256_digest(this._ctx); 87 | } 88 | 89 | 90 | function SHA512() { 91 | HashBase.call(this); 92 | } 93 | 94 | outil.inherits(SHA512, HashBase); 95 | 96 | SHA512.prototype._init = function() { 97 | return _hash.sha512_new(); 98 | } 99 | 100 | SHA512.prototype._update = function(data) { 101 | _hash.sha512_update(this._ctx, data); 102 | } 103 | 104 | SHA512.prototype._digest = function() { 105 | return _hash.sha512_digest(this._ctx); 106 | } 107 | 108 | 109 | function createHash(type) { 110 | switch (type) { 111 | case 'md5': 112 | return new MD5(); 113 | case 'sha1': 114 | return new SHA1(); 115 | case 'sha256': 116 | return new SHA256(); 117 | case 'sha512': 118 | return new SHA512(); 119 | default: 120 | throw new RangeError('invalid hash type: ' + type); 121 | } 122 | } 123 | 124 | 125 | // helpers 126 | 127 | function hashDealloc(hash) { 128 | _hash.dealloc(hash._ctx); 129 | hash._ctx = null; 130 | } 131 | 132 | 133 | // exports 134 | 135 | exports.createHash = createHash; 136 | exports.MD5 = MD5; 137 | exports.SHA1 = SHA1; 138 | exports.SHA256 = SHA256; 139 | exports.SHA512 = SHA512; 140 | -------------------------------------------------------------------------------- /src/bindings/poll.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../binding.h" 7 | #include 8 | 9 | 10 | /* 11 | * poll(2). Args: 12 | * - 0: fds array 13 | * - 1: timeout 14 | */ 15 | static duk_ret_t poll_poll(duk_context* ctx) { 16 | struct pollfd fds[1024]; 17 | struct pollfd *pfds; 18 | double dtimeout; 19 | size_t i, n; 20 | int r, timeout; 21 | 22 | if (!duk_is_array(ctx, 0)) { 23 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "'pfds' must be an array"); 24 | return -42; /* control never returns here */ 25 | } 26 | duk_require_valid_index(ctx, 1); 27 | if (duk_is_null(ctx, 1)) { 28 | timeout = -1; 29 | } else { 30 | dtimeout = duk_require_number(ctx, 1); 31 | assert(!duk_is_nan(ctx, 1)); 32 | timeout = dtimeout * 1000; 33 | } 34 | 35 | n = duk_get_length(ctx, 0); 36 | if (n > 1024) { 37 | pfds = malloc(sizeof(struct pollfd) * n); 38 | if (pfds == NULL) { 39 | SJS_THROW_ERRNO_ERROR2(ENOMEM); 40 | return -42; /* control never returns here */ 41 | } 42 | } else { 43 | pfds = fds; 44 | } 45 | 46 | for (i = 0; i < n; i++) { 47 | int fd, events; 48 | duk_get_prop_index(ctx, 0, i); 49 | duk_get_prop_string(ctx, -1, "fd"); 50 | fd = duk_require_int(ctx, -1); 51 | duk_pop(ctx); 52 | duk_get_prop_string(ctx, -1, "events"); 53 | events = duk_require_int(ctx, -1); 54 | duk_pop(ctx); 55 | pfds[i].fd = fd; 56 | pfds[i].events = events; 57 | pfds[i].revents = 0; 58 | duk_pop(ctx); 59 | } 60 | 61 | r = poll(pfds, n, timeout); 62 | if (r == -1) { 63 | if (pfds != fds) { 64 | free(pfds); 65 | pfds = NULL; 66 | } 67 | SJS_THROW_ERRNO_ERROR(); 68 | return -42; /* control never returns here */ 69 | } 70 | 71 | /* TODO: consider duplicating the original array (or even modifying it) instead */ 72 | duk_push_object(ctx); 73 | duk_push_int(ctx, r); 74 | duk_put_prop_string(ctx, -2, "nevents"); 75 | duk_push_array(ctx); 76 | for (i = 0; i < n; i++) { 77 | duk_push_object(ctx); 78 | duk_push_int(ctx, pfds[i].fd); 79 | duk_put_prop_string(ctx, -2, "fd"); 80 | duk_push_int(ctx, pfds[i].events); 81 | duk_put_prop_string(ctx, -2, "events"); 82 | duk_push_int(ctx, pfds[i].revents); 83 | duk_put_prop_string(ctx, -2, "revents"); 84 | duk_put_prop_index(ctx, -2, i); 85 | } 86 | duk_put_prop_string(ctx, -2, "fds"); 87 | 88 | if (pfds != fds) { 89 | free(pfds); 90 | pfds = NULL; 91 | } 92 | 93 | return 1; 94 | } 95 | 96 | 97 | #define X(name) {#name, name} 98 | static const duk_number_list_entry module_consts[] = { 99 | X(POLLIN), 100 | X(POLLOUT), 101 | X(POLLPRI), 102 | #ifdef POLLRDHUP 103 | X(POLLRDHUP), 104 | #endif 105 | X(POLLERR), 106 | X(POLLHUP), 107 | X(POLLNVAL), 108 | { NULL, 0.0 } 109 | }; 110 | #undef X 111 | 112 | static const duk_function_list_entry module_funcs[] = { 113 | /* name, function, nargs */ 114 | { "poll", poll_poll, 2 }, 115 | { NULL, NULL, 0 } 116 | }; 117 | 118 | 119 | void sjs__binding_poll_init(duk_context* ctx) { 120 | sjs__register_binding(ctx, "poll", module_funcs, module_consts); 121 | } 122 | 123 | -------------------------------------------------------------------------------- /modules/console.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const outil = require('utils').object; 5 | const sys = require('system'); 6 | const time = require('time'); 7 | 8 | 9 | function Console(stdout, stderr) { 10 | if (!(this instanceof Console)) { 11 | return new Console(stdout, stderr); 12 | } 13 | 14 | if (!stdout || typeof stdout.write !== 'function') { 15 | throw new TypeError('Console expects a writable stream instance'); 16 | } 17 | if (!stderr) { 18 | stderr = stdout; 19 | } else if (typeof stderr.write !== 'function') { 20 | throw new TypeError('Console expects writable stream instances'); 21 | } 22 | 23 | var prop = { 24 | writable: true, 25 | enumerable: false, 26 | configurable: true 27 | }; 28 | prop.value = stdout; 29 | Object.defineProperty(this, '_stdout', prop); 30 | prop.value = stderr; 31 | Object.defineProperty(this, '_stderr', prop); 32 | prop.value = new Map(); 33 | Object.defineProperty(this, '_times', prop); 34 | 35 | // bind the prototype functions to this Console instance 36 | var keys = Object.keys(Console.prototype); 37 | for (var v = 0; v < keys.length; v++) { 38 | var k = keys[v]; 39 | this[k] = this[k].bind(this); 40 | } 41 | } 42 | 43 | 44 | Console.prototype.log = function() { 45 | this._stdout.write(outil.format.apply(null, arguments) + '\n'); 46 | }; 47 | 48 | 49 | Console.prototype.info = Console.prototype.log; 50 | 51 | 52 | Console.prototype.warn = function() { 53 | this._stderr.write(outil.format.apply(null, arguments) + '\n'); 54 | }; 55 | 56 | 57 | Console.prototype.error = Console.prototype.warn; 58 | 59 | 60 | Console.prototype.dir = function(object, options) { 61 | options = Object.assign({customInspect: false}, options); 62 | this._stdout.write(outil.inspect(object, options) + '\n'); 63 | }; 64 | 65 | 66 | Console.prototype.time = function(label) { 67 | this._times.set(label, performance.now()); 68 | }; 69 | 70 | 71 | Console.prototype.timeEnd = function(label) { 72 | const t = this._times.get(label); 73 | if (!t) { 74 | return; 75 | } 76 | const duration = performance.now() - t; 77 | this.log('%s: %sms', label, duration.toFixed(3)); 78 | this._times.delete(label); 79 | }; 80 | 81 | 82 | Console.prototype.trace = function() { 83 | var err = new Error(); 84 | err.name = 'Trace'; 85 | err.message = outil.format.apply(null, arguments); 86 | 87 | // remove entry for this very function 88 | var tmpStack = err.stack.split('\n'); 89 | tmpStack.splice(1, 1); 90 | err.stack = tmpStack.join('\n'); 91 | 92 | this.error(err.stack); 93 | }; 94 | 95 | 96 | Console.prototype.assert = function(expression) { 97 | if (!expression) { 98 | const argsLen = arguments.length || 1; 99 | const arr = new Array(argsLen - 1); 100 | for (var i = 1; i < argsLen; i++) { 101 | arr[i - 1] = arguments[i]; 102 | } 103 | assert.ok(false, outil.format.apply(null, arr)); 104 | } 105 | }; 106 | 107 | 108 | var dummy = function() {}; 109 | var defaultConsole = new Console(sys.stdout, sys.stderr); 110 | var handler = { 111 | get: function(target, name) { 112 | var value = target[name]; 113 | return typeof value==='function' ? value : dummy; 114 | } 115 | }; 116 | 117 | module.exports = new Proxy(defaultConsole, handler); 118 | module.exports.Console = Console; 119 | -------------------------------------------------------------------------------- /docs/source/design.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _design: 3 | 4 | Design 5 | ====== 6 | 7 | Skookum JS is build by abstracting the undelying platform as little as possible and giving the developer full control 8 | of low level POSIX APIs. 9 | 10 | .. note:: 11 | In release ``18.6.0`` SkookumJS went through a major internal redesign. The old design is still documented in this 12 | page, for posterity. 13 | 14 | The SkookumJS VM is inspired by Node.js in the following ways: 15 | 16 | * A single binary is provided, which contains all modules already builtin. 17 | * Binary addons can be created, which can be linked with the ``sjs`` binary. 18 | 19 | In addition, a ``libsjs`` library target is available for other applications to link with / embed. 20 | 21 | Implementation details 22 | ---------------------- 23 | 24 | * The `Duktape `_ engine provides JavaScript capabilities. 25 | * The `REPL (interactive mode) `_ uses 26 | `linenoise `_. 27 | * Modules can be either written in pure JavaScript or in C. See the :ref:`modules` section. 28 | 29 | Retrospective 30 | ------------- 31 | 32 | After first implementing the old design, I came to realization that it's more troublesome than the current one. 33 | Specifically, these are issues that promted the redesign: 34 | 35 | * Cumbersome fiddling with ``SJS_PATH`` to make sure the built binary and tests had access to modules. 36 | * Complex build system with many shared libraries. 37 | * Hard to have multiple versions side by side. 38 | * Potential standard library / application mismatch. 39 | 40 | In retrospect this should have been the design from the get go. It still offers the same amount of flexibility 41 | (binary addons, `libsjs`) while making it easier to use and build. 42 | 43 | ------ 44 | 45 | Design (old) 46 | ------------ 47 | 48 | The VM is modeled after other runtimes such as Ruby's or Python's. 49 | 50 | The runtime can be logically divided into 3 parts: 51 | 52 | * The interpreter CLI: it's a relatively simple application which creates a `sjs` VM and evaluates code using it. For 53 | the `REPL (interactive mode) `_ it uses 54 | `linenoise `_. 55 | * The VM library: `sjs` (the CLI) links with `libsjs` (the VM), which encapsulates all functionality and embed the 56 | `Duktape `_ engine. This makes other applications capable of running JavaScript code by linking 57 | with `libsjs` and creating a VM. 58 | * The modules: in `sjs` modules can be either written in pure JavaScript or in C. See the :ref:`modules` section. 59 | 60 | 61 | .. image:: .static/sjs-libsjs-diagram.png 62 | :align: center 63 | 64 | 65 | The VM 66 | ^^^^^^ 67 | 68 | The `sjs` VM is currently single threaded, bu thread aware. That is: users can create as many VMs as they wish, as long 69 | as there is a single VM running for a given thread. There is no global state. There is no builtin mechanism for 70 | inter-VM communication. 71 | 72 | See the :ref:`vmapi` section for details on the C API. 73 | 74 | 75 | Inspiration 76 | ^^^^^^^^^^^ 77 | 78 | Skookum JS was not inspired by browser JavaScript runtimes but by more "traditional" runtimes such as Ruby's MRI or 79 | Python's CPython. 80 | 81 | This means that the model is not inherently asynchronous and event-driven, as in a browser or 82 | `Node JS `_. `sjs` does provide the necessary tools to build asynchronous / event-driven 83 | frameworks on top of the provided modules. 84 | -------------------------------------------------------------------------------- /test/test-net-socket-options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const net = require('net'); 5 | 6 | 7 | // NB: this test does not work if the machine is Big Endian 8 | 9 | var sock = new net.Socket(net.AF_INET, net.SOCK_STREAM); 10 | var r; 11 | 12 | // for getsockopt we set a boolen we get an int back 13 | sock.setsockopt(net.SOL_SOCKET, net.SO_REUSEADDR, true); 14 | r = sock.getsockopt(net.SOL_SOCKET, net.SO_REUSEADDR); 15 | assert.equal(!!r, true); 16 | sock.setsockopt(net.SOL_SOCKET, net.SO_REUSEADDR, false); 17 | r = sock.getsockopt(net.SOL_SOCKET, net.SO_REUSEADDR); 18 | assert.equal(!!r, false); 19 | 20 | sock.setsockopt(net.SOL_SOCKET, net.SO_REUSEPORT, true); 21 | sock.setsockopt(net.SOL_SOCKET, net.SO_REUSEPORT, false); 22 | 23 | // this is how we set non-int options 24 | var lingerOpts = new Buffer(8); 25 | lingerOpts.writeInt32LE(1, 0); 26 | lingerOpts.writeInt32LE(100, 4); 27 | sock.setsockopt(net.SOL_SOCKET, net.SO_LINGER, lingerOpts.toString()); 28 | 29 | // for getting the options parse the returned string with a Buffer 30 | r = sock.getsockopt(net.SOL_SOCKET, net.SO_LINGER, 8); 31 | var resBuf = new Buffer(r); 32 | assert.equal(resBuf.readInt32LE(0), 1) 33 | assert.equal(resBuf.readInt32LE(4), 100) 34 | 35 | // first reset the linger time 36 | lingerOpts.fill(0); 37 | lingerOpts.writeInt32LE(1, 0); 38 | sock.setsockopt(net.SOL_SOCKET, net.SO_LINGER, lingerOpts.toString()); 39 | 40 | // now disable it (otherwise the time is remembered) 41 | lingerOpts.fill(0); 42 | sock.setsockopt(net.SOL_SOCKET, net.SO_LINGER, lingerOpts.toString()); 43 | r = sock.getsockopt(net.SOL_SOCKET, net.SO_LINGER, 8); 44 | resBuf = new Buffer(r); 45 | assert.equal(resBuf.readInt32LE(0), 0) 46 | assert.equal(resBuf.readInt32LE(4), 0) 47 | 48 | // test SO_SNDTIMEO and SO_RCVTIMEO 49 | var sndTimeout = new Buffer(16) // struct timeval is 16 bytes on 64-bit 50 | sndTimeout.writeInt32LE(1, 0); // tv_sec = 1 51 | sndTimeout.writeInt32LE(0, 4); 52 | sndTimeout.writeInt32LE(0, 8); // tv_usec = 0 53 | sndTimeout.writeInt32LE(0, 12); 54 | sock.setsockopt(net.SOL_SOCKET, net.SO_SNDTIMEO, sndTimeout.toString()); 55 | r = sock.getsockopt(net.SOL_SOCKET,net.SO_SNDTIMEO,16); 56 | resBuf = new Buffer(r); 57 | assert.equal(resBuf.readInt32LE(0), 1); 58 | assert.equal(resBuf.readInt32LE(4), 0); 59 | assert.equal(resBuf.readInt32LE(8), 0); 60 | assert.equal(resBuf.readInt32LE(12), 0); 61 | 62 | var rcvTimeout = new Buffer(16) // struct timeval is 16 bytes on 64-bit 63 | rcvTimeout.writeInt32LE(0, 0); // tv_sec = 0 64 | rcvTimeout.writeInt32LE(0, 4); 65 | rcvTimeout.writeInt32LE(10000, 8); // tv_usec = 10000 66 | rcvTimeout.writeInt32LE(0, 12); 67 | console.log(rcvTimeout); 68 | sock.setsockopt(net.SOL_SOCKET, net.SO_RCVTIMEO, rcvTimeout.toString()); 69 | r = sock.getsockopt(net.SOL_SOCKET,net.SO_RCVTIMEO,16); 70 | resBuf = new Buffer(r); 71 | // value of getsockopt for SO_RCVTIMEO is changed by the kernel 72 | // for the set-value of 10000, the get-value is 12000 on my machine, 73 | // but I can't guarantee whatever relationship those two numbers 74 | // have will remain consistent from machine to machine, kernel to kernel, 75 | // distro to distro, etc 76 | // see [this StackOverflow post](https://stackoverflow.com/questions/42536919/why-so-rcvtimeo-timeout-differs-after-it-was-set) 77 | // so, I just check that the value isn't too far off 78 | var val,rx,ry 79 | assert.equal(resBuf.readInt32LE(0), 0); 80 | assert.equal(resBuf.readInt32LE(4), 0); 81 | val = resBuf.readInt32LE(8); 82 | rx = (val > (10000/2)); 83 | ry = (val < (10000*2)); 84 | assert.equal(rx,true); 85 | assert.equal(ry,true); 86 | assert.equal(resBuf.readInt32LE(12), 0); 87 | 88 | sock.close(); 89 | -------------------------------------------------------------------------------- /src/bindings/random/mt19937ar.h: -------------------------------------------------------------------------------- 1 | /* 2 | A C-program for MT19937, with initialization improved 2002/1/26. 3 | Coded by Takuji Nishimura and Makoto Matsumoto. 4 | 5 | Before using, initialize the state by using init_genrand(seed) 6 | or init_by_array(init_key, key_length). 7 | 8 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 9 | All rights reserved. 10 | Copyright (C) 2005, Mutsuo Saito 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions 15 | are met: 16 | 17 | 1. Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | 2. Redistributions in binary form must reproduce the above copyright 21 | notice, this list of conditions and the following disclaimer in the 22 | documentation and/or other materials provided with the distribution. 23 | 24 | 3. The names of its contributors may not be used to endorse or promote 25 | products derived from this software without specific prior written 26 | permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 32 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 33 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 34 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 35 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 36 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 37 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 38 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | */ 40 | 41 | /* 42 | Changes from the original by Saúl Ibarra Corretgé 43 | - Moved state to a typedef 44 | - Switched types to uint32_t 45 | - Formatting 46 | */ 47 | 48 | #include 49 | 50 | 51 | /* Period parameters */ 52 | #define N 624 53 | #define M 397 54 | #define MATRIX_A 0x9908b0dfUL /* constant vector a */ 55 | #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ 56 | #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ 57 | 58 | /* State */ 59 | typedef struct { 60 | uint32_t state[N]; 61 | int index; 62 | } mt_t; 63 | 64 | /* initializes mt[N] with a seed */ 65 | void mt_init(mt_t* mtt, uint32_t s); 66 | 67 | /* initialize by an array with array-length */ 68 | /* init_key is the array for initializing keys */ 69 | /* key_length is its length */ 70 | /* slight change for C++, 2004/2/26 */ 71 | void mt_init_by_array(mt_t* mtt, uint32_t init_key[], unsigned int key_length); 72 | 73 | /* generates a random number on [0,0xffffffff]-interval */ 74 | uint32_t mt_genrand_int32(mt_t* mtt); 75 | 76 | /* generates a random number on [0,0x7fffffff]-interval */ 77 | uint32_t mt_genrand_int31(mt_t* mtt); 78 | 79 | /* These real versions are due to Isaku Wada, 2002/01/09 added */ 80 | /* generates a random number on [0,1]-real-interval */ 81 | double mt_genrand_real1(mt_t* mtt); 82 | 83 | /* generates a random number on [0,1)-real-interval */ 84 | double mt_genrand_real2(mt_t* mtt); 85 | 86 | /* generates a random number on (0,1)-real-interval */ 87 | double mt_genrand_real3(mt_t* mtt); 88 | 89 | /* generates a random number on [0,1) with 53-bit resolution*/ 90 | double mt_genrand_res53(mt_t* mtt); 91 | -------------------------------------------------------------------------------- /src/bindings/select.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "../binding.h" 6 | #include 7 | 8 | 9 | static void array2fdset(duk_context* ctx, int array_idx, fd_set* fdset, int* maxfd) { 10 | size_t i, n; 11 | 12 | n = duk_get_length(ctx, array_idx); 13 | for (i = 0; i < n; i++) { 14 | int fd; 15 | duk_get_prop_index(ctx, array_idx, i); 16 | fd = duk_require_int(ctx, -1); 17 | duk_pop(ctx); 18 | if (fd < 0 || fd >= FD_SETSIZE) { 19 | duk_error(ctx, DUK_ERR_RANGE_ERROR, "invalid fd: %d", fd); 20 | return; /* control never returns here */ 21 | } 22 | FD_SET(fd, fdset); 23 | if (fd > *maxfd) { 24 | *maxfd = fd; 25 | } 26 | } 27 | } 28 | 29 | 30 | static void fdset2array(duk_context* ctx, fd_set* fdset, int array_idx) { 31 | int fd, i; 32 | 33 | i = 0; 34 | for (fd = 0; fd < FD_SETSIZE; fd++) { 35 | if (FD_ISSET(fd, fdset)) { 36 | duk_push_int(ctx, fd); 37 | duk_put_prop_index(ctx, array_idx, i); 38 | i++; 39 | } 40 | } 41 | } 42 | 43 | 44 | /* 45 | * select(2). Args: 46 | * - 0: rfds 47 | * - 1: wfds 48 | * - 2: xfds 49 | * - 3: timeout 50 | */ 51 | static duk_ret_t select_select(duk_context* ctx) { 52 | fd_set rfds; 53 | fd_set wfds; 54 | fd_set xfds; 55 | int maxfd, r; 56 | struct timeval tv; 57 | struct timeval* tvp; 58 | duk_idx_t array_idx; 59 | 60 | if (!duk_is_array(ctx, 0) || !duk_is_array(ctx, 1) || !duk_is_array(ctx, 2)) { 61 | duk_error(ctx, DUK_ERR_TYPE_ERROR, "'rfds', 'wfds' and 'xfds' must be arrays"); 62 | return -42; /* control never returns here */ 63 | } 64 | 65 | duk_require_valid_index(ctx, 3); 66 | if (duk_is_null(ctx, 3)) { 67 | tvp = NULL; 68 | } else { 69 | double timeout; 70 | timeout = duk_require_number(ctx, 3); 71 | assert(timeout >= 0); 72 | tv.tv_sec = (unsigned long) timeout; 73 | tv.tv_usec = (unsigned long)(timeout * 1000000) % 1000000; 74 | tvp = &tv; 75 | } 76 | 77 | /* convert 3 arrays to fd_set */ 78 | FD_ZERO(&rfds); 79 | FD_ZERO(&wfds); 80 | FD_ZERO(&xfds); 81 | maxfd = -1; 82 | array2fdset(ctx, 0, &rfds, &maxfd); 83 | array2fdset(ctx, 1, &wfds, &maxfd); 84 | array2fdset(ctx, 2, &xfds, &maxfd); 85 | 86 | /* select! */ 87 | r = select(maxfd+1, &rfds, &wfds, &xfds, tvp); 88 | if (r == -1) { 89 | SJS_THROW_ERRNO_ERROR(); 90 | return -42; /* control never returns here */ 91 | } 92 | 93 | /* convert fd_set to array */ 94 | duk_push_object(ctx); 95 | array_idx = duk_push_array(ctx); 96 | fdset2array(ctx, &rfds, array_idx); 97 | duk_put_prop_string(ctx, -2, "rfds"); 98 | array_idx = duk_push_array(ctx); 99 | fdset2array(ctx, &wfds, array_idx); 100 | duk_put_prop_string(ctx, -2, "wfds"); 101 | array_idx = duk_push_array(ctx); 102 | fdset2array(ctx, &xfds, array_idx); 103 | duk_put_prop_string(ctx, -2, "xfds"); 104 | 105 | return 1; 106 | } 107 | 108 | 109 | #define X(name) {#name, name} 110 | static const duk_number_list_entry module_consts[] = { 111 | X(FD_SETSIZE), 112 | { NULL, 0.0 } 113 | }; 114 | #undef X 115 | 116 | 117 | static const duk_function_list_entry module_funcs[] = { 118 | /* name, function, nargs */ 119 | { "select", select_select, 4 }, 120 | { NULL, NULL, 0 } 121 | }; 122 | 123 | 124 | void sjs__binding_select_init(duk_context* ctx) { 125 | sjs__register_binding(ctx, "select", module_funcs, module_consts); 126 | } 127 | 128 | -------------------------------------------------------------------------------- /docs/source/module_system.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _module_system: 3 | 4 | The module system 5 | ================= 6 | 7 | `sjs` uses a `CommonJS `_ compliant module system, also inspired by 8 | `the Node module system `_. 9 | 10 | 11 | Loading modules 12 | --------------- 13 | 14 | Modules are loaded using the :js:func:`require` function. Given a *module id*, it returns the module exported 15 | data. 16 | 17 | The module id can be a relative path or a non-relative path, in which case the module is loaded from the system 18 | search directories. See :ref:`modsearchpath`. 19 | 20 | 21 | Module context 22 | -------------- 23 | 24 | When a module is being loaded the following *module globals* are available: 25 | 26 | .. js:attribute:: __filename 27 | 28 | Path to the file being executed. In the global scope it constains the filename which is currently being executed, 29 | ```` if running in the REPL, ```` if code is being read from `stdin`, or ```` if evaluating 30 | code straight from the CLI. Inside a module, it contains the absolute path to the module file. 31 | 32 | .. js:attribute:: __dirname 33 | 34 | Directory where the file being evaluated is, obtained by applying :man:`dirname(3)` over ``__filename``. 35 | 36 | .. js:attribute:: module 37 | 38 | The current :js:class:`Module` object instance. 39 | 40 | .. js:attribute:: exports 41 | 42 | The current module exports. It's a reference to ``module.exports``. 43 | 44 | .. js:function:: require(id) 45 | 46 | Loads the requested module and returns the module's exports. 47 | 48 | Example, assuming some ``foo.js`` file with the following content: 49 | 50 | :: 51 | 52 | function foo() { 53 | return 42; 54 | } 55 | 56 | exports.foo = foo; 57 | 58 | It can be loaded and used as follows: 59 | 60 | :: 61 | 62 | const mod = require('./foo'); 63 | 64 | print(mod.foo()); 65 | // prints 42 66 | 67 | 68 | The Module object 69 | ^^^^^^^^^^^^^^^^^ 70 | 71 | .. js:class:: Module 72 | 73 | Object representing a JavaScript module (for the lack of a better term). 74 | 75 | .. js:attribute:: Module.filename 76 | 77 | Fully resolved filename of the module. 78 | 79 | .. js:attribute:: Module.id 80 | 81 | Same as ``filename``. 82 | 83 | .. js:attribute:: Module.loaded 84 | 85 | Boolean attribute indicated if the module was loaded or if it's in the process of being loaded. 86 | 87 | .. js:attribute:: Module.exports 88 | 89 | Object containing the functions and attributes to be exported. 90 | 91 | 92 | The "main" module 93 | ^^^^^^^^^^^^^^^^^ 94 | 95 | The :js:func:`require` function has a ``main`` attribute, referencing the current module only for the "main" module. 96 | Otherwise it's ``undefined``. 97 | 98 | The following construct can be used in order to differentiate if a module was require()-d or directly run: 99 | 100 | :: 101 | 102 | if (require.main === module) { 103 | // module was directly run 104 | } 105 | 106 | 107 | .. _modsearchpath: 108 | 109 | Module search paths 110 | ------------------- 111 | 112 | Modules are located by their *module id*. This module id can be one of: 113 | 114 | - a relative path: ex. ``./foo`` or ``../foo`` 115 | - a regular module id: ex. ``system`` 116 | 117 | Absolute paths are not supported. 118 | 119 | Relative paths are resolved relative to the *calling* module, or the module which contains the call to 120 | :js:func:`require`. 121 | 122 | Regular module ids are resolved by looking into the :ref:`modsystem` paths in ``system.path``. The list of paths 123 | to search for modules is dynamic and can be modified at runtime. The following are the builtin system paths: 124 | 125 | - ``/usr/lib/sjs/modules`` 126 | - ``/usr/local/lib/sjs/modules`` 127 | - ``~/.local/sjs/modules`` 128 | -------------------------------------------------------------------------------- /docs/source/modules/console.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modconsole: 3 | 4 | console 5 | ======= 6 | 7 | This module provides well known logging facilities for JavaScript programs. This module (partially) implements 8 | the `CommonJS Console specification `_. 9 | 10 | 11 | Console object 12 | -------------- 13 | 14 | The `Console` object encapsulates all functionality in the `console` module in the form of a class. The module exports 15 | a default instance of this class though more can be created (which is rarely needed). 16 | 17 | .. js:class:: console.Console(stdout, [stderr]) 18 | 19 | Create an instance of a console object. 20 | 21 | :param stdout: Object representing standard out. It must implement the same API as :js:class:`io.File`. 22 | :param stderr: Analogous to `stdout`, but representing the standard error. If omitted `stdout` is used. 23 | 24 | .. js:function:: console.Console.prototype.assert(expression, [message], [...]) 25 | 26 | Simple assertion test using the :ref:`modassert` module. The given `expression` is evaluated and if it's 27 | falsey the given `message` is given with an ``AssertionError``. 28 | 29 | :: 30 | 31 | console.assert(false, 'Whoops %s', 'Houston, we\'ve got a problem!'); 32 | // AssertionError: Whoops Houston, we've got a problem! 33 | 34 | .. js:function:: console.Console.prototype.log 35 | 36 | Outpits the given data to ``stdout`` with a newline. Data is formatted using :js:func:`utils.object.format`. 37 | 38 | :: 39 | 40 | console.log('Hello %s! Here is a number: %d and an object: %j', 'sjs', 42, {foo: 'foo', bar: 'bar'}) 41 | // Hello sjs! Here is a number: 42 and an object: {"foo":"foo","bar":"bar"} 42 | 43 | .. js:function:: console.Console.prototype.info 44 | 45 | Alias for :js:func:`console.Console.prototype.log`. 46 | 47 | .. js:function:: console.Console.prototype.error 48 | 49 | Similar to :js:func:`console.Console.prototype.log` but it outputs to ``stderr``. 50 | 51 | .. js:function:: console.Console.prototype.warn 52 | 53 | Alias for :js:func:`console.Console.prototype.error`. 54 | 55 | .. js:function:: console.Console.prototype.dir(obj, [options]) 56 | 57 | Inspect the given object using :js:func:`` and print output to ``stdout``. 58 | 59 | :: 60 | 61 | console.dir(console); 62 | // { log: [Function], 63 | // info: [Function], 64 | // warn: [Function], 65 | // error: [Function], 66 | // dir: [Function], 67 | // time: [Function], 68 | // timeEnd: [Function], 69 | // trace: [Function], 70 | // assert: [Function], 71 | // Console: [Function: Console] } 72 | 73 | .. note:: 74 | The `customInspect` option is ignored if given. 75 | 76 | .. js:function:: console.Console.prototype.time(label) 77 | .. js:function:: console.Console.prototype.timeEnd(label) 78 | 79 | Create a labeled timestamp. When ``timeEnd`` is called the time difference is computed and printed to 80 | ``staout``. 81 | 82 | :: 83 | 84 | console.time('foo'); 85 | // (wait some time...) 86 | console.timeEnd('foo'); 87 | // foo: 6535.996ms 88 | 89 | .. js:function:: console.Console.prototype.trace([message], [...]) 90 | 91 | Prints a stack trace at the current position to ``stderr``. If an optional message and formatting options are 92 | given the message is formatted using :js:func:`utils.object.format`. 93 | 94 | :: 95 | 96 | console.trace(); 97 | // Trace 98 | // at global (:1) preventsyield 99 | 100 | 101 | Functions 102 | --------- 103 | 104 | .. js:function:: console.assert 105 | .. js:function:: console.log 106 | .. js:function:: console.info 107 | .. js:function:: console.warn 108 | .. js:function:: console.error 109 | .. js:function:: console.dir 110 | .. js:function:: console.time 111 | .. js:function:: console.timeEnd 112 | .. js:function:: console.trace 113 | 114 | Functions bound to the default :js:class:`console.Console` instance. 115 | -------------------------------------------------------------------------------- /src/bindings/pwd.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../binding.h" 7 | #include 8 | 9 | 10 | /* 11 | * Convert a struct passwd into an object 12 | * struct passwd { 13 | * char *pw_name; 14 | * char *pw_passwd; 15 | * uid_t pw_uid; 16 | * gid_t pw_gid; 17 | * char *pw_gecos; 18 | * char *pw_dir; 19 | * char *pw_shell; 20 | * }; 21 | */ 22 | static void passwd2obj(duk_context* ctx, struct passwd* pw) { 23 | duk_idx_t idx; 24 | idx = duk_push_object(ctx); 25 | 26 | duk_push_string(ctx, pw->pw_name); 27 | duk_put_prop_string(ctx, idx, "name"); 28 | 29 | duk_push_string(ctx, pw->pw_passwd); 30 | duk_put_prop_string(ctx, idx, "passwd"); 31 | 32 | duk_push_int(ctx, pw->pw_uid); 33 | duk_put_prop_string(ctx, idx, "uid"); 34 | 35 | duk_push_int(ctx, pw->pw_gid); 36 | duk_put_prop_string(ctx, idx, "gid"); 37 | 38 | duk_push_string(ctx, pw->pw_gecos); 39 | duk_put_prop_string(ctx, idx, "gecos"); 40 | 41 | duk_push_string(ctx, pw->pw_dir); 42 | duk_put_prop_string(ctx, idx, "dir"); 43 | 44 | duk_push_string(ctx, pw->pw_shell); 45 | duk_put_prop_string(ctx, idx, "shell"); 46 | } 47 | 48 | 49 | static duk_ret_t pwd_getpwnam(duk_context* ctx) { 50 | const char* name; 51 | long initsize; 52 | char* buf; 53 | size_t bufsize; 54 | struct passwd pw; 55 | struct passwd* rpw; 56 | int r; 57 | 58 | name = duk_require_string(ctx, 0); 59 | 60 | initsize = sysconf(_SC_GETPW_R_SIZE_MAX); 61 | if (initsize <= 0) { 62 | bufsize = 4096; 63 | } else { 64 | bufsize = (size_t) initsize; 65 | } 66 | 67 | buf = NULL; 68 | 69 | for (;;) { 70 | free(buf); 71 | buf = malloc(bufsize); 72 | 73 | if (buf == NULL) { 74 | SJS_THROW_ERRNO_ERROR2(ENOMEM); 75 | return -42; /* control never returns here */ 76 | } 77 | 78 | r = getpwnam_r(name, &pw, buf, bufsize, &rpw); 79 | 80 | if (r != ERANGE) { 81 | break; 82 | } 83 | 84 | bufsize *= 2; 85 | } 86 | 87 | if (r != 0) { 88 | SJS_THROW_ERRNO_ERROR2(r); 89 | return -42; /* control never returns here */ 90 | } 91 | 92 | if (rpw == NULL) { 93 | free(buf); 94 | duk_push_null(ctx); 95 | return 1; 96 | } else { 97 | passwd2obj(ctx, &pw); 98 | return 1; 99 | } 100 | } 101 | 102 | 103 | static duk_ret_t pwd_getpwuid(duk_context* ctx) { 104 | uid_t uid; 105 | long initsize; 106 | char* buf; 107 | size_t bufsize; 108 | struct passwd pw; 109 | struct passwd* rpw; 110 | int r; 111 | 112 | uid = duk_require_int(ctx, 0); 113 | 114 | initsize = sysconf(_SC_GETPW_R_SIZE_MAX); 115 | if (initsize <= 0) { 116 | bufsize = 4096; 117 | } else { 118 | bufsize = (size_t) initsize; 119 | } 120 | 121 | buf = NULL; 122 | 123 | for (;;) { 124 | free(buf); 125 | buf = malloc(bufsize); 126 | 127 | if (buf == NULL) { 128 | SJS_THROW_ERRNO_ERROR2(ENOMEM); 129 | return -42; /* control never returns here */ 130 | } 131 | 132 | r = getpwuid_r(uid, &pw, buf, bufsize, &rpw); 133 | 134 | if (r != ERANGE) { 135 | break; 136 | } 137 | 138 | bufsize *= 2; 139 | } 140 | 141 | if (r != 0) { 142 | SJS_THROW_ERRNO_ERROR2(r); 143 | return -42; /* control never returns here */ 144 | } 145 | 146 | if (rpw == NULL) { 147 | free(buf); 148 | duk_push_null(ctx); 149 | return 1; 150 | } else { 151 | passwd2obj(ctx, &pw); 152 | return 1; 153 | } 154 | } 155 | 156 | 157 | static const duk_function_list_entry module_funcs[] = { 158 | { "getpwnam", pwd_getpwnam, 1 /*nargs*/ }, 159 | { "getpwuid", pwd_getpwuid, 1 /*nargs*/ }, 160 | { NULL, NULL, 0 } 161 | }; 162 | 163 | 164 | void sjs__binding_pwd_init(duk_context* ctx) { 165 | sjs__register_binding(ctx, "pwd", module_funcs, NULL); 166 | } 167 | 168 | -------------------------------------------------------------------------------- /modules/io.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _io = _system.binding('io'); 4 | const _select = require('_io_select'); 5 | const _poll = require('_io_poll'); 6 | 7 | const errno = require('errno'); 8 | const os = require('os'); 9 | const path = require('path'); 10 | const outil = require('utils').object; 11 | 12 | 13 | function File(path, f, mode, buffering) { 14 | this._path = path; 15 | this._f = f; 16 | this._mode = mode; 17 | 18 | if (buffering !== undefined) { 19 | switch (buffering) { 20 | case -1: 21 | // default, do nothing 22 | break; 23 | case 0: 24 | case 1: 25 | // 0 is unbuffered, 1 is line-buffered 26 | _io.setvbuf(this._f, buffering); 27 | break; 28 | default: 29 | this.close(); 30 | throw new RangeError('invalid value for "buffering": ' + buffering); 31 | } 32 | } 33 | 34 | outil.finalizer(this, fileDealloc); 35 | 36 | Object.defineProperty(this, 'path', { 37 | get: function() { 38 | return this._path; 39 | } 40 | }); 41 | 42 | Object.defineProperty(this, 'fd', { 43 | get: function() { 44 | if (this._f === null) { 45 | return -1; 46 | } else { 47 | return _io.fileno(this._f); 48 | } 49 | } 50 | }); 51 | 52 | Object.defineProperty(this, 'mode', { 53 | get: function() { 54 | return this._mode; 55 | } 56 | }); 57 | 58 | Object.defineProperty(this, 'closed', { 59 | get: function() { 60 | return this._f === null; 61 | } 62 | }); 63 | } 64 | 65 | 66 | File.prototype.read = function(nread) { 67 | checkFile.call(this); 68 | if (nread === undefined || typeof nread === 'number') { 69 | nread = (nread >>> 0) || 4096; 70 | } 71 | // we will validate if the passed argument is a buffer in C 72 | return _io.fread(this._f, nread); 73 | } 74 | 75 | 76 | File.prototype.readLine = function(nread) { 77 | checkFile.call(this); 78 | if (nread === undefined || typeof nread === 'number') { 79 | nread = (nread >>> 0) || 4096; 80 | } 81 | // we will validate if the passed argument is a buffer in C 82 | return _io.fgets(this._f, nread); 83 | } 84 | 85 | 86 | File.prototype.write = function(data) { 87 | checkFile.call(this); 88 | return _io.fwrite(this._f, data); 89 | } 90 | 91 | 92 | File.prototype.writeLine = function(data) { 93 | checkFile.call(this); 94 | return _io.fwrite(this._f, data) + _io.fwrite(this._f, '\n'); 95 | } 96 | 97 | 98 | File.prototype.flush = function() { 99 | checkFile.call(this); 100 | _io.fflush(this._f); 101 | } 102 | 103 | 104 | File.prototype.close = function() { 105 | if (this._f !== null) { 106 | _io.fclose(this._f); 107 | this._f = null; 108 | } 109 | } 110 | 111 | 112 | function open(fpath, mode, buffering) { 113 | const npath = path.normalize(fpath); 114 | const f = _io.fopen(npath, mode); 115 | 116 | return new File(npath, f, mode, buffering); 117 | } 118 | 119 | 120 | function fdopen(fd, mode, fpath, buffering) { 121 | const npath = path.normalize(fpath); 122 | const f = _io.fdopen(fd, mode); 123 | 124 | return new File(npath, f, mode, buffering); 125 | } 126 | 127 | 128 | function readFile(fpath) { 129 | const f = open(fpath, 'rb'); 130 | const st = os.fstat(f.fd); 131 | const data = f.read(st.size); 132 | 133 | f.close(); 134 | 135 | return data; 136 | } 137 | 138 | // internal helpers 139 | 140 | function fileDealloc(f) { 141 | if (f._f !== null) { 142 | _io.fclose(f._f); 143 | } 144 | } 145 | 146 | 147 | function checkFile() { 148 | if (this._f === null) { 149 | const e = new Error('[Errno ' + errno.EBADF + '] ' + errno.strerror(errno.EBADF)); 150 | e.errno = errno.EBADF; 151 | throw e; 152 | } 153 | } 154 | 155 | 156 | // exports 157 | 158 | exports.open = open; 159 | exports.fdopen = fdopen; 160 | exports.readFile = readFile; 161 | exports.select = _select; 162 | exports.poll = _poll; 163 | -------------------------------------------------------------------------------- /docs/source/modules/process.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modprocess: 3 | 4 | process 5 | ======= 6 | 7 | This module provides Unix process management utilities. 8 | 9 | 10 | Process object 11 | -------------- 12 | 13 | The `Process` object represents a child process which was spawned and provides the necessary tools to 14 | control it. 15 | 16 | .. js:attribute:: process.Process.pid 17 | 18 | The process ID of the child process. Note that if ``shell: true`` was specified when spawning, the returned ID 19 | is that of the shell. 20 | 21 | .. js:class:: process.Process(options) 22 | 23 | Creates a process object. Process objects are created with the :js:func:`process.spawn` factory function. 24 | 25 | .. js:attribute:: process.Process.stdin 26 | 27 | :js:class:`io.File` object representing the child process' `stdin`. It's write-only and any data written to 28 | it will show up as input data in the child process. 29 | 30 | .. js:attribute:: process.Process.stdout 31 | 32 | :js:class:`io.File` object representing the child process' `stdout`. It's read-only and any data the child process 33 | writes to its standard output will be available to read. 34 | 35 | .. js:attribute:: process.Process.stdin 36 | 37 | :js:class:`io.File` object representing the child process' `stderr`. It's read-only and any data the child process 38 | writes to its standard error will be available to read. 39 | 40 | .. js:attribute:: process.Process.wait 41 | 42 | Wait untill the child process is finished. Returns an object with 2 properties: 43 | 44 | * exit_status: the status code when the process exited. If the process didn't exit normally or due to 45 | a call to :man:`exit(3)` (os :man:`exit(2)`) the value will be 0. 46 | * term_signal: the number of the signal that caused the child process to terminate. If the process didn't 47 | terminate because of a signal the value will be 0. 48 | 49 | 50 | Functions 51 | --------- 52 | 53 | .. js:function:: process.daemonize 54 | 55 | Detaches the current process from the terminal and continues to run in the background as a system daemon. 56 | This is perfomed using the typical Unix double-fork approach. The working directory is changed to ``/`` and all 57 | `stdio` file descriptors are replaced with ``/dev/null``. Similar to :man:`daemon(3)`. 58 | 59 | .. js:function:: process.spawn(cmd, [options]) 60 | 61 | Creates a child process to run the given command. `cmd` should be an ``Array`` with the shell-escaped arguments 62 | or a string containing the full command, if the ``shell`` option is used. 63 | 64 | The `options` object customizes how the child process is executed. The following properties are supported: 65 | 66 | * `cwd`: working directory for the new process. 67 | * `env`: object containing the environment for the new process. The calling process' environment will be 68 | used in case ``null`` is provided. 69 | * `shell`: if ``true`` a shell will be used to spawn the command, thus running ``/bin/sh -c cmd``. 70 | * `uid`: the effective user ID for the new process. 71 | * `gid`: the effective group ID for the new process. 72 | 73 | Example: 74 | 75 | :: 76 | 77 | sjs> const proc = require('process'); 78 | = undefined 79 | sjs> var p = proc.spawn(['ls', '-l'], {stdin: null, stdout: 'pipe', stderr: null}); 80 | = undefined 81 | sjs> print(p.stdout.read()); 82 | total 56 83 | -rw-r--r-- 1 saghul saghul 117 May 7 23:40 AUTHORS 84 | drwxr-xr-x 3 saghul saghul 4096 Jun 6 09:17 build 85 | -rw-r--r-- 1 saghul saghul 3021 May 17 01:22 CHANGELOG.md 86 | -rw-r--r-- 1 saghul saghul 3938 Jun 4 18:16 CMakeLists.txt 87 | -rw-r--r-- 1 saghul saghul 619 May 2 11:02 CONTRIBUTING.md 88 | drwxr-xr-x 4 saghul saghul 4096 Jun 2 23:44 docs 89 | drwxr-xr-x 3 saghul saghul 4096 Mar 18 12:50 include 90 | -rw-r--r-- 1 saghul saghul 1080 May 1 23:30 LICENSE 91 | -rw-r--r-- 1 saghul saghul 667 May 6 01:38 Makefile 92 | drwxr-xr-x 4 saghul saghul 4096 Jun 6 02:41 modules 93 | -rw-r--r-- 1 saghul saghul 2829 Jun 3 00:52 README.md 94 | drwxr-xr-x 4 saghul saghul 4096 Jun 2 23:42 src 95 | drwxr-xr-x 3 saghul saghul 4096 Jun 6 09:14 test 96 | drwxr-xr-x 2 saghul saghul 4096 Apr 22 02:16 tools 97 | 98 | = undefined 99 | sjs> 100 | -------------------------------------------------------------------------------- /docs/source/.static/sjs-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xmlSkookum JS 77 | -------------------------------------------------------------------------------- /src/bindings/hash/hash.c: -------------------------------------------------------------------------------- 1 | 2 | #include "../../binding.h" 3 | #include 4 | 5 | #include "md5.h" 6 | #include "sha1.h" 7 | #include "sha2.h" 8 | 9 | 10 | #define DECLARE_HASH(name, HASH_CTX, HASH_Init, HASH_Update, HASH_Final, HASH_DIGEST_LENGTH) \ 11 | static duk_ret_t hash_##name##_new(duk_context* ctx) { \ 12 | HASH_CTX* hash_ctx; \ 13 | hash_ctx = malloc(sizeof(*hash_ctx)); \ 14 | if (hash_ctx == NULL) { \ 15 | SJS_THROW_ERRNO_ERROR2(ENOMEM); \ 16 | return -42; \ 17 | } else { \ 18 | HASH_Init(hash_ctx); \ 19 | duk_push_pointer(ctx, (void*) hash_ctx); \ 20 | return 1; \ 21 | } \ 22 | } \ 23 | \ 24 | static duk_ret_t hash_##name##_update(duk_context* ctx) { \ 25 | HASH_CTX* hash_ctx; \ 26 | const char* data; \ 27 | size_t len; \ 28 | hash_ctx = duk_require_pointer(ctx, 0); \ 29 | data = duk_require_lstring(ctx, 1, &len); \ 30 | HASH_Update(hash_ctx, (const uint8_t*) data, len); \ 31 | duk_push_undefined(ctx); \ 32 | return 1; \ 33 | } \ 34 | \ 35 | static duk_ret_t hash_##name##_digest(duk_context* ctx) { \ 36 | HASH_CTX* hash_ctx; \ 37 | HASH_CTX tmp; \ 38 | char* digest; \ 39 | hash_ctx = duk_require_pointer(ctx, 0); \ 40 | memcpy(&tmp, hash_ctx, sizeof(*hash_ctx)); \ 41 | digest = duk_push_fixed_buffer(ctx, HASH_DIGEST_LENGTH); \ 42 | HASH_Final((uint8_t*) digest, &tmp); \ 43 | return 1; \ 44 | } \ 45 | 46 | 47 | DECLARE_HASH(md5, MD5_CTX, MD5Init, MD5Update, MD5Final, MD5_DIGEST_LENGTH) 48 | DECLARE_HASH(sha1, SHA1_CTX, SHA1Init, SHA1Update, SHA1Final, SHA1_DIGEST_LENGTH) 49 | DECLARE_HASH(sha256, SHA256_CTX, SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH) 50 | DECLARE_HASH(sha512, SHA512_CTX, SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH) 51 | 52 | #undef DECLARE_HASH 53 | 54 | 55 | static duk_ret_t hash_dealloc(duk_context* ctx) { 56 | void* hash_ctx; 57 | hash_ctx = duk_require_pointer(ctx, 0); 58 | free(hash_ctx); 59 | duk_push_undefined(ctx); 60 | return 1; 61 | } 62 | 63 | 64 | #define EXPORT_HASH(name) \ 65 | { #name"_new", hash_##name##_new, 0 }, \ 66 | { #name"_update", hash_##name##_update, 2 }, \ 67 | { #name"_digest", hash_##name##_digest, 1 }, \ 68 | 69 | static const duk_function_list_entry module_funcs[] = { 70 | EXPORT_HASH(md5) 71 | EXPORT_HASH(sha1) 72 | EXPORT_HASH(sha256) 73 | EXPORT_HASH(sha512) 74 | { "dealloc", hash_dealloc, 1 /*nargs*/ }, 75 | { NULL, NULL, 0 } 76 | }; 77 | 78 | #undef EXPORT_HASH 79 | 80 | 81 | void sjs__binding_hash_init(duk_context* ctx) { 82 | sjs__register_binding(ctx, "hash", module_funcs, NULL); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /docs/source/usage.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _usage: 3 | 4 | Usage 5 | ===== 6 | 7 | Following its design, `sjs` can be used in 2 ways: the CLI and the C API. 8 | 9 | 10 | CLI 11 | --- 12 | 13 | This is the typical way to run JavaScript applications with `sjs`, by using the ``sjs`` binary. 14 | 15 | :: 16 | 17 | sjs -h 18 | Usage: sjs [options] [ | | - ] 19 | 20 | -h show help text 21 | -i enter interactive mode after executing argument file(s) / eval code 22 | -e CODE evaluate code 23 | 24 | If is omitted, interactive mode is started automatically. 25 | 26 | With the `sjs` CLI you can execute a file, eval some code directly from the command line, or enter the interactive 27 | mode. 28 | 29 | .. note:: 30 | When using the CLI `sjs` will try to pretify the output of every command by serializing it using a JSON variant 31 | called `JX `_ which Duktape includes. 32 | 33 | .. _vmapi: 34 | 35 | Embedding sjs in your application 36 | --------------------------------- 37 | 38 | The `sjs` VM API allows applications to execute JavaScript code using the `sjs` engine. The prime example is the `sjs` 39 | CLI, see ``src/cli/main.c``. 40 | 41 | With this C API applications willing to run JavaScript code can embed the `sjs` engine or link with `libsjs`. 42 | 43 | Data types 44 | ^^^^^^^^^^ 45 | 46 | .. c:type:: sjs_vm_t 47 | 48 | Opaque type encapsulating the `sjs` VM. 49 | 50 | .. c:type:: duk_context 51 | 52 | Opaque type encapsulating the Duktape engine. 53 | 54 | 55 | API 56 | ^^^ 57 | 58 | **Main VM API** 59 | 60 | .. c:function:: sjs_vm_t* sjs_vm_create(void) 61 | 62 | Create a `sjs` VM. 63 | 64 | :returns: The initialized VM. 65 | 66 | .. c:function:: void sjs_vm_destroy(sjs_vm_t* vm) 67 | 68 | Destroy the given VM. It can no longer be used after calling this function. 69 | 70 | :param vm: The VM which will be destroyed. 71 | 72 | .. c:function:: void sjs_vm_setup_args(sjs_vm_t* vm, int argc, char* argv[]) 73 | 74 | Setup the VM's arguments. This is required for initializing some internals in the :ref:`modsystem` module. 75 | 76 | :param vm: The VM reference. 77 | :param argc: Number of arguments in the array. 78 | :param argv: Array with arguments, in comman-line form. 79 | 80 | .. c:function:: duk_context* sjs_vm_get_duk_ctx(sjs_vm_t* vm) 81 | 82 | Get the reference to the Duktape engine associated with the VM. 83 | 84 | :param vm: The VM reference. 85 | :returns: The Duktape context. 86 | 87 | .. c:function:: sjs_vm_t* sjs_vm_get_vm(duk_context* ctx) 88 | 89 | Get the reference to the `sjs` VM associated with the given Duktape context. 90 | 91 | :param ctx: The Duktape context. 92 | :returns: The `sjs` VM instance. 93 | 94 | .. c:function:: int sjs_vm_eval_code(const sjs_vm_t* vm, const char* filename, const char* code, size_t len, FILE* foutput, FILE* ferror) 95 | 96 | Evaluate the given JavaScript `code`. The code is wrapped in a CommonJS module function and executed. 97 | 98 | :param vm: The VM reference. 99 | :param filename: Indicates the filename that is being executed. It will be printed in tracebacks and such. 100 | :param code: What is going to be executed. 101 | :param len: Length of the code. 102 | :param foutput: Stream where to print the result of the evaulated code (can be NULL). 103 | :param ferror: Stream where to print errors, if any (can be NULL). 104 | :returns: 0 if the code was evaluated without errors, != 0 otherwise. 105 | 106 | .. c:function:: int sjs_vm_eval_code_global(const sjs_vm_t* vm, const char* filename, const char* code, size_t len, FILE* foutput, FILE* ferror) 107 | 108 | Similar to :c:func:`sjs_vm_eval_code` but it evaluates the code in the global scope instead of creating a new 109 | CommonJS style context. 110 | 111 | .. c:function:: int sjs_vm_eval_file(const sjs_vm_t* vm, const char* filename, FILE* foutput, FILE* ferror) 112 | 113 | Evaluate the given file as JavaScript code. The code is wrapped in a CommonJS module function and executed. 114 | 115 | :param vm: The VM reference. 116 | :param filename: The file to be evaluated. 117 | :param foutput: Stream where to print the result of the evaulated code (can be NULL). 118 | :param ferror: Stream where to print errors, if any (can be NULL). 119 | :returns: 0 if the code was evaluated without errors, != 0 otherwise. 120 | 121 | **Utility functions** 122 | 123 | .. c:function:: int sjs_path_normalize(const char* path, char* normalized_path, size_t normalized_path_len) 124 | 125 | Normalize the given `path` into the given buffer. Mormalizing a path includes tilde expansions and :man:`realpath(3)`. 126 | 127 | :param path: The path which needs to be normalized. 128 | :param normalized_path: Buffer to store the normalized path. 129 | :param normalized_path_len: Size of `normalized_path`. 130 | :returns: 0 on success, or < 0 on failure. The returned code is the negated `errno`. 131 | 132 | .. c:function:: int sjs_path_expanduser(const char* path, char* normalized_path, size_t normalized_path_len) 133 | 134 | Similar to :c:func:`sjs_path_normalize` but in only performs tilde expansion. 135 | 136 | -------------------------------------------------------------------------------- /src/builtins.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "duktape.h" 5 | #include "duk_module_node.h" 6 | #include "embedjs.h" 7 | 8 | INCBIN(assert, "modules/assert.js"); 9 | INCBIN(codecs, "modules/codecs.js"); 10 | INCBIN(codecs_punycode, "modules/_codecs_punycode.js"); 11 | INCBIN(codecs_utf8, "modules/_codecs_utf8.js"); 12 | INCBIN(console, "modules/console.js"); 13 | INCBIN(errno, "modules/errno.js"); 14 | INCBIN(hash, "modules/hash.js"); 15 | INCBIN(io, "modules/io.js"); 16 | INCBIN(io_poll, "modules/_io_poll.js"); 17 | INCBIN(io_select, "modules/_io_select.js"); 18 | INCBIN(net, "modules/net.js"); 19 | INCBIN(os, "modules/os.js"); 20 | INCBIN(path, "modules/path.js"); 21 | INCBIN(process, "modules/process.js"); 22 | INCBIN(pwd, "modules/pwd.js"); 23 | INCBIN(random, "modules/random.js"); 24 | INCBIN(system, "modules/system.js"); 25 | INCBIN(time, "modules/time.js"); 26 | INCBIN(utils, "modules/utils.js"); 27 | INCBIN(utils_object, "modules/_utils_object.js"); 28 | INCBIN(utils_unicode, "modules/_utils_unicode.js"); 29 | INCBIN(utils_unorm, "modules/_utils_unicode_unorm.js"); 30 | INCBIN(uuid, "modules/uuid.js"); 31 | 32 | 33 | void sjs__register_bultins(duk_context* ctx) { 34 | /* 35 | * Standalone modules, they have no dependencies. 36 | */ 37 | duk_push_lstring(ctx, (const char*) sjs__code_codecs_punycode_data, sjs__code_codecs_punycode_size); 38 | duk_module_node_register_builtin(ctx, "_codecs_punycode"); 39 | 40 | duk_push_lstring(ctx, (const char*) sjs__code_codecs_utf8_data, sjs__code_codecs_utf8_size); 41 | duk_module_node_register_builtin(ctx, "_codecs_utf8"); 42 | 43 | duk_push_lstring(ctx, (const char*) sjs__code_codecs_data, sjs__code_codecs_size); 44 | duk_module_node_register_builtin(ctx, "codecs"); 45 | 46 | duk_push_lstring(ctx, (const char*) sjs__code_errno_data, sjs__code_errno_size); 47 | duk_module_node_register_builtin(ctx, "errno"); 48 | 49 | duk_push_lstring(ctx, (const char*) sjs__code_os_data, sjs__code_os_size); 50 | duk_module_node_register_builtin(ctx, "os"); 51 | 52 | duk_push_lstring(ctx, (const char*) sjs__code_path_data, sjs__code_path_size); 53 | duk_module_node_register_builtin(ctx, "path"); 54 | 55 | duk_push_lstring(ctx, (const char*) sjs__code_pwd_data, sjs__code_pwd_size); 56 | duk_module_node_register_builtin(ctx, "pwd"); 57 | 58 | duk_push_lstring(ctx, (const char*) sjs__code_utils_object_data, sjs__code_utils_object_size); 59 | duk_module_node_register_builtin(ctx, "_utils_object"); 60 | 61 | duk_push_lstring(ctx, (const char*) sjs__code_utils_unorm_data, sjs__code_utils_unorm_size); 62 | duk_module_node_register_builtin(ctx, "_utils_unicode_unorm"); 63 | 64 | duk_push_lstring(ctx, (const char*) sjs__code_utils_unicode_data, sjs__code_utils_unicode_size); 65 | duk_module_node_register_builtin(ctx, "_utils_unicode"); 66 | 67 | duk_push_lstring(ctx, (const char*) sjs__code_utils_data, sjs__code_utils_size); 68 | duk_module_node_register_builtin(ctx, "utils"); 69 | 70 | duk_push_lstring(ctx, (const char*) sjs__code_uuid_data, sjs__code_uuid_size); 71 | duk_module_node_register_builtin(ctx, "uuid"); 72 | 73 | /* 74 | * Dependent modules. These depend on some of the above. 75 | */ 76 | duk_push_lstring(ctx, (const char*) sjs__code_assert_data, sjs__code_assert_size); 77 | duk_module_node_register_builtin(ctx, "assert"); 78 | 79 | duk_push_lstring(ctx, (const char*) sjs__code_hash_data, sjs__code_hash_size); 80 | duk_module_node_register_builtin(ctx, "hash"); 81 | 82 | duk_push_lstring(ctx, (const char*) sjs__code_io_select_data, sjs__code_io_select_size); 83 | duk_module_node_register_builtin(ctx, "_io_select"); 84 | 85 | duk_push_lstring(ctx, (const char*) sjs__code_io_poll_data, sjs__code_io_poll_size); 86 | duk_module_node_register_builtin(ctx, "_io_poll"); 87 | 88 | duk_push_lstring(ctx, (const char*) sjs__code_io_data, sjs__code_io_size); 89 | duk_module_node_register_builtin(ctx, "io"); 90 | 91 | duk_push_lstring(ctx, (const char*) sjs__code_net_data, sjs__code_net_size); 92 | duk_module_node_register_builtin(ctx, "net"); 93 | 94 | duk_push_lstring(ctx, (const char*) sjs__code_random_data, sjs__code_random_size); 95 | duk_module_node_register_builtin(ctx, "random"); 96 | 97 | /* 98 | * Dependent modules. These depend on some of the above. 99 | */ 100 | duk_push_lstring(ctx, (const char*) sjs__code_time_data, sjs__code_time_size); 101 | duk_module_node_register_builtin(ctx, "time"); 102 | 103 | duk_push_lstring(ctx, (const char*) sjs__code_system_data, sjs__code_system_size); 104 | duk_module_node_register_builtin(ctx, "system"); 105 | 106 | /* 107 | * Dependent modules. These depend on some of the above. 108 | */ 109 | duk_push_lstring(ctx, (const char*) sjs__code_console_data, sjs__code_console_size); 110 | duk_module_node_register_builtin(ctx, "console"); 111 | 112 | duk_push_lstring(ctx, (const char*) sjs__code_process_data, sjs__code_process_size); 113 | duk_module_node_register_builtin(ctx, "process"); 114 | } 115 | 116 | -------------------------------------------------------------------------------- /docs/source/modules/io.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _modio: 3 | 4 | io 5 | == 6 | 7 | This module provies access to high i/o primitives and streams. 8 | 9 | 10 | File object 11 | ----------- 12 | 13 | .. js:class:: io.File 14 | 15 | Class representing a `stdio `_ stream. 16 | 17 | This class created through :js:func:`io.open` or :js:func:`io.fdopen`, never directly. 18 | 19 | .. js:attribute:: io.File.path 20 | 21 | Returns the opened file's path. 22 | 23 | .. js:attribute:: io.File.fd 24 | 25 | Returns the file descriptor associated with the file. 26 | 27 | .. js:attribute:: io.File.mode 28 | 29 | Returns the mode in which the file was opened. 30 | 31 | .. js:attribute:: io.File.closed 32 | 33 | Boolean flag indicating if the file was closed. 34 | 35 | .. js:function:: io.FIle.prototype.read(nread) 36 | 37 | Read data from the file. 38 | 39 | :param nread: Amount of data to receive. If not specified it defaults to 4096. Alternatively, a `Buffer` 40 | can be passed, and data will be read into it. 41 | :returns: The data that was read as a `Uint8Array` or the amount of data read as a number, if a `Buffer` was passed. 42 | 43 | .. seealso:: 44 | :man:`fread(3)` 45 | 46 | .. js:function:: io.FIle.prototype.readLine(nread) 47 | 48 | Similar to :js:func:`io.FIle.prototype.read`, but stops at the newline (``\n``) character. 49 | This is the recommended function to read from `stdin`, but not from binary files. 50 | 51 | .. seealso:: 52 | :man:`fgets(3)` 53 | 54 | .. js:function:: io.FIle.prototype.write(data) 55 | 56 | Write data on the file. 57 | 58 | :param data: The data that will be written (can be a string or a `Buffer`). 59 | :returns: The number of bytes from `data` which were actually written. 60 | 61 | .. seealso:: 62 | :man:`fwrite(3)` 63 | 64 | .. js:function:: io.FIle.prototype.writeLine(data) 65 | 66 | Same as :js:func:`io.FIle.prototype.write`, but add a newline (``\n``) at the end. 67 | 68 | .. js:function:: io.FIle.prototype.flush 69 | 70 | Flush the buffered write data to the file. 71 | 72 | .. seealso:: 73 | :man:`fflush(3)` 74 | 75 | .. js:function:: io.FIle.prototype.close 76 | 77 | Close the file. 78 | 79 | 80 | Functions 81 | --------- 82 | 83 | .. js:function:: io.open(path, mode, [buffering]) 84 | 85 | Opens the file at the given `path` in the given mode. Check :man:`fopen(3)` for the `mode` details. 86 | It returns a :js:class:`io.File` object. 87 | 88 | If `buffering` is specified, it must be ``-1`` (for default buffering), ``0`` (for unbuffeered) or ``1`` for 89 | line buffering). See :man:`setvbuf(3)`. 90 | 91 | .. js:function:: io.fdopen(fd, mode, [path], [buffering]) 92 | 93 | Opens the fiven file descriptor in `fd` as a :js:class:`io.File` object. The given `mode` must be compatible with 94 | how the file descriptor was opened. `path` is purely informational. 95 | 96 | If `buffering` is specified, it must be ``-1`` (for default buffering), ``0`` (for unbuffeered) or ``1`` for 97 | line buffering). See :man:`setvbuf(3)`. 98 | 99 | .. seealso:: 100 | :man:`fdopen(3)` 101 | 102 | .. js:function:: io.readFile(path) 103 | 104 | Returns the contents of the file at the given `path` as a `Uint8Array`. 105 | 106 | 107 | io.select 108 | --------- 109 | 110 | This object provides access to :man:`select(2)`. 111 | 112 | .. js:function:: select.select(rfds, wfds, xfds, timeout) 113 | 114 | Wait until any of the given file descriptors are ready for reading, writing or have a pending exceptional 115 | condition. 116 | 117 | :param rfds: Array of file descriptors to monitor for reading. 118 | :param wfds: Array of file descriptors to monitor for writing. 119 | :param xfds: Array of file descriptors to monitor for pending exceptional conditions. 120 | :param timeout: Amount of time to wait. ``null`` means unlimited. This function might return early if interrupted 121 | by a signal. 122 | :returns: An object containing 3 properties: `rfds`, `wfds` and `xfds`, containing the file descriptors which are 123 | ready for each condition respectively. 124 | 125 | For more information see :man:`select(2)`. 126 | 127 | 128 | io.poll 129 | ------- 130 | 131 | This object provides access to :man:`poll(2)`. 132 | 133 | .. js:data:: poll.POLLIN 134 | .. js:data:: poll.POLLOUT 135 | .. js:data:: poll.POLLPRI 136 | .. js:data:: poll.POLLRDHUP 137 | .. js:data:: poll.POLLERR 138 | .. js:data:: poll.POLLHUP 139 | .. js:data:: poll.POLLINVAL 140 | 141 | Constants to be used in the `events` or `revents` fields of a ``pollfd`` object. Check :man:`poll(2)` for 142 | more information. Note that not all these constants might be available on your platform. 143 | 144 | .. js:function:: poll.poll(pfds, timeout) 145 | 146 | Examines the given file descriptors to see if some of them are ready for i/o or if certain events have 147 | occurred on them. 148 | 149 | :param pfds: An array of ``pollfd`` objects to be examined. A ``pollfd`` object is any object which has a `fd` and 150 | a `events` properties. The `events` property must contain the or-ed events that the user is interested in 151 | examining. 152 | :param timeout: Amount of time to wait. ``null`` means unlimited. This function might return early if interrupted 153 | by a signal. 154 | :returns: An array of ``pollfd`` objects, containing `fd`, `events` and `revents` properties. `fd` and `events` 155 | match the given ones, and `revents` indicates the received events. 156 | 157 | For more information see :man:`poll(2)`. 158 | -------------------------------------------------------------------------------- /src/bindings/gai.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../binding.h" 9 | #include 10 | 11 | 12 | /* Parse the given sockaddr and leave an address object in the stack top. */ 13 | static void gai__parse_address(duk_context* ctx, struct sockaddr* addr) { 14 | switch (addr->sa_family) { 15 | case AF_INET: 16 | { 17 | struct sockaddr_in* addr4; 18 | char host[INET_ADDRSTRLEN]; 19 | int port; 20 | addr4 = (struct sockaddr_in*) addr; 21 | if (inet_ntop(AF_INET, &(addr4->sin_addr), host, sizeof(host)) == NULL) { 22 | duk_push_undefined(ctx); 23 | return; 24 | } 25 | port = ntohs(addr4->sin_port); 26 | duk_push_object(ctx); 27 | duk_push_string(ctx, host); 28 | duk_put_prop_string(ctx, -2, "host"); 29 | duk_push_int(ctx, port); 30 | duk_put_prop_string(ctx, -2, "port"); 31 | break; 32 | } 33 | case AF_INET6: 34 | { 35 | struct sockaddr_in6* addr6; 36 | char host[INET6_ADDRSTRLEN]; 37 | int port; 38 | addr6 = (struct sockaddr_in6*) addr; 39 | if (inet_ntop(AF_INET6, &(addr6->sin6_addr), host, sizeof(host)) == NULL) { 40 | duk_push_undefined(ctx); 41 | return; 42 | } 43 | port = ntohs(addr6->sin6_port); 44 | duk_push_object(ctx); 45 | duk_push_string(ctx, host); 46 | duk_put_prop_string(ctx, -2, "host"); 47 | duk_push_int(ctx, port); 48 | duk_put_prop_string(ctx, -2, "port"); 49 | duk_push_int(ctx, addr6->sin6_flowinfo); 50 | duk_put_prop_string(ctx, -2, "flowinfo"); 51 | duk_push_int(ctx, addr6->sin6_scope_id); 52 | duk_put_prop_string(ctx, -2, "scopeid"); 53 | break; 54 | } 55 | default: 56 | abort(); 57 | } 58 | } 59 | 60 | 61 | /* 62 | * Getaddrinfo. Args: 63 | * - 0: hostname 64 | * - 1: servname 65 | * - 2: hints 66 | */ 67 | static duk_ret_t gai_getaddrinfo(duk_context* ctx) { 68 | int r; 69 | const char* hostname; 70 | const char* servname; 71 | struct addrinfo hints; 72 | struct addrinfo* res; 73 | struct addrinfo* res0; 74 | 75 | hostname = duk_get_string(ctx, 0); 76 | servname = duk_get_string(ctx, 1); 77 | 78 | memset(&hints, 0, sizeof(hints)); 79 | duk_get_prop_string(ctx, 2, "family"); 80 | hints.ai_family = duk_get_int(ctx, -1); 81 | duk_pop(ctx); 82 | duk_get_prop_string(ctx, 2, "type"); 83 | hints.ai_socktype = duk_get_int(ctx, -1); 84 | duk_pop(ctx); 85 | duk_get_prop_string(ctx, 2, "protocol"); 86 | hints.ai_protocol = duk_get_int(ctx, -1); 87 | duk_pop(ctx); 88 | duk_get_prop_string(ctx, 2, "flags"); 89 | hints.ai_flags = duk_get_int(ctx, -1); 90 | duk_pop(ctx); 91 | 92 | r = getaddrinfo(hostname, servname, &hints, &res0); 93 | if (r != 0) { 94 | duk_push_error_object(ctx, DUK_ERR_ERROR, "[gaierror %d] %s", r, gai_strerror(r)); 95 | duk_push_int(ctx, r); 96 | duk_put_prop_string(ctx, -2, "code"); 97 | if (r == EAI_SYSTEM) { 98 | duk_push_int(ctx, errno); 99 | duk_put_prop_string(ctx, -2, "errno"); 100 | } 101 | duk_throw(ctx); 102 | return -42; /* control never returns here */ 103 | } else { 104 | int i = 0; 105 | duk_push_array(ctx); 106 | for (res = res0; res; res = res->ai_next) { 107 | if (res->ai_family != AF_INET && res->ai_family != AF_INET6) { 108 | continue; 109 | } 110 | 111 | /* object with family, socktype, protocol, canonname, and address */ 112 | duk_push_object(ctx); 113 | 114 | duk_push_int(ctx, res->ai_family); 115 | duk_put_prop_string(ctx, -2, "family"); 116 | duk_push_int(ctx, res->ai_socktype); 117 | duk_put_prop_string(ctx, -2, "type"); 118 | duk_push_int(ctx, res->ai_protocol); 119 | duk_put_prop_string(ctx, -2, "protocol"); 120 | duk_push_string(ctx, res->ai_canonname ? res->ai_canonname : ""); 121 | duk_put_prop_string(ctx, -2, "canonname"); 122 | gai__parse_address(ctx, res->ai_addr); 123 | duk_put_prop_string(ctx, -2, "address"); 124 | /* stack: [... array obj ] */ 125 | 126 | duk_put_prop_index(ctx, -2, i); 127 | i++; 128 | } 129 | } 130 | 131 | freeaddrinfo(res0); 132 | return 1; 133 | } 134 | 135 | 136 | static duk_ret_t gai_gstrerror(duk_context* ctx) { 137 | int r = duk_require_int(ctx, 0); 138 | const char* msg = gai_strerror(r); 139 | duk_push_string(ctx, msg); 140 | return 1; 141 | } 142 | 143 | 144 | #define X(name) {#name, name} 145 | static const duk_number_list_entry module_consts[] = { 146 | /* ai_flags */ 147 | X(AI_ADDRCONFIG), 148 | X(AI_ALL), 149 | X(AI_CANONNAME), 150 | X(AI_NUMERICHOST), 151 | X(AI_NUMERICSERV), 152 | X(AI_V4MAPPED), 153 | /* gai errors */ 154 | X(EAI_AGAIN), 155 | X(EAI_BADFLAGS), 156 | #ifdef EAI_BADHINTS 157 | X(EAI_BADHINTS), 158 | #endif 159 | X(EAI_FAIL), 160 | X(EAI_FAMILY), 161 | X(EAI_MEMORY), 162 | X(EAI_NONAME), 163 | X(EAI_OVERFLOW), 164 | #ifdef EAI_PROTOCOL 165 | X(EAI_PROTOCOL), 166 | #endif 167 | X(EAI_SERVICE), 168 | X(EAI_SOCKTYPE), 169 | X(EAI_SYSTEM), 170 | { NULL, 0.0 } 171 | }; 172 | #undef X 173 | 174 | 175 | static const duk_function_list_entry module_funcs[] = { 176 | /* name, function, nargs */ 177 | { "getaddrinfo", gai_getaddrinfo, 3 }, 178 | { "gai_strerror", gai_gstrerror, 1 }, 179 | { NULL, NULL, 0 } 180 | }; 181 | 182 | 183 | void sjs__binding_gai_init(duk_context* ctx) { 184 | sjs__register_binding(ctx, "gai", module_funcs, module_consts); 185 | } 186 | 187 | -------------------------------------------------------------------------------- /modules/uuid.js: -------------------------------------------------------------------------------- 1 | // uuid.js 2 | // 3 | // Copyright (c) 2010-2012 Robert Kieffer 4 | // MIT License - http://opensource.org/licenses/mit-license.php 5 | 6 | // Unique ID creation requires a high quality random # generator which 7 | // returns 128-bits of randomness, since that's what's usually required 8 | const os = require('os'); 9 | var _rng = function() { 10 | return os.urandom(16); 11 | }; 12 | 13 | // Maps for number <-> hex string conversion 14 | var _byteToHex = []; 15 | var _hexToByte = {}; 16 | for (var i = 0; i < 256; i++) { 17 | _byteToHex[i] = (i + 0x100).toString(16).substr(1); 18 | _hexToByte[_byteToHex[i]] = i; 19 | } 20 | 21 | // **`parse()` - Parse a UUID into it's component bytes** 22 | function parse(s, buf, offset) { 23 | var i = (buf && offset) || 0, ii = 0; 24 | 25 | buf = buf || []; 26 | s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) { 27 | if (ii < 16) { // Don't overflow! 28 | buf[i + ii++] = _hexToByte[oct]; 29 | } 30 | }); 31 | 32 | // Zero out remaining bytes if string was short 33 | while (ii < 16) { 34 | buf[i + ii++] = 0; 35 | } 36 | 37 | return buf; 38 | } 39 | 40 | // **`unparse()` - Convert UUID byte array (ala parse()) into a string** 41 | function unparse(buf, offset) { 42 | var i = offset || 0, bth = _byteToHex; 43 | return bth[buf[i++]] + bth[buf[i++]] + 44 | bth[buf[i++]] + bth[buf[i++]] + '-' + 45 | bth[buf[i++]] + bth[buf[i++]] + '-' + 46 | bth[buf[i++]] + bth[buf[i++]] + '-' + 47 | bth[buf[i++]] + bth[buf[i++]] + '-' + 48 | bth[buf[i++]] + bth[buf[i++]] + 49 | bth[buf[i++]] + bth[buf[i++]] + 50 | bth[buf[i++]] + bth[buf[i++]]; 51 | } 52 | 53 | // **`v1()` - Generate time-based UUID** 54 | // 55 | // Inspired by https://github.com/LiosK/UUID.js 56 | // and http://docs.python.org/library/uuid.html 57 | 58 | // random #'s we need to init node and clockseq 59 | var _seedBytes = _rng(); 60 | 61 | // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) 62 | var _nodeId = [ 63 | _seedBytes[0] | 0x01, 64 | _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5] 65 | ]; 66 | 67 | // Per 4.2.2, randomize (14 bit) clockseq 68 | var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; 69 | 70 | // Previous uuid creation time 71 | var _lastMSecs = 0, _lastNSecs = 0; 72 | 73 | // See https://github.com/broofa/node-uuid for API details 74 | function v1(options, buf, offset) { 75 | var i = buf && offset || 0; 76 | var b = buf || []; 77 | 78 | options = options || {}; 79 | 80 | var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; 81 | 82 | // UUID timestamps are 100 nano-second units since the Gregorian epoch, 83 | // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so 84 | // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' 85 | // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. 86 | var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime(); 87 | 88 | // Per 4.2.1.2, use count of uuid's generated during the current clock 89 | // cycle to simulate higher resolution clock 90 | var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; 91 | 92 | // Time since last uuid creation (in msecs) 93 | var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; 94 | 95 | // Per 4.2.1.2, Bump clockseq on clock regression 96 | if (dt < 0 && options.clockseq === undefined) { 97 | clockseq = clockseq + 1 & 0x3fff; 98 | } 99 | 100 | // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new 101 | // time interval 102 | if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { 103 | nsecs = 0; 104 | } 105 | 106 | // Per 4.2.1.2 Throw error if too many uuids are requested 107 | if (nsecs >= 10000) { 108 | throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); 109 | } 110 | 111 | _lastMSecs = msecs; 112 | _lastNSecs = nsecs; 113 | _clockseq = clockseq; 114 | 115 | // Per 4.1.4 - Convert from unix epoch to Gregorian epoch 116 | msecs += 12219292800000; 117 | 118 | // `time_low` 119 | var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; 120 | b[i++] = tl >>> 24 & 0xff; 121 | b[i++] = tl >>> 16 & 0xff; 122 | b[i++] = tl >>> 8 & 0xff; 123 | b[i++] = tl & 0xff; 124 | 125 | // `time_mid` 126 | var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; 127 | b[i++] = tmh >>> 8 & 0xff; 128 | b[i++] = tmh & 0xff; 129 | 130 | // `time_high_and_version` 131 | b[i++] = tmh >>> 24 & 0xf | 0x10; // include version 132 | b[i++] = tmh >>> 16 & 0xff; 133 | 134 | // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) 135 | b[i++] = clockseq >>> 8 | 0x80; 136 | 137 | // `clock_seq_low` 138 | b[i++] = clockseq & 0xff; 139 | 140 | // `node` 141 | var node = options.node || _nodeId; 142 | for (var n = 0; n < 6; n++) { 143 | b[i + n] = node[n]; 144 | } 145 | 146 | return buf ? buf : unparse(b); 147 | } 148 | 149 | // **`v4()` - Generate random UUID** 150 | 151 | // See https://github.com/broofa/node-uuid for API details 152 | function v4(options, buf, offset) { 153 | // Deprecated - 'format' argument, as supported in v1.2 154 | var i = buf && offset || 0; 155 | 156 | if (typeof(options) == 'string') { 157 | buf = options == 'binary' ? new Array(16) : null; 158 | options = null; 159 | } 160 | options = options || {}; 161 | 162 | var rnds = _rng(); 163 | 164 | // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` 165 | rnds[6] = (rnds[6] & 0x0f) | 0x40; 166 | rnds[8] = (rnds[8] & 0x3f) | 0x80; 167 | 168 | // Copy bytes to buffer, if provided 169 | if (buf) { 170 | for (var ii = 0; ii < 16; ii++) { 171 | buf[i + ii] = rnds[ii]; 172 | } 173 | } 174 | 175 | return buf || unparse(rnds); 176 | } 177 | 178 | // Export public API 179 | var uuid = v4; 180 | uuid.v1 = v1; 181 | uuid.v4 = v4; 182 | uuid.parse = parse; 183 | uuid.unparse = unparse; 184 | 185 | module.exports = uuid; 186 | -------------------------------------------------------------------------------- /src/bindings/hash/sha1.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: sha1.c,v 1.23 2014/01/08 06:14:57 tedu Exp $ */ 2 | 3 | /* 4 | * SHA-1 in C 5 | * By Steve Reid 6 | * 100% Public Domain 7 | * 8 | * Test Vectors (from FIPS PUB 180-1) 9 | * "abc" 10 | * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 11 | * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 12 | * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 13 | * A million repetitions of "a" 14 | * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 15 | */ 16 | 17 | #include "includes.h" 18 | 19 | #ifndef WITH_OPENSSL 20 | 21 | #include 22 | #include 23 | #include "sha1.h" 24 | 25 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 26 | 27 | /* 28 | * blk0() and blk() perform the initial expand. 29 | * I got the idea of expanding during the round function from SSLeay 30 | */ 31 | #if BYTE_ORDER == LITTLE_ENDIAN 32 | # define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 33 | |(rol(block->l[i],8)&0x00FF00FF)) 34 | #else 35 | # define blk0(i) block->l[i] 36 | #endif 37 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 38 | ^block->l[(i+2)&15]^block->l[i&15],1)) 39 | 40 | /* 41 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 42 | */ 43 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 44 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 45 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 46 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 47 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 48 | 49 | typedef union { 50 | u_int8_t c[64]; 51 | u_int32_t l[16]; 52 | } CHAR64LONG16; 53 | 54 | /* 55 | * Hash a single 512-bit block. This is the core of the algorithm. 56 | */ 57 | void 58 | SHA1Transform(u_int32_t state[5], const u_int8_t buffer[SHA1_BLOCK_LENGTH]) 59 | { 60 | u_int32_t a, b, c, d, e; 61 | u_int8_t workspace[SHA1_BLOCK_LENGTH]; 62 | CHAR64LONG16 *block = (CHAR64LONG16 *)workspace; 63 | 64 | (void)memcpy(block, buffer, SHA1_BLOCK_LENGTH); 65 | 66 | /* Copy context->state[] to working vars */ 67 | a = state[0]; 68 | b = state[1]; 69 | c = state[2]; 70 | d = state[3]; 71 | e = state[4]; 72 | 73 | /* 4 rounds of 20 operations each. Loop unrolled. */ 74 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 75 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 76 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 77 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 78 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 79 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 80 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 81 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 82 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 83 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 84 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 85 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 86 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 87 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 88 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 89 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 90 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 91 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 92 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 93 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 94 | 95 | /* Add the working vars back into context.state[] */ 96 | state[0] += a; 97 | state[1] += b; 98 | state[2] += c; 99 | state[3] += d; 100 | state[4] += e; 101 | 102 | /* Wipe variables */ 103 | a = b = c = d = e = 0; 104 | } 105 | 106 | 107 | /* 108 | * SHA1Init - Initialize new context 109 | */ 110 | void 111 | SHA1Init(SHA1_CTX *context) 112 | { 113 | 114 | /* SHA1 initialization constants */ 115 | context->count = 0; 116 | context->state[0] = 0x67452301; 117 | context->state[1] = 0xEFCDAB89; 118 | context->state[2] = 0x98BADCFE; 119 | context->state[3] = 0x10325476; 120 | context->state[4] = 0xC3D2E1F0; 121 | } 122 | 123 | 124 | /* 125 | * Run your data through this. 126 | */ 127 | void 128 | SHA1Update(SHA1_CTX *context, const u_int8_t *data, size_t len) 129 | { 130 | size_t i, j; 131 | 132 | j = (size_t)((context->count >> 3) & 63); 133 | context->count += (len << 3); 134 | if ((j + len) > 63) { 135 | (void)memcpy(&context->buffer[j], data, (i = 64-j)); 136 | SHA1Transform(context->state, context->buffer); 137 | for ( ; i + 63 < len; i += 64) 138 | SHA1Transform(context->state, (u_int8_t *)&data[i]); 139 | j = 0; 140 | } else { 141 | i = 0; 142 | } 143 | (void)memcpy(&context->buffer[j], &data[i], len - i); 144 | } 145 | 146 | 147 | /* 148 | * Add padding and return the message digest. 149 | */ 150 | void 151 | SHA1Pad(SHA1_CTX *context) 152 | { 153 | u_int8_t finalcount[8]; 154 | u_int i; 155 | 156 | for (i = 0; i < 8; i++) { 157 | finalcount[i] = (u_int8_t)((context->count >> 158 | ((7 - (i & 7)) * 8)) & 255); /* Endian independent */ 159 | } 160 | SHA1Update(context, (u_int8_t *)"\200", 1); 161 | while ((context->count & 504) != 448) 162 | SHA1Update(context, (u_int8_t *)"\0", 1); 163 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 164 | } 165 | 166 | void 167 | SHA1Final(u_int8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context) 168 | { 169 | u_int i; 170 | 171 | SHA1Pad(context); 172 | for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { 173 | digest[i] = (u_int8_t) 174 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 175 | } 176 | memset(context, 0, sizeof(*context)); 177 | } 178 | #endif /* !WITH_OPENSSL */ 179 | -------------------------------------------------------------------------------- /docs/source/.static/sjs-libsjs-diagram.svg: -------------------------------------------------------------------------------- 1 | 2 |
libsjs
libsjs
Operating System
Operating System<br>
sjs CLI
sjs CLI<br>
sjs C modules
sjs C modules<br>
sjs JavaScript Modules
sjs JavaScript Modules<br>
-------------------------------------------------------------------------------- /src/bindings/hash/sha2.h: -------------------------------------------------------------------------------- 1 | /* OpenBSD: sha2.h,v 1.6 2004/06/22 01:57:30 jfb Exp */ 2 | 3 | /* 4 | * FILE: sha2.h 5 | * AUTHOR: Aaron D. Gifford 6 | * 7 | * Copyright (c) 2000-2001, Aaron D. Gifford 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the copyright holder nor the names of contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | * 34 | * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ 35 | */ 36 | 37 | /* OPENBSD ORIGINAL: include/sha2.h */ 38 | 39 | #ifndef _SSHSHA2_H 40 | #define _SSHSHA2_H 41 | 42 | #include "includes.h" 43 | 44 | #ifdef WITH_OPENSSL 45 | # include 46 | # if !defined(HAVE_EVP_SHA256) && (OPENSSL_VERSION_NUMBER >= 0x00907000L) 47 | # define _NEED_SHA2 1 48 | # endif 49 | #else 50 | # define _NEED_SHA2 1 51 | #endif 52 | 53 | #if defined(_NEED_SHA2) && !defined(HAVE_SHA256_UPDATE) 54 | 55 | /*** SHA-256/384/512 Various Length Definitions ***********************/ 56 | #define SHA256_BLOCK_LENGTH 64 57 | #define SHA256_DIGEST_LENGTH 32 58 | #define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) 59 | #define SHA384_BLOCK_LENGTH 128 60 | #define SHA384_DIGEST_LENGTH 48 61 | #define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) 62 | #define SHA512_BLOCK_LENGTH 128 63 | #define SHA512_DIGEST_LENGTH 64 64 | #define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) 65 | 66 | 67 | /*** SHA-256/384/512 Context Structures *******************************/ 68 | typedef struct _SHA256_CTX { 69 | u_int32_t state[8]; 70 | u_int64_t bitcount; 71 | u_int8_t buffer[SHA256_BLOCK_LENGTH]; 72 | } SHA256_CTX; 73 | typedef struct _SHA512_CTX { 74 | u_int64_t state[8]; 75 | u_int64_t bitcount[2]; 76 | u_int8_t buffer[SHA512_BLOCK_LENGTH]; 77 | } SHA512_CTX; 78 | 79 | typedef SHA512_CTX SHA384_CTX; 80 | 81 | void SHA256_Init(SHA256_CTX *); 82 | void SHA256_Transform(u_int32_t state[8], const u_int8_t [SHA256_BLOCK_LENGTH]); 83 | void SHA256_Update(SHA256_CTX *, const u_int8_t *, size_t) 84 | __attribute__((__bounded__(__string__,2,3))); 85 | void SHA256_Pad(SHA256_CTX *); 86 | void SHA256_Final(u_int8_t [SHA256_DIGEST_LENGTH], SHA256_CTX *) 87 | __attribute__((__bounded__(__minbytes__,1,SHA256_DIGEST_LENGTH))); 88 | char *SHA256_End(SHA256_CTX *, char *) 89 | __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); 90 | char *SHA256_File(const char *, char *) 91 | __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); 92 | char *SHA256_FileChunk(const char *, char *, off_t, off_t) 93 | __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); 94 | char *SHA256_Data(const u_int8_t *, size_t, char *) 95 | __attribute__((__bounded__(__string__,1,2))) 96 | __attribute__((__bounded__(__minbytes__,3,SHA256_DIGEST_STRING_LENGTH))); 97 | 98 | void SHA384_Init(SHA384_CTX *); 99 | void SHA384_Transform(u_int64_t state[8], const u_int8_t [SHA384_BLOCK_LENGTH]); 100 | void SHA384_Update(SHA384_CTX *, const u_int8_t *, size_t) 101 | __attribute__((__bounded__(__string__,2,3))); 102 | void SHA384_Pad(SHA384_CTX *); 103 | void SHA384_Final(u_int8_t [SHA384_DIGEST_LENGTH], SHA384_CTX *) 104 | __attribute__((__bounded__(__minbytes__,1,SHA384_DIGEST_LENGTH))); 105 | char *SHA384_End(SHA384_CTX *, char *) 106 | __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); 107 | char *SHA384_File(const char *, char *) 108 | __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); 109 | char *SHA384_FileChunk(const char *, char *, off_t, off_t) 110 | __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); 111 | char *SHA384_Data(const u_int8_t *, size_t, char *) 112 | __attribute__((__bounded__(__string__,1,2))) 113 | __attribute__((__bounded__(__minbytes__,3,SHA384_DIGEST_STRING_LENGTH))); 114 | 115 | void SHA512_Init(SHA512_CTX *); 116 | void SHA512_Transform(u_int64_t state[8], const u_int8_t [SHA512_BLOCK_LENGTH]); 117 | void SHA512_Update(SHA512_CTX *, const u_int8_t *, size_t) 118 | __attribute__((__bounded__(__string__,2,3))); 119 | void SHA512_Pad(SHA512_CTX *); 120 | void SHA512_Final(u_int8_t [SHA512_DIGEST_LENGTH], SHA512_CTX *) 121 | __attribute__((__bounded__(__minbytes__,1,SHA512_DIGEST_LENGTH))); 122 | char *SHA512_End(SHA512_CTX *, char *) 123 | __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); 124 | char *SHA512_File(const char *, char *) 125 | __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); 126 | char *SHA512_FileChunk(const char *, char *, off_t, off_t) 127 | __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); 128 | char *SHA512_Data(const u_int8_t *, size_t, char *) 129 | __attribute__((__bounded__(__string__,1,2))) 130 | __attribute__((__bounded__(__minbytes__,3,SHA512_DIGEST_STRING_LENGTH))); 131 | 132 | #endif /* defined(_NEED_SHA2) && !defined(HAVE_SHA256_UPDATE) */ 133 | 134 | #endif /* _SSHSHA2_H */ 135 | --------------------------------------------------------------------------------