Rewrite with working async
This commit is contained in:
-299
@@ -1,299 +0,0 @@
|
||||
// any_t: a value of any kind, and JS's operators on it
|
||||
#pragma once
|
||||
|
||||
#include "number.h"
|
||||
|
||||
// A value of any kind. An object in it is the pointer to its cell, so it keeps
|
||||
// its identity. A field can also hold absent
|
||||
|
||||
typedef enum {
|
||||
JSRT_KIND_ABSENT,
|
||||
JSRT_KIND_UNDEFINED,
|
||||
JSRT_KIND_NULL,
|
||||
JSRT_KIND_BOOL,
|
||||
JSRT_KIND_INT,
|
||||
JSRT_KIND_FLOAT,
|
||||
JSRT_KIND_STRING,
|
||||
JSRT_KIND_OBJECT,
|
||||
} jsrt_kind;
|
||||
|
||||
typedef struct {
|
||||
jsrt_kind kind;
|
||||
union {
|
||||
bool b;
|
||||
int32_t i;
|
||||
double f;
|
||||
string_t s;
|
||||
void *o;
|
||||
} as;
|
||||
} any_t;
|
||||
|
||||
// Box a value. A pointer that is a sentinel (NULL, JSRT_UNDEFINED,
|
||||
// JSRT_ABSENT) boxes as null, undefined or absent
|
||||
any_t __any_undefined(void);
|
||||
any_t __any_null(void);
|
||||
any_t __any_absent(void);
|
||||
any_t __any_from_bool(bool b);
|
||||
any_t __any_from_int(int32_t n);
|
||||
any_t __any_from_float(double x);
|
||||
any_t __any_from_string(string_t s);
|
||||
any_t __any_from_object(void *o);
|
||||
|
||||
// Unbox a value. A value of another kind throws. A string or an object unboxes
|
||||
// null, undefined and absent to their sentinels
|
||||
bool __any_as_bool(any_t a);
|
||||
int32_t __any_as_int(any_t a);
|
||||
double __any_as_float(any_t a); // an int too
|
||||
string_t __any_as_string(any_t a);
|
||||
void *__any_as_object(any_t a);
|
||||
|
||||
// The kind of a value
|
||||
bool __any_is_absent(any_t a);
|
||||
bool __any_is_undefined(any_t a);
|
||||
bool __any_is_null(any_t a);
|
||||
bool __any_is_nullish(any_t a); // undefined or null
|
||||
bool __any_is_boolean(any_t a);
|
||||
bool __any_is_number(any_t a);
|
||||
bool __any_is_string(any_t a);
|
||||
bool __any_is_object(any_t a); // a fn too
|
||||
bool __any_is_function(any_t a);
|
||||
|
||||
// JS's operators, with JS's conversions
|
||||
any_t __any_add(any_t a, any_t b);
|
||||
any_t __any_sub(any_t a, any_t b);
|
||||
any_t __any_mul(any_t a, any_t b);
|
||||
any_t __any_div(any_t a, any_t b);
|
||||
any_t __any_mod(any_t a, any_t b);
|
||||
any_t __any_pow(any_t a, any_t b);
|
||||
any_t __any_neg(any_t a);
|
||||
any_t __any_plus(any_t a); // ToNumber
|
||||
any_t __any_inc(any_t a);
|
||||
any_t __any_dec(any_t a);
|
||||
|
||||
bool __any_lt(any_t a, any_t b);
|
||||
bool __any_lte(any_t a, any_t b);
|
||||
bool __any_gt(any_t a, any_t b);
|
||||
bool __any_gte(any_t a, any_t b);
|
||||
bool __any_eq(any_t a, any_t b);
|
||||
bool __any_neq(any_t a, any_t b);
|
||||
bool __any_strict_eq(any_t a, any_t b);
|
||||
bool __any_strict_neq(any_t a, any_t b);
|
||||
|
||||
// JS's abstract operations
|
||||
bool __any_truthy(any_t a); // ToBoolean
|
||||
int32_t __any_to_int32(any_t a); // ToInt32
|
||||
string_t __any_to_string(any_t a); // ToString
|
||||
string_t __any_typeof(any_t a);
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <math.h>
|
||||
|
||||
// ── any: boxing ──
|
||||
|
||||
any_t __any_undefined(void) { return (any_t){ .kind = JSRT_KIND_UNDEFINED }; }
|
||||
any_t __any_null(void) { return (any_t){ .kind = JSRT_KIND_NULL }; }
|
||||
any_t __any_absent(void) { return (any_t){ .kind = JSRT_KIND_ABSENT }; }
|
||||
any_t __any_from_bool(bool b) { return (any_t){ .kind = JSRT_KIND_BOOL, .as.b = b }; }
|
||||
any_t __any_from_int(int32_t n) { return (any_t){ .kind = JSRT_KIND_INT, .as.i = n }; }
|
||||
any_t __any_from_float(double x) { return (any_t){ .kind = JSRT_KIND_FLOAT, .as.f = x }; }
|
||||
|
||||
// A pointer that can be a sentinel, as the kind it stands for, else as `kind`
|
||||
static any_t jsrt_from_ptr(void *p, jsrt_kind kind) {
|
||||
if (p == NULL) return __any_null();
|
||||
if (p == JSRT_UNDEFINED) return __any_undefined();
|
||||
if (p == JSRT_ABSENT) return __any_absent();
|
||||
return (any_t){ .kind = kind, .as.o = p };
|
||||
}
|
||||
|
||||
any_t __any_from_string(string_t s) { return jsrt_from_ptr(s, JSRT_KIND_STRING); }
|
||||
any_t __any_from_object(void *o) { return jsrt_from_ptr(o, JSRT_KIND_OBJECT); }
|
||||
|
||||
bool __any_as_bool(any_t a) {
|
||||
if (a.kind != JSRT_KIND_BOOL) jsrt_type_error();
|
||||
return a.as.b;
|
||||
}
|
||||
|
||||
double __any_as_float(any_t a) {
|
||||
if (a.kind == JSRT_KIND_INT) return a.as.i;
|
||||
if (a.kind != JSRT_KIND_FLOAT) jsrt_type_error();
|
||||
return a.as.f;
|
||||
}
|
||||
|
||||
int32_t __any_as_int(any_t a) {
|
||||
return a.kind == JSRT_KIND_INT ? a.as.i : (int32_t)__any_as_float(a);
|
||||
}
|
||||
|
||||
// A pointer of kind `kind`, or the sentinel of a nullish or absent value
|
||||
static void *jsrt_as_ptr(any_t a, jsrt_kind kind) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return NULL;
|
||||
case JSRT_KIND_UNDEFINED: return JSRT_UNDEFINED;
|
||||
case JSRT_KIND_ABSENT: return JSRT_ABSENT;
|
||||
default:
|
||||
if (a.kind != kind) jsrt_type_error();
|
||||
return a.as.o;
|
||||
}
|
||||
}
|
||||
|
||||
string_t __any_as_string(any_t a) { return jsrt_as_ptr(a, JSRT_KIND_STRING); }
|
||||
void *__any_as_object(any_t a) { return jsrt_as_ptr(a, JSRT_KIND_OBJECT); }
|
||||
|
||||
// ── any: kinds ──
|
||||
|
||||
bool __any_is_absent(any_t a) { return a.kind == JSRT_KIND_ABSENT; }
|
||||
bool __any_is_undefined(any_t a) { return a.kind == JSRT_KIND_UNDEFINED || a.kind == JSRT_KIND_ABSENT; }
|
||||
bool __any_is_null(any_t a) { return a.kind == JSRT_KIND_NULL; }
|
||||
bool __any_is_nullish(any_t a) { return __any_is_undefined(a) || __any_is_null(a); }
|
||||
bool __any_is_boolean(any_t a) { return a.kind == JSRT_KIND_BOOL; }
|
||||
bool __any_is_number(any_t a) { return a.kind == JSRT_KIND_INT || a.kind == JSRT_KIND_FLOAT; }
|
||||
bool __any_is_string(any_t a) { return a.kind == JSRT_KIND_STRING; }
|
||||
bool __any_is_object(any_t a) { return a.kind == JSRT_KIND_OBJECT; }
|
||||
|
||||
bool __any_is_function(any_t a) {
|
||||
return a.kind == JSRT_KIND_OBJECT && jsrt_fn_cells[((jsrt_header *)a.as.o)->cell];
|
||||
}
|
||||
|
||||
// ── any: abstract operations ──
|
||||
|
||||
// ToNumber. An object would call its `valueOf` or `toString` first, which the
|
||||
// runtime cannot do, so it gives NaN
|
||||
static double jsrt_to_number(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return 0;
|
||||
case JSRT_KIND_BOOL: return a.as.b;
|
||||
case JSRT_KIND_INT: return a.as.i;
|
||||
case JSRT_KIND_FLOAT: return a.as.f;
|
||||
case JSRT_KIND_STRING: return __native_string_to_number(a.as.s);
|
||||
default: return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
bool __any_truthy(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_BOOL: return a.as.b;
|
||||
case JSRT_KIND_INT: return a.as.i != 0;
|
||||
case JSRT_KIND_FLOAT: return a.as.f != 0 && !isnan(a.as.f);
|
||||
case JSRT_KIND_STRING: return a.as.s->length != 0;
|
||||
case JSRT_KIND_OBJECT: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __any_to_int32(any_t a) {
|
||||
double x = jsrt_to_number(a);
|
||||
|
||||
if (isnan(x) || isinf(x)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = fmod(trunc(x), 4294967296.0);
|
||||
return (int32_t)(uint32_t)(int64_t)(x < 0 ? x + 4294967296.0 : x);
|
||||
}
|
||||
|
||||
// ToString. An object would call its `toString`, which the runtime cannot do
|
||||
string_t __any_to_string(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_UNDEFINED: case JSRT_KIND_ABSENT: return jsrt_string_lit("undefined");
|
||||
case JSRT_KIND_NULL: return jsrt_string_lit("null");
|
||||
case JSRT_KIND_BOOL: return jsrt_string_lit(a.as.b ? "true" : "false");
|
||||
case JSRT_KIND_INT: return __native_number_to_string(a.as.i);
|
||||
case JSRT_KIND_FLOAT: return __native_number_to_string(a.as.f);
|
||||
case JSRT_KIND_STRING: return a.as.s;
|
||||
default: return jsrt_string_lit(__any_is_function(a) ? "function" : "[object Object]");
|
||||
}
|
||||
}
|
||||
|
||||
string_t __any_typeof(any_t a) {
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_UNDEFINED: case JSRT_KIND_ABSENT: return jsrt_string_lit("undefined");
|
||||
case JSRT_KIND_BOOL: return jsrt_string_lit("boolean");
|
||||
case JSRT_KIND_INT: case JSRT_KIND_FLOAT: return jsrt_string_lit("number");
|
||||
case JSRT_KIND_STRING: return jsrt_string_lit("string");
|
||||
case JSRT_KIND_OBJECT: return jsrt_string_lit(__any_is_function(a) ? "function" : "object");
|
||||
default: return jsrt_string_lit("object");
|
||||
}
|
||||
}
|
||||
|
||||
// ── any: operators ──
|
||||
|
||||
any_t __any_add(any_t a, any_t b) {
|
||||
// An object becomes a string (see `__any_to_string`), so it concatenates
|
||||
bool strings = a.kind == JSRT_KIND_STRING || b.kind == JSRT_KIND_STRING
|
||||
|| a.kind == JSRT_KIND_OBJECT || b.kind == JSRT_KIND_OBJECT;
|
||||
|
||||
if (strings) {
|
||||
return __any_from_string(__native_string_concat(__any_to_string(a), __any_to_string(b)));
|
||||
}
|
||||
|
||||
return __any_from_float(jsrt_to_number(a) + jsrt_to_number(b));
|
||||
}
|
||||
|
||||
any_t __any_sub(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) - jsrt_to_number(b)); }
|
||||
any_t __any_mul(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) * jsrt_to_number(b)); }
|
||||
any_t __any_div(any_t a, any_t b) { return __any_from_float(jsrt_to_number(a) / jsrt_to_number(b)); }
|
||||
any_t __any_mod(any_t a, any_t b) { return __any_from_float(fmod(jsrt_to_number(a), jsrt_to_number(b))); }
|
||||
any_t __any_pow(any_t a, any_t b) { return __any_from_float(pow(jsrt_to_number(a), jsrt_to_number(b))); }
|
||||
any_t __any_neg(any_t a) { return __any_from_float(-jsrt_to_number(a)); }
|
||||
any_t __any_plus(any_t a) { return __any_from_float(jsrt_to_number(a)); }
|
||||
any_t __any_inc(any_t a) { return __any_from_float(jsrt_to_number(a) + 1); }
|
||||
any_t __any_dec(any_t a) { return __any_from_float(jsrt_to_number(a) - 1); }
|
||||
|
||||
// ── any: comparisons ──
|
||||
|
||||
// Two strings compare by code units. Anything else compares as numbers, where
|
||||
// NaN makes every comparison false
|
||||
bool __any_lt(any_t a, any_t b) {
|
||||
bool strings = a.kind == JSRT_KIND_STRING && b.kind == JSRT_KIND_STRING;
|
||||
return strings ? jsrt_string_cmp(a.as.s, b.as.s) < 0 : jsrt_to_number(a) < jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_lte(any_t a, any_t b) {
|
||||
bool strings = a.kind == JSRT_KIND_STRING && b.kind == JSRT_KIND_STRING;
|
||||
return strings ? jsrt_string_cmp(a.as.s, b.as.s) <= 0 : jsrt_to_number(a) <= jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_gt(any_t a, any_t b) { return __any_lt(b, a); }
|
||||
bool __any_gte(any_t a, any_t b) { return __any_lte(b, a); }
|
||||
|
||||
bool __any_strict_eq(any_t a, any_t b) {
|
||||
if (__any_is_number(a) && __any_is_number(b)) {
|
||||
return jsrt_to_number(a) == jsrt_to_number(b);
|
||||
}
|
||||
|
||||
if (__any_is_undefined(a) || __any_is_undefined(b)) {
|
||||
return __any_is_undefined(a) && __any_is_undefined(b);
|
||||
}
|
||||
|
||||
if (a.kind != b.kind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a.kind) {
|
||||
case JSRT_KIND_NULL: return true;
|
||||
case JSRT_KIND_BOOL: return a.as.b == b.as.b;
|
||||
case JSRT_KIND_STRING: return jsrt_string_eq(a.as.s, b.as.s);
|
||||
default: return a.as.o == b.as.o;
|
||||
}
|
||||
}
|
||||
|
||||
bool __any_strict_neq(any_t a, any_t b) { return !__any_strict_eq(a, b); }
|
||||
|
||||
// Undefined and null equal each other and nothing else. Values of one kind,
|
||||
// and objects, compare as `===` does. Other primitives compare as numbers
|
||||
bool __any_eq(any_t a, any_t b) {
|
||||
if (__any_is_nullish(a) || __any_is_nullish(b)) {
|
||||
return __any_is_nullish(a) && __any_is_nullish(b);
|
||||
}
|
||||
|
||||
bool same = a.kind == b.kind || (__any_is_number(a) && __any_is_number(b));
|
||||
|
||||
if (same || a.kind == JSRT_KIND_OBJECT || b.kind == JSRT_KIND_OBJECT) {
|
||||
return __any_strict_eq(a, b);
|
||||
}
|
||||
|
||||
return jsrt_to_number(a) == jsrt_to_number(b);
|
||||
}
|
||||
|
||||
bool __any_neq(any_t a, any_t b) { return !__any_eq(a, b); }
|
||||
|
||||
#endif // JSRT_IMPLEMENTATION
|
||||
+16
-38
@@ -1,5 +1,6 @@
|
||||
// Values and objects: the types that every other part uses, the object
|
||||
// header and its tags, the allocator and errors. See docs/model.md
|
||||
// Values: the types that every other part uses, the allocator and errors.
|
||||
// An object is a struct of its class, which the emitted C defines, and every
|
||||
// pointer to one is a real object: undefined and null are values of their own
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -12,40 +13,24 @@ typedef struct { char _; } null_t;
|
||||
// A string: UTF-16 code units, as JS counts them
|
||||
typedef struct jsrt_string *string_t;
|
||||
|
||||
// The start of every object: the number of its cell, which tells its struct
|
||||
// and, for a fn, its code
|
||||
typedef struct { uint32_t cell; } jsrt_header;
|
||||
|
||||
// A pointer to an object or a string can also be one of these. NULL is null.
|
||||
// They are told apart by their address only
|
||||
extern jsrt_header jsrt_undefined_cell, jsrt_absent_cell;
|
||||
#define JSRT_UNDEFINED ((void *)&jsrt_undefined_cell)
|
||||
#define JSRT_ABSENT ((void *)&jsrt_absent_cell) // a field that an object lacks
|
||||
|
||||
// Whether the objects of each cell are fns, by cell. The emitted C defines it
|
||||
extern const bool jsrt_fn_cells[];
|
||||
|
||||
// A new heap cell of `size` bytes, all zero
|
||||
void *jsrt_alloc(size_t size);
|
||||
void *__rt_alloc(size_t size);
|
||||
|
||||
// `items`, an array's elements of `size` bytes each, with room for at least
|
||||
// `need` of them. `*cap` is the room that it has, and is set to the new room
|
||||
void *jsrt_grow(void *items, int32_t *cap, int32_t need, size_t size);
|
||||
// `need` of them. `*cap` is the room that it has, and is set to the new room.
|
||||
// The new room is all zero
|
||||
void *__rt_grow(void *items, int32_t *cap, int32_t need, size_t size);
|
||||
|
||||
// Throws a TypeError. Exceptions are not supported yet, so it stops the program
|
||||
_Noreturn void jsrt_type_error(void);
|
||||
|
||||
// `p`, a pointer to an object or a string. A sentinel for null, undefined or
|
||||
// absent throws a TypeError
|
||||
void *jsrt_some(void *p);
|
||||
// Throws a TypeError, which says `what`. Exceptions are not supported yet, so
|
||||
// it stops the program
|
||||
_Noreturn void __rt_type_error(const char *what);
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
jsrt_header jsrt_undefined_cell, jsrt_absent_cell;
|
||||
|
||||
void *jsrt_alloc(size_t size) {
|
||||
void *__rt_alloc(size_t size) {
|
||||
void *p = calloc(1, size);
|
||||
|
||||
if (!p) {
|
||||
@@ -56,7 +41,7 @@ void *jsrt_alloc(size_t size) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void *jsrt_grow(void *items, int32_t *cap, int32_t need, size_t size) {
|
||||
void *__rt_grow(void *items, int32_t *cap, int32_t need, size_t size) {
|
||||
if (need <= *cap) {
|
||||
return items;
|
||||
}
|
||||
@@ -69,21 +54,14 @@ void *jsrt_grow(void *items, int32_t *cap, int32_t need, size_t size) {
|
||||
abort();
|
||||
}
|
||||
|
||||
memset((char *)items + (size_t)*cap * size, 0, (size_t)(room - *cap) * size);
|
||||
*cap = room;
|
||||
return items;
|
||||
}
|
||||
|
||||
_Noreturn void jsrt_type_error(void) {
|
||||
fputs("TypeError\n", stderr);
|
||||
_Noreturn void __rt_type_error(const char *what) {
|
||||
fprintf(stderr, "TypeError: %s\n", what);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *jsrt_some(void *p) {
|
||||
if (p == NULL || p == JSRT_UNDEFINED || p == JSRT_ABSENT) {
|
||||
jsrt_type_error();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif // JSRT_IMPLEMENTATION
|
||||
|
||||
+9
-9
@@ -31,7 +31,7 @@
|
||||
// ── sync ──
|
||||
|
||||
undefined_t __native_print_string(string_t s) {
|
||||
char *utf8 = jsrt_string_utf8(s);
|
||||
char *utf8 = __rt_string_utf8(s);
|
||||
puts(utf8);
|
||||
free(utf8);
|
||||
return (undefined_t){0};
|
||||
@@ -178,7 +178,7 @@ static void jsrt_io_release(int32_t h) {
|
||||
// The op that the last wait gave. A handle of any other op is an error
|
||||
static jsrt_io_op *jsrt_io_given(int32_t h) {
|
||||
if (h < 0 || h != jsrt_io.given) {
|
||||
jsrt_type_error();
|
||||
__rt_type_error("an IO result read outside its op's callback");
|
||||
}
|
||||
|
||||
return &jsrt_io.ops[h];
|
||||
@@ -320,7 +320,7 @@ int32_t __native_io_queue_timeout(double ms) {
|
||||
// `flags` and `mode` are open(2)'s, as numbers
|
||||
int32_t __native_io_queue_open(string_t path, int32_t flags, int32_t mode) {
|
||||
int32_t h = jsrt_io_new(JSRT_IO_OPEN);
|
||||
jsrt_io.ops[h].buf = jsrt_string_utf8(path);
|
||||
jsrt_io.ops[h].buf = __rt_string_utf8(path);
|
||||
jsrt_io.ops[h].flags = flags | O_CLOEXEC;
|
||||
jsrt_io.ops[h].mode = mode;
|
||||
return jsrt_io_queue(h);
|
||||
@@ -334,7 +334,7 @@ int32_t __native_io_queue_read(int32_t fd, int32_t len, double offset) {
|
||||
op->fd = fd;
|
||||
op->len = len > 0 ? (uint32_t)len : 0;
|
||||
op->off = offset < 0 ? (uint64_t)-1 : (uint64_t)offset;
|
||||
op->buf = jsrt_alloc(op->len ? op->len : 1);
|
||||
op->buf = __rt_alloc(op->len ? op->len : 1);
|
||||
return jsrt_io_queue(h);
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ int32_t __native_io_queue_write(int32_t fd, string_t data, double offset) {
|
||||
int32_t h = jsrt_io_new(JSRT_IO_WRITE);
|
||||
jsrt_io_op *op = &jsrt_io.ops[h];
|
||||
op->fd = fd;
|
||||
op->buf = jsrt_string_utf8(data);
|
||||
op->buf = __rt_string_utf8(data);
|
||||
op->len = (uint32_t)strlen(op->buf);
|
||||
op->off = offset < 0 ? (uint64_t)-1 : (uint64_t)offset;
|
||||
return jsrt_io_queue(h);
|
||||
@@ -359,9 +359,9 @@ int32_t __native_io_queue_close(int32_t fd) {
|
||||
// Gives the file's size, as `__native_io_result_number`
|
||||
int32_t __native_io_queue_stat(string_t path) {
|
||||
int32_t h = jsrt_io_new(JSRT_IO_STAT);
|
||||
char *utf8 = jsrt_string_utf8(path);
|
||||
char *utf8 = __rt_string_utf8(path);
|
||||
size_t size = strlen(utf8) + 1;
|
||||
char *buf = jsrt_alloc(sizeof(struct statx) + size);
|
||||
char *buf = __rt_alloc(sizeof(struct statx) + size);
|
||||
memcpy(buf + sizeof(struct statx), utf8, size);
|
||||
free(utf8);
|
||||
jsrt_io.ops[h].buf = buf;
|
||||
@@ -373,7 +373,7 @@ int32_t __native_io_queue_stat(string_t path) {
|
||||
// -EALREADY
|
||||
int32_t __native_io_queue_cancel(int32_t target) {
|
||||
if (target < 0 || target >= jsrt_io.cap) {
|
||||
jsrt_type_error();
|
||||
__rt_type_error("a cancel of an op that does not exist");
|
||||
}
|
||||
|
||||
int32_t h = jsrt_io_new(JSRT_IO_CANCEL);
|
||||
@@ -439,7 +439,7 @@ int32_t __native_io_status(int32_t h) {
|
||||
string_t __native_io_result_string(int32_t h) {
|
||||
jsrt_io_op *op = jsrt_io_given(h);
|
||||
bool data = op->kind == JSRT_IO_READ && op->res > 0;
|
||||
return jsrt_string_from_utf8(op->buf, data ? (size_t)op->res : 0);
|
||||
return __rt_string_from_utf8(op->buf, data ? (size_t)op->res : 0);
|
||||
}
|
||||
|
||||
// The size of a stat's file. The status for any other op
|
||||
|
||||
+9
-7
@@ -3,13 +3,16 @@
|
||||
//
|
||||
// Each header declares its part, and if JSRT_IMPLEMENTATION is defined before
|
||||
// this is included, also defines it. The emitted C is one file, which does
|
||||
// that, and includes this before any other header. The natives are declared
|
||||
// by the emitted C itself, from compiler/src/native.rs, and defined here
|
||||
// that, and includes this before any other header
|
||||
//
|
||||
// core.h values, the object header, the allocator, errors
|
||||
// str.h strings and the string natives
|
||||
// number.h the number natives
|
||||
// any.h any_t and JS's operators on it
|
||||
// The fns that the program can call have one of two prefixes:
|
||||
// __rt_* the same on every platform: JS's operators, strings, numbers,
|
||||
// the allocator and errors
|
||||
// __native_* the platform's IO, which another platform's runtime replaces
|
||||
//
|
||||
// core.h values, the allocator, errors
|
||||
// str.h strings
|
||||
// number.h numbers, and their conversions to and from strings
|
||||
// io.h IO natives, async on io_uring (uring.h), and the host's loop
|
||||
//
|
||||
// The host owns the event loop. The program exports two fns, which the host
|
||||
@@ -36,5 +39,4 @@ void jsrt_complete(int32_t h);
|
||||
|
||||
#include "str.h"
|
||||
#include "number.h"
|
||||
#include "any.h"
|
||||
#include "io.h"
|
||||
|
||||
+33
-10
@@ -1,8 +1,31 @@
|
||||
// The number natives: conversions between numbers and strings
|
||||
// Numbers: conversions between numbers and strings
|
||||
#pragma once
|
||||
|
||||
#include "str.h"
|
||||
|
||||
// JS's Number::toString
|
||||
string_t __rt_number_to_string(double x);
|
||||
|
||||
// JS's ToNumber on a string
|
||||
double __rt_string_to_number(string_t s);
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// JS's `x ** y`: C's pow, except that a NaN exponent always gives NaN, and so does
|
||||
// an infinite exponent when |x| is 1. C gives 1 for both
|
||||
static inline double __rt_pow(double x, double y) {
|
||||
if (isnan(y) || (isinf(y) && fabs(x) == 1)) return NAN;
|
||||
return pow(x, y);
|
||||
}
|
||||
|
||||
// JS's ToInt32: the number modulo 2^32, as a signed int. NaN and the infinities are 0
|
||||
static inline int32_t __rt_to_int32(double x) {
|
||||
if (!isfinite(x)) return 0;
|
||||
double m = fmod(trunc(x), 4294967296.0);
|
||||
if (m < 0) m += 4294967296.0;
|
||||
return (int32_t)(uint32_t)m;
|
||||
}
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
@@ -11,10 +34,10 @@
|
||||
|
||||
// JS's Number::toString: the fewest digits that give the number back. Plain
|
||||
// notation while the decimal point is within 21 places, else exponential
|
||||
string_t __native_number_to_string(double x) {
|
||||
if (isnan(x)) return jsrt_string_lit("NaN");
|
||||
if (isinf(x)) return jsrt_string_lit(x < 0 ? "-Infinity" : "Infinity");
|
||||
if (x == 0) return jsrt_string_lit("0");
|
||||
string_t __rt_number_to_string(double x) {
|
||||
if (isnan(x)) return __rt_string_lit("NaN");
|
||||
if (isinf(x)) return __rt_string_lit(x < 0 ? "-Infinity" : "Infinity");
|
||||
if (x == 0) return __rt_string_lit("0");
|
||||
|
||||
// The fewest digits: buf is [-]d[.ddd]e±xx
|
||||
char buf[64];
|
||||
@@ -61,19 +84,19 @@ string_t __native_number_to_string(double x) {
|
||||
sprintf(o, "%c%s%se%c%d", digits[0], n > 1 ? "." : "", digits + 1, e < 0 ? '-' : '+', abs(e));
|
||||
}
|
||||
|
||||
return jsrt_string_lit(out);
|
||||
return __rt_string_lit(out);
|
||||
}
|
||||
|
||||
string_t __native_number_to_fixed(double x, int32_t digits) {
|
||||
string_t __rt_number_to_fixed(double x, int32_t digits) {
|
||||
char buf[400];
|
||||
snprintf(buf, sizeof buf, "%.*f", digits, x);
|
||||
return jsrt_string_lit(buf);
|
||||
return __rt_string_lit(buf);
|
||||
}
|
||||
|
||||
// JS's ToNumber on a string: a decimal number with whitespace around it, or
|
||||
// only whitespace, which is 0. Anything else is NaN
|
||||
double __native_string_to_number(string_t s) {
|
||||
char *utf8 = jsrt_string_utf8(s);
|
||||
double __rt_string_to_number(string_t s) {
|
||||
char *utf8 = __rt_string_utf8(s);
|
||||
char *start = utf8, *end;
|
||||
|
||||
while (*start == ' ' || (*start >= '\t' && *start <= '\r')) start++;
|
||||
|
||||
+57
-43
@@ -1,40 +1,44 @@
|
||||
// Strings: UTF-16 code units, as JS counts them, and the string natives
|
||||
// Strings: UTF-16 code units, as JS counts them
|
||||
#pragma once
|
||||
|
||||
#include "core.h"
|
||||
|
||||
string_t jsrt_string_lit(const char *utf8);
|
||||
string_t jsrt_string_from_utf8(const char *utf8, size_t size);
|
||||
int32_t jsrt_string_length(string_t s);
|
||||
bool jsrt_string_eq(string_t a, string_t b);
|
||||
int32_t jsrt_string_cmp(string_t a, string_t b);
|
||||
|
||||
// `s[i]`: the code unit at `i`, as a string, or JSRT_UNDEFINED past the end
|
||||
string_t jsrt_string_at(string_t s, int32_t i);
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// A string. The emitted C makes its literals as static strings of this
|
||||
struct jsrt_string {
|
||||
int32_t length;
|
||||
uint16_t units[];
|
||||
};
|
||||
|
||||
static string_t jsrt_string_new(int32_t length) {
|
||||
string_t s = jsrt_alloc(sizeof *s + (size_t)length * sizeof(uint16_t));
|
||||
string_t __rt_string_lit(const char *utf8);
|
||||
string_t __rt_string_from_utf8(const char *utf8, size_t size);
|
||||
int32_t __rt_string_length(string_t s);
|
||||
bool __rt_string_eq(string_t a, string_t b);
|
||||
int32_t __rt_string_cmp(string_t a, string_t b);
|
||||
|
||||
// `a + b`
|
||||
string_t __rt_string_concat(string_t a, string_t b);
|
||||
|
||||
// `s[i]`: the code unit at `i`, as a string. `i` must be in the string
|
||||
string_t __rt_string_at(string_t s, int32_t i);
|
||||
|
||||
#ifdef JSRT_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static string_t __rt_string_new(int32_t length) {
|
||||
string_t s = __rt_alloc(sizeof *s + (size_t)length * sizeof(uint16_t));
|
||||
s->length = length;
|
||||
return s;
|
||||
}
|
||||
|
||||
string_t jsrt_string_lit(const char *utf8) {
|
||||
return jsrt_string_from_utf8(utf8, strlen(utf8));
|
||||
string_t __rt_string_lit(const char *utf8) {
|
||||
return __rt_string_from_utf8(utf8, strlen(utf8));
|
||||
}
|
||||
|
||||
// The `size` bytes at `utf8`, which need not end in a 0
|
||||
string_t jsrt_string_from_utf8(const char *utf8, size_t size) {
|
||||
string_t __rt_string_from_utf8(const char *utf8, size_t size) {
|
||||
const unsigned char *p = (const unsigned char *)utf8, *end = p + size;
|
||||
string_t s = jsrt_string_new((int32_t)size * 2);
|
||||
string_t s = __rt_string_new((int32_t)size * 2);
|
||||
int32_t n = 0;
|
||||
|
||||
while (p < end) {
|
||||
@@ -59,15 +63,15 @@ string_t jsrt_string_from_utf8(const char *utf8, size_t size) {
|
||||
return s;
|
||||
}
|
||||
|
||||
int32_t jsrt_string_length(string_t s) {
|
||||
int32_t __rt_string_length(string_t s) {
|
||||
return s->length;
|
||||
}
|
||||
|
||||
bool jsrt_string_eq(string_t a, string_t b) {
|
||||
bool __rt_string_eq(string_t a, string_t b) {
|
||||
return a->length == b->length && !memcmp(a->units, b->units, (size_t)a->length * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
int32_t jsrt_string_cmp(string_t a, string_t b) {
|
||||
int32_t __rt_string_cmp(string_t a, string_t b) {
|
||||
int32_t n = a->length < b->length ? a->length : b->length;
|
||||
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
@@ -79,19 +83,36 @@ int32_t jsrt_string_cmp(string_t a, string_t b) {
|
||||
return a->length < b->length ? -1 : a->length > b->length;
|
||||
}
|
||||
|
||||
string_t jsrt_string_at(string_t s, int32_t i) {
|
||||
if (i < 0 || i >= s->length) {
|
||||
return JSRT_UNDEFINED;
|
||||
string_t __rt_string_concat(string_t a, string_t b) {
|
||||
string_t s = __rt_string_new(a->length + b->length);
|
||||
memcpy(s->units, a->units, (size_t)a->length * sizeof(uint16_t));
|
||||
memcpy(s->units + a->length, b->units, (size_t)b->length * sizeof(uint16_t));
|
||||
return s;
|
||||
}
|
||||
|
||||
// The strings of one ASCII unit, made once each, the first time they are needed
|
||||
static string_t __rt_ascii_strings[128];
|
||||
|
||||
string_t __rt_string_at(string_t s, int32_t i) {
|
||||
uint16_t unit = s->units[i];
|
||||
|
||||
if (unit < 128 && __rt_ascii_strings[unit]) {
|
||||
return __rt_ascii_strings[unit];
|
||||
}
|
||||
|
||||
string_t c = __rt_string_new(1);
|
||||
c->units[0] = unit;
|
||||
|
||||
if (unit < 128) {
|
||||
__rt_ascii_strings[unit] = c;
|
||||
}
|
||||
|
||||
string_t c = jsrt_string_new(1);
|
||||
c->units[0] = s->units[i];
|
||||
return c;
|
||||
}
|
||||
|
||||
// `s` as UTF-8, in a new buffer for the caller to free
|
||||
static char *jsrt_string_utf8(string_t s) {
|
||||
char *utf8 = jsrt_alloc((size_t)s->length * 3 + 1);
|
||||
static char *__rt_string_utf8(string_t s) {
|
||||
char *utf8 = __rt_alloc((size_t)s->length * 3 + 1);
|
||||
char *out = utf8;
|
||||
|
||||
for (int32_t i = 0; i < s->length; i++) {
|
||||
@@ -124,30 +145,23 @@ static char *jsrt_string_utf8(string_t s) {
|
||||
return utf8;
|
||||
}
|
||||
|
||||
// ── natives ──
|
||||
// ── for the stdlib ──
|
||||
|
||||
string_t __native_string_concat(string_t a, string_t b) {
|
||||
string_t s = jsrt_string_new(a->length + b->length);
|
||||
memcpy(s->units, a->units, (size_t)a->length * sizeof(uint16_t));
|
||||
memcpy(s->units + a->length, b->units, (size_t)b->length * sizeof(uint16_t));
|
||||
return s;
|
||||
}
|
||||
|
||||
string_t __native_string_slice(string_t s, int32_t start, int32_t end) {
|
||||
string_t __rt_string_slice(string_t s, int32_t start, int32_t end) {
|
||||
start = start < 0 ? 0 : start > s->length ? s->length : start;
|
||||
end = end < start ? start : end > s->length ? s->length : end;
|
||||
|
||||
string_t r = jsrt_string_new(end - start);
|
||||
string_t r = __rt_string_new(end - start);
|
||||
memcpy(r->units, s->units + start, (size_t)(end - start) * sizeof(uint16_t));
|
||||
return r;
|
||||
}
|
||||
|
||||
int32_t __native_string_char_code_at(string_t s, int32_t i) {
|
||||
int32_t __rt_string_char_code_at(string_t s, int32_t i) {
|
||||
return s->units[i];
|
||||
}
|
||||
|
||||
string_t __native_string_from_char_code(int32_t code) {
|
||||
string_t s = jsrt_string_new(1);
|
||||
string_t __rt_string_from_char_code(int32_t code) {
|
||||
string_t s = __rt_string_new(1);
|
||||
s->units[0] = (uint16_t)code;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -149,5 +149,3 @@ Array.prototype = {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
__link_proto_array(Array.prototype);
|
||||
|
||||
@@ -8,5 +8,3 @@ Boolean.prototype = {
|
||||
toString() { return this ? "true" : "false"; },
|
||||
valueOf() { return this; },
|
||||
};
|
||||
|
||||
__link_proto_boolean(Boolean.prototype);
|
||||
|
||||
@@ -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));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
};
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user