Initial ES modules support
This commit is contained in:
+3
-1
@@ -8,7 +8,9 @@
|
||||
// - Holes are not skipped: a callback sees undefined for a hole
|
||||
|
||||
// `Array(n)` and `new Array(n)` make an array of `n` holes
|
||||
function Array(n) {
|
||||
import { relativeIndex } from "jsrt:string";
|
||||
|
||||
export function Array(n) {
|
||||
const a = [];
|
||||
if (n !== undefined) a.length = n;
|
||||
return a;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Converts any value to a boolean, like the spec's ToBoolean
|
||||
function Boolean(x) {
|
||||
export function Boolean(x) {
|
||||
return !!x;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Not as the spec says: it prints its first arg only, because more args need
|
||||
// rest params, which need arrays
|
||||
const console = {
|
||||
import { __native_print_string } from "jsrt:native";
|
||||
|
||||
export const console = {
|
||||
log(x) {
|
||||
// As node does: `-0` prints as `-0`, though String(-0) is "0"
|
||||
const negativeZero = typeof x === "number" && x === 0 && 1 / x < 0;
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
// Not supported yet: `throw()` on a generator, and `finally` blocks in
|
||||
// `return()`, which need exceptions
|
||||
|
||||
import { __is_promise, __reject, __resolve } from "jsrt:promise";
|
||||
|
||||
// A generator's object is `co` itself
|
||||
const __GeneratorProto = {
|
||||
export const __GeneratorProto = {
|
||||
// The body runs up to its `initial_yield`, after its params
|
||||
start() {
|
||||
this.step(undefined);
|
||||
@@ -39,7 +41,7 @@ const __GeneratorProto = {
|
||||
// An async fn returns a promise, which its return fulfils. A rejected await
|
||||
// rejects it, and the rest of the body does not run, as an uncaught throw at the
|
||||
// await would
|
||||
const __AsyncProto = {
|
||||
export const __AsyncProto = {
|
||||
// The body runs up to its first await, in the call
|
||||
start() {
|
||||
const promise = new Promise(undefined);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Runs after the program's top level: its microtasks. The host then runs
|
||||
// the event loop (see loop.js)
|
||||
__drain_microtasks();
|
||||
+6
-19
@@ -1,20 +1,7 @@
|
||||
// The stdlib, compiled in front of every program. Its files are joined in the
|
||||
// order of `STDLIB` in compiler/src/main.rs, and this one is first
|
||||
//
|
||||
// Built on the natives in compiler/src/native.rs, the common layer of
|
||||
// operations on primitive values. The compiler's own code for operators (`+` on
|
||||
// strings and so on) uses those natives directly, not these functions.
|
||||
//
|
||||
// Restrictions:
|
||||
// - The prototypes of `String`, `Number`, `Boolean` and `Array` are fixed:
|
||||
// the compiler reads them by name, as the prototypes of the primitives and of
|
||||
// arrays. User code may not change them (no monkey-patching)
|
||||
// - `new String(x)`, `new Number(x)` and `new Boolean(x)` (wrapper objects) are
|
||||
// not supported. These functions only convert
|
||||
//
|
||||
// Needs `typeof`. `String.prototype.concat` is not here, because it needs rest
|
||||
// params, which need arrays. `console.log` prints one arg only, for the same
|
||||
// reason
|
||||
// The global values that are not functions. `undefined` is a binding like any
|
||||
// other: a global name refers to the prelude's export of that name (see
|
||||
// prelude.js)
|
||||
|
||||
const NaN = 0 / 0;
|
||||
const Infinity = 1 / 0;
|
||||
export const undefined = void 0;
|
||||
export const NaN = 0 / 0;
|
||||
export const Infinity = 1 / 0;
|
||||
|
||||
+13
-1
@@ -5,6 +5,18 @@
|
||||
// A failure rejects with the negative errno, not an `Error`. A read or a write
|
||||
// that the kernel does not finish in one op is not continued
|
||||
|
||||
import {
|
||||
__native_io_queue_close,
|
||||
__native_io_queue_open,
|
||||
__native_io_queue_read,
|
||||
__native_io_queue_stat,
|
||||
__native_io_queue_write,
|
||||
__native_io_result_number,
|
||||
__native_io_result_string,
|
||||
__native_io_status,
|
||||
} from "jsrt:native";
|
||||
import { __pending } from "jsrt:loop";
|
||||
|
||||
// A promise of what op `h` gives when it completes: its status, its data, or
|
||||
// a number. A result can only be read before the next wait, so these read it
|
||||
// when the op completes
|
||||
@@ -39,7 +51,7 @@ function __io_ok(h) {
|
||||
return __io_status(h).then((status) => (status < 0 ? Promise.reject(status) : status));
|
||||
}
|
||||
|
||||
const fs = {
|
||||
export const fs = {
|
||||
readFile(path) {
|
||||
let fd = -1;
|
||||
let text = "";
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
// The event loop's JS side. The host owns the loop (see lib/rt/jsrt.h). A
|
||||
// macrotask is an IO op or a timer, which the host runs: it calls
|
||||
// `__complete(h)` when op `h` is done. A microtask is a promise job, in
|
||||
// `__microtasks`. After the program's top level, end.js runs all microtasks,
|
||||
// and after each macrotask, `__complete` does
|
||||
// `__microtasks`. After every module's body, the entry runs all microtasks
|
||||
// (see compiler/src/link.rs), and after each macrotask, `__complete` does
|
||||
|
||||
// What to do when IO op `h` completes: `__pending[h]`, a fn that gets `h`.
|
||||
// Handles are small ints, and a handle is used again after its op completes
|
||||
const __pending = [];
|
||||
export const __pending = [];
|
||||
|
||||
// Promise jobs, in order. `head` is the next one, so that taking a job does
|
||||
// not move the others. A job is a closure, which keeps its own data: the
|
||||
@@ -14,11 +14,11 @@ const __pending = [];
|
||||
// values of its own promise (see `docs/model.md`, section 3.3)
|
||||
const __microtasks = { jobs: [], head: 0 };
|
||||
|
||||
function __queue_job(job) {
|
||||
export function __queue_job(job) {
|
||||
__microtasks.jobs.push(job);
|
||||
}
|
||||
|
||||
function __drain_microtasks() {
|
||||
export function __drain_microtasks() {
|
||||
const q = __microtasks;
|
||||
|
||||
while (q.head < q.jobs.length) {
|
||||
@@ -33,8 +33,8 @@ function __drain_microtasks() {
|
||||
}
|
||||
|
||||
// What the host calls when op `h` is done: its callback, then the microtasks.
|
||||
// The compiler finds it by name
|
||||
function __complete(h) {
|
||||
// The compiler finds it as this module's export
|
||||
export function __complete(h) {
|
||||
const done = __pending[h];
|
||||
__pending[h] = undefined;
|
||||
if (done !== undefined) done(h);
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import {
|
||||
__rt_number_to_fixed,
|
||||
__rt_number_to_string,
|
||||
__rt_string_concat,
|
||||
__rt_string_from_char_code,
|
||||
__rt_string_to_number,
|
||||
} from "jsrt:native";
|
||||
|
||||
// Converts any value to a number, like the spec's ToNumber
|
||||
function Number(x) {
|
||||
export function Number(x) {
|
||||
switch (typeof x) {
|
||||
case "number": return x;
|
||||
case "string": return __rt_string_to_number(x);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// `new Object()` makes an empty object. Called without `new`, it does the same
|
||||
function Object() {
|
||||
export function Object() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// The prelude: its exports are the global names of every module, such as
|
||||
// `String` or `console`. A module that does not declare a name, and does not
|
||||
// import it, refers to the prelude's export of that name. A module can declare
|
||||
// a binding of the same name, which hides it (see compiler/src/link.rs)
|
||||
//
|
||||
// The stdlib is built on the natives in compiler/src/native.rs, the common
|
||||
// layer of operations on primitive values, which `jsrt:native` exports. The
|
||||
// compiler's own code for operators (`+` on strings and so on) uses the runtime
|
||||
// directly, not these functions.
|
||||
//
|
||||
// Restrictions:
|
||||
// - The prototypes of `String`, `Number`, `Boolean` and `Array` are fixed:
|
||||
// the compiler reads them as the prelude's exports, as the prototypes of the
|
||||
// primitives and of arrays. User code may not change them (no monkey-patching)
|
||||
// - `new String(x)`, `new Number(x)` and `new Boolean(x)` (wrapper objects) are
|
||||
// not supported. These functions only convert
|
||||
// - A global name cannot be assigned to
|
||||
//
|
||||
// `String.prototype.concat` is not here, because it needs rest params, which
|
||||
// need arrays. `console.log` prints one arg only, for the same reason
|
||||
//
|
||||
// The natives are global names too, as the runtime's own fns, with `__pending`,
|
||||
// where an IO op's callback waits for the host (see loop.js). The compiler
|
||||
// needs `jsrt:coroutine` for async fns and generators, so the prelude loads it.
|
||||
// It finds the host's `__complete` in `jsrt:loop`
|
||||
//
|
||||
// The modules run in the order of these imports, before any other module
|
||||
|
||||
export { undefined, NaN, Infinity } from "jsrt:globals";
|
||||
export { Object } from "jsrt:object";
|
||||
export { String } from "jsrt:string";
|
||||
export { Number } from "jsrt:number";
|
||||
export { Boolean } from "jsrt:boolean";
|
||||
export { Array } from "jsrt:array";
|
||||
export { Promise, queueMicrotask } from "jsrt:promise";
|
||||
export { setTimeout, clearTimeout } from "jsrt:timers";
|
||||
export { fs } from "jsrt:io";
|
||||
export { console } from "jsrt:console";
|
||||
export * from "jsrt:native";
|
||||
export { __pending } from "jsrt:loop";
|
||||
|
||||
import "jsrt:coroutine";
|
||||
@@ -15,12 +15,14 @@
|
||||
// - A fn that makes a closure keeps out of the common path: a captured arg is
|
||||
// in a box, which every call from one place shares
|
||||
|
||||
import { __queue_job } from "jsrt:loop";
|
||||
|
||||
// The states of a promise
|
||||
const __PENDING = 0;
|
||||
const __FULFILLED = 1;
|
||||
const __REJECTED = 2;
|
||||
|
||||
function Promise(executor) {
|
||||
export function Promise(executor) {
|
||||
const p = { __proto__: this.__proto__, state: __PENDING, value: undefined, reason: undefined, resolved: false, reactions: [] };
|
||||
|
||||
if (executor !== undefined) {
|
||||
@@ -122,19 +124,19 @@ Promise.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function __is_promise(x) {
|
||||
export function __is_promise(x) {
|
||||
return typeof x === "object" && x !== null && x.reactions !== undefined;
|
||||
}
|
||||
|
||||
// Resolve `p` with `value`, unless it was resolved or rejected before
|
||||
function __resolve(p, value) {
|
||||
export function __resolve(p, value) {
|
||||
if (p.resolved) return;
|
||||
p.resolved = true;
|
||||
__fulfil_or_adopt(p, value);
|
||||
}
|
||||
|
||||
// Reject `p` with `reason`, unless it was resolved or rejected before
|
||||
function __reject(p, reason) {
|
||||
export function __reject(p, reason) {
|
||||
if (p.resolved) return;
|
||||
p.resolved = true;
|
||||
__settle_rejected(p, reason);
|
||||
@@ -194,6 +196,6 @@ function __adopt(p, thenable) {
|
||||
);
|
||||
}
|
||||
|
||||
function queueMicrotask(f) {
|
||||
export function queueMicrotask(f) {
|
||||
__queue_job(f);
|
||||
}
|
||||
|
||||
+10
-2
@@ -1,5 +1,13 @@
|
||||
import {
|
||||
__rt_number_to_string,
|
||||
__rt_string_char_code_at,
|
||||
__rt_string_concat,
|
||||
__rt_string_from_char_code,
|
||||
__rt_string_slice,
|
||||
} from "jsrt:native";
|
||||
|
||||
// Converts any value to a string, like the spec's ToString
|
||||
function String(x) {
|
||||
export function String(x) {
|
||||
switch (typeof x) {
|
||||
case "string": return x;
|
||||
case "number": return __rt_number_to_string(x);
|
||||
@@ -109,7 +117,7 @@ function clampIndex(i, len) {
|
||||
}
|
||||
|
||||
// An index where a negative value counts from the end, clamped to [0, len]
|
||||
function relativeIndex(i, len) {
|
||||
export function relativeIndex(i, len) {
|
||||
return clampIndex(i < 0 ? i + len : i, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
//
|
||||
// Not as the spec says: `setTimeout` takes no args for the callback
|
||||
|
||||
function setTimeout(f, ms) {
|
||||
import { __native_io_queue_cancel, __native_io_queue_timeout } from "jsrt:native";
|
||||
import { __pending } from "jsrt:loop";
|
||||
|
||||
export function setTimeout(f, ms) {
|
||||
// As in node, a delay below 1 ms is 1 ms
|
||||
const delay = ms === undefined || !(ms >= 1) ? 1 : ms;
|
||||
const timer = { handle: __native_io_queue_timeout(delay), active: true };
|
||||
@@ -18,7 +21,7 @@ function setTimeout(f, ms) {
|
||||
return timer;
|
||||
}
|
||||
|
||||
function clearTimeout(timer) {
|
||||
export function clearTimeout(timer) {
|
||||
if (timer === undefined || timer === null || !timer.active) return;
|
||||
timer.active = false;
|
||||
__native_io_queue_cancel(timer.handle);
|
||||
|
||||
Reference in New Issue
Block a user