Move argv, environ to 'main'. Move getchar, putchar, getbuffer, putbuffer, print to 'stdio'.

parent 88baba0c
......@@ -26,7 +26,7 @@ $(MAKEHEADERS): makeheaders.c
gcc makeheaders.c -o makeheaders
%.h: %.c
./makeheaders src/*.c
./makeheaders src/*.c src/structs/*.h
%.o: %.c %.h
$(CC) $< -c -o [email protected] $(CFLAGS)
......
......@@ -104,7 +104,7 @@ you have it. Then we just need to write an ethernet driver in assembly. ;)
- [x] Networking! I've got a very basic networking library added that appears to work so far.
# API v0
# API v0.1
My goal is to have the smallest set of orthogonal system primitives.
In that sense, the API is very UNIX-y. I try to use familiar terminology where
......@@ -116,6 +116,18 @@ For `dukboot`, it really just serves as a way for you to hook your JavaScript
to the software & hardware interupts so you can control the whole thing in a
JS environment.
The main difference between API v0.1 and the previous API v0.0 is I've decided
to reorganize everything to be consistant. Before there was a mix of objects,
arrays, numbers, and functions. Now there are just functions inside objects.
This has two advantages: you don't have to remember whether something is a
function or not (it always is!) and we can make tasks such as parsing environment
variables or command line arguments *lazy* meaning if you never use them, they're
never run. This makes it easier to build the implementation, because we can
more easily stub functions (i.e. `function () { throw Error "Not Implemented Yet" }`)
than we can stub arrays or numbers or strings. Also, functions are the most flexible
in terms of gracefully extending the API (e.g. adding parameters in the future)
in a backwards compatible fashion.
## OS integration
Dukboot currently only runs hosted in another operating system, much like Node.
......@@ -123,31 +135,45 @@ Therefore, in these hosted environments it has the traditional garbage that come
with being a Linux / Windows / MacOS process. I provide access to these features,
not because I endorse them, but because they are unavoidable on these platforms.
### argv : [ String ]
The "os" module provides access to those features that are implemented by the
host operating system, that probably *wouldn't* be implemented in a standalone
dukboot unikernel.
### main.argv() => [ String ]
Command line arguments! It's an array of strings, because while you might be under the
impression that the command line arguments should be one big string, shells
actually do split the argument string into tokens and pass each individual token.
It is unprocessed, meaning that argv[0] should be the name used to execute
dukboot, and argv[1] is probably the name of your JavaScript file, so your "first"
argument is actually argv[2] - but that's just how Operating Systems work, sorry.
It is unprocessed, meaning that `main.argv()[0]` should be the name used to execute
dukboot, and `main.argv()[1]` is probably the name of your JavaScript file, so your "first"
argument is actually `main.argv()[2]` - but that's just how Operating Systems work, sorry.
The first thing you should do with argv is pass it to a parser library like [`minimist`](https://github.com/substack/minimist)
to make it less ridiculous to work with.
### environ : { String : String }
### main.environ() => { String : String }
Environment Variables! Those magical invisible global variables that hang around your
process like a ghost, subtly influencing branching and making it harder to figure
out why it works on my machine but not on yours. They're available in the `environ`
object. The keys match the environment variable names and the values match the
environment variable values. For now it is read only, because the only time
out why it works on my machine but not on yours. They're available in the object
returned by `main.environ()`. The keys match the environment variable names and the values match the
environment variable values. For now the environment is read only, because the only time
you should be changing them is if/when you spawn a new process. All keys and values
are strings.
### print
This is just here temporarily until I add "proper" support for `stdout` or a proper `console.log` implementation.
### getchar : () => Number | Error EOF | undefined
This is a sweet and simple little function, and for now it's the only way to get at
the standard input stream (*stdin* in POSIX parlance). Working with binary input
are strings, because the creators of UNIX did not bless us with a type system.
### stdio.print( String ) => void
This does what it's name suggests, but it was always a hack and is now deprecated.
To print strings to the standard output, write a wrapper around `putbuffer`.
I'll probably provide a decent wrapper (`putstring`? how does that sound?) or
recommend a fancy `printf` module in the near future. I should note here that
I consider "logging" to have a distinctly different semantic that standard out,
and plan to provide MUCH better facilities for logging built into `dukboot` than
most languages have had hereforto. I want something like the [`debug`](npm.im/debug)
package cross-bred with Chrome DevTools with remote logging built in. Logs are
important, and could and should be invaluable tools for analyzing systems in
production, which is often not the case due to subpar native logging facilities.
### stdio.getchar : () => Number | Error EOF | undefined
This is a sweet and simple little function, and ~~for now it's the only way to get at
the standard input stream (*stdin* in POSIX parlance)~~ you can use it instead of
getbuffer when you only want to get one byte at a time. Working with binary input
is easier because it returns raw numbers rather than encoding them as text. Since
I mostly forsee *stdin* being used as an interface between programs I think
encouraging a binary protocol (like HTTP2 or Protobuf) makes sense here. String
......@@ -190,13 +216,13 @@ process incoming data, and send it to...
### stdout : pull-stream Sink
### putchar : (Number) => Number | Error EOF | undefined
### stdio.putchar : (Number) => Number | Error EOF | undefined
Writes a single byte to `stdout`. Be fancy and write your numbers in hex! See examples/stdin/stdout-unbuffered.js
Should return the same number you gave it, the EOF error if stdout got closed (untested),
undefined if non-blocking write is supported and the output was temporarily unavailable (untested),
or throw an error from the underlying C layer.
### putbuffer : (Uint8Array) => Number | Error EOF | undefined
### stdio.putbuffer(Uint8Array) => Number | Error EOF | undefined
Unfortunately `putchar` pays for its simplicity by sacrificing speed.
Switching from the JS layer to the C layer and back again for every byte can
negatively impact performance if you have a lot of bytes to write.
......@@ -208,7 +234,7 @@ See the examples/stdin/stdout-buffered.js which runs 36x faster than the unbuffe
Returns the number of bytes written, which is equal to the size of the buffer.
I might add support for start / end indices.
### getbuffer : (Uint8Array) => Number | Error EOF | undefined
### stdio.getbuffer(Uint8Array) => Number | Error EOF | undefined
I just added this for symmetry with putbuffer. I imagine it has
similar performance benefits over reading bytes one at a time, but I haven't
tested that yet. See examples/stdin/stdin-buffered.js
......
......@@ -24,7 +24,7 @@ function (args) {
}
// modules.webserver();
var print = modules.print
var print = stdio.print
var netLib = modules.netLib
var port = 8080;
......
......@@ -7,7 +7,7 @@ function (args) {
var compile = modules.compile;
// modules.webserver();
var print = modules.print
var print = args.stdio.print
var netLib = modules.netLib
var port = 8080;
......
function (o) {
o.modules.print(o.OpenMP.id + '/' + o.OpenMP.numThreads)
o.stdio.print(o.OpenMP.id + '/' + o.OpenMP.numThreads)
}
......@@ -4,8 +4,8 @@ import pull from 'pull-stream'
if (imports.OpenMP.id === 0) {
var c = pull.count()
c(null, imports.modules.print)
c(null, imports.modules.print)
c(null, imports.modules.print)
c(null, imports.modules.print)
c(null, imports.stdio.print)
c(null, imports.stdio.print)
c(null, imports.stdio.print)
c(null, imports.stdio.print)
}
......@@ -3,8 +3,8 @@ function (o) {
// Results will (probably) be printed out of order.
var c;
while (true) {
c = o.modules.getchar()
c = o.stdio.getchar()
if (c instanceof Error) break
o.modules.print(o.OpenMP.id + ': ' + c)
o.stdio.print(o.OpenMP.id + ': ' + c)
}
}
......@@ -7,7 +7,7 @@ function (o) {
function read (end, cb) {
if (end) return cb(end)
var c = o.modules.getchar()
var c = o.stdio.getchar()
if (c === undefined) return schedule([read, end, cb])
if (c instanceof Error) return cb(c)
return schedule([cb, null, c])
......@@ -28,19 +28,19 @@ function (o) {
read(null, function next(end, data) {
if (end == true) return
if (end) throw end
o.modules.print(String.fromCharCode(data))
o.stdio.print(String.fromCharCode(data))
// schedule([read, null, next])
read(null, next)
})
var i = 0;
try {
while (true) {
o.modules.print("Tick count: " + i)
o.stdio.print("Tick count: " + i)
tick()
i++
}
} catch (e) {
o.modules.print(e)
o.stdio.print(e)
}
}
}
......@@ -4,8 +4,8 @@ function (o) {
if (o.OpenMP.id === 0) {
var c;
while (true) {
c = o.modules.getchar()
o.modules.print(o.OpenMP.id + ': ' + c)
c = o.stdio.getchar()
o.stdio.print(o.OpenMP.id + ': ' + c)
if (c instanceof Error) break
o.modules.sleep(100)
}
......
......@@ -6,12 +6,12 @@ function (o) {
var c
var buffer = new Uint8Array(8);
while (true) {
c = o.modules.getbuffer(buffer)
c = o.stdio.getbuffer(buffer)
if (c instanceof Error) break
if (c === undefined) o.modules.sleep(100)
if (c > 0) {
o.modules.print(o.OpenMP.id + ': read ' + c + ' bytes')
o.modules.print(UTF8decoder.decode(buffer.subarray(0, c)))
o.stdio.print(o.OpenMP.id + ': read ' + c + ' bytes')
o.stdio.print(UTF8decoder.decode(buffer.subarray(0, c)))
}
}
}
......
......@@ -22,7 +22,7 @@ function (o) {
for (var n = 0; n < 10000; n++) {
chars.push(i)
}
o.modules.putbuffer(new Uint8Array(chars))
o.stdio.putbuffer(new Uint8Array(chars))
chars = []
}
}
......
......@@ -6,7 +6,7 @@ function (o) {
var c;
for (var i = 0x20; i < 0x7E; i++) {
for (var n = 0; n < 10000; n++) {
o.modules.putchar(i)
o.stdio.putchar(i)
}
}
}
......
......@@ -5,10 +5,10 @@ function (o) {
var c;
var chars = [];
while (true) {
c = o.modules.getchar()
c = o.stdio.getchar()
if (c !== undefined) chars.push(c)
if (c instanceof Error) break
}
o.modules.print(new TextDecoder().decode(new Uint8Array(chars)))
o.stdio.print(new TextDecoder().decode(new Uint8Array(chars)))
}
}
......@@ -7,7 +7,7 @@ function (o) {
function read (end, cb) {
if (end) return cb(end)
var c = o.modules.getchar()
var c = o.stdio.getchar()
if (c === undefined) return schedule([read, end, cb])
if (c instanceof Error) return cb(c)
return schedule([cb, null, c])
......@@ -29,19 +29,19 @@ function (o) {
var chars = []
readUnicode(null, function next(end, char) {
if (end) throw end
o.modules.print(char)
o.stdio.print(char)
schedule([readUnicode, null, next])
// readUnicode(null, next)
})
var i = 0;
try {
while (true) {
o.modules.print("Tick count: " + i)
o.stdio.print("Tick count: " + i)
tick()
i++
}
} catch (e) {
o.modules.print(e)
o.stdio.print(e)
}
}
}
function (modules) {
function (imports) {
if (typeof global === 'undefined') {
(function () {
var global = new Function('return this;')();
......@@ -12,8 +12,8 @@ function (modules) {
}
// A stupid way to make dependencies available, that will
// probably necessary to be compatible with ye olde node.
global.dir = modules.dir
var testme = modules.compile('subexample/grandchild.js');
modules.print(JSON.stringify(testme(3, 4)));
global.dir = imports.modules.dir
var testme = imports.modules.compile('subexample/grandchild.js');
imports.stdio.print(JSON.stringify(testme(3, 4)));
return
}
function (args) {
var env = args.environ;
var argv = args.argv;
var modules = args.modules;
var Queues = args.Queues;
function (imports) {
var env = imports.main.environ();
var argv = imports.main.argv();
var modules = imports.modules;
var Queues = imports.Queues;
var timers = Queues.timers;
var testme = modules.compile('child.js');
testme(modules);
// modules.print(Object.keys(args.modules));
// modules.print(args.Queues.timers.length);
testme(imports);
return
}
function (args) {
var env = args.environ;
var argv = args.argv;
var modules = args.modules;
var Queues = args.Queues;
function (imports) {
var env = imports.main.environ();
var argv = imports.main.argv();
var modules = imports.modules;
var Queues = imports.Queues;
var timers = Queues.timers;
var compile = modules.compile;
var printTime = compile('printTime.js')(modules.print);
var compile = imports.modules.compile;
var printTime = compile('printTime.js')(imports.stdio.print);
var orderedInsert = compile('orderedInsert.js')();
var byMstimestampAscending = function (a, b) {
return a.t - b.t;
......
#include "duktape/duktape.h"
#include "app_push_argv.h"
int app_push_argv(duk_context *ctx, int argc, char *argv[]) {
#include "c_argv.h"
int c_argv(duk_context *ctx) {
// Construct argv array
duk_idx_t argv_idx = duk_push_array(ctx);
int i = 0;
while (i < argc && i < 100) {
duk_push_string(ctx, argv[i]);
while (i < mainArgs.argc && i < 100) {
duk_push_string(ctx, mainArgs.argv[i]);
duk_put_prop_index(ctx, argv_idx, i);
i++;
}
return 0;
return 1;
}
#include "duktape/duktape.h"
#include "app_push_environ.h"
int app_push_environ(duk_context *ctx) {
#include "c_environ.h"
int c_environ(duk_context *ctx) {
int keylen = 0;
char *val;
char *equalsign;
......@@ -20,5 +20,5 @@ int app_push_environ(duk_context *ctx) {
duk_put_prop_lstring(ctx, environ_idx, environ[i], keylen);
i++;
}
return 0;
return 1;
}
......@@ -3,7 +3,7 @@
#include <string.h>
#include "duktape/duktape.h"
#include "c_getbuffer.h"
duk_size_t c_getbuffer (duk_context *ctx) {
int c_getbuffer (duk_context *ctx) {
char * buffer;
duk_size_t length;
buffer = duk_require_buffer_data(ctx, 0, &length);
......
......@@ -3,7 +3,7 @@
#include <string.h>
#include "duktape/duktape.h"
#include "c_putbuffer.h"
duk_size_t c_putbuffer (duk_context *ctx) {
int c_putbuffer (duk_context *ctx) {
char * buffer;
duk_size_t length;
buffer = duk_require_buffer_data(ctx, 0, &length);
......
......@@ -15,7 +15,13 @@ extern char **environ;
#include "duktape/duktape.h"
#include "main.h"
mainArgs_t mainArgs;
int main(int argc, char *argv[] /* char *environ[] */) {
mainArgs.argc = argc;
mainArgs.argv = argv;
// mainArgs.environ = environ;
// Re-open stdin and stdout in binary mode
freopen(NULL, "rb", stdin);
freopen(NULL, "wb", stdout);
......@@ -70,6 +76,7 @@ int main(int argc, char *argv[] /* char *environ[] */) {
}
// Construct the prime argument
// OpenMP
duk_push_object(ctx);
duk_push_object(ctx);
duk_push_int(ctx, omp_get_thread_num());
......@@ -77,18 +84,30 @@ int main(int argc, char *argv[] /* char *environ[] */) {
duk_push_int(ctx, omp_get_num_threads());
duk_put_prop_string(ctx, -2, "numThreads");
duk_put_prop_string(ctx, -2, "OpenMP");
// Create argv object
app_push_argv(ctx, argc, argv);
// Provide access to the environment
duk_push_object(ctx);
duk_push_c_function(ctx, c_argv, 0);
duk_put_prop_string(ctx, -2, "argv");
// Create environment variables object
app_push_environ(ctx);
duk_push_c_function(ctx, c_environ, 0);
duk_put_prop_string(ctx, -2, "environ");
duk_put_prop_string(ctx, -2, "main");
// Provide access to stdin and stdout
duk_push_object(ctx);
duk_push_c_function(ctx, native_print, DUK_VARARGS);
duk_put_prop_string(ctx, -2, "print");
duk_push_c_function(ctx, c_getchar, 0);
duk_put_prop_string(ctx, -2, "getchar");
duk_push_c_function(ctx, c_putchar, 1);
duk_put_prop_string(ctx, -2, "putchar");
duk_push_c_function(ctx, c_getbuffer, 1);
duk_put_prop_string(ctx, -2, "getbuffer");
duk_push_c_function(ctx, c_putbuffer, 1);
duk_put_prop_string(ctx, -2, "putbuffer");
duk_put_prop_string(ctx, -2, "stdio");
// Provide access to native functions
duk_push_object(ctx);
duk_push_c_function(ctx, app_push_dir, 1);
duk_put_prop_string(ctx, -2, "dir");
duk_push_c_function(ctx, native_print, DUK_VARARGS);
duk_put_prop_string(ctx, -2, "print");
app_unstash_global_string(ctx, "Duktape"); // summon from the magic stash
duk_put_prop_string(ctx, -2, "Duktape");
duk_push_c_function(ctx, app_push_trusted_function, 1);
......@@ -97,14 +116,6 @@ int main(int argc, char *argv[] /* char *environ[] */) {
duk_put_prop_string(ctx, -2, "assert_safe_path");
duk_push_c_function(ctx, native_sleep, 1);
duk_put_prop_string(ctx, -2, "sleep");
duk_push_c_function(ctx, c_getchar, 0);
duk_put_prop_string(ctx, -2, "getchar");
duk_push_c_function(ctx, c_putchar, 1);
duk_put_prop_string(ctx, -2, "putchar");
duk_push_c_function(ctx, c_getbuffer, 1);
duk_put_prop_string(ctx, -2, "getbuffer");
duk_push_c_function(ctx, c_putbuffer, 1);
duk_put_prop_string(ctx, -2, "putbuffer");
app_push_netLib(ctx);
duk_put_prop_string(ctx, -2, "netLib");
duk_put_prop_string(ctx, -2, "modules");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment