2 Commits

Author SHA1 Message Date
John Alanbrook
94394c6845 initial attempt 2025-06-21 13:12:23 -05:00
John Alanbrook
b33c7adb07 remove string/number auto type coercion 2025-06-19 23:02:51 -05:00
15 changed files with 204 additions and 577 deletions

View File

@@ -6,7 +6,7 @@ ar_timer = 60
actor_memory = 0
net_service = 0.1
reply_timeout = 60
actor_max = "10_000"
actor_max = 10_000
stack_max = 0
[actors]
[actors.prosperon/sdl_video]

View File

@@ -2,6 +2,10 @@ JAVASCRIPT VISION
I see objects as being a sort of combination of a lisp cell and a record: symbols, which are used internally, and are private and non iterable, and record string values, which are iterable, readable, and writable; of course everything becomes locked in when stone.
This lets the runtime have its own set of symbols, accessible via "cell.+", "cell.-", etc.
but then you can also have modules, imported via use, which expose their own symbols: use('math') can give you a set of symbols like "torads", which you can then define on your objects to specify how they should react to "torads".
CELLSCRIPT
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.

View File

@@ -310,7 +310,7 @@ var input_state = {
// 1) input runs completely independently
function poll_input() {
send(video, {kind:'input', op:'get'}, evs => {
send(video, {kind:'input', op:'get'}, (evs=[]) => {
for (var ev of evs) {
if (ev.type == 'quit')
$_.stop()

View File

@@ -117,7 +117,9 @@ $_.receiver(function(msg) {
response = {error: "Unknown kind: " + msg.kind};
}
} catch (e) {
response = {error: e.toString()};
log.console(typeof e)
log.console(typeof null)
response = {error: e};
log.error(e)
}

View File

@@ -267,7 +267,7 @@ config.system ??= {}
config.system.__proto__ = default_config
ENETSERVICE = config.system.net_service
REPLYTIMEOUT = config.system.reply_timeout
REPLYTIMEOUT = Number(config.system.reply_timeout)
globalThis.text = use('text')
@@ -353,14 +353,13 @@ function guid(bits = 256)
var HEADER = Symbol()
function create_actor(desc = {id:guid()}) {
function create_actor(defn = {id:guid()}) {
var actor = {}
actor[ACTORDATA] = desc
actor[ACTORDATA] = defn
return actor
}
var $_ = create_actor()
$_.random = hidden.rand
$_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5."
@@ -511,6 +510,7 @@ $_.start = function start(cb, program, ...args) {
$_.stop = function stop(actor) {
if (!actor) {
log.system(`actor stopping.`)
need_stop = true
return
}
@@ -608,6 +608,7 @@ function actor_send(actor, message) {
return
}
log.system(`Unable to send message to actor ${json.encode(actor[ACTORDATA])}`)
log.system(`message was ${json.encode(message)}`)
}
// Holds all messages queued during the current turn.
@@ -678,13 +679,13 @@ function turn(msg)
load_actor_config(cell.args.program)
actor_mod.register_actor(cell.id, turn, cell.args.main, config.system.ar_timer)
actor_mod.register_actor(cell.id, turn, cell.args.main, 60)
if (config.system.actor_memory)
js.mem_limit(config.system.actor_memory)
if (Number(config.system.actor_memory))
js.mem_limit(Number(config.system.actor_memory))
if (config.system.stack_max)
js.max_stacksize(config.system.stack_max);
js.max_stacksize(Number(config.system.stack_max))
overling = cell.args.overling
root = cell.args.root

View File

@@ -117,6 +117,7 @@ static void exit_handler(void)
void actor_free(cell_rt *actor)
{
// Delete it out of actors first so it can no longer get messages
printf("killing off %s\n", actor->id);
SDL_LockMutex(actors_mutex);
shdel(actors, actor->id);
int remaining = shlen(actors);
@@ -788,8 +789,20 @@ int uncaught_exception(JSContext *js, JSValue v)
}
JSValue exp = JS_GetException(js);
if (JS_IsNull(rt->on_exception)) {
JSValue stack = JS_GetPropertyStr(js, exp, "stack");
const char *ss = JS_ToCString(js,stack);
const char *se = JS_ToCString(js, exp);
printf("Uncaught exception %s:\n%s\n", se, ss);
JS_FreeCString(js,ss);
JS_FreeCString(js,se);
JS_FreeValue(js,stack);
} else {
JSValue ret = JS_Call(js, rt->on_exception, JS_NULL, 1, &exp);
JS_FreeValue(js,ret);
}
JS_FreeValue(js, exp);
JS_FreeValue(js,v);
SDL_UnlockMutex(rt->mutex);

View File

@@ -180,7 +180,11 @@ int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v); }
JSValue number2js(JSContext *js, double g) { return JS_NewFloat64(js,g); }
double js2number(JSContext *js, JSValue v) {
double g;
JS_ToFloat64(js, &g, v);
if (JS_ToFloat64(js, &g, v) < 0) {
// If conversion fails, clear the exception and return 0
JS_FreeValue(js, JS_GetException(js));
return 0;
}
if (isnan(g)) g = 0;
return g;
}
@@ -696,9 +700,9 @@ JSValue js_util_camera_globals(JSContext *js, JSValue self, int argc, JSValue *a
JSValue camera = argv[0];
if(JS_IsNull(camera)) return JS_NULL;
HMM_Vec2 size; HMM_Vec3 pos; HMM_Quat rotation;
double fov = 0; int ortho; double near_z = 0; double far_z = 0;
HMM_Vec2 anchor;
HMM_Vec2 size = {0}; HMM_Vec3 pos = {0}; HMM_Quat rotation = {0,0,0,1};
double fov = 0; int ortho = 0; double near_z = 0; double far_z = 0;
HMM_Vec2 anchor = {0};
JS_GETPROP(js, size, camera, size, vec2)
JS_GETPROP(js, fov, camera, fov, number)
@@ -709,8 +713,6 @@ JSValue js_util_camera_globals(JSContext *js, JSValue self, int argc, JSValue *a
JS_GETPROP(js, pos, camera, pos, vec3)
JS_GETPROP(js, rotation, camera, rotation, quat)
rotation.w = 1;
HMM_Mat4 proj, view;
if(ortho) {

View File

@@ -22,7 +22,6 @@ QJSCLASS(blob,)
static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv) {
blob *bd = NULL;
// new Blob()
if (argc == 0) {
// empty antestone blob
@@ -40,9 +39,9 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
// new Blob(length, logical/random)
else if (argc == 2 && JS_IsNumber(argv[0])) {
int64_t length_bits;
if (JS_ToInt64(ctx, &length_bits, argv[0]) < 0) {
if (JS_ToInt64(ctx, &length_bits, argv[0]) < 0)
return JS_EXCEPTION;
}
if (length_bits < 0) length_bits = 0;
if (JS_IsBool(argv[1])) {

View File

@@ -2,7 +2,11 @@
#include "jsffi.h"
JSC_CCALL(os_gc, JS_RunGC(JS_GetRuntime(js)) )
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_mem_limit,
double val;
JS_ToFloat64(js, &val, argv[0]);
JS_SetMemoryLimit(JS_GetRuntime(js), val);
)
JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0])))

View File

@@ -152,7 +152,6 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC
nota_write_number(&enc->nb, d);
break;
}
case JS_TAG_BIG_INT:
case JS_TAG_FLOAT64: {
const char *str = JS_ToCString(ctx, replaced);
nota_write_number_str(&enc->nb, str);

View File

@@ -1781,8 +1781,6 @@ JSC_CCALL(sdl_createWindowAndRenderer,
#include "qjs_wota.h"
JSValue js_sdl_video_use(JSContext *js) {
printf("initing on thread %d\n", SDL_GetThreadID(NULL));
if (!SDL_Init(SDL_INIT_VIDEO))
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());

View File

@@ -129,7 +129,7 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC
break;
}
case JS_TAG_FLOAT64:
case JS_TAG_BIG_INT: {
{
double d;
if (JS_ToFloat64(ctx, &d, replaced) < 0) {
wota_write_sym(&enc->wb, WOTA_NULL);

View File

@@ -221,8 +221,6 @@ DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)

View File

@@ -782,7 +782,7 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o
static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
int argc, JSValueConst *argv);
static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
JSValue val, BOOL is_array_ctor);
JSValue val);
static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
JSValueConst val, int flags, int scope_idx);
JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
@@ -864,7 +864,7 @@ static JSProperty *add_property(JSContext *ctx,
JSObject *p, JSAtom prop, int prop_flags);
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1);
static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
static int JS_CreateProperty(JSContext *ctx, JSObject *p,
JSAtom prop, JSValueConst val,
@@ -882,9 +882,9 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
static int js_string_compare(JSContext *ctx,
const JSString *p1, const JSString *p2);
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
JSValue prop, JSValue val, int flags);
static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
JSObject *p, JSAtom prop);
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
@@ -2546,73 +2546,6 @@ static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
}
}
/* This test must be fast if atom is not a numeric index (e.g. a
method name). Return JS_NULL if not a numeric
index. JS_EXCEPTION can also be returned. */
static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
{
JSRuntime *rt = ctx->rt;
JSAtomStruct *p1;
JSString *p;
int c, ret;
JSValue num, str;
if (__JS_AtomIsTaggedInt(atom))
return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
assert(atom < rt->atom_size);
p1 = rt->atom_array[atom];
if (p1->atom_type != JS_ATOM_TYPE_STRING)
return JS_NULL;
switch(atom) {
case JS_ATOM_minus_zero:
return __JS_NewFloat64(ctx, -0.0);
case JS_ATOM_Infinity:
return __JS_NewFloat64(ctx, INFINITY);
case JS_ATOM_minus_Infinity:
return __JS_NewFloat64(ctx, -INFINITY);
case JS_ATOM_NaN:
return __JS_NewFloat64(ctx, NAN);
default:
break;
}
p = p1;
if (p->len == 0)
return JS_NULL;
c = string_get(p, 0);
if (!is_num(c) && c != '-')
return JS_NULL;
/* this is ECMA CanonicalNumericIndexString primitive */
num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
if (JS_IsException(num))
return num;
str = JS_ToString(ctx, num);
if (JS_IsException(str)) {
JS_FreeValue(ctx, num);
return str;
}
ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
JS_FreeValue(ctx, str);
if (ret == 0) {
return num;
} else {
JS_FreeValue(ctx, num);
return JS_NULL;
}
}
/* return -1 if exception or TRUE/FALSE */
int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
{
JSValue num;
num = JS_AtomIsNumericIndex1(ctx, atom);
if (likely(JS_IsNull(num)))
return FALSE;
if (JS_IsException(num))
return -1;
JS_FreeValue(ctx, num);
return TRUE;
}
void JS_FreeAtom(JSContext *ctx, JSAtom v)
{
if (!__JS_AtomIsConst(v))
@@ -7534,8 +7467,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
uint32_t len, idx, cur_len;
int i, ret;
/* Note: this call can reallocate the properties of 'p' */
ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
ret = JS_ToArrayLengthFree(ctx, &len, val);
if (ret)
return -1;
/* JS_ToArrayLengthFree() must be done before the read-only test */
@@ -8258,8 +8190,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
/* the range of the Array length property is always tested before */
if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
uint32_t array_length;
if (JS_ToArrayLengthFree(ctx, &array_length,
JS_DupValue(ctx, val), FALSE)) {
if (JS_ToArrayLengthFree(ctx, &array_length, JS_DupValue(ctx, val))) {
return -1;
}
/* this code relies on the fact that Uint32 are never allocated */
@@ -9355,177 +9286,68 @@ done:
goto done;
}
typedef enum JSToNumberHintEnum {
TON_FLAG_NUMBER,
TON_FLAG_NUMERIC,
} JSToNumberHintEnum;
static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
JSToNumberHintEnum flag)
/* Strict JS_ToNumber - only accepts numbers (int or float64), raises error otherwise */
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
{
uint32_t tag;
JSValue ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_FLOAT64:
case JS_TAG_INT:
case JS_TAG_EXCEPTION:
ret = val;
break;
case JS_TAG_BOOL:
case JS_TAG_NULL:
ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
break;
case JS_TAG_OBJECT:
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
if (JS_IsException(val))
return JS_EXCEPTION;
goto redo;
case JS_TAG_STRING:
case JS_TAG_STRING_ROPE:
{
const char *str;
const char *p;
size_t len;
str = JS_ToCStringLen(ctx, &len, val);
JS_FreeValue(ctx, val);
if (!str)
return JS_EXCEPTION;
p = str;
p += skip_spaces(p);
if ((p - str) == len) {
ret = JS_NewInt32(ctx, 0);
} else {
int flags = ATOD_ACCEPT_BIN_OCT;
ret = js_atof(ctx, p, &p, 0, flags);
if (!JS_IsException(ret)) {
p += skip_spaces(p);
if ((p - str) != len) {
JS_FreeValue(ctx, ret);
ret = JS_NAN;
}
}
}
JS_FreeCString(ctx, str);
}
break;
case JS_TAG_SYMBOL:
JS_FreeValue(ctx, val);
return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
case JS_TAG_FLOAT64:
return JS_DupValue(ctx, val);
default:
JS_FreeValue(ctx, val);
ret = JS_NAN;
break;
return JS_ThrowTypeError(ctx, "value is not a number");
}
return ret;
}
static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
{
return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
}
static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
{
return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
}
static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
{
return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
}
static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
JSValue val)
{
double d;
uint32_t tag;
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
goto fail;
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
d = JS_VALUE_GET_INT(val);
break;
case JS_TAG_FLOAT64:
d = JS_VALUE_GET_FLOAT64(val);
break;
return val;
default:
abort();
JS_FreeValue(ctx, val);
return JS_ThrowTypeError(ctx, "value is not a number");
}
*pres = d;
}
static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) {
// 1) convert and take ownership of `val`
JSValue num = JS_ToNumberFree(ctx, val);
// 2) did ToNumber itself throw?
if (JS_IsException(num))
return -1;
// 3) inspect the tag
uint32_t tag = JS_VALUE_GET_TAG(num);
if (tag == JS_TAG_INT) {
*pres = (double)JS_VALUE_GET_INT(num);
JS_FreeValue(ctx, num);
return 0;
fail:
*pres = JS_FLOAT64_NAN;
}
if (JS_TAG_IS_FLOAT64(tag)) {
*pres = JS_VALUE_GET_FLOAT64(num);
JS_FreeValue(ctx, num);
return 0;
}
// 4) anything else is an error
JS_FreeValue(ctx, num);
JS_ThrowTypeError(ctx, "cannot convert to float64");
return -1;
}
static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
{
uint32_t tag;
tag = JS_VALUE_GET_TAG(val);
if (tag <= JS_TAG_NULL) {
*pres = JS_VALUE_GET_INT(val);
return 0;
} else if (JS_TAG_IS_FLOAT64(tag)) {
*pres = JS_VALUE_GET_FLOAT64(val);
return 0;
} else {
return __JS_ToFloat64Free(ctx, pres, val);
}
}
int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
{
return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
}
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
{
return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
}
/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
{
uint32_t tag;
JSValue ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
break;
case JS_TAG_FLOAT64:
{
double d = JS_VALUE_GET_FLOAT64(val);
if (isnan(d)) {
ret = JS_NewInt32(ctx, 0);
} else {
/* convert -0 to +0 */
d = trunc(d) + 0.0;
ret = JS_NewFloat64(ctx, d);
}
}
break;
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
return val;
goto redo;
}
return ret;
}
/* Note: the integer value is satured to 32 bits */
static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
{
@@ -9559,13 +9381,9 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
}
break;
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
*pres = 0;
val = JS_ThrowTypeError(ctx, "could not coerce to integer");
return -1;
}
goto redo;
}
*pres = ret;
return 0;
}
@@ -9623,13 +9441,9 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
}
return 0;
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
*pres = 0;
val = JS_ThrowTypeError(ctx, "could not coerce to int64");
return -1;
}
goto redo;
}
}
int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
@@ -9693,13 +9507,11 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
}
break;
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
JS_ThrowTypeError(ctx, "could not convert to number.");
*pres = 0;
return -1;
}
goto redo;
}
*pres = ret;
return 0;
}
@@ -9750,13 +9562,11 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
}
break;
default:
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val)) {
JS_ThrowTypeError(ctx, "could not convert to number.");
*pres = 0;
return -1;
}
goto redo;
ret = -1;
}
*pres = ret;
return 0;
}
@@ -9772,7 +9582,7 @@ static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
}
static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
JSValue val, BOOL is_array_ctor)
JSValue val)
{
uint32_t tag, len;
@@ -9789,8 +9599,8 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
len = v;
}
break;
default:
if (JS_TAG_IS_FLOAT64(tag)) {
case JS_TAG_FLOAT64:
{
double d;
d = JS_VALUE_GET_FLOAT64(val);
if (!(d >= 0 && d <= UINT32_MAX))
@@ -9798,39 +9608,18 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
len = (uint32_t)d;
if (len != d)
goto fail;
} else {
uint32_t len1;
if (is_array_ctor) {
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
return -1;
/* cannot recurse because val is a number */
if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
return -1;
} else {
/* legacy behavior: must do the conversion twice and compare */
if (JS_ToUint32(ctx, &len, val)) {
JS_FreeValue(ctx, val);
return -1;
}
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
return -1;
/* cannot recurse because val is a number */
if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
return -1;
if (len1 != len) {
fail:
JS_ThrowRangeError(ctx, "invalid array length");
return -1;
}
}
}
break;
default:
*plen = 0;
return -1;
}
*plen = len;
return 0;
fail:
*plen = 0;
return -1;
}
#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
@@ -10714,42 +10503,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
op1 = sp[-1];
/* fast path for float64 */
if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
goto handle_float64;
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1))
goto exception;
tag = JS_VALUE_GET_TAG(op1);
switch(tag) {
case JS_TAG_INT:
{
int64_t v64;
v64 = JS_VALUE_GET_INT(op1);
switch(op) {
case OP_inc:
case OP_dec:
v = 2 * (op - OP_dec) - 1;
v64 += v;
break;
case OP_plus:
break;
case OP_neg:
if (v64 == 0) {
sp[-1] = __JS_NewFloat64(ctx, -0.0);
return 0;
} else {
v64 = -v64;
}
break;
default:
abort();
}
sp[-1] = JS_NewInt64(ctx, v64);
}
break;
default:
handle_float64:
{
if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) {
double d;
d = JS_VALUE_GET_FLOAT64(op1);
switch(op) {
@@ -10767,11 +10521,9 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
abort();
}
sp[-1] = __JS_NewFloat64(ctx, d);
}
break;
}
return 0;
exception:
}
sp[-1] = JS_NULL;
return -1;
}
@@ -10783,8 +10535,8 @@ static __exception int js_post_inc_slow(JSContext *ctx,
/* XXX: allow custom operators */
op1 = sp[-1];
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
if (!JS_IsNumber(op1)) {
JS_ThrowTypeError(ctx, "operator only works on numbers");
sp[-1] = JS_NULL;
return -1;
}
@@ -10793,30 +10545,12 @@ static __exception int js_post_inc_slow(JSContext *ctx,
return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
}
static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
{
JSValue op1;
op1 = sp[-1];
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1))
goto exception;
int32_t v1;
if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
goto exception;
sp[-1] = JS_NewInt32(ctx, ~v1);
return 0;
exception:
sp[-1] = JS_NULL;
return -1;
}
static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
OPCodeEnum op)
{
JSValue op1, op2;
uint32_t tag1, tag2;
double d1, d2;
double d1, d2, dr;
op1 = sp[-2];
op2 = sp[-1];
@@ -10841,62 +10575,8 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
goto handle_float64;
}
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
op2 = JS_ToNumericFree(ctx, op2);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
goto exception;
}
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
int32_t v1, v2;
int64_t v;
v1 = JS_VALUE_GET_INT(op1);
v2 = JS_VALUE_GET_INT(op2);
switch(op) {
case OP_sub:
v = (int64_t)v1 - (int64_t)v2;
break;
case OP_mul:
v = (int64_t)v1 * (int64_t)v2;
if (v == 0 && (v1 | v2) < 0) {
sp[-2] = __JS_NewFloat64(ctx, -0.0);
return 0;
}
break;
case OP_div:
sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
return 0;
case OP_mod:
if (v1 < 0 || v2 <= 0) {
sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
return 0;
} else {
v = (int64_t)v1 % (int64_t)v2;
}
break;
case OP_pow:
sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
return 0;
default:
abort();
}
sp[-2] = JS_NewInt64(ctx, v);
} else {
double dr;
/* float64 result */
if (JS_ToFloat64Free(ctx, &d1, op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
if (JS_ToFloat64Free(ctx, &d2, op2))
goto exception;
handle_float64:
switch(op) {
case OP_sub:
@@ -10918,8 +10598,9 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
abort();
}
sp[-2] = __JS_NewFloat64(ctx, dr);
}
return 0;
exception:
sp[-2] = JS_NULL;
sp[-1] = JS_NULL;
@@ -10974,11 +10655,35 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
/* anything else → TypeError */
} else {
/* Get type names */
JSAtom type1_atom = js_operator_typeof(ctx, op1);
JSAtom type2_atom = js_operator_typeof(ctx, op2);
const char *type1_name = JS_AtomToCString(ctx, type1_atom);
const char *type2_name = JS_AtomToCString(ctx, type2_atom);
/* Convert values to strings for display */
const char *val1_str = JS_ToCString(ctx, op1);
const char *val2_str = JS_ToCString(ctx, op2);
/* Create detailed error message */
JS_ThrowTypeError(ctx,
"Relational operators only supported on two strings or two numbers");
"TypeError: cannot compare \"%s\" (%s) with \"%s\" (%s)",
val1_str ? val1_str : "(conversion failed)",
type1_name ? type1_name : "unknown",
val2_str ? val2_str : "(conversion failed)",
type2_name ? type2_name : "unknown");
/* Free the C strings */
if (val1_str) JS_FreeCString(ctx, val1_str);
if (val2_str) JS_FreeCString(ctx, val2_str);
JS_FreeCString(ctx, type1_name);
JS_FreeCString(ctx, type2_name);
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
goto exception;
sp[-2] = JS_NULL;
sp[-1] = JS_NULL;
return -1;
}
/* free the two input values and push the result */
@@ -10986,105 +10691,6 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
JS_FreeValue(ctx, op2);
sp[-2] = JS_NewBool(ctx, res);
return 0;
exception:
sp[-2] = JS_NULL;
sp[-1] = JS_NULL;
return -1;
}
static BOOL tag_is_number(uint32_t tag)
{
return (tag == JS_TAG_INT ||
tag == JS_TAG_FLOAT64);
}
static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
BOOL is_neq)
{
JSValue op1, op2;
int res;
uint32_t tag1, tag2;
op1 = sp[-2];
op2 = sp[-1];
redo:
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
if (tag_is_number(tag1) && tag_is_number(tag2)) {
if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
} else if ((tag1 == JS_TAG_FLOAT64 &&
(tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
(tag2 == JS_TAG_FLOAT64 &&
(tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
double d1, d2;
if (tag1 == JS_TAG_FLOAT64) {
d1 = JS_VALUE_GET_FLOAT64(op1);
} else {
d1 = JS_VALUE_GET_INT(op1);
}
if (tag2 == JS_TAG_FLOAT64) {
d2 = JS_VALUE_GET_FLOAT64(op2);
} else {
d2 = JS_VALUE_GET_INT(op2);
}
res = (d1 == d2);
}
} else if (tag1 == tag2) {
res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
} else if (tag1 == JS_TAG_NULL && tag2 == JS_TAG_NULL) {
res = TRUE;
} else if (tag_is_string(tag1) && tag_is_string(tag2)) {
/* needed when comparing strings and ropes */
res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
} else if ((tag_is_string(tag1) && tag_is_number(tag2)) ||
(tag_is_string(tag2) && tag_is_number(tag1))) {
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
op2 = JS_ToNumericFree(ctx, op2);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
goto exception;
}
res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
} else if (tag1 == JS_TAG_BOOL) {
op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
goto redo;
} else if (tag2 == JS_TAG_BOOL) {
op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
goto redo;
} else if ((tag1 == JS_TAG_OBJECT &&
(tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) ||
(tag2 == JS_TAG_OBJECT &&
(tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) {
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
if (JS_IsException(op2)) {
JS_FreeValue(ctx, op1);
goto exception;
}
goto redo;
} else {
res = FALSE;
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
}
done:
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
return 0;
exception:
sp[-2] = JS_NULL;
sp[-1] = JS_NULL;
return -1;
}
/* XXX: Should take JSValueConst arguments */
@@ -14469,9 +14075,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
} else {
sf->cur_pc = pc;
if (js_not_slow(ctx, sp))
goto exception;
sp[-1] = JS_NULL;
}
}
BREAK;
@@ -14603,8 +14207,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
@@ -24252,9 +23854,9 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
case OP_typeof:
if (OPTIMIZE) {
/* simplify typeof tests */
if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
if (code_match(&cc, pos_next, OP_push_atom_value, M2(OP_strict_eq, OP_strict_neq), -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
int op1 = cc.op;
int op2 = -1;
switch (cc.atom) {
case JS_ATOM_null:
@@ -29220,7 +28822,7 @@ static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
return obj;
if (argc == 1 && JS_IsNumber(argv[0])) {
uint32_t len;
if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
goto fail;
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
goto fail;
@@ -31120,10 +30722,49 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
if (argc == 0) {
val = JS_NewInt32(ctx, 0);
} else {
val = JS_ToNumeric(ctx, argv[0]);
if (JS_IsException(val))
return val;
uint32_t tag = JS_VALUE_GET_NORM_TAG(argv[0]);
switch(tag) {
case JS_TAG_FLOAT64:
case JS_TAG_INT:
val = argv[0];
break;
case JS_TAG_BOOL:
val = JS_NewInt32(ctx, JS_VALUE_GET_INT(argv[0]));
break;
case JS_TAG_STRING:
case JS_TAG_STRING_ROPE:
{
const char *str;
const char *p;
size_t len;
str = JS_ToCStringLen(ctx, &len, val);
JS_FreeValue(ctx, val);
if (!str)
return JS_EXCEPTION;
p = str;
p += skip_spaces(p);
if ((p - str) == len) {
val = JS_NewInt32(ctx, 0);
} else {
int flags = ATOD_ACCEPT_BIN_OCT;
val = js_atof(ctx, p, &p, 0, flags);
if (!JS_IsException(val)) {
p += skip_spaces(p);
if ((p - str) != len) {
JS_FreeValue(ctx, val);
val = JS_NAN;
}
}
}
JS_FreeCString(ctx, str);
}
break;
default:
val = JS_ThrowTypeError(ctx, "cannot convert to a number");
}
}
if (!JS_IsNull(new_target)) {
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
if (!JS_IsException(obj))
@@ -32648,12 +32289,6 @@ static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
return ret;
}
static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_ToQuotedString(ctx, this_val);
}
/* return 0 if before the first char */
static int string_prevc(JSString *p, int *pidx)
{
@@ -33041,7 +32676,6 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = {
JS_ALIAS_DEF("trimLeft", "trimStart" ),
JS_CFUNC_DEF("toString", 0, js_string_toString ),
JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
JS_CFUNC_DEF("__quote", 1, js_string___quote ),
JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
@@ -35242,9 +34876,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
goto exception;
goto concat_primitive;
} else if (cl == JS_CLASS_NUMBER) {
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
goto exception;
set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
goto concat_primitive;
} else if (cl == JS_CLASS_BOOLEAN)
{
@@ -35471,18 +35103,6 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
}
}
space = JS_DupValue(ctx, space0);
if (JS_IsObject(space)) {
JSObject *p = JS_VALUE_GET_OBJ(space);
if (p->class_id == JS_CLASS_NUMBER) {
space = JS_ToNumberFree(ctx, space);
} else if (p->class_id == JS_CLASS_STRING) {
space = JS_ToStringFree(ctx, space);
}
if (JS_IsException(space)) {
JS_FreeValue(ctx, space);
goto exception;
}
}
if (JS_IsNumber(space)) {
int n;
if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))

View File

@@ -70,12 +70,9 @@ typedef uint32_t JSAtom;
#define JS_LIMB_BITS 32
#endif
#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS
enum {
/* all tags with a reference count are negative */
JS_TAG_FIRST = -9, /* first negative tag */
JS_TAG_BIG_INT = -9,
JS_TAG_SYMBOL = -8,
JS_TAG_STRING = -7,
JS_TAG_STRING_ROPE = -6,
@@ -86,11 +83,9 @@ enum {
JS_TAG_INT = 0,
JS_TAG_BOOL = 1,
JS_TAG_NULL = 2,
// TAG_UNDEFINED
JS_TAG_UNINITIALIZED = 4,
JS_TAG_CATCH_OFFSET = 5,
JS_TAG_EXCEPTION = 6,
JS_TAG_SHORT_BIG_INT = 7,
JS_TAG_FLOAT64 = 8,
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
};
@@ -116,7 +111,6 @@ typedef const struct __JSValue *JSValueConst;
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
@@ -145,7 +139,6 @@ typedef uint64_t JSValue;
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
#define JS_VALUE_GET_INT(v) (int)(v)
#define JS_VALUE_GET_BOOL(v) (int)(v)
#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v)
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
@@ -208,11 +201,6 @@ typedef union JSValueUnion {
int32_t int32;
double float64;
void *ptr;
#if JS_SHORT_BIG_INT_BITS == 32
int32_t short_big_int;
#else
int64_t short_big_int;
#endif
} JSValueUnion;
typedef struct JSValue {
@@ -228,7 +216,6 @@ typedef struct JSValue {
#define JS_VALUE_GET_INT(v) ((v).u.int32)
#define JS_VALUE_GET_BOOL(v) ((v).u.int32)
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
@@ -682,7 +669,7 @@ static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
{
return JS_ToInt32(ctx, (int32_t*)pres, val);
}
int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom);
int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val);
int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val);