This commit is contained in:
2026-01-25 20:29:49 -06:00
parent 8325253f1a
commit 086508bacd
12 changed files with 131 additions and 321 deletions

View File

@@ -56,7 +56,7 @@ static:
# Bootstrap: build cell from scratch using meson (only needed once) # Bootstrap: build cell from scratch using meson (only needed once)
# Also installs core scripts to ~/.cell/core # Also installs core scripts to ~/.cell/core
bootstrap: bootstrap:
meson setup build_bootstrap -Dbuildtype=debugoptimized -Db_sanitize=address meson setup build_bootstrap -Dbuildtype=debugoptimized
meson compile -C build_bootstrap meson compile -C build_bootstrap
cp build_bootstrap/cell . cp build_bootstrap/cell .
cp build_bootstrap/libcell_runtime.dylib . cp build_bootstrap/libcell_runtime.dylib .

View File

@@ -162,10 +162,6 @@ globalThis.log = function(name, args) {
} }
} }
log.console(
'hello'
)
function disrupt(err) function disrupt(err)
{ {
if (is_function(err.toString)) { if (is_function(err.toString)) {

View File

@@ -214,7 +214,7 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC
nota_stack_push(enc, replaced); nota_stack_push(enc, replaced);
JSValue to_json = JS_GetPropertyStr(ctx, replaced, "toJSON"); JSValue to_json = JS_GetPropertyStr(ctx, replaced, "toJSON");
if (JS_IsFunction(ctx, to_json)) { if (JS_IsFunction(to_json)) {
JSValue result = JS_Call(ctx, to_json, replaced, 0, NULL); JSValue result = JS_Call(ctx, to_json, replaced, 0, NULL);
JS_FreeValue(ctx, to_json); JS_FreeValue(ctx, to_json);
if (!JS_IsException(result)) { if (!JS_IsException(result)) {
@@ -239,14 +239,14 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC
uint32_t non_function_count = 0; uint32_t non_function_count = 0;
for (uint32_t i = 0; i < plen; i++) { for (uint32_t i = 0; i < plen; i++) {
JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom); JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom);
if (!JS_IsFunction(ctx, prop_val)) non_function_count++; if (!JS_IsFunction(prop_val)) non_function_count++;
JS_FreeValue(ctx, prop_val); JS_FreeValue(ctx, prop_val);
} }
nota_write_record(&enc->nb, non_function_count); nota_write_record(&enc->nb, non_function_count);
for (uint32_t i = 0; i < plen; i++) { for (uint32_t i = 0; i < plen; i++) {
JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom); JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom);
if (!JS_IsFunction(ctx, prop_val)) { if (!JS_IsFunction(prop_val)) {
const char *prop_name = JS_AtomToCString(ctx, ptab[i].atom); const char *prop_name = JS_AtomToCString(ctx, ptab[i].atom);
JSValue prop_key = JS_AtomToValue(ctx, ptab[i].atom); JSValue prop_key = JS_AtomToValue(ctx, ptab[i].atom);
nota_write_text(&enc->nb, prop_name); nota_write_text(&enc->nb, prop_name);
@@ -338,7 +338,7 @@ static JSValue js_nota_encode(JSContext *ctx, JSValueConst this_val, int argc, J
enc->ctx = ctx; enc->ctx = ctx;
enc->visitedStack = JS_NewArray(ctx); enc->visitedStack = JS_NewArray(ctx);
enc->cycle = 0; enc->cycle = 0;
enc->replacer = (argc > 1 && JS_IsFunction(ctx, argv[1])) ? argv[1] : JS_NULL; enc->replacer = (argc > 1 && JS_IsFunction(argv[1])) ? argv[1] : JS_NULL;
nota_buffer_init(&enc->nb, 128); nota_buffer_init(&enc->nb, 128);
nota_encode_value(enc, argv[0], JS_NULL, JS_NewString(ctx, "")); nota_encode_value(enc, argv[0], JS_NULL, JS_NewString(ctx, ""));
@@ -366,7 +366,7 @@ static JSValue js_nota_decode(JSContext *js, JSValueConst self, int argc, JSValu
if (nota == -1) return JS_EXCEPTION; if (nota == -1) return JS_EXCEPTION;
if (!nota) return JS_NULL; if (!nota) return JS_NULL;
JSValue reviver = (argc > 1 && JS_IsFunction(js, argv[1])) ? argv[1] : JS_NULL; JSValue reviver = (argc > 1 && JS_IsFunction(argv[1])) ? argv[1] : JS_NULL;
JSValue ret; JSValue ret;
JSValue holder = JS_NewObject(js); JSValue holder = JS_NewObject(js);
js_do_nota_decode(js, &ret, (char*)nota, holder, JS_NewString(js, ""), reviver); js_do_nota_decode(js, &ret, (char*)nota, holder, JS_NewString(js, ""), reviver);

View File

@@ -145,7 +145,7 @@ static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val, int a
ENetHost *host = JS_GetOpaque(this_val, enet_host_id); ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
if (!host) return JS_EXCEPTION; if (!host) return JS_EXCEPTION;
if (argc < 1 || !JS_IsFunction(ctx, argv[0])) return JS_ThrowTypeError(ctx, "Expected a callback function as first argument"); if (argc < 1 || !JS_IsFunction(argv[0])) return JS_ThrowTypeError(ctx, "Expected a callback function as first argument");
JSValue callback = JS_DupValue(ctx, argv[0]); JSValue callback = JS_DupValue(ctx, argv[0]);
double secs; double secs;

View File

@@ -102,7 +102,7 @@ static void scores_cb(PDScoresList *scores, const char *errorMessage) {
JSC_SCALL(scoreboards_addScore, JSC_SCALL(scoreboards_addScore,
if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized"); if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized");
uint32_t value = (uint32_t)js2number(js, argv[1]); uint32_t value = (uint32_t)js2number(js, argv[1]);
if (argc > 2 && JS_IsFunction(js, argv[2])) { if (argc > 2 && JS_IsFunction(argv[2])) {
g_scoreboard_js = js; g_scoreboard_js = js;
JS_FreeValue(js, g_add_score_callback); JS_FreeValue(js, g_add_score_callback);
g_add_score_callback = JS_DupValue(js, argv[2]); g_add_score_callback = JS_DupValue(js, argv[2]);
@@ -112,7 +112,7 @@ JSC_SCALL(scoreboards_addScore,
JSC_SCALL(scoreboards_getPersonalBest, JSC_SCALL(scoreboards_getPersonalBest,
if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized"); if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized");
if (argc > 1 && JS_IsFunction(js, argv[1])) { if (argc > 1 && JS_IsFunction(argv[1])) {
g_scoreboard_js = js; g_scoreboard_js = js;
JS_FreeValue(js, g_personal_best_callback); JS_FreeValue(js, g_personal_best_callback);
g_personal_best_callback = JS_DupValue(js, argv[1]); g_personal_best_callback = JS_DupValue(js, argv[1]);
@@ -129,7 +129,7 @@ JSC_CCALL(scoreboards_freeScore,
JSC_CCALL(scoreboards_getScoreboards, JSC_CCALL(scoreboards_getScoreboards,
if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized"); if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized");
if (argc > 0 && JS_IsFunction(js, argv[0])) { if (argc > 0 && JS_IsFunction(argv[0])) {
g_scoreboard_js = js; g_scoreboard_js = js;
JS_FreeValue(js, g_boards_list_callback); JS_FreeValue(js, g_boards_list_callback);
g_boards_list_callback = JS_DupValue(js, argv[0]); g_boards_list_callback = JS_DupValue(js, argv[0]);
@@ -145,7 +145,7 @@ JSC_CCALL(scoreboards_freeBoardsList,
JSC_SCALL(scoreboards_getScores, JSC_SCALL(scoreboards_getScores,
if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized"); if (!pd_scoreboards) return JS_ThrowInternalError(js, "scoreboards not initialized");
if (argc > 1 && JS_IsFunction(js, argv[1])) { if (argc > 1 && JS_IsFunction(argv[1])) {
g_scoreboard_js = js; g_scoreboard_js = js;
JS_FreeValue(js, g_scores_callback); JS_FreeValue(js, g_scores_callback);
g_scores_callback = JS_DupValue(js, argv[1]); g_scores_callback = JS_DupValue(js, argv[1]);

View File

@@ -111,7 +111,7 @@ JSC_CCALL(actor_on_exception,
) )
JSC_CCALL(actor_clock, JSC_CCALL(actor_clock,
if (!JS_IsFunction(js, argv[0])) if (!JS_IsFunction(argv[0]))
return JS_ThrowReferenceError(js, "Argument must be a function."); return JS_ThrowReferenceError(js, "Argument must be a function.");
cell_rt *actor = JS_GetContextOpaque(js); cell_rt *actor = JS_GetContextOpaque(js);
@@ -119,7 +119,7 @@ JSC_CCALL(actor_clock,
) )
JSC_CCALL(actor_delay, JSC_CCALL(actor_delay,
if (!JS_IsFunction(js, argv[0])) if (!JS_IsFunction(argv[0]))
return JS_ThrowReferenceError(js, "Argument must be a function."); return JS_ThrowReferenceError(js, "Argument must be a function.");
cell_rt *actor = JS_GetContextOpaque(js); cell_rt *actor = JS_GetContextOpaque(js);

View File

@@ -88,7 +88,7 @@ static void encode_object_properties(WotaEncodeContext *enc, JSValueConst val, J
for (uint32_t i = 0; i < plen; i++) { for (uint32_t i = 0; i < plen; i++) {
JSValue prop_val = JS_GetProperty(ctx, val, ptab[i].atom); JSValue prop_val = JS_GetProperty(ctx, val, ptab[i].atom);
if (!JS_IsFunction(ctx, prop_val)) { if (!JS_IsFunction(prop_val)) {
atoms[non_function_count] = ptab[i].atom; atoms[non_function_count] = ptab[i].atom;
props[non_function_count++] = prop_val; props[non_function_count++] = prop_val;
} else } else
@@ -200,7 +200,7 @@ static void wota_encode_value(WotaEncodeContext *enc, JSValueConst val, JSValueC
} }
wota_stack_push(enc, replaced); wota_stack_push(enc, replaced);
JSValue to_json = JS_GetPropertyStr(ctx, replaced, "toJSON"); JSValue to_json = JS_GetPropertyStr(ctx, replaced, "toJSON");
if (JS_IsFunction(ctx, to_json)) { if (JS_IsFunction(to_json)) {
JSValue result = JS_Call(ctx, to_json, replaced, 0, NULL); JSValue result = JS_Call(ctx, to_json, replaced, 0, NULL);
JS_FreeValue(ctx, to_json); JS_FreeValue(ctx, to_json);
if (!JS_IsException(result)) { if (!JS_IsException(result)) {
@@ -359,7 +359,7 @@ static JSValue js_wota_encode(JSContext *ctx, JSValueConst this_val, int argc, J
{ {
if (argc < 1) return JS_ThrowTypeError(ctx, "wota.encode requires at least 1 argument"); if (argc < 1) return JS_ThrowTypeError(ctx, "wota.encode requires at least 1 argument");
size_t total_bytes; size_t total_bytes;
void *wota = value2wota(ctx, argv[0], JS_IsFunction(ctx,argv[1]) ? argv[1] : JS_NULL, &total_bytes); void *wota = value2wota(ctx, argv[0], JS_IsFunction(argv[1]) ? argv[1] : JS_NULL, &total_bytes);
JSValue ret = js_new_blob_stoned_copy(ctx, wota, total_bytes); JSValue ret = js_new_blob_stoned_copy(ctx, wota, total_bytes);
free(wota); free(wota);
return ret; return ret;
@@ -372,7 +372,7 @@ static JSValue js_wota_decode(JSContext *ctx, JSValueConst this_val, int argc, J
uint8_t *buf = js_get_blob_data(ctx, &len, argv[0]); uint8_t *buf = js_get_blob_data(ctx, &len, argv[0]);
if (buf == (uint8_t *)-1) return JS_EXCEPTION; if (buf == (uint8_t *)-1) return JS_EXCEPTION;
if (!buf || len == 0) return JS_ThrowTypeError(ctx, "No blob data present"); if (!buf || len == 0) return JS_ThrowTypeError(ctx, "No blob data present");
JSValue reviver = (argc > 1 && JS_IsFunction(ctx, argv[1])) ? argv[1] : JS_NULL; JSValue reviver = (argc > 1 && JS_IsFunction(argv[1])) ? argv[1] : JS_NULL;
char *data_ptr = (char *)buf; char *data_ptr = (char *)buf;
JSValue result = JS_NULL; JSValue result = JS_NULL;
JSValue holder = JS_NewObject(ctx); JSValue holder = JS_NewObject(ctx);

View File

@@ -101,13 +101,11 @@ DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none) DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none) DEF( return_undef, 1, 0, 0, none)
DEF( throw, 1, 1, 0, none) DEF( throw, 1, 1, 0, none)
DEF( throw_error, 6, 0, 0, atom_u8) DEF( throw_error, 6, 0, 0, atom_u8)
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */ bytecode string */

View File

@@ -103,7 +103,7 @@
/* dump profiling statistics (counters and sampling) when freeing the runtime */ /* dump profiling statistics (counters and sampling) when freeing the runtime */
//#define DUMP_PROFILE //#define DUMP_PROFILE
#define RC_TRACE //#define RC_TRACE
/* test the GC by forcing it before each object allocation */ /* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC //#define FORCE_GC_AT_MALLOC
@@ -1034,6 +1034,8 @@ static void gc_decref_child_dbg(JSRuntime *rt, JSGCObjectHeader *parent,
const char *file, int line); const char *file, int line);
#endif #endif
static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p);
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val); int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val);
static blob *js_get_blob(JSContext *ctx, JSValueConst val); static blob *js_get_blob(JSContext *ctx, JSValueConst val);
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
@@ -4900,12 +4902,6 @@ static BOOL js_is_bytecode_function(JSValueConst val)
return f->kind == JS_FUNC_KIND_BYTECODE; return f->kind == JS_FUNC_KIND_BYTECODE;
} }
/* Check if a value is a function that uses proxy-call syntax (fn.a -> fn("a")) */
static BOOL js_is_proxy_callable(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_FUNCTION;
}
/* return NULL without exception if not a function or no bytecode */ /* return NULL without exception if not a function or no bytecode */
static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
{ {
@@ -5194,9 +5190,8 @@ static int js_intrinsic_array_set(JSContext *ctx, JSArray *arr, uint32_t idx, JS
} }
if (idx >= arr->len) { if (idx >= arr->len) {
for (uint32_t i = arr->len; i < idx; i++) { for (uint32_t i = arr->len; i < idx; i++)
arr->values[i] = JS_NULL; arr->values[i] = JS_NULL;
}
arr->len = idx + 1; arr->len = idx + 1;
arr->values[idx] = val; arr->values[idx] = val;
} else { } else {
@@ -7984,12 +7979,6 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
return -1; return -1;
} }
BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
{
(void)ctx; /* unused */
return JS_VALUE_GET_TAG(val) == JS_TAG_FUNCTION;
}
BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
{ {
JSFunction *f; JSFunction *f;
@@ -8336,7 +8325,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
uint32_t tag; uint32_t tag;
JSValue ret; JSValue ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val); tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) { switch(tag) {
case JS_TAG_FLOAT64: case JS_TAG_FLOAT64:
@@ -9982,7 +9970,6 @@ static __exception int JS_CopyDataProperties(JSContext *ctx,
JSObject *pexcl = NULL; JSObject *pexcl = NULL;
int ret, gpn_flags; int ret, gpn_flags;
JSPropertyDescriptor desc; JSPropertyDescriptor desc;
BOOL is_enumerable;
if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
return 0; return 0;
@@ -10821,39 +10808,32 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
#endif #endif
/* Proxy method-call: detect [func, "name", ...args] /* Proxy method-call: detect [func, "name", ...args]
and rewrite as func("name", [args]) */ and rewrite as func("name", [args]) */
if (js_is_proxy_callable(call_argv[-2])) {
if (!JS_IsFunction(ctx, call_argv[-1])) {
int t = JS_VALUE_GET_TAG(call_argv[-1]);
if (t == JS_TAG_STRING || t == JS_TAG_SYMBOL)
is_proxy = TRUE;
}
}
if (is_proxy) { if (JS_IsFunction(call_argv[-2])) {
JSValue name = call_argv[-1]; JSValue name = call_argv[-1];
if (!JS_IsText(name)) {
ret_val = JS_ThrowTypeError(ctx, "second argument must be a string");
goto exception;
}
JSValue args = JS_NewArrayLen(ctx, call_argc); JSValue args = JS_NewArrayLen(ctx, call_argc);
if (unlikely(JS_IsException(args))) if (unlikely(JS_IsException(args)))
goto exception; goto exception;
/* Move args into the array, then null out stack slots. */ /* Move args into the array, then null out stack slots. */
JSArray *ar = JS_VALUE_GET_ARRAY(args);
for (i = 0; i < call_argc; i++) { for (i = 0; i < call_argc; i++) {
int r = JS_SetPropertyNumber(ctx, args, i, call_argv[i]); ar->values[i] = call_argv[i];
call_argv[i] = JS_NULL; call_argv[i] = JS_NULL;
if (unlikely(r < 0)) {
JS_FreeValue(ctx, args);
goto exception;
}
} }
{ JSValue proxy_argv[2];
JSValue proxy_argv[2]; proxy_argv[0] = name; /* still owned by stack; freed by normal cleanup */
proxy_argv[0] = name; /* still owned by stack; freed by normal cleanup */ proxy_argv[1] = args;
proxy_argv[1] = args;
ret_val = JS_CallInternal(ctx, call_argv[-2], JS_NULL, ret_val = JS_CallInternal(ctx, call_argv[-2], JS_NULL,
2, proxy_argv, 0); 2, proxy_argv, 0);
JS_FreeValue(ctx, args); JS_FreeValue(ctx, args);
}
} else { } else {
ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
call_argc, call_argv, 0); call_argc, call_argv, 0);
@@ -10878,57 +10858,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
if (unlikely(JS_IsException(ret_val))) if (unlikely(JS_IsException(ret_val)))
goto exception; goto exception;
call_argv = sp - call_argc; call_argv = sp - call_argc;
JSArray *ar = JS_VALUE_GET_ARRAY(ret_val);
for (i = 0; i < call_argc; i++) { for (i = 0; i < call_argc; i++) {
ret = JS_SetPropertyNumber(ctx, ret_val, i, call_argv[i]); ar->values[i] = call_argv[i];
call_argv[i] = JS_NULL; call_argv[i] = JS_NULL;
if (ret < 0) {
JS_FreeValue(ctx, ret_val);
goto exception;
}
} }
sp -= call_argc; sp -= call_argc;
*sp++ = ret_val; *sp++ = ret_val;
} }
BREAK; BREAK;
CASE(OP_apply):
{
int magic;
BOOL is_proxy = FALSE;
magic = get_u16(pc);
pc += 2;
sf->cur_pc = pc;
/* Proxy method-call with spread: detect ["name", func, args_array]
and rewrite as func("name", args_array) */
if (js_is_proxy_callable(sp[-2])) {
if (!JS_IsFunction(ctx, sp[-3])) {
int t = JS_VALUE_GET_TAG(sp[-3]);
if (t == JS_TAG_STRING || t == JS_TAG_SYMBOL)
is_proxy = TRUE;
}
}
if (is_proxy) {
JSValue proxy_argv[2];
proxy_argv[0] = sp[-3]; /* name */
proxy_argv[1] = sp[-1]; /* args array already built by bytecode */
ret_val = JS_CallInternal(ctx, sp[-2], JS_NULL,
2, proxy_argv, 0);
} else {
ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
}
if (unlikely(JS_IsException(ret_val)))
goto exception;
JS_FreeValue(ctx, sp[-3]);
JS_FreeValue(ctx, sp[-2]);
JS_FreeValue(ctx, sp[-1]);
sp -= 3;
*sp++ = ret_val;
}
BREAK;
CASE(OP_return): CASE(OP_return):
ret_val = *--sp; ret_val = *--sp;
goto done; goto done;
@@ -10992,51 +10930,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
*sp++ = ret_val; *sp++ = ret_val;
} }
BREAK; BREAK;
/* could merge with OP_apply */
CASE(OP_apply_eval):
{
int scope_idx;
uint32_t len;
JSValue *tab;
JSValueConst obj;
scope_idx = get_u16(pc) + ARG_SCOPE_END;
pc += 2;
sf->cur_pc = pc;
/* Fast path: check arity before building arg list */
if (JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_FUNCTION &&
JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_ARRAY) {
JSFunction *callee = JS_VALUE_GET_FUNCTION(sp[-2]);
JSArray *arr = JS_VALUE_GET_ARRAY(sp[-1]);
if (unlikely(arr->len > callee->length)) {
JS_ThrowTypeError(ctx, "too many arguments");
goto exception;
}
}
tab = build_arg_list(ctx, &len, sp[-1]);
if (!tab)
goto exception;
if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
if (len >= 1)
obj = tab[0];
else
obj = JS_NULL;
ret_val = JS_EvalObject(ctx, JS_NULL, obj,
JS_EVAL_TYPE_DIRECT, scope_idx);
} else {
ret_val = JS_Call(ctx, sp[-2], JS_NULL, len,
(JSValueConst *)tab);
}
free_arg_list(ctx, tab, len);
if (unlikely(JS_IsException(ret_val)))
goto exception;
JS_FreeValue(ctx, sp[-2]);
JS_FreeValue(ctx, sp[-1]);
sp -= 2;
*sp++ = ret_val;
}
BREAK;
CASE(OP_regexp): CASE(OP_regexp):
{ {
sp[-2] = js_regexp_constructor_internal(ctx, sp[-2], sp[-1]); sp[-2] = js_regexp_constructor_internal(ctx, sp[-2], sp[-1]);
@@ -11617,7 +11510,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
obj = sp[-1]; obj = sp[-1];
/* Functions don't support property access in cell script */ /* Functions don't support property access in cell script */
if (js_is_proxy_callable(obj)) { if (JS_IsFunction(obj)) {
JS_ThrowTypeError(ctx, "cannot get property of function"); JS_ThrowTypeError(ctx, "cannot get property of function");
goto exception; goto exception;
} }
@@ -11704,7 +11597,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
/* Proxy method-call sugar: func.name(...) -> func("name", [args...]) /* Proxy method-call sugar: func.name(...) -> func("name", [args...])
OP_get_field2 is only emitted when a call immediately follows. */ OP_get_field2 is only emitted when a call immediately follows. */
if (js_is_proxy_callable(obj)) { if (JS_IsFunction(obj)) {
val = JS_AtomToValue(ctx, atom); /* "name" */ val = JS_AtomToValue(ctx, atom); /* "name" */
if (unlikely(JS_IsException(val))) if (unlikely(JS_IsException(val)))
goto exception; goto exception;
@@ -11779,8 +11672,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
profile_record_prop_site(rt, b, (uint32_t)(pc - b->byte_code_buf), atom); profile_record_prop_site(rt, b, (uint32_t)(pc - b->byte_code_buf), atom);
#endif #endif
/* Functions don't support property assignment in cell script */ /* Functions don't support property assignment in cell script */
if (js_is_proxy_callable(sp[-2])) { if (!JS_IsObject(sp[-2])) {
JS_ThrowTypeError(ctx, "cannot set property of function"); JS_ThrowTypeError(ctx, "tried to set property of non-object");
goto exception; goto exception;
} }
@@ -11829,14 +11722,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
CASE(OP_define_method): CASE(OP_define_method):
CASE(OP_define_method_computed): CASE(OP_define_method_computed):
{ {
JSValue getter, setter, value; JSValue value;
JSValueConst obj; JSValueConst obj;
JSAtom atom; JSAtom atom;
int ret, op_flags; int ret, op_flags;
BOOL is_computed; BOOL is_computed;
#define OP_DEFINE_METHOD_METHOD 0 #define OP_DEFINE_METHOD_METHOD 0
#define OP_DEFINE_METHOD_GETTER 1
#define OP_DEFINE_METHOD_SETTER 2
#define OP_DEFINE_METHOD_ENUMERABLE 4 #define OP_DEFINE_METHOD_ENUMERABLE 4
is_computed = (opcode == OP_define_method_computed); is_computed = (opcode == OP_define_method_computed);
@@ -11854,8 +11745,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
obj = sp[-2 - is_computed]; obj = sp[-2 - is_computed];
op_flags &= 3; op_flags &= 3;
value = JS_NULL; value = JS_NULL;
getter = JS_NULL;
setter = JS_NULL;
if (op_flags == OP_DEFINE_METHOD_METHOD) { if (op_flags == OP_DEFINE_METHOD_METHOD) {
value = sp[-1]; value = sp[-1];
} }
@@ -11884,13 +11774,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
CASE(OP_get_array_el): CASE(OP_get_array_el):
{ {
JSValue val; JSValue val;
/* Functions don't support property access in cell script */
if (js_is_proxy_callable(sp[-2])) {
JS_ThrowTypeError(ctx, "cannot get property of function");
goto exception;
}
sf->cur_pc = pc; sf->cur_pc = pc;
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
JS_FreeValue(ctx, sp[-2]); JS_FreeValue(ctx, sp[-2]);
@@ -11904,35 +11787,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
CASE(OP_get_array_el2): CASE(OP_get_array_el2):
{ {
JSValue val; JSValue val;
/* Proxy method-call sugar for bracket calls: func[key](...) */
if (js_is_proxy_callable(sp[-2])) {
/* Keep [func, key] and normalize key to property-key (string/symbol). */
switch (JS_VALUE_GET_TAG(sp[-1])) {
case JS_TAG_INT:
/* Convert integer to string */
sf->cur_pc = pc;
ret_val = JS_ToString(ctx, sp[-1]);
if (JS_IsException(ret_val))
goto exception;
JS_FreeValue(ctx, sp[-1]);
sp[-1] = ret_val;
break;
case JS_TAG_STRING:
case JS_TAG_SYMBOL:
break;
default:
sf->cur_pc = pc;
ret_val = JS_ToPropertyKey(ctx, sp[-1]);
if (JS_IsException(ret_val))
goto exception;
JS_FreeValue(ctx, sp[-1]);
sp[-1] = ret_val;
break;
}
BREAK; /* skip JS_GetPropertyValue, keep [func, key] on stack */
}
sf->cur_pc = pc; sf->cur_pc = pc;
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
sp[-1] = val; sp[-1] = val;
@@ -11944,13 +11798,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
CASE(OP_get_array_el3): CASE(OP_get_array_el3):
{ {
JSValue val; JSValue val;
/* Functions don't support property access in cell script */
if (js_is_proxy_callable(sp[-2])) {
JS_ThrowTypeError(ctx, "cannot get property of function");
goto exception;
}
switch (JS_VALUE_GET_TAG(sp[-2])) { switch (JS_VALUE_GET_TAG(sp[-2])) {
case JS_TAG_INT: case JS_TAG_INT:
case JS_TAG_STRING: case JS_TAG_STRING:
@@ -11959,7 +11806,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
break; break;
default: default:
/* must be tested nefore JS_ToPropertyKey */ /* must be tested nefore JS_ToPropertyKey */
if (unlikely(JS_IsNull(sp[-2]) || JS_IsNull(sp[-2]))) { if (unlikely(JS_IsNull(sp[-2]))) {
JS_ThrowTypeError(ctx, "value has no property"); JS_ThrowTypeError(ctx, "value has no property");
goto exception; goto exception;
} }
@@ -12003,7 +11850,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
JS_ThrowReferenceErrorNotDefined(ctx, atom); JS_ThrowReferenceErrorNotDefined(ctx, atom);
JS_FreeAtom(ctx, atom); JS_FreeAtom(ctx, atom);
goto exception; goto exception;
val = JS_NULL;
} else { } else {
val = JS_GetProperty(ctx, sp[-2], atom); val = JS_GetProperty(ctx, sp[-2], atom);
} }
@@ -12020,7 +11866,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
int ret; int ret;
/* Functions don't support property assignment in cell script */ /* Functions don't support property assignment in cell script */
if (js_is_proxy_callable(sp[-3])) { if (JS_IsFunction(sp[-3])) {
JS_ThrowTypeError(ctx, "cannot set property of function"); JS_ThrowTypeError(ctx, "cannot set property of function");
goto exception; goto exception;
} }
@@ -15721,13 +15567,12 @@ static __exception int js_parse_object_literal(JSParseState *s)
JSAtom name = JS_ATOM_NULL; JSAtom name = JS_ATOM_NULL;
const uint8_t *start_ptr; const uint8_t *start_ptr;
int prop_type; int prop_type;
BOOL has_proto;
if (next_token(s)) if (next_token(s))
goto fail; goto fail;
/* XXX: add an initial length that will be patched back */ /* XXX: add an initial length that will be patched back */
emit_op(s, OP_object); emit_op(s, OP_object);
has_proto = FALSE;
while (s->token.val != '}') { while (s->token.val != '}') {
/* specific case for getter/setter */ /* specific case for getter/setter */
start_ptr = s->token.ptr; start_ptr = s->token.ptr;
@@ -16127,18 +15972,6 @@ duplicate:
return js_parse_error(s, "duplicate parameter names not allowed in this context"); return js_parse_error(s, "duplicate parameter names not allowed in this context");
} }
/* tok = TOK_VAR or TOK_DEF. Return whether a reference
must be taken to the variable for proper 'with' or global variable
evaluation */
/* Note: this function is needed only because variable references are
not yet optimized in destructuring */
static BOOL need_var_reference(JSParseState *s, int tok)
{
JSFunctionDef *fd = s->cur_func;
/* var now behaves like let - no reference needed */
return FALSE;
}
static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
{ {
JSAtom name; JSAtom name;
@@ -16238,20 +16071,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
var_name = js_parse_destructuring_var(s, tok, is_arg); var_name = js_parse_destructuring_var(s, tok, is_arg);
if (var_name == JS_ATOM_NULL) if (var_name == JS_ATOM_NULL)
goto prop_error; goto prop_error;
if (need_var_reference(s, tok)) { /* no need to make a reference for let/const */
/* Must make a reference for proper `with` semantics */ opcode = OP_scope_get_var;
emit_op(s, OP_scope_get_var); scope = s->cur_func->scope_level;
emit_atom(s, var_name); label_lvalue = -1;
emit_u16(s, s->cur_func->scope_level); depth_lvalue = 0;
JS_FreeAtom(s->ctx, var_name);
goto lvalue1;
} else {
/* no need to make a reference for let/const */
opcode = OP_scope_get_var;
scope = s->cur_func->scope_level;
label_lvalue = -1;
depth_lvalue = 0;
}
} else { } else {
if (js_parse_left_hand_side_expr(s)) if (js_parse_left_hand_side_expr(s))
goto prop_error; goto prop_error;
@@ -16313,7 +16137,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
js_parse_error(s, "invalid destructuring target"); js_parse_error(s, "invalid destructuring target");
goto prop_error; goto prop_error;
} }
if (!tok || need_var_reference(s, tok)) { if (!tok) {
/* generate reference */ /* generate reference */
/* source -- source source */ /* source -- source source */
emit_op(s, OP_dup); emit_op(s, OP_dup);
@@ -17527,32 +17351,14 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
if (s->token.val == '=') { if (s->token.val == '=') {
if (next_token(s)) if (next_token(s))
goto var_error; goto var_error;
if (need_var_reference(s, tok)) {
/* Must make a reference for proper `with` semantics */
int opcode, scope, label;
JSAtom name1;
emit_op(s, OP_scope_get_var); if (js_parse_assign_expr2(s, parse_flags))
emit_atom(s, name); goto var_error;
emit_u16(s, fd->scope_level); set_object_name(s, name);
if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) emit_op(s, (tok == TOK_DEF || tok == TOK_VAR) ?
goto var_error; OP_scope_put_var_init : OP_scope_put_var);
if (js_parse_assign_expr2(s, parse_flags)) { emit_atom(s, name);
JS_FreeAtom(ctx, name1); emit_u16(s, fd->scope_level);
goto var_error;
}
set_object_name(s, name);
put_lvalue(s, opcode, scope, name1, label,
PUT_LVALUE_NOKEEP, FALSE);
} else {
if (js_parse_assign_expr2(s, parse_flags))
goto var_error;
set_object_name(s, name);
emit_op(s, (tok == TOK_DEF || tok == TOK_VAR) ?
OP_scope_put_var_init : OP_scope_put_var);
emit_atom(s, name);
emit_u16(s, fd->scope_level);
}
} else { } else {
if (tok == TOK_DEF) { if (tok == TOK_DEF) {
js_parse_error(s, "missing initializer for const variable"); js_parse_error(s, "missing initializer for const variable");
@@ -20198,12 +20004,6 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
} }
break; break;
case OP_apply_eval: /* convert scope index to adjusted variable index */
scope = get_u16(bc_buf + pos + 1);
mark_eval_captured_variables(ctx, s, scope);
dbuf_putc(&bc_out, op);
dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
break;
case OP_scope_get_var_checkthis: case OP_scope_get_var_checkthis:
case OP_scope_get_var_undef: case OP_scope_get_var_undef:
case OP_scope_get_var: case OP_scope_get_var:
@@ -24223,7 +24023,7 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
static int check_function(JSContext *ctx, JSValueConst obj) static int check_function(JSContext *ctx, JSValueConst obj)
{ {
if (likely(JS_IsFunction(ctx, obj))) if (likely(JS_IsFunction(obj)))
return 0; return 0;
JS_ThrowTypeError(ctx, "not a function"); JS_ThrowTypeError(ctx, "not a function");
return -1; return -1;
@@ -25273,7 +25073,7 @@ static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
method = JS_GetProperty(ctx, r, JS_ATOM_exec); method = JS_GetProperty(ctx, r, JS_ATOM_exec);
if (JS_IsException(method)) if (JS_IsException(method))
return method; return method;
if (JS_IsFunction(ctx, method)) { if (JS_IsFunction(method)) {
ret = JS_CallFree(ctx, method, r, 1, &s); ret = JS_CallFree(ctx, method, r, 1, &s);
if (JS_IsException(ret)) if (JS_IsException(ret))
return ret; return ret;
@@ -25593,7 +25393,7 @@ static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
JS_FreeCString(ctx, str); JS_FreeCString(ctx, str);
if (JS_IsException(obj)) if (JS_IsException(obj))
return obj; return obj;
if (argc > 1 && JS_IsFunction(ctx, argv[1])) { if (argc > 1 && JS_IsFunction(argv[1])) {
reviver = argv[1]; reviver = argv[1];
root = JS_NewObject(ctx); root = JS_NewObject(ctx);
if (JS_IsException(root)) { if (JS_IsException(root)) {
@@ -25638,7 +25438,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
if (JS_IsException(f)) if (JS_IsException(f))
goto exception; goto exception;
if (JS_IsFunction(ctx, f)) { if (JS_IsFunction(f)) {
v = JS_CallFree(ctx, f, val, 1, &key); v = JS_CallFree(ctx, f, val, 1, &key);
JS_FreeValue(ctx, val); JS_FreeValue(ctx, val);
val = v; val = v;
@@ -25662,7 +25462,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
switch (JS_VALUE_GET_NORM_TAG(val)) { switch (JS_VALUE_GET_NORM_TAG(val)) {
case JS_TAG_ARRAY: case JS_TAG_ARRAY:
case JS_TAG_OBJECT: case JS_TAG_OBJECT:
if (JS_IsFunction(ctx, val)) if (JS_IsFunction(val))
break; break;
case JS_TAG_STRING: case JS_TAG_STRING:
case JS_TAG_STRING_ROPE: case JS_TAG_STRING_ROPE:
@@ -25872,7 +25672,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
jsc->stack = JS_NewArray(ctx); jsc->stack = JS_NewArray(ctx);
if (JS_IsException(jsc->stack)) if (JS_IsException(jsc->stack))
goto exception; goto exception;
if (JS_IsFunction(ctx, replacer)) { if (JS_IsFunction(replacer)) {
jsc->replacer_func = replacer; jsc->replacer_func = replacer;
} else { } else {
res = JS_IsArray(ctx, replacer); res = JS_IsArray(ctx, replacer);
@@ -27027,7 +26827,7 @@ array_fail:
} }
/* Handle function - return source or native stub */ /* Handle function - return source or native stub */
if (JS_IsFunction(ctx, arg)) { if (JS_IsFunction(arg)) {
JSFunction *fn = JS_VALUE_GET_FUNCTION(arg); JSFunction *fn = JS_VALUE_GET_FUNCTION(arg);
if (fn->kind == JS_FUNC_KIND_BYTECODE) { if (fn->kind == JS_FUNC_KIND_BYTECODE) {
JSFunctionBytecode *b = fn->u.func.function_bytecode; JSFunctionBytecode *b = fn->u.func.function_bytecode;
@@ -27236,7 +27036,7 @@ static JSValue make_replacement(JSContext *ctx, int argc, JSValueConst *argv, in
{ {
JSValue rep; JSValue rep;
if (argc > 2 && JS_IsFunction(ctx, argv[2])) { if (argc > 2 && JS_IsFunction(argv[2])) {
JSValue args[2]; JSValue args[2];
args[0] = match_val; args[0] = match_val;
args[1] = JS_NewInt32(ctx, found); args[1] = JS_NewInt32(ctx, found);
@@ -27259,7 +27059,7 @@ static int JS_IsRegExp(JSContext *ctx, JSValueConst v)
JSValue exec = JS_GetPropertyStr(ctx, v, "exec"); JSValue exec = JS_GetPropertyStr(ctx, v, "exec");
if (JS_IsException(exec)) return -1; if (JS_IsException(exec)) return -1;
int ok = JS_IsFunction(ctx, exec); int ok = JS_IsFunction(exec);
JS_FreeValue(ctx, exec); JS_FreeValue(ctx, exec);
return ok; return ok;
} }
@@ -27843,7 +27643,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val,
JSValue result = JS_NewArrayLen(ctx, len); JSValue result = JS_NewArrayLen(ctx, len);
if (JS_IsException(result)) return result; if (JS_IsException(result)) return result;
if (JS_IsFunction(ctx, argv[1])) { if (JS_IsFunction(argv[1])) {
/* Fill with function results */ /* Fill with function results */
JSValueConst func = argv[1]; JSValueConst func = argv[1];
int arity = 0; int arity = 0;
@@ -27896,7 +27696,7 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val,
return result; return result;
} }
if (JS_IsFunction(ctx, argv[1])) { if (JS_IsFunction(argv[1])) {
/* Map */ /* Map */
JSValueConst func = argv[1]; JSValueConst func = argv[1];
int arity = (JSFunction*)JS_VALUE_GET_FUNCTION(argv[1])->length; int arity = (JSFunction*)JS_VALUE_GET_FUNCTION(argv[1])->length;
@@ -28297,7 +28097,7 @@ static JSValue js_cell_array_reduce(JSContext *ctx, JSValueConst this_val,
{ {
if (argc < 2) return JS_NULL; if (argc < 2) return JS_NULL;
if (!JS_IsArray(ctx, argv[0])) return JS_NULL; if (!JS_IsArray(ctx, argv[0])) return JS_NULL;
if (!JS_IsFunction(ctx, argv[1])) return JS_NULL; if (!JS_IsFunction(argv[1])) return JS_NULL;
JSValueConst arr = argv[0]; JSValueConst arr = argv[0];
JSValueConst func = argv[1]; JSValueConst func = argv[1];
@@ -28387,13 +28187,12 @@ static JSValue js_cell_array_for(JSContext *ctx, JSValueConst this_val,
{ {
if (argc < 2) return JS_NULL; if (argc < 2) return JS_NULL;
if (!JS_IsArray(ctx, argv[0])) return JS_NULL; if (!JS_IsArray(ctx, argv[0])) return JS_NULL;
if (!JS_IsFunction(ctx, argv[1])) return JS_NULL; if (!JS_IsFunction(argv[1])) return JS_NULL;
JSArray *arr = JS_VALUE_GET_ARRAY(argv[0]);
JSValueConst arr = argv[0];
JSValueConst func = argv[1]; JSValueConst func = argv[1];
int64_t len; int len = arr->len;
if (js_get_length64(ctx, &len, arr))
return JS_EXCEPTION;
if (len == 0) return JS_NULL; if (len == 0) return JS_NULL;
int reverse = argc > 2 && JS_ToBool(ctx, argv[2]); int reverse = argc > 2 && JS_ToBool(ctx, argv[2]);
@@ -28403,18 +28202,21 @@ static JSValue js_cell_array_for(JSContext *ctx, JSValueConst this_val,
int arity = (JSFunction*)JS_VALUE_GET_FUNCTION(argv[1])->length; int arity = (JSFunction*)JS_VALUE_GET_FUNCTION(argv[1])->length;
if (reverse) { if (reverse) {
for (int64_t i = len - 1; i >= 0; i--) { for (int i = len - 1; i >= 0; i--) {
JSValue item = JS_GetPropertyInt64(ctx, arr, i);
if (JS_IsException(item)) return JS_EXCEPTION;
JSValue result; JSValue result;
if (arity == 1) { if (arity == 1) {
result = JS_Call(ctx, func, JS_NULL, 1, &item); JSValue item = JS_DupValue(ctx, arr->values[i]);
result = JS_CallInternal(ctx, func, JS_NULL, 1, &item, 0);
JS_FreeValue(ctx, item);
} else { } else {
JSValue args[2] = { item, JS_NewInt64(ctx, i) }; JSValue args[2];
result = JS_Call(ctx, func, JS_NULL, 2, args); args[0] = JS_DupValue(ctx, arr->values[i]);
args[1] = JS_NewInt32(ctx, i);
result = JS_CallInternal(ctx, func, JS_NULL, 2, args, 0);
JS_FreeValue(ctx, args[0]);
JS_FreeValue(ctx, args[1]); JS_FreeValue(ctx, args[1]);
} }
JS_FreeValue(ctx, item);
if (JS_IsException(result)) return JS_EXCEPTION; if (JS_IsException(result)) return JS_EXCEPTION;
if (!JS_IsNull(exit_val) && js_strict_eq(ctx, result, exit_val)) { if (!JS_IsNull(exit_val) && js_strict_eq(ctx, result, exit_val)) {
return result; return result;
@@ -28422,18 +28224,21 @@ static JSValue js_cell_array_for(JSContext *ctx, JSValueConst this_val,
JS_FreeValue(ctx, result); JS_FreeValue(ctx, result);
} }
} else { } else {
for (int64_t i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
JSValue item = JS_GetPropertyInt64(ctx, arr, i);
if (JS_IsException(item)) return JS_EXCEPTION;
JSValue result; JSValue result;
if (arity == 1) { if (arity == 1) {
result = JS_Call(ctx, func, JS_NULL, 1, &item); JSValue item = JS_DupValue(ctx, arr->values[i]);
result = JS_CallInternal(ctx, func, JS_NULL, 1, &item, 0);
JS_FreeValue(ctx, item);
} else { } else {
JSValue args[2] = { item, JS_NewInt64(ctx, i) }; JSValue args[2];
result = JS_Call(ctx, func, JS_NULL, 2, args); args[0] = JS_DupValue(ctx, arr->values[i]);
args[1] = JS_NewInt32(ctx, i);
result = JS_CallInternal(ctx, func, JS_NULL, 2, args, 0);
JS_FreeValue(ctx, args[0]);
JS_FreeValue(ctx, args[1]); JS_FreeValue(ctx, args[1]);
} }
JS_FreeValue(ctx, item);
if (JS_IsException(result)) return JS_EXCEPTION; if (JS_IsException(result)) return JS_EXCEPTION;
if (!JS_IsNull(exit_val) && js_strict_eq(ctx, result, exit_val)) { if (!JS_IsNull(exit_val) && js_strict_eq(ctx, result, exit_val)) {
return result; return result;
@@ -28466,7 +28271,7 @@ static JSValue js_cell_array_find(JSContext *ctx, JSValueConst this_val,
from = reverse ? len - 1 : 0; from = reverse ? len - 1 : 0;
} }
if (!JS_IsFunction(ctx, argv[1])) { if (!JS_IsFunction(argv[1])) {
/* Compare exactly */ /* Compare exactly */
JSValue target = argv[1]; JSValue target = argv[1];
if (reverse) { if (reverse) {
@@ -28549,7 +28354,7 @@ static JSValue js_cell_array_filter(JSContext *ctx, JSValueConst this_val,
{ {
if (argc < 2) return JS_NULL; if (argc < 2) return JS_NULL;
if (!JS_IsArray(ctx, argv[0])) return JS_NULL; if (!JS_IsArray(ctx, argv[0])) return JS_NULL;
if (!JS_IsFunction(ctx, argv[1])) return JS_NULL; if (!JS_IsFunction(argv[1])) return JS_NULL;
JSValueConst arr = argv[0]; JSValueConst arr = argv[0];
JSValueConst func = argv[1]; JSValueConst func = argv[1];
@@ -28768,7 +28573,7 @@ static JSValue js_cell_object(JSContext *ctx, JSValueConst this_val,
JSValue arg = argv[0]; JSValue arg = argv[0];
/* object(object) - shallow mutable copy */ /* object(object) - shallow mutable copy */
if (JS_IsObject(arg) && !JS_IsArray(ctx, arg) && !JS_IsFunction(ctx, arg)) { if (JS_IsObject(arg) && !JS_IsArray(ctx, arg) && !JS_IsFunction(arg)) {
if (argc < 2 || JS_IsNull(argv[1])) { if (argc < 2 || JS_IsNull(argv[1])) {
/* Shallow copy */ /* Shallow copy */
JSValue result = JS_NewObject(ctx); JSValue result = JS_NewObject(ctx);
@@ -28896,7 +28701,7 @@ static JSValue js_cell_object(JSContext *ctx, JSValueConst this_val,
JSValue val; JSValue val;
if (argc < 2 || JS_IsNull(argv[1])) { if (argc < 2 || JS_IsNull(argv[1])) {
val = JS_TRUE; val = JS_TRUE;
} else if (JS_IsFunction(ctx, argv[1])) { } else if (JS_IsFunction(argv[1])) {
val = JS_Call(ctx, argv[1], JS_NULL, 1, &key); val = JS_Call(ctx, argv[1], JS_NULL, 1, &key);
if (JS_IsException(val)) { if (JS_IsException(val)) {
JS_FreeAtom(ctx, atom); JS_FreeAtom(ctx, atom);
@@ -28927,7 +28732,7 @@ static JSValue js_cell_fn_apply(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
if (argc < 1) return JS_NULL; if (argc < 1) return JS_NULL;
if (!JS_IsFunction(ctx, argv[0])) return JS_DupValue(ctx, argv[0]); if (!JS_IsFunction(argv[0])) return JS_DupValue(ctx, argv[0]);
JSValueConst func = argv[0]; JSValueConst func = argv[0];
@@ -29028,7 +28833,7 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst this_val,
if (JS_IsBool(argv[1])) { if (JS_IsBool(argv[1])) {
int is_one = JS_ToBool(ctx, argv[1]); int is_one = JS_ToBool(ctx, argv[1]);
bd = blob_new_with_fill((size_t)length_bits, is_one); bd = blob_new_with_fill((size_t)length_bits, is_one);
} else if (JS_IsFunction(ctx, argv[1])) { } else if (JS_IsFunction(argv[1])) {
/* Random function provided */ /* Random function provided */
size_t bytes = (length_bits + 7) / 8; size_t bytes = (length_bits + 7) / 8;
bd = blob_new((size_t)length_bits); bd = blob_new((size_t)length_bits);
@@ -29893,7 +29698,7 @@ static JSValue js_cell_splat(JSContext *ctx, JSValueConst this_val,
/* Call to_data if present */ /* Call to_data if present */
JSValue to_data = JS_GetPropertyStr(ctx, obj, "to_data"); JSValue to_data = JS_GetPropertyStr(ctx, obj, "to_data");
if (JS_IsFunction(ctx, to_data)) { if (JS_IsFunction(to_data)) {
JSValue args[1] = { result }; JSValue args[1] = { result };
JSValue extra = JS_Call(ctx, to_data, obj, 1, args); JSValue extra = JS_Call(ctx, to_data, obj, 1, args);
if (!JS_IsException(extra) && JS_IsObject(extra)) { if (!JS_IsException(extra) && JS_IsObject(extra)) {
@@ -29933,7 +29738,7 @@ static JSValue js_cell_length(JSContext *ctx, JSValueConst this_val,
return JS_NULL; return JS_NULL;
/* Functions return arity (accessed directly, not via properties) */ /* Functions return arity (accessed directly, not via properties) */
if (JS_IsFunction(ctx, val)) { if (JS_IsFunction(val)) {
JSFunction *f = JS_VALUE_GET_FUNCTION(val); JSFunction *f = JS_VALUE_GET_FUNCTION(val);
return JS_NewInt32(ctx, f->length); return JS_NewInt32(ctx, f->length);
} }
@@ -29963,7 +29768,7 @@ static JSValue js_cell_length(JSContext *ctx, JSValueConst this_val,
if (JS_IsObject(val)) { if (JS_IsObject(val)) {
JSValue len = JS_GetPropertyStr(ctx, val, "length"); JSValue len = JS_GetPropertyStr(ctx, val, "length");
if (!JS_IsException(len) && !JS_IsNull(len)) { if (!JS_IsException(len) && !JS_IsNull(len)) {
if (JS_IsFunction(ctx, len)) { if (JS_IsFunction(len)) {
JSValue result = JS_Call(ctx, len, val, 0, NULL); JSValue result = JS_Call(ctx, len, val, 0, NULL);
JS_FreeValue(ctx, len); JS_FreeValue(ctx, len);
return result; return result;
@@ -29991,7 +29796,7 @@ static JSValue js_cell_call(JSContext *ctx, JSValueConst this_val,
return JS_ThrowTypeError(ctx, "call requires a function argument"); return JS_ThrowTypeError(ctx, "call requires a function argument");
JSValue func = argv[0]; JSValue func = argv[0];
if (!JS_IsFunction(ctx, func)) if (!JS_IsFunction(func))
return JS_ThrowTypeError(ctx, "first argument must be a function"); return JS_ThrowTypeError(ctx, "first argument must be a function");
JSValue this_arg = JS_NULL; JSValue this_arg = JS_NULL;
@@ -30042,7 +29847,7 @@ static JSValue js_cell_is_data(JSContext *ctx, JSValueConst this_val,
JSValue val = argv[0]; JSValue val = argv[0];
if (!JS_IsObject(val)) return JS_FALSE; if (!JS_IsObject(val)) return JS_FALSE;
if (JS_IsArray(ctx, val)) return JS_FALSE; if (JS_IsArray(ctx, val)) return JS_FALSE;
if (JS_IsFunction(ctx, val)) return JS_FALSE; if (JS_IsFunction(val)) return JS_FALSE;
if (js_get_blob(ctx, val)) return JS_FALSE; if (js_get_blob(ctx, val)) return JS_FALSE;
/* Check if it's a plain object (prototype is Object.prototype or null) */ /* Check if it's a plain object (prototype is Object.prototype or null) */
return JS_TRUE; return JS_TRUE;
@@ -30053,7 +29858,7 @@ static JSValue js_cell_is_function(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
if (argc < 1) return JS_FALSE; if (argc < 1) return JS_FALSE;
return JS_NewBool(ctx, JS_IsFunction(ctx, argv[0])); return JS_NewBool(ctx, JS_IsFunction(argv[0]));
} }
/* is_logical(val) - check if value is a boolean (true or false) */ /* is_logical(val) - check if value is a boolean (true or false) */
@@ -30164,7 +29969,7 @@ static JSValue js_cell_is_proto(JSContext *ctx, JSValueConst this_val,
JSValue proto = JS_GetPrototype(ctx, val); JSValue proto = JS_GetPrototype(ctx, val);
while (!JS_IsNull(proto) && !JS_IsException(proto)) { while (!JS_IsNull(proto) && !JS_IsException(proto)) {
/* If master is a function with prototype property, check that */ /* If master is a function with prototype property, check that */
if (JS_IsFunction(ctx, master)) { if (JS_IsFunction(master)) {
JSValue master_proto = JS_GetPropertyStr(ctx, master, "prototype"); JSValue master_proto = JS_GetPropertyStr(ctx, master, "prototype");
if (!JS_IsException(master_proto) && !JS_IsNull(master_proto)) { if (!JS_IsException(master_proto) && !JS_IsNull(master_proto)) {
JSObject *p1 = JS_VALUE_GET_OBJ(proto); JSObject *p1 = JS_VALUE_GET_OBJ(proto);
@@ -30436,7 +30241,7 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg)
{ {
memset(dbg, 0, sizeof(*dbg)); memset(dbg, 0, sizeof(*dbg));
if (!JS_IsFunction(js, fn)) return; if (!JS_IsFunction(fn)) return;
JSFunction *f = JS_VALUE_GET_FUNCTION(fn); JSFunction *f = JS_VALUE_GET_FUNCTION(fn);
const char *fn_name = NULL; const char *fn_name = NULL;
@@ -31155,7 +30960,7 @@ static JSValue js_cell_json_decode(JSContext *ctx, JSValueConst this_val,
JS_FreeCString(ctx, str); JS_FreeCString(ctx, str);
/* Apply reviver if provided */ /* Apply reviver if provided */
if (argc > 1 && JS_IsFunction(ctx, argv[1]) && !JS_IsException(result)) { if (argc > 1 && JS_IsFunction(argv[1]) && !JS_IsException(result)) {
/* Create wrapper object to pass to reviver */ /* Create wrapper object to pass to reviver */
JSValue wrapper = JS_NewObject(ctx); JSValue wrapper = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, wrapper, "", result); JS_SetPropertyStr(ctx, wrapper, "", result);

View File

@@ -533,17 +533,23 @@ static inline JS_BOOL JS_IsString(JSValueConst v)
JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE; JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE;
} }
static inline JS_BOOL JS_IsText(JSValueConst v) { return JS_IsString(v); }
static inline JS_BOOL JS_IsSymbol(JSValueConst v) static inline JS_BOOL JS_IsSymbol(JSValueConst v)
{ {
return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL;
} }
static inline JS_BOOL JS_IsFunction(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_FUNCTION;
}
static inline JS_BOOL JS_IsObject(JSValueConst v) static inline JS_BOOL JS_IsObject(JSValueConst v)
{ {
return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;
} }
int JS_IsArray(JSContext *ctx, JSValueConst val); int JS_IsArray(JSContext *ctx, JSValueConst val);
JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
// Fundamental // Fundamental
int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres); int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres);

View File

@@ -436,7 +436,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
if (actor->disrupt) return; if (actor->disrupt) return;
JS_FreeValue(actor->context, actor->unneeded); JS_FreeValue(actor->context, actor->unneeded);
if (!JS_IsFunction(actor->context, fn)) { if (!JS_IsFunction(fn)) {
actor->unneeded = JS_NULL; actor->unneeded = JS_NULL;
goto END; goto END;
} }

View File

@@ -1077,7 +1077,7 @@ return {
var parent = {a: 1} var parent = {a: 1}
var mixin1 = {b: 2} var mixin1 = {b: 2}
var mixin2 = {c: 3} var mixin2 = {c: 3}
var child = meme(parent, mixin1, mixin2) var child = meme(parent, [mixin1, mixin2])
if (child.a != 1 || child.b != 2 || child.c != 3) throw "meme multiple mixins failed" if (child.a != 1 || child.b != 2 || child.c != 3) throw "meme multiple mixins failed"
}, },
@@ -1847,7 +1847,7 @@ return {
var fn = function() {} var fn = function() {}
var caught = false var caught = false
try { try {
var x = fn["length"] var x = fn["length"]()
} catch (e) { } catch (e) {
caught = true caught = true
} }
@@ -1875,14 +1875,14 @@ return {
test_call_invokes_function: function() { test_call_invokes_function: function() {
var fn = function(a, b) { return a + b } var fn = function(a, b) { return a + b }
var result = call(fn, null, 3, 4) var result = call(fn, null, [3, 4])
if (result != 7) throw "call(fn, null, 3, 4) should return 7" if (result != 7) throw "call(fn, null, 3, 4) should return 7"
}, },
test_call_with_this_binding: function() { test_call_with_this_binding: function() {
var obj = { value: 10 } var obj = { value: 10 }
var fn = function(x) { return this.value + x } var fn = function(x) { return this.value + x }
var result = call(fn, obj, 5) var result = call(fn, obj, [5])
if (result != 15) throw "call(fn, obj, 5) should return 15" if (result != 15) throw "call(fn, obj, 5) should return 15"
}, },
@@ -2071,8 +2071,13 @@ return {
var proxy = function(name, args) { var proxy = function(name, args) {
return `key:${name}` return `key:${name}`
} }
var result = proxy[42]() var caught = false
if (result != "key:42") throw "proxy with integer bracket key failed" try {
var result = proxy[42]()
} catch (e) {
caught = true
}
if (!caught) throw "proxy with integer bracket key should throw"
}, },
// ============================================================================ // ============================================================================
@@ -3036,7 +3041,7 @@ return {
test_call_many_args: function() { test_call_many_args: function() {
var fn = function(a, b, c, d) { return a * b + c * d } var fn = function(a, b, c, d) { return a * b + c * d }
var result = call(fn, null, 2, 3, 4, 5) var result = call(fn, null, [2, 3, 4, 5])
if (result != 26) throw "call many args failed" if (result != 26) throw "call many args failed"
}, },
@@ -3045,7 +3050,7 @@ return {
value: 10, value: 10,
multiply: function(x) { return this.value * x } multiply: function(x) { return this.value * x }
} }
var result = call(obj.multiply, obj, 5) var result = call(obj.multiply, obj, [5])
if (result != 50) throw "call method style failed" if (result != 50) throw "call method style failed"
}, },