Rewrite with working async

This commit is contained in:
2026-10-16 08:00:06 +00:00
parent d0c2f291e9
commit 1e4647347f
36 changed files with 5106 additions and 654 deletions
-2
View File
@@ -149,5 +149,3 @@ Array.prototype = {
return true;
},
};
__link_proto_array(Array.prototype);
-2
View File
@@ -8,5 +8,3 @@ Boolean.prototype = {
toString() { return this ? "true" : "false"; },
valueOf() { return this; },
};
__link_proto_boolean(Boolean.prototype);
+3 -1
View File
@@ -2,6 +2,8 @@
// rest params, which need arrays
const console = {
log(x) {
__native_print_string(String(x));
// As node does: `-0` prints as `-0`, though String(-0) is "0"
const negativeZero = typeof x === "number" && x === 0 && 1 / x < 0;
__native_print_string(negativeZero ? "-0" : String(x));
},
};
+62
View File
@@ -0,0 +1,62 @@
// The drivers of async fns and generators
//
// The compiler lowers each async fn and generator to a coroutine (see
// compiler/src/transform/async.rs): a step closure, which runs the body until
// it suspends or returns, and an object `co`:
//
// { __proto__: <a proto below>, step, value, awaiting, result, done }
//
// A yield puts its value in `value`, an await puts its promise in `awaiting`,
// and a return puts its value in `result` and sets `done`. The fn returns
// `co.start()`. Only these drivers differ between the kinds
//
// Not supported yet: `throw()` on a generator, and `finally` blocks in
// `return()`, which need exceptions
// A generator's object is `co` itself
const __GeneratorProto = {
// The body runs up to its `initial_yield`, after its params
start() {
this.step(undefined);
return this;
},
next(input) {
if (this.done) return { value: undefined, done: true };
this.step(input);
if (this.done) return { value: this.result, done: true };
return { value: this.value, done: false };
},
return(value) {
this.done = true;
return { value: value, done: true };
},
};
// 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 = {
// The body runs up to its first await, in the call
start() {
const promise = new Promise(undefined);
this.run(promise);
return promise;
},
run(promise) {
this.step(undefined);
if (this.done) __resolve(promise, this.result);
else this.awaiting.then(() => this.run(promise), (reason) => __reject(promise, reason));
},
// What an await waits on: a promise as it is, any other value as a fulfilled one.
// The step reads the value from it when it resumes
toPromise(x) {
return __is_promise(x) ? x : Promise.resolve(x);
},
};
+3 -2
View File
@@ -6,8 +6,9 @@
// strings and so on) uses those natives directly, not these functions.
//
// Restrictions:
// - The prototypes linked with `__link_proto_*` are fixed. User code may not
// change them (no monkey-patching)
// - 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
//
+2 -3
View File
@@ -32,12 +32,11 @@ function __drain_microtasks() {
q.head = 0;
}
// What the host calls when op `h` is done: its callback, then the microtasks
// What the host calls when op `h` is done: its callback, then the microtasks.
// The compiler finds it by name
function __complete(h) {
const done = __pending[h];
__pending[h] = undefined;
if (done !== undefined) done(h);
__drain_microtasks();
}
__link_host_complete(__complete);
+5 -7
View File
@@ -2,7 +2,7 @@
function Number(x) {
switch (typeof x) {
case "number": return x;
case "string": return __native_string_to_number(x);
case "string": return __rt_string_to_number(x);
case "boolean": return x ? 1 : 0;
case "undefined": return NaN;
case "object": return x === null ? 0 : NaN;
@@ -23,15 +23,13 @@ Number.isInteger = function (x) { return Number.isFinite(x) && x % 1 === 0; };
// In these methods `this` is the primitive number itself
Number.prototype = {
toString(radix) {
if (radix === undefined || radix === 10) return __native_number_to_string(this);
if (radix === undefined || radix === 10) return __rt_number_to_string(this);
return integerToString(this, radix);
},
toFixed(digits) { return __native_number_to_fixed(this, digits === undefined ? 0 : digits); },
toFixed(digits) { return __rt_number_to_fixed(this, digits === undefined ? 0 : digits); },
valueOf() { return this; },
};
__link_proto_number(Number.prototype);
// An integer in `radix` (2 to 36). Only integers: the fraction is dropped
function integerToString(n, radix) {
let negative = n < 0;
@@ -41,9 +39,9 @@ function integerToString(n, radix) {
let out = "";
do {
let digit = n % radix;
out = __native_string_concat(__native_string_from_char_code(digit < 10 ? 48 + digit : 87 + digit), out);
out = __rt_string_concat(__rt_string_from_char_code(digit < 10 ? 48 + digit : 87 + digit), out);
n = (n - digit) / radix;
} while (n > 0);
return negative ? __native_string_concat("-", out) : out;
return negative ? __rt_string_concat("-", out) : out;
}
+21 -23
View File
@@ -2,7 +2,7 @@
function String(x) {
switch (typeof x) {
case "string": return x;
case "number": return __native_number_to_string(x);
case "number": return __rt_number_to_string(x);
case "boolean": return x ? "true" : "false";
case "undefined": return "undefined";
case "function": return "function () { [native code] }";
@@ -14,7 +14,7 @@ function String(x) {
}
String.fromCharCode = function (code) {
return __native_string_from_char_code(code);
return __rt_string_from_char_code(code);
};
// In these methods `this` is the primitive string itself
@@ -25,41 +25,41 @@ String.prototype = {
// ── Characters ──
charAt(i) {
if (i < 0 || i >= this.length) return "";
return __native_string_slice(this, i, i + 1);
return __rt_string_slice(this, i, i + 1);
},
charCodeAt(i) {
if (i < 0 || i >= this.length) return NaN;
return __native_string_char_code_at(this, i);
return __rt_string_char_code_at(this, i);
},
at(i) {
if (i < 0) i += this.length;
if (i < 0 || i >= this.length) return undefined;
return __native_string_slice(this, i, i + 1);
return __rt_string_slice(this, i, i + 1);
},
// ── Search ──
indexOf(search, from) {
let start = from === undefined ? 0 : clampIndex(from, this.length);
for (let i = start; i + search.length <= this.length; i++) {
if (__native_string_slice(this, i, i + search.length) === search) return i;
if (__rt_string_slice(this, i, i + search.length) === search) return i;
}
return -1;
},
lastIndexOf(search) {
for (let i = this.length - search.length; i >= 0; i--) {
if (__native_string_slice(this, i, i + search.length) === search) return i;
if (__rt_string_slice(this, i, i + search.length) === search) return i;
}
return -1;
},
includes(search, from) { return this.indexOf(search, from) !== -1; },
startsWith(search, from) {
let start = from === undefined ? 0 : clampIndex(from, this.length);
return __native_string_slice(this, start, start + search.length) === search;
return __rt_string_slice(this, start, start + search.length) === search;
},
endsWith(search, end) {
let stop = end === undefined ? this.length : clampIndex(end, this.length);
let start = stop - search.length;
return start >= 0 && __native_string_slice(this, start, stop) === search;
return start >= 0 && __rt_string_slice(this, start, stop) === search;
},
// ── Substrings ──
@@ -67,13 +67,13 @@ String.prototype = {
let len = this.length;
let s = relativeIndex(start === undefined ? 0 : start, len);
let e = relativeIndex(end === undefined ? len : end, len);
return s < e ? __native_string_slice(this, s, e) : "";
return s < e ? __rt_string_slice(this, s, e) : "";
},
substring(start, end) {
let len = this.length;
let s = clampIndex(start === undefined ? 0 : start, len);
let e = clampIndex(end === undefined ? len : end, len);
return s < e ? __native_string_slice(this, s, e) : __native_string_slice(this, e, s);
return s < e ? __rt_string_slice(this, s, e) : __rt_string_slice(this, e, s);
},
// ── Case ──
@@ -84,27 +84,25 @@ String.prototype = {
trim() { return this.trimStart().trimEnd(); },
trimStart() {
let i = 0;
while (i < this.length && isSpace(__native_string_char_code_at(this, i))) i++;
return __native_string_slice(this, i, this.length);
while (i < this.length && isSpace(__rt_string_char_code_at(this, i))) i++;
return __rt_string_slice(this, i, this.length);
},
trimEnd() {
let i = this.length;
while (i > 0 && isSpace(__native_string_char_code_at(this, i - 1))) i--;
return __native_string_slice(this, 0, i);
while (i > 0 && isSpace(__rt_string_char_code_at(this, i - 1))) i--;
return __rt_string_slice(this, 0, i);
},
// ── Building ──
repeat(count) {
let s = "";
for (let i = 0; i < count; i++) s = __native_string_concat(s, this);
for (let i = 0; i < count; i++) s = __rt_string_concat(s, this);
return s;
},
padStart(length, fill) { return pad(this, length, fill, true); },
padEnd(length, fill) { return pad(this, length, fill, false); },
};
__link_proto_string(String.prototype);
// An index clamped to [0, len]
function clampIndex(i, len) {
return i < 0 ? 0 : i > len ? len : i;
@@ -119,9 +117,9 @@ function relativeIndex(i, len) {
function mapCodes(s, lo, hi, shift) {
let out = "";
for (let i = 0; i < s.length; i++) {
let c = __native_string_char_code_at(s, i);
let c = __rt_string_char_code_at(s, i);
if (c >= lo && c <= hi) c += shift;
out = __native_string_concat(out, __native_string_from_char_code(c));
out = __rt_string_concat(out, __rt_string_from_char_code(c));
}
return out;
}
@@ -136,8 +134,8 @@ function pad(s, length, fill, atStart) {
if (s.length >= length || f.length === 0) return s;
let padding = "";
while (padding.length < length - s.length) padding = __native_string_concat(padding, f);
padding = __native_string_slice(padding, 0, length - s.length);
while (padding.length < length - s.length) padding = __rt_string_concat(padding, f);
padding = __rt_string_slice(padding, 0, length - s.length);
return atStart ? __native_string_concat(padding, s) : __native_string_concat(s, padding);
return atStart ? __rt_string_concat(padding, s) : __rt_string_concat(s, padding);
}