massive cleanup

This commit is contained in:
2026-02-04 14:26:17 -06:00
parent d4635f2a75
commit 2c9d039271
2 changed files with 89 additions and 649 deletions

View File

@@ -1735,7 +1735,6 @@ enum OPCodeEnum {
static JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
static JSValue js_call_bound_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
static JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags);
static JSValue JS_EvalObject (JSContext *ctx, JSValue this_obj, JSValue val, int flags, int scope_idx);
int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop);
JSValue __attribute__ ((format (printf, 2, 3)))
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
@@ -1820,7 +1819,6 @@ static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *ar
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx);
static int js_string_compare (JSContext *ctx, const JSText *p1, const JSText *p2);
static JSValue JS_ToNumber (JSContext *ctx, JSValue val);
static int JS_SetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
static int JS_GetOwnPropertyInternal (JSContext *ctx,
@@ -1832,7 +1830,6 @@ static __exception int js_get_length32 (JSContext *ctx, uint32_t *pres, JSValue
static __exception int js_get_length64 (JSContext *ctx, int64_t *pres, JSValue obj);
static void free_arg_list (JSContext *ctx, JSValue *tab, uint32_t len);
static JSValue *build_arg_list (JSContext *ctx, uint32_t *plen, JSValue *parray_arg);
static BOOL js_get_fast_array (JSContext *ctx, JSValue obj, JSValue **arrpp, uint32_t *countp);
static JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_error_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
@@ -2091,7 +2088,7 @@ static inline BOOL js_check_stack_overflow (JSRuntime *rt,
size_t alloca_size) {
uintptr_t sp;
sp = js_get_stack_pointer () - alloca_size;
return unlikely (sp < rt->stack_limit);
return unlikely (sp < (uintptr_t)rt->stack_limit);
}
#endif
@@ -3357,22 +3354,6 @@ void JS_FreeCString (JSContext *ctx, const char *ptr) {
(void)ptr;
}
/* return < 0, 0 or > 0 */
static int js_string_compare (JSContext *ctx, const JSText *p1, const JSText *p2) {
int len, i;
int len1 = (int)JSText_len (p1);
int len2 = (int)JSText_len (p2);
(void)ctx;
len = min_int (len1, len2);
for (i = 0; i < len; i++) {
uint32_t c1 = string_get (p1, i);
uint32_t c2 = string_get (p2, i);
if (c1 != c2) return c1 < c2 ? -1 : 1;
}
if (len1 == len2) return 0;
return len1 < len2 ? -1 : 1;
}
static JSValue JS_ConcatString1 (JSContext *ctx, const JSText *p1, const JSText *p2) {
JSText *p;
uint32_t len;
@@ -3380,8 +3361,7 @@ static JSValue JS_ConcatString1 (JSContext *ctx, const JSText *p1, const JSText
int len2 = (int)JSText_len (p2);
len = len1 + len2;
if (len > JS_STRING_LEN_MAX)
return JS_ThrowInternalError (ctx, "string too long");
/* len is uint32_t, JS_STRING_LEN_MAX is 56 bits, so this always fits */
p = js_alloc_string (ctx, len);
if (!p) return JS_EXCEPTION;
/* Pack first string */
@@ -3418,18 +3398,6 @@ static BOOL JS_ConcatStringInPlace (JSContext *ctx, JSText *p1, JSValue op2) {
return FALSE;
}
static JSValue JS_ConcatString2 (JSContext *ctx, JSValue op1, JSValue op2) {
JSValue ret;
JSText *p1, *p2;
p1 = JS_VALUE_GET_STRING (op1);
if (JS_ConcatStringInPlace (ctx, p1, op2)) {
return op1;
}
p2 = JS_VALUE_GET_STRING (op2);
ret = JS_ConcatString1 (ctx, p1, p2);
return ret;
}
/* Helper for string value comparison (handles immediate and heap strings) */
static int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, BOOL eq_only) {
(void)ctx;
@@ -3527,13 +3495,6 @@ static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) {
return ret_val;
}
static JSRecord *get_proto_obj (JSValue proto_val) {
if (!JS_IsRecord (proto_val))
return NULL;
else
return JS_VALUE_GET_OBJ (proto_val);
}
/* WARNING: proto must be an object or JS_NULL */
JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto_val, JSClassID class_id) {
JSGCRef proto_ref;
@@ -3610,12 +3571,6 @@ static JSFunctionBytecode *JS_GetFunctionBytecode (JSValue val) {
return f->u.func.function_bytecode;
}
static JSValue js_get_function_name (JSContext *ctx, JSValue fn) {
JSValue name_str = JS_NULL;
// TODO: implement
return name_str;
}
// TODO: needs reworked
static int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj) {
(void)ctx;
@@ -3758,28 +3713,6 @@ static JSValue js_new_function (JSContext *ctx, JSFunctionKind kind) {
return JS_MKPTR (func);
}
/* Allocate GC-managed frame for closure support */
static JSFrame *js_new_frame (JSContext *ctx, JSFunction *func,
JSFrame *caller, uint32_t ret_pc) {
JSFunctionBytecode *b = func->u.func.function_bytecode;
uint16_t slot_count = b->arg_count + b->var_count + b->stack_size;
size_t size = sizeof (JSFrame) + slot_count * sizeof (JSValue);
JSFrame *f = js_mallocz (ctx, size);
if (!f) return NULL;
f->header = objhdr_make (slot_count, OBJ_FRAME, false, false, false, false);
f->function = JS_MKPTR(func);
f->caller = caller ? JS_MKPTR(caller) : JS_NULL;
f->return_pc = ret_pc;
/* Initialize all slots to JS_NULL */
for (int i = 0; i < slot_count; i++)
f->slots[i] = JS_NULL;
return f;
}
/* Get pointer to an upvalue in outer scope frame chain.
depth=0 is current frame, depth=1 is immediate outer, etc.
Returns NULL if depth exceeds the frame chain.
@@ -3797,76 +3730,6 @@ static inline JSValue *get_upvalue_ptr (JSValue frame_val, int depth, int slot)
return &frame->slots[slot];
}
/* Compute memory used by various object types */
/* XXX: poor man's approach to handling multiply referenced objects */
typedef struct JSMemoryUsage_helper {
double memory_used_count;
double str_count;
double str_size;
int64_t js_func_count;
double js_func_size;
int64_t js_func_code_size;
int64_t js_func_pc2line_count;
int64_t js_func_pc2line_size;
} JSMemoryUsage_helper;
static void compute_value_size (JSValue val, JSMemoryUsage_helper *hp);
static void compute_JSText_size (JSText *str, JSMemoryUsage_helper *hp) {
/* UTF-32 packs 2 chars per 64-bit word */
word_t len = JSText_len (str);
size_t data_words = (len + 1) / 2;
hp->str_count += 1;
hp->str_size += sizeof (*str) + data_words * sizeof (uint64_t);
}
static void compute_bytecode_size (JSFunctionBytecode *b,
JSMemoryUsage_helper *hp) {
int memory_used_count, js_func_size, i;
memory_used_count = 0;
js_func_size = offsetof (JSFunctionBytecode, debug);
if (b->vardefs) {
js_func_size += (b->arg_count + b->var_count) * sizeof (*b->vardefs);
}
if (b->cpool) {
js_func_size += b->cpool_count * sizeof (*b->cpool);
for (i = 0; i < b->cpool_count; i++) {
JSValue val = b->cpool[i];
compute_value_size (val, hp);
}
}
if (b->closure_var) {
js_func_size += b->closure_var_count * sizeof (*b->closure_var);
}
if (!b->read_only_bytecode && b->byte_code_buf) {
hp->js_func_code_size += b->byte_code_len;
}
if (b->has_debug) {
js_func_size += sizeof (*b) - offsetof (JSFunctionBytecode, debug);
if (b->debug.source) {
memory_used_count++;
js_func_size += b->debug.source_len + 1;
}
if (b->debug.pc2line_len) {
memory_used_count++;
hp->js_func_pc2line_count += 1;
hp->js_func_pc2line_size += b->debug.pc2line_len;
}
}
hp->js_func_size += js_func_size;
hp->js_func_count += 1;
hp->memory_used_count += memory_used_count;
}
static void compute_value_size (JSValue val, JSMemoryUsage_helper *hp) {
switch (JS_VALUE_GET_TAG (val)) {
case JS_TAG_STRING:
compute_JSText_size (JS_VALUE_GET_STRING (val), hp);
break;
}
}
void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s) {
}
@@ -4007,28 +3870,6 @@ fail:
return 0;
}
/* return a string property without executing arbitrary JS code (used
when dumping the stack trace or in debug print). */
static const char *get_prop_string (JSContext *ctx, JSValue obj, JSValue prop) {
if (!JS_IsRecord (obj)) return NULL;
JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj);
int slot = rec_find_slot (rec, prop);
if (slot <= 0) {
/* we look at one level in the prototype to handle the 'name'
field of the Error objects */
JSRecord *proto = rec->proto;
if (!proto) return NULL;
slot = rec_find_slot (proto, prop);
if (slot <= 0) return NULL;
rec = proto;
}
JSValue val = rec->slots[slot].val;
if (!JS_IsText (val)) return NULL;
return JS_ToCString (ctx, val);
}
/* in order to avoid executing arbitrary code during the stack trace
generation, we only look at simple 'name' properties containing a
string. */
@@ -4257,12 +4098,6 @@ static JSValue JS_ThrowTypeErrorNotAnObject (JSContext *ctx) {
return JS_ThrowTypeError (ctx, "not an object");
}
static JSValue JS_ThrowReferenceErrorNotDefined (JSContext *ctx,
JSValue name) {
char buf[KEY_GET_STR_BUF_SIZE];
return JS_ThrowReferenceError (ctx, "'%s' is not defined", JS_KeyGetStr (ctx, buf, sizeof (buf), name));
}
static JSValue JS_ThrowReferenceErrorUninitialized (JSContext *ctx,
JSValue name) {
char buf[KEY_GET_STR_BUF_SIZE];
@@ -4582,29 +4417,6 @@ static JSValue JS_Invoke (JSContext *ctx, JSValue this_val, JSValue method, int
return ret;
}
/* Simplified delete_property using JSRecord.
Deletion fails only if object is stone. */
static int delete_property (JSContext *ctx, JSRecord *rec, JSValue key) {
/* Cannot delete from a stone object */
if (obj_is_stone (rec)) return FALSE;
int slot = rec_find_slot (rec, key);
if (slot < 0) {
/* not found - success (nothing to delete) */
return TRUE;
}
/* Free key and value */
/* Mark as tombstone */
rec->slots[slot].key = JS_EXCEPTION; /* tombstone marker */
rec->slots[slot].val = JS_NULL;
rec->len--;
/* tombs tracking removed - not needed with copying GC */
return TRUE;
}
/* GC-SAFE: May trigger GC if record needs to resize */
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val) {
if (!JS_IsRecord (this_obj)) {
@@ -5674,20 +5486,6 @@ static void js_print_raw_string (JSPrintValueState *s, JSValue val) {
js_print_raw_string2 (s, val, FALSE);
}
static BOOL is_ascii_ident (const JSText *p) {
int i, c;
int len = (int)JSText_len (p);
if (len == 0) return FALSE;
for (i = 0; i < len; i++) {
c = string_get (p, i);
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
return FALSE;
}
return TRUE;
}
static void js_print_comma (JSPrintValueState *s, int *pcomma_state) {
switch (*pcomma_state) {
case 0:
@@ -5707,75 +5505,6 @@ static void js_print_more_items (JSPrintValueState *s, int *pcomma_state, uint32
js_printf (s, "... %u more item%s", n, n > 1 ? "s" : "");
}
static void js_print_object (JSPrintValueState *s, JSRecord *p) {
JSRuntime *rt = s->rt;
int comma_state;
BOOL is_array;
uint32_t i;
comma_state = 0;
is_array = FALSE;
if (REC_GET_CLASS_ID(p) == JS_CLASS_REGEXP && s->ctx && !s->options.raw_dump) {
JSValue str
= js_regexp_toString (s->ctx, JS_MKPTR (p), 0, NULL);
if (JS_IsException (str)) goto default_obj;
js_print_raw_string (s, str);
comma_state = 2;
} else if (REC_GET_CLASS_ID(p) == JS_CLASS_ERROR && s->ctx && !s->options.raw_dump) {
JSValue str
= js_error_toString (s->ctx, JS_MKPTR (p), 0, NULL);
if (JS_IsException (str)) goto default_obj;
js_print_raw_string (s, str);
/* dump the stack if present */
str = JS_GetProperty (s->ctx, JS_MKPTR (p), JS_KEY_stack);
if (JS_IsText (str)) {
js_putc (s, '\n');
js_print_raw_string2 (s, str, TRUE);
}
comma_state = 2;
} else {
default_obj:
if (REC_GET_CLASS_ID(p) != JS_CLASS_OBJECT) {
const char *name = rt->class_array[REC_GET_CLASS_ID(p)].class_name;
if (name) js_printf (s, "%s ", name);
}
js_printf (s, "{ ");
}
/* Print properties from JSRecord */
JSRecord *rec = (JSRecord *)p;
uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
uint32_t j = 0;
for (i = 1; i <= mask; i++) {
JSValue k = rec->slots[i].key;
if (!rec_key_is_empty (k) && !rec_key_is_tomb (k)) {
if (j < s->options.max_item_count) {
js_print_comma (s, &comma_state);
js_print_value (s, k);
js_printf (s, ": ");
js_print_value (s, rec->slots[i].val);
}
j++;
}
}
if (j > s->options.max_item_count)
js_print_more_items (s, &comma_state, j - s->options.max_item_count);
if (!is_array) {
if (comma_state != 2) { js_printf (s, " }"); }
} else {
js_printf (s, " ]");
}
}
static int js_print_stack_index (JSPrintValueState *s, JSRecord *p) {
int i;
for (i = 0; i < s->level; i++)
if (s->print_stack[i] == p) return i;
return -1;
}
static void js_print_value (JSPrintValueState *s, JSValue val) {
uint32_t tag = JS_VALUE_GET_NORM_TAG (val);
const char *str;
@@ -6280,18 +6009,6 @@ static JSValue js_throw_type_error (JSContext *ctx, JSValue this_val, int argc,
#define GLOBAL_VAR_OFFSET 0x40000000
#define ARGUMENT_VAR_OFFSET 0x20000000
/* Access an Array's internal JSValue array if available */
static BOOL js_get_fast_array (JSContext *ctx, JSValue obj, JSValue **arrpp, uint32_t *countp) {
/* Fast path for intrinsic arrays */
if (JS_IsArray (obj)) {
JSArray *arr = JS_VALUE_GET_ARRAY (obj);
*countp = arr->len;
*arrpp = arr->values;
return TRUE;
}
return FALSE;
}
static __exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JSValue source, JSValue excluded, BOOL setprop) {
JSValue keys, key, val;
uint32_t i, key_count;
@@ -6336,11 +6053,6 @@ exception:
return -1;
}
/* only valid inside C functions */
static JSValue JS_GetActiveFunction (JSContext *ctx) {
return ctx->rt->current_stack_frame->cur_func;
}
static JSValue js_closure2 (JSContext *ctx, JSValue func_obj, JSFunctionBytecode *b, JSStackFrame *sf) {
JSFunction *f;
f = JS_VALUE_GET_FUNCTION (func_obj);
@@ -6482,6 +6194,28 @@ static JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue thi
case JS_CFUNC_3:
ret_val = func.f3 (ctx, this_obj, arg_buf[0], arg_buf[1], arg_buf[2]);
break;
case JS_CFUNC_4:
ret_val = func.f4 (ctx, this_obj, arg_buf[0], arg_buf[1], arg_buf[2], arg_buf[3]);
break;
/* Pure functions (no this_val) */
case JS_CFUNC_PURE:
ret_val = func.pure (ctx, argc, arg_buf);
break;
case JS_CFUNC_PURE_0:
ret_val = func.pure0 (ctx);
break;
case JS_CFUNC_PURE_1:
ret_val = func.pure1 (ctx, arg_buf[0]);
break;
case JS_CFUNC_PURE_2:
ret_val = func.pure2 (ctx, arg_buf[0], arg_buf[1]);
break;
case JS_CFUNC_PURE_3:
ret_val = func.pure3 (ctx, arg_buf[0], arg_buf[1], arg_buf[2]);
break;
case JS_CFUNC_PURE_4:
ret_val = func.pure4 (ctx, arg_buf[0], arg_buf[1], arg_buf[2], arg_buf[3]);
break;
default:
abort ();
}
@@ -6531,7 +6265,7 @@ static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj, JSValue
JSStackFrame sf_s, *sf = &sf_s;
const uint8_t *pc;
int opcode, arg_allocated_size, i;
JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
JSValue *stack_buf, *var_buf, *arg_buf, *sp, ret_val;
size_t alloca_size;
#if !DIRECT_DISPATCH
@@ -8280,9 +8014,6 @@ exception:
}
ret_val = JS_EXCEPTION;
done:
/* free the local variables and stack */
for (pval = local_buf; pval < sp; pval++) {
}
rt->current_stack_frame = sf->prev_frame;
if (unlikely (caller_ctx->trace_hook)
@@ -11432,24 +11163,6 @@ static __exception int js_parse_array_literal (JSParseState *s) {
return js_parse_expect (s, ']');
}
/* XXX: remove */
static BOOL has_with_scope (JSFunctionDef *s, int scope_level) {
/* check if scope chain contains a with statement */
while (s) {
int scope_idx = s->scopes[scope_level].first;
while (scope_idx >= 0) {
JSVarDef *vd = &s->vars[scope_idx];
if (js_key_equal_str (vd->var_name, "_with_")) return TRUE;
scope_idx = vd->scope_next;
}
/* check parent scopes */
scope_level = s->parent_scope_level;
s = s->parent;
}
return FALSE;
}
static __exception int get_lvalue (JSParseState *s, int *popcode, int *pscope, JSValue *pname, int *plabel, int *pdepth, BOOL keep, int tok) {
JSFunctionDef *fd;
int opcode, scope, label, depth;
@@ -12949,47 +12662,6 @@ static BOOL is_label (JSParseState *s) {
&& peek_token (s, FALSE) == ':');
}
/* test if the current token is a let keyword. Use simplistic look-ahead
* scanner */
static int is_let (JSParseState *s, int decl_mask) {
int res = FALSE;
const uint8_t *last_token_ptr;
if (token_is_pseudo_keyword (s, JS_KEY_let)) {
JSParsePos pos;
js_parse_get_pos (s, &pos);
for (;;) {
last_token_ptr = s->token.ptr;
if (next_token (s)) {
res = -1;
break;
}
if (s->token.val == '[') {
/* let [ is a syntax restriction:
it never introduces an ExpressionStatement */
res = TRUE;
break;
}
if (s->token.val == '{'
|| (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
|| s->token.val == TOK_YIELD || s->token.val == TOK_AWAIT) {
/* Check for possible ASI if not scanning for Declaration */
/* XXX: should also check that `{` introduces a BindingPattern,
but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
if (!has_lf_in_range (last_token_ptr, s->token.ptr)
|| (decl_mask & DECL_MASK_OTHER)) {
res = TRUE;
break;
}
break;
}
break;
}
if (js_parse_seek_token (s, &pos)) { res = -1; }
}
return res;
}
/* for-in and for-of loops are not supported */
static __exception int js_parse_for_in_of (JSParseState *s, int label_name) {
return js_parse_error (s, "'for in' and 'for of' loops are not supported");
@@ -15097,18 +14769,6 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
return -1;
}
return 0;
fail:
/* continue the copy to keep the atom refcounts consistent */
/* XXX: find a better solution ? */
for (; pos < bc_len; pos = pos_next) {
op = bc_buf[pos];
len = opcode_info[op].size;
pos_next = pos + len;
dbuf_put (&bc_out, bc_buf + pos, len);
}
dbuf_free (&s->byte_code);
s->byte_code = bc_out;
return -1;
}
/* the pc2line table gives a source position for each PC value */
@@ -17061,7 +16721,6 @@ static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *
err = js_parse_program (s);
if (err) {
fail:
free_token (s, &s->token);
ctx->current_parse_fd = NULL; /* Clear GC root before freeing */
js_free_function_def (ctx, fd);
@@ -17102,19 +16761,6 @@ static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *in
return ret;
}
static JSValue JS_EvalObject (JSContext *ctx, JSValue this_obj, JSValue val, int flags, int scope_idx) {
JSValue ret;
const char *str;
size_t len;
if (!JS_IsText (val)) return val;
str = JS_ToCStringLen (ctx, &len, val);
if (!str) return JS_EXCEPTION;
ret = JS_EvalInternal (ctx, this_obj, str, len, "<input>", flags, scope_idx);
JS_FreeCString (ctx, str);
return ret;
}
JSValue JS_EvalThis (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int eval_flags) {
int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
JSValue ret;
@@ -17446,9 +17092,6 @@ static int JS_WriteFunctionBytecode (BCWriterState *s, const uint8_t *bc_buf1, i
js_free (s->ctx, bc_buf);
return 0;
fail:
js_free (s->ctx, bc_buf);
return -1;
}
static void JS_WriteString (BCWriterState *s, JSText *p) {
@@ -17464,87 +17107,6 @@ static void JS_WriteString (BCWriterState *s, JSText *p) {
static int JS_WriteObjectRec (BCWriterState *s, JSValue obj);
static int JS_WriteFunctionTag (BCWriterState *s, JSValue obj) {
JSFunctionBytecode *b = JS_VALUE_GET_PTR (obj);
uint32_t flags;
int idx, i;
bc_put_u8 (s, BC_TAG_FUNCTION_BYTECODE);
flags = idx = 0;
bc_set_flags (&flags, &idx, b->has_prototype, 1);
bc_set_flags (&flags, &idx, b->has_simple_parameter_list, 1);
bc_set_flags (&flags, &idx, b->func_kind, 2);
bc_set_flags (&flags, &idx, b->has_debug, 1);
bc_set_flags (&flags, &idx, b->is_direct_or_indirect_eval, 1);
assert (idx <= 16);
bc_put_u16 (s, flags);
bc_put_u8 (s, b->js_mode);
bc_put_key (s, b->func_name);
bc_put_leb128 (s, b->arg_count);
bc_put_leb128 (s, b->var_count);
bc_put_leb128 (s, b->defined_arg_count);
bc_put_leb128 (s, b->stack_size);
bc_put_leb128 (s, b->closure_var_count);
bc_put_leb128 (s, b->cpool_count);
bc_put_leb128 (s, b->byte_code_len);
if (b->vardefs) {
/* XXX: this field is redundant */
bc_put_leb128 (s, b->arg_count + b->var_count);
for (i = 0; i < b->arg_count + b->var_count; i++) {
JSVarDef *vd = &b->vardefs[i];
bc_put_key (s, vd->var_name);
bc_put_leb128 (s, vd->scope_level);
bc_put_leb128 (s, vd->scope_next + 1);
flags = idx = 0;
bc_set_flags (&flags, &idx, vd->var_kind, 4);
bc_set_flags (&flags, &idx, vd->is_const, 1);
bc_set_flags (&flags, &idx, vd->is_lexical, 1);
bc_set_flags (&flags, &idx, vd->is_captured, 1);
assert (idx <= 8);
bc_put_u8 (s, flags);
}
} else {
bc_put_leb128 (s, 0);
}
for (i = 0; i < b->closure_var_count; i++) {
JSClosureVar *cv = &b->closure_var[i];
bc_put_key (s, cv->var_name);
bc_put_leb128 (s, cv->var_idx);
flags = idx = 0;
bc_set_flags (&flags, &idx, cv->is_local, 1);
bc_set_flags (&flags, &idx, cv->is_arg, 1);
bc_set_flags (&flags, &idx, cv->is_const, 1);
bc_set_flags (&flags, &idx, cv->is_lexical, 1);
bc_set_flags (&flags, &idx, cv->var_kind, 4);
assert (idx <= 8);
bc_put_u8 (s, flags);
}
if (JS_WriteFunctionBytecode (s, b->byte_code_buf, b->byte_code_len))
goto fail;
if (b->has_debug) {
bc_put_key (s, b->debug.filename);
bc_put_leb128 (s, b->debug.pc2line_len);
dbuf_put (&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
if (b->debug.source) {
bc_put_leb128 (s, b->debug.source_len);
dbuf_put (&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len);
} else {
bc_put_leb128 (s, 0);
}
}
for (i = 0; i < b->cpool_count; i++) {
if (JS_WriteObjectRec (s, b->cpool[i])) goto fail;
}
return 0;
fail:
return -1;
}
static int JS_WriteObjectTag (BCWriterState *s, JSValue obj) {
JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (obj);
uint32_t mask = (uint32_t)objhdr_cap56 (rec->mist_hdr);
@@ -17660,7 +17222,6 @@ static int JS_WriteObjectRec (BCWriterState *s, JSValue obj) {
}
break;
default:
invalid_tag:
JS_ThrowInternalError (s->ctx, "unsupported tag (%d)", tag);
goto fail;
}
@@ -17904,10 +17465,7 @@ static JSText *JS_ReadString (BCReaderState *s) {
uint32_t i;
if (bc_get_leb128 (s, &len)) return NULL;
if (len > JS_STRING_LEN_MAX) {
JS_ThrowInternalError (s->ctx, "string too long");
return NULL;
}
/* len is uint32_t, JS_STRING_LEN_MAX is 56 bits, so this always fits */
p = js_alloc_string (s->ctx, len);
if (!p) {
s->error_state = -1;
@@ -18528,33 +18086,6 @@ static JSValue *build_arg_list (JSContext *ctx, uint32_t *plen, JSValue *parray_
return NULL;
}
/* magic value: 0 = normal apply, 2 = Reflect.apply */
static JSValue js_function_apply (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) {
JSValue this_arg, array_arg;
uint32_t len;
JSValue *tab, ret;
JSFunction *f;
if (check_function (ctx, this_val)) return JS_EXCEPTION;
f = JS_VALUE_GET_FUNCTION (this_val);
this_arg = argv[0];
array_arg = argv[1];
if (JS_VALUE_GET_TAG (array_arg) == JS_TAG_NULL && magic != 2) {
return JS_Call (ctx, this_val, this_arg, 0, NULL);
}
/* Fast path: check arity before building arg list */
if (JS_IsArray (array_arg)) {
JSArray *arr = JS_VALUE_GET_ARRAY (array_arg);
if (unlikely (arr->len > f->length))
return JS_ThrowTypeError (ctx, "too many arguments");
}
tab = build_arg_list (ctx, &len, &argv[1]);
if (!tab) return JS_EXCEPTION;
ret = JS_Call (ctx, this_val, this_arg, len, (JSValue *)tab);
free_arg_list (ctx, tab, len);
return ret;
}
/* Error class */
static JSValue js_error_constructor (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) {
JSValue obj, msg;
@@ -18655,15 +18186,6 @@ static JSValue js_array_includes (JSContext *ctx, JSValue this_val, int argc, JS
return JS_NewBool (ctx, TRUE);
}
static int string_cmp (JSText *p1, JSText *p2, int x1, int x2, int len) {
int i, c1, c2;
for (i = 0; i < len; i++) {
if ((c1 = string_get (p1, x1 + i)) != (c2 = string_get (p2, x2 + i)))
return c1 - c2;
}
return 0;
}
/* return < 0 if exception or TRUE/FALSE */
static int js_is_regexp (JSContext *ctx, JSValue obj);
@@ -19131,146 +18653,6 @@ fail:
return JS_EXCEPTION;
}
/* delete portions of a string that match a given regex */
static JSValue JS_RegExpDelete (JSContext *ctx, JSValue this_val, JSValue arg) {
JSRegExp *re = js_get_regexp (ctx, this_val, TRUE);
JSText *str;
JSValue str_val, val;
uint8_t *re_bytecode;
int ret;
uint8_t **capture, *str_buf;
uint16_t *utf16_buf = NULL;
int utf16_len = 0;
int capture_count, shift, re_flags;
int next_src_pos, start, end;
int64_t last_index;
JSText *b;
if (!re) return JS_EXCEPTION;
b = pretext_init (ctx, 0);
if (!b) return JS_EXCEPTION;
capture = NULL;
str_val = JS_ToString (ctx, arg);
if (JS_IsException (str_val)) goto fail;
str = JS_VALUE_GET_STRING (str_val);
re_bytecode = (uint8_t *)re->bytecode->packed;
re_flags = lre_get_flags (re_bytecode);
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
last_index = 0;
} else {
val = JS_GetPropertyStr (ctx, this_val, "lastIndex");
if (JS_IsException (val) || JS_ToLength (ctx, &last_index, val))
goto fail;
}
capture_count = lre_get_capture_count (re_bytecode);
if (capture_count > 0) {
capture = js_malloc (ctx, sizeof (capture[0]) * capture_count * 2);
if (!capture) goto fail;
}
/* Convert UTF-32 string to UTF-16 for regex engine */
utf16_buf = js_string_to_utf16 (ctx, str, &utf16_len);
if (!utf16_buf) goto fail;
shift = 1; /* UTF-16 mode */
str_buf = (uint8_t *)utf16_buf;
next_src_pos = 0;
for (;;) {
if (last_index > utf16_len) break;
ret = lre_exec (capture, re_bytecode, str_buf, last_index, utf16_len, shift, ctx);
if (ret != 1) {
if (ret >= 0) {
if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
if (JS_SetPropertyStr (ctx, this_val, "lastIndex", JS_NewInt32 (ctx, 0))
< 0)
goto fail;
}
} else {
if (ret == LRE_RET_TIMEOUT) {
JS_ThrowInterrupted (ctx);
} else {
JS_ThrowInternalError (ctx, "out of memory in regexp execution");
}
goto fail;
}
break;
}
start = (capture[0] - str_buf) >> shift;
end = (capture[1] - str_buf) >> shift;
last_index = end;
if (next_src_pos < start) {
b = pretext_concat (ctx, b, str, next_src_pos, start);
if (!b) goto fail;
}
next_src_pos = end;
if (!(re_flags & LRE_FLAG_GLOBAL)) {
if (JS_SetPropertyStr (ctx, this_val, "lastIndex", JS_NewInt32 (ctx, end))
< 0)
goto fail;
break;
}
if (end == start) {
/* Advance by one code unit or one code point if unicode mode */
if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= utf16_len) {
end++;
} else {
/* Check for surrogate pair in UTF-16 buffer */
uint16_t c = utf16_buf[end];
end++;
if (is_hi_surrogate (c) && end < utf16_len
&& is_lo_surrogate (utf16_buf[end])) {
end++;
}
}
}
last_index = end;
}
b = pretext_concat (ctx, b, str, next_src_pos, (uint32_t)JSText_len (str));
if (!b) goto fail;
js_free (ctx, capture);
js_free (ctx, utf16_buf);
return pretext_end (ctx, b);
fail:
js_free (ctx, capture);
js_free (ctx, utf16_buf);
return JS_EXCEPTION;
}
static JSValue JS_RegExpExec (JSContext *ctx, JSValue r, JSValue s) {
JSValue method, ret;
method = JS_GetProperty (ctx, r, JS_KEY_exec);
if (JS_IsException (method)) return method;
if (JS_IsFunction (method)) {
ret = JS_Call (ctx, method, r, 1, &s);
if (JS_IsException (ret)) return ret;
if (!JS_IsObject (ret) && !JS_IsNull (ret)) {
return JS_ThrowTypeError (
ctx, "RegExp exec method must return an object or null");
}
return ret;
}
return js_regexp_exec (ctx, r, 1, &s);
}
static int js_is_standard_regexp (JSContext *ctx, JSValue rx) {
JSValue val;
int res;
val = JS_GetPropertyStr (ctx, rx, "constructor");
if (JS_IsException (val)) return -1;
// rx.constructor === RegExp
res = js_strict_eq (ctx, val, ctx->regexp_ctor);
if (res) {
val = JS_GetProperty (ctx, rx, JS_KEY_exec);
if (JS_IsException (val)) return -1;
// rx.exec === RE_exec
res = JS_IsCFunction (val, js_regexp_exec, 0);
}
return res;
}
static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
JS_CFUNC_DEF ("exec", 1, js_regexp_exec),
JS_CFUNC_DEF ("compile", 2, js_regexp_compile),
@@ -19753,7 +19135,6 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
JS_PopGCRef (ctx, &val_ref);
return 0;
}
concat_primitive:
switch (JS_VALUE_GET_NORM_TAG (val_ref.val)) {
case JS_TAG_STRING_IMM:
val_ref.val = JS_ToQuotedString (ctx, val_ref.val);
@@ -25052,7 +24433,7 @@ void js_debug_info (JSContext *js, JSValue fn, js_debug *dbg) {
dbg->closure_n = b->closure_var_count;
dbg->param_n = b->arg_count;
dbg->vararg = 1;
dbg->source = b->debug.source;
dbg->source = (const uint8_t *)b->debug.source;
dbg->srclen = b->debug.source_len;
dbg->line = 0; /* see below */
return;

View File

@@ -176,8 +176,6 @@ void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);
Value Extraction
============================================================ */
#define JS_VALUE_GET_INT(v) ((int)(v) >> 1)
/* Get primary tag (low 2-3 bits) */
static inline int
JS_VALUE_GET_TAG (JSValue v) {
@@ -811,7 +809,14 @@ typedef enum JSCFunctionEnum {
JS_CFUNC_1, /* JSValue f(ctx, this_val, arg0) */
JS_CFUNC_2, /* JSValue f(ctx, this_val, arg0, arg1) */
JS_CFUNC_3, /* JSValue f(ctx, this_val, arg0, arg1, arg2) */
JS_CFUNC_4
JS_CFUNC_4,
/* Pure functions (no this_val) - for global utility functions */
JS_CFUNC_PURE, /* JSValue f(ctx, argc, argv) - generic pure */
JS_CFUNC_PURE_0, /* JSValue f(ctx) */
JS_CFUNC_PURE_1, /* JSValue f(ctx, arg0) */
JS_CFUNC_PURE_2, /* JSValue f(ctx, arg0, arg1) */
JS_CFUNC_PURE_3, /* JSValue f(ctx, arg0, arg1, arg2) */
JS_CFUNC_PURE_4 /* JSValue f(ctx, arg0, arg1, arg2, arg3) */
} JSCFunctionEnum;
/* Fixed-arity C function types for fast paths */
@@ -827,6 +832,16 @@ typedef JSValue JSCFunction4 (JSContext *ctx, JSValue this_val,
JSValue arg0, JSValue arg1,
JSValue arg2, JSValue arg3);
/* Pure function types (no this_val) */
typedef JSValue JSCFunctionPure (JSContext *ctx, int argc, JSValue *argv);
typedef JSValue JSCFunctionPure0 (JSContext *ctx);
typedef JSValue JSCFunctionPure1 (JSContext *ctx, JSValue arg0);
typedef JSValue JSCFunctionPure2 (JSContext *ctx, JSValue arg0, JSValue arg1);
typedef JSValue JSCFunctionPure3 (JSContext *ctx, JSValue arg0, JSValue arg1,
JSValue arg2);
typedef JSValue JSCFunctionPure4 (JSContext *ctx, JSValue arg0, JSValue arg1,
JSValue arg2, JSValue arg3);
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic) (JSContext *ctx, JSValue this_val, int argc,
@@ -839,6 +854,13 @@ typedef union JSCFunctionType {
JSCFunction2 *f2;
JSCFunction3 *f3;
JSCFunction4 *f4;
/* Pure function pointers */
JSCFunctionPure *pure;
JSCFunctionPure0 *pure0;
JSCFunctionPure1 *pure1;
JSCFunctionPure2 *pure2;
JSCFunctionPure3 *pure3;
JSCFunctionPure4 *pure4;
} JSCFunctionType;
JSValue JS_NewCFunction2 (JSContext *ctx, JSCFunction *func, const char *name,
@@ -955,6 +977,43 @@ typedef struct JSCFunctionListEntry {
.u \
= {.func = { 3, JS_CFUNC_3, { .f3 = func1 } } } \
}
/* Pure function (no this_val) macros */
#define JS_CFUNC_PURE_DEF(name, length, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { length, JS_CFUNC_PURE, { .pure = func1 } } } \
}
#define JS_CFUNC_PURE0_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 0, JS_CFUNC_PURE_0, { .pure0 = func1 } } } \
}
#define JS_CFUNC_PURE1_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 1, JS_CFUNC_PURE_1, { .pure1 = func1 } } } \
}
#define JS_CFUNC_PURE2_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 2, JS_CFUNC_PURE_2, { .pure2 = func1 } } } \
}
#define JS_CFUNC_PURE3_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 3, JS_CFUNC_PURE_3, { .pure3 = func1 } } } \
}
#define JS_CFUNC_PURE4_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 4, JS_CFUNC_PURE_4, { .pure4 = func1 } } } \
}
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) \
{ \
name, 0, JS_DEF_CFUNC, magic, .u = { \