diff --git a/packages/runtime/src/scr_array.c b/packages/runtime/src/scr_array.c index b9e0efa74..c7132f528 100644 --- a/packages/runtime/src/scr_array.c +++ b/packages/runtime/src/scr_array.c @@ -5982,8 +5982,8 @@ void scr_arr_copy_metadata(ScrArr *a, ScrDyn *target) { target->v.arr.properties->v.obj.class_name = cls->name; ScrDyn *fields = scr_arr_class_fields(a); if (fields) { - for (size_t i = 0; i < fields->v.obj.len; i++) - scr_dyn_copy_entry(target, fields, &fields->v.obj.entries[i]); + for (size_t i = 0; i < scr_dyn_obj_count(fields); i++) + scr_dyn_copy_prop_at(target, fields, i); scr_dyn_release(fields); } return; @@ -5994,7 +5994,7 @@ void scr_arr_copy_metadata(ScrArr *a, ScrDyn *target) { return; metadata = SCR_AX(a)->metadata; if (!metadata) return; - for (size_t i = 0; i < metadata->v.obj.len; i++) scr_dyn_copy_entry(target, metadata, &metadata->v.obj.entries[i]); + for (size_t i = 0; i < scr_dyn_obj_count(metadata); i++) scr_dyn_copy_prop_at(target, metadata, i); } ScrArr *scr_arr_named_keys(ScrArr *a) { @@ -6008,9 +6008,9 @@ ScrArr *scr_arr_named_keys(ScrArr *a) { scr_arr_push_ref(keys, scr_str_new(x->props[i].key, x->props[i].key_len)); ScrDyn *fields = scr_arr_class_fields(a); if (fields) { - for (size_t i = 0; i < fields->v.obj.len; i++) { - const ScrDynEntry *entry = &fields->v.obj.entries[i]; - if (entry->enumerable) scr_arr_push_ref(keys, scr_str_new(entry->key, entry->key_len)); + for (size_t i = 0; i < scr_dyn_obj_count(fields); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(fields, i); + if (entry.enumerable) scr_arr_push_ref(keys, scr_str_new(entry.key, entry.key_len)); } scr_dyn_release(fields); return keys; @@ -6022,10 +6022,10 @@ ScrArr *scr_arr_named_keys(ScrArr *a) { return keys; } if (!metadata || metadata->kind != SCR_DYN_OBJ) return keys; - for (size_t i = 0; i < metadata->v.obj.len; i++) { - const ScrDynEntry *entry = &metadata->v.obj.entries[i]; - if (!entry->enumerable || scr_arr_prop_index(a, entry->key, entry->key_len) != SIZE_MAX) continue; - scr_arr_push_ref(keys, scr_str_new(entry->key, entry->key_len)); + for (size_t i = 0; i < scr_dyn_obj_count(metadata); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(metadata, i); + if (!entry.enumerable || scr_arr_prop_index(a, entry.key, entry.key_len) != SIZE_MAX) continue; + scr_arr_push_ref(keys, scr_str_new(entry.key, entry.key_len)); } return keys; } @@ -6038,9 +6038,9 @@ bool scr_arr_has_named_key(ScrArr *a, ScrStr *key) { if (metadata && metadata->kind == SCR_DYN_HANDLE && metadata->v.handle.tag == SCR_DYNH_MATCH_SOURCE) return true; /* index, input and groups, the only keys a pending result lists */ if (!metadata || metadata->kind != SCR_DYN_OBJ) return false; - for (size_t i = 0; i < metadata->v.obj.len; i++) { - const ScrDynEntry *entry = &metadata->v.obj.entries[i]; - if (entry->key_len == key->len && memcmp(entry->key, key->data, key->len) == 0) return true; + for (size_t i = 0; i < scr_dyn_obj_count(metadata); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(metadata, i); + if (entry.key_len == key->len && memcmp(entry.key, key->data, key->len) == 0) return true; } return false; } @@ -6067,20 +6067,22 @@ void scr_arr_adopt_view_properties(ScrArr *a, ScrDyn *view, ScrArr *target) { from->prop_cap = cap; } ScrDyn *named = view && view->kind == SCR_DYN_ARR ? view->v.arr.properties : NULL; - if (!named || named->v.obj.len == 0) return; + if (!named || scr_dyn_obj_count(named) == 0) return; /* The view's numeric properties are copies of the native ones (every * write to them goes to the native array), so only the others move. */ bool numeric = false; - for (size_t i = 0; i < named->v.obj.len && !numeric; i++) { + for (size_t i = 0; i < scr_dyn_obj_count(named) && !numeric; i++) { double number; - numeric = scr_arr_numeric_key(named->v.obj.entries[i].key, named->v.obj.entries[i].key_len, &number); + size_t key_len; + const char *key = scr_dyn_obj_key_at(named, i, &key_len); + numeric = scr_arr_numeric_key(key, key_len, &number); } if (numeric) { ScrDyn *rest = scr_dyn_new_obj(); - for (size_t i = 0; i < named->v.obj.len; i++) { - const ScrDynEntry *entry = &named->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(named); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(named, i); double number; - if (!scr_arr_numeric_key(entry->key, entry->key_len, &number)) scr_dyn_copy_entry(rest, named, entry); + if (!scr_arr_numeric_key(entry.key, entry.key_len, &number)) scr_dyn_copy_prop_at(rest, named, i); } named = rest; } else { @@ -6088,7 +6090,7 @@ void scr_arr_adopt_view_properties(ScrArr *a, ScrDyn *view, ScrArr *target) { } ScrArrExt *x = scr_arr_ext(a); ScrDyn *old = x->metadata; - x->metadata = named->v.obj.len ? named : NULL; + x->metadata = scr_dyn_obj_count(named) ? named : NULL; if (!x->metadata) scr_dyn_release(named); scr_dyn_release(old); } diff --git a/packages/runtime/src/scr_assert.c b/packages/runtime/src/scr_assert.c index bf5d1c875..6e9384f15 100644 --- a/packages/runtime/src/scr_assert.c +++ b/packages/runtime/src/scr_assert.c @@ -718,7 +718,7 @@ static bool scr_assert_dyn_deep_eq(const ScrDyn *a, const ScrDyn *b) { if (a->v.arr.properties || b->v.arr.properties) { if (!a->v.arr.properties || !b->v.arr.properties) { const ScrDyn *table = a->v.arr.properties ? a->v.arr.properties : b->v.arr.properties; - if (table->v.obj.len != 0) return false; + if (scr_dyn_obj_count(table) != 0) return false; } else if (!scr_assert_dyn_deep_eq(a->v.arr.properties, b->v.arr.properties)) return false; } return true; @@ -738,11 +738,11 @@ static bool scr_assert_dyn_deep_eq(const ScrDyn *a, const ScrDyn *b) { } else if (a->null_proto != b->null_proto || a->prototype != b->prototype) { return false; } - if (a->v.obj.len != b->v.obj.len) return false; - for (size_t i = 0; i < a->v.obj.len; i++) { - const ScrDynEntry *ent = &a->v.obj.entries[i]; - ScrDyn *bv = scr_dyn_obj_get(b, ent->key, ent->key_len); - if (!bv || !scr_assert_dyn_deep_eq(ent->value, bv)) return false; + if (scr_dyn_obj_count(a) != scr_dyn_obj_count(b)) return false; + for (size_t i = 0; i < scr_dyn_obj_count(a); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(a, i); + ScrDyn *bv = scr_dyn_obj_get(b, ent.key, ent.key_len); + if (!bv || !scr_assert_dyn_deep_eq(ent.value, bv)) return false; } return true; } @@ -782,30 +782,29 @@ static bool scr_assert_loose_nan(const ScrDyn *d) { return d->kind == SCR_DYN_NUM && d->v.num != d->v.num; } -/* An enumerable own entry, or NULL. Borrowed. */ -static const ScrDynEntry *scr_assert_loose_entry(const ScrDyn *d, const char *key, size_t len) { - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *ent = &d->v.obj.entries[i]; - if (ent->key_len == len && memcmp(ent->key, key, len) == 0) return ent->enumerable ? ent : NULL; - } - return NULL; +/* The value of an enumerable own property, or NULL. Borrowed. */ +static const ScrDyn *scr_assert_loose_entry(const ScrDyn *d, const char *key, size_t len) { + size_t i = scr_dyn_obj_index_of(d, key, len); + if (i == SIZE_MAX) return NULL; + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); + return ent.enumerable ? ent.value : NULL; } -static bool scr_assert_loose_counted(const ScrDynEntry *ent) { - return ent->enumerable && !(ent->key_len == 6 && memcmp(ent->key, "%error", 6) == 0); +static bool scr_assert_loose_counted(ScrDynProp ent) { + return ent.enumerable && !(ent.key_len == 6 && memcmp(ent.key, "%error", 6) == 0); } /* keyCheck and objEquiv over two objects' enumerable own keys. */ static bool scr_assert_loose_keys(const ScrDyn *a, const ScrDyn *b) { size_t na = 0, nb = 0; - for (size_t i = 0; i < a->v.obj.len; i++) na += scr_assert_loose_counted(&a->v.obj.entries[i]); - for (size_t i = 0; i < b->v.obj.len; i++) nb += scr_assert_loose_counted(&b->v.obj.entries[i]); + for (size_t i = 0; i < scr_dyn_obj_count(a); i++) na += scr_assert_loose_counted(scr_dyn_obj_prop_at(a, i)); + for (size_t i = 0; i < scr_dyn_obj_count(b); i++) nb += scr_assert_loose_counted(scr_dyn_obj_prop_at(b, i)); if (na != nb) return false; - for (size_t i = 0; i < b->v.obj.len; i++) { - const ScrDynEntry *ent = &b->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(b); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(b, i); if (!scr_assert_loose_counted(ent)) continue; - const ScrDynEntry *other = scr_assert_loose_entry(a, ent->key, ent->key_len); - if (!other || !scr_assert_loose_eq(other->value, ent->value)) return false; + const ScrDyn *other = scr_assert_loose_entry(a, ent.key, ent.key_len); + if (!other || !scr_assert_loose_eq(other, ent.value)) return false; } return true; } @@ -1180,8 +1179,8 @@ static bool scr_assert_loose_array(const ScrDyn *a, const ScrDyn *b) { if (pa || pb) { if (!pa || !pb) { const ScrDyn *table = pa ? pa : pb; - for (size_t i = 0; i < table->v.obj.len; i++) - if (scr_assert_loose_counted(&table->v.obj.entries[i])) return false; + for (size_t i = 0; i < scr_dyn_obj_count(table); i++) + if (scr_assert_loose_counted(scr_dyn_obj_prop_at(table, i))) return false; } else if (!scr_assert_loose_keys(pa, pb)) { return false; } @@ -1295,7 +1294,7 @@ static void scr_assert_cf_value(ScrAssertBuf *b, const ScrDyn *d, size_t indent, * or `{`, then its entries. */ static void scr_assert_cf_object(ScrAssertBuf *b, const ScrDyn *d, size_t indent, size_t depth, const char *cls) { - if (d->v.obj.len == 0) { + if (scr_dyn_obj_count(d) == 0) { if (cls) { ab_cstr(b, cls); ab_cstr(b, " {}"); @@ -1320,25 +1319,25 @@ static void scr_assert_cf_object(ScrAssertBuf *b, const ScrDyn *d, size_t indent } else if (d->null_proto) ab_cstr(b, "[Object: null prototype] "); /* Render every entry, then sort the RENDERED texts (Node's * sorted:true sorts formatted entries, quotes included). */ - ScrCfEntry *ents = malloc(d->v.obj.len * sizeof *ents); + ScrCfEntry *ents = malloc(scr_dyn_obj_count(d) * sizeof *ents); if (!ents) scr_assert_oom(); - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *ent = &d->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); ScrAssertBuf eb = {0}; - scr_assert_cf_key(&eb, ent->key, ent->key_len); + scr_assert_cf_key(&eb, ent.key, ent.key_len); ab_cstr(&eb, ": "); - scr_assert_cf_value(&eb, ent->value, indent + 2, depth - 1); + scr_assert_cf_value(&eb, ent.value, indent + 2, depth - 1); ents[i].text = eb.data ? eb.data : malloc(1); if (!ents[i].text) scr_assert_oom(); ents[i].len = eb.len; } - qsort(ents, d->v.obj.len, sizeof *ents, scr_assert_cf_entry_cmp); + qsort(ents, scr_dyn_obj_count(d), sizeof *ents, scr_assert_cf_entry_cmp); ab_char(b, '{'); - for (size_t i = 0; i < d->v.obj.len; i++) { + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { ab_char(b, '\n'); scr_assert_cf_pad(b, indent + 2); ab_bytes(b, ents[i].text, ents[i].len); - if (i + 1 < d->v.obj.len) ab_char(b, ','); + if (i + 1 < scr_dyn_obj_count(d)) ab_char(b, ','); free(ents[i].text); } free(ents); @@ -1364,11 +1363,11 @@ static const char *scr_assert_cf_bytes_class(ScrBytesElem elem) { } -static bool scr_assert_cf_error_extra(const ScrDynEntry *ent) { - if (!ent->enumerable || (ent->key_len > 0 && ent->key[0] == '%')) return false; - return !((ent->key_len == 4 && memcmp(ent->key, "name", 4) == 0) || - (ent->key_len == 7 && memcmp(ent->key, "message", 7) == 0) || - (ent->key_len == 5 && memcmp(ent->key, "stack", 5) == 0)); +static bool scr_assert_cf_error_extra(ScrDynProp ent) { + if (!ent.enumerable || (ent.key_len > 0 && ent.key[0] == '%')) return false; + return !((ent.key_len == 4 && memcmp(ent.key, "name", 4) == 0) || + (ent.key_len == 7 && memcmp(ent.key, "message", 7) == 0) || + (ent.key_len == 5 && memcmp(ent.key, "stack", 5) == 0)); } /* An encoded Error: Node's stackless `[Name: message]` (assertion_error.js @@ -1386,8 +1385,8 @@ static void scr_assert_cf_error(ScrAssertBuf *b, const ScrDyn *d, size_t indent, } ab_char(b, ']'); size_t extra = 0; - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *ent = &d->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); if (scr_assert_cf_error_extra(ent)) extra++; } if (extra == 0) return; @@ -1395,13 +1394,13 @@ static void scr_assert_cf_error(ScrAssertBuf *b, const ScrDyn *d, size_t indent, ScrCfEntry *ents = malloc(extra * sizeof *ents); if (!ents) scr_assert_oom(); size_t n = 0; - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *ent = &d->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); if (!scr_assert_cf_error_extra(ent)) continue; ScrAssertBuf eb = {0}; - scr_assert_cf_key(&eb, ent->key, ent->key_len); + scr_assert_cf_key(&eb, ent.key, ent.key_len); ab_cstr(&eb, ": "); - scr_assert_cf_value(&eb, ent->value, indent + 2, depth - 1); + scr_assert_cf_value(&eb, ent.value, indent + 2, depth - 1); ents[n].text = eb.data ? eb.data : malloc(1); if (!ents[n].text) scr_assert_oom(); ents[n++].len = eb.len; @@ -1989,11 +1988,11 @@ static bool scr_assert_partial_eq(const ScrDyn *a, const ScrDyn *b); /* objEquiv over the expected side's enumerable own keys. */ static bool scr_assert_partial_keys(const ScrDyn *a, const ScrDyn *b) { - for (size_t i = 0; i < b->v.obj.len; i++) { - const ScrDynEntry *ent = &b->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(b); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(b, i); if (!scr_assert_loose_counted(ent)) continue; - const ScrDynEntry *other = scr_assert_loose_entry(a, ent->key, ent->key_len); - if (!other || !scr_assert_partial_eq(other->value, ent->value)) return false; + const ScrDyn *other = scr_assert_loose_entry(a, ent.key, ent.key_len); + if (!other || !scr_assert_partial_eq(other, ent.value)) return false; } return true; } @@ -2043,11 +2042,11 @@ static bool scr_assert_partial_array(const ScrDyn *a, const ScrDyn *b) { const ScrDyn *pb = b->v.arr.properties; if (pb) { const ScrDyn *pa = a->v.arr.properties; - for (size_t i = 0; i < pb->v.obj.len; i++) { - const ScrDynEntry *ent = &pb->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(pb); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(pb, i); if (!scr_assert_loose_counted(ent)) continue; - const ScrDynEntry *other = pa ? scr_assert_loose_entry(pa, ent->key, ent->key_len) : NULL; - if (!other || !scr_assert_partial_eq(other->value, ent->value)) return false; + const ScrDyn *other = pa ? scr_assert_loose_entry(pa, ent.key, ent.key_len) : NULL; + if (!other || !scr_assert_partial_eq(other, ent.value)) return false; } } size_t pos = 0; @@ -2434,11 +2433,11 @@ void scr_assert_loose_deep_result(bool equal, bool negated, ScrStr *msg, bool ha void scr_assert_expects_err_dyn(ScrDyn *actual, ScrDyn *expected, ScrStr *msg, bool has_msg) { if (actual->kind == SCR_DYN_OBJ && expected->kind == SCR_DYN_OBJ) { bool ok = true; - for (size_t i = 0; i < expected->v.obj.len && ok; i++) { - const ScrDynEntry *e = &expected->v.obj.entries[i]; - if (e->key_len == 6 && memcmp(e->key, "%error", 6) == 0) continue; - ScrDyn *av = scr_dyn_obj_get(actual, e->key, e->key_len); - if (av == NULL || !scr_assert_dyn_deep_eq(av, e->value)) ok = false; + for (size_t i = 0; i < scr_dyn_obj_count(expected) && ok; i++) { + ScrDynProp e = scr_dyn_obj_prop_at(expected, i); + if (e.key_len == 6 && memcmp(e.key, "%error", 6) == 0) continue; + ScrDyn *av = scr_dyn_obj_get(actual, e.key, e.key_len); + if (av == NULL || !scr_assert_dyn_deep_eq(av, e.value)) ok = false; } if (ok) return; } diff --git a/packages/runtime/src/scr_builtin_values.c b/packages/runtime/src/scr_builtin_values.c index f8dd2b79b..084a1d50d 100644 --- a/packages/runtime/src/scr_builtin_values.c +++ b/packages/runtime/src/scr_builtin_values.c @@ -441,8 +441,7 @@ ScrDyn *scr_buffer_constructor_value(void) { ScrDyn *table = scr_dyn_function_properties(scr_bv_buffer); ScrDyn *getter = scr_dyn_new_func(scr_closure_new(NULL, 0), scr_bv_prototype, 0, "native:function", "get prototype"); scr_dyn_obj_set_accessor(table, "prototype", 9, getter, NULL); - ScrDynEntry *entry = &table->v.obj.entries[table->v.obj.len - 1]; - entry->enumerable = entry->configurable = false; + scr_dyn_obj_set_attrs_at(table, scr_dyn_obj_count(table) - 1, false, false, false); scr_dyn_obj_set(table, "poolSize", 8, scr_dyn_new_num(pool_size)); ScrDyn *is_encoding = NULL; for (size_t i = 0; i < sizeof statics / sizeof statics[0]; i++) { diff --git a/packages/runtime/src/scr_bytes_io.c b/packages/runtime/src/scr_bytes_io.c index daeb47c60..0c4bee4ee 100644 --- a/packages/runtime/src/scr_bytes_io.c +++ b/packages/runtime/src/scr_bytes_io.c @@ -1757,11 +1757,11 @@ ScrStr *scr_fs_mkdtemp_sync_chk(const ScrDyn *prefix, const ScrDyn *opts, const if (!scr_fs_encoding_chk(opts)) return NULL; bool utf8 = true; if (opts->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < opts->v.obj.len; i++) { - const ScrDynEntry *e = &opts->v.obj.entries[i]; - if (scr_fs_dyn_absent(e->value)) continue; - if (strcmp(e->key, "encoding") == 0) { - if (!scr_fs_utf8_encoding(e->value)) utf8 = false; + for (size_t i = 0; i < scr_dyn_obj_count(opts); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(opts, i); + if (scr_fs_dyn_absent(e.value)) continue; + if (strcmp(e.key, "encoding") == 0) { + if (!scr_fs_utf8_encoding(e.value)) utf8 = false; continue; } utf8 = false; /* an unmodeled effectful option */ diff --git a/packages/runtime/src/scr_console_native.c b/packages/runtime/src/scr_console_native.c index 492d57d82..e11ffd3af 100644 --- a/packages/runtime/src/scr_console_native.c +++ b/packages/runtime/src/scr_console_native.c @@ -507,8 +507,8 @@ static bool scr_console_holds_buffer(const ScrDyn *d) { if (scr_console_holds_buffer(d->v.arr.items[i])) return true; return false; case SCR_DYN_OBJ: - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDyn *value = d->v.obj.entries[i].value; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + const ScrDyn *value = scr_dyn_obj_value_at(d, i); if (value && scr_console_holds_buffer(value)) return true; } return false; @@ -626,7 +626,7 @@ static ScrStr **scr_table_own_keys(const ScrDyn *item, size_t *count) { return keys; } if (item->kind != SCR_DYN_OBJ) return NULL; - size_t len = item->v.obj.len; + size_t len = scr_dyn_obj_count(item); const char **names = calloc(len ? len : 1, sizeof *names); size_t *lens = calloc(len ? len : 1, sizeof *lens); size_t *order = calloc(len ? len : 1, sizeof *order); @@ -634,10 +634,10 @@ static ScrStr **scr_table_own_keys(const ScrDyn *item, size_t *count) { if (!names || !lens || !order || !keys) scr_trap("scriptc: out of memory\n"); size_t n = 0; for (size_t i = 0; i < len; i++) { - const ScrDynEntry *ent = &item->v.obj.entries[i]; - if (!ent->enumerable) continue; - names[n] = ent->key; - lens[n] = ent->key_len; + ScrDynProp prop = scr_dyn_obj_prop_at(item, i); + if (!prop.enumerable) continue; + names[n] = prop.key; + lens[n] = prop.key_len; n++; } scr_table_key_order(names, lens, n, order); @@ -652,16 +652,14 @@ static ScrStr **scr_table_own_keys(const ScrDyn *item, size_t *count) { /* item[key] when it is an own property; NULL otherwise. +1. */ static ScrDyn *scr_table_own_value(const ScrDyn *item, const ScrStr *key) { if (item->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < item->v.obj.len; i++) { - const ScrDynEntry *ent = &item->v.obj.entries[i]; - if (ent->key_len != key->len || memcmp(ent->key, key->data, key->len) != 0) continue; - if (ent->accessor) { - scr_table_fence("console.table rows with accessor properties have no lowering"); - return NULL; - } - return scr_dyn_retain(ent->value); + size_t i = scr_dyn_obj_index_of(item, key->data, key->len); + if (i == SIZE_MAX) return NULL; + ScrDynProp prop = scr_dyn_obj_prop_at(item, i); + if (prop.accessor) { + scr_table_fence("console.table rows with accessor properties have no lowering"); + return NULL; } - return NULL; + return scr_dyn_retain(prop.value); } if (item->kind == SCR_DYN_ARR || item->kind == SCR_DYN_BYTES) { size_t len = item->kind == SCR_DYN_ARR ? item->v.arr.len : item->v.bytes->len; diff --git a/packages/runtime/src/scr_decl_adopt.c b/packages/runtime/src/scr_decl_adopt.c index fc1c4456c..ffac1f802 100644 --- a/packages/runtime/src/scr_decl_adopt.c +++ b/packages/runtime/src/scr_decl_adopt.c @@ -63,10 +63,10 @@ static bool scr_decl_adopt_plain(const ScrDyn *n, const ScrDynPath *path, bool l else if (n->v.obj.source_access && !n->v.obj.source_identity) want = "a plain object"; else { - for (size_t i = 0; i < n->v.obj.len && !want; i++) { - const ScrDynEntry *e = &n->v.obj.entries[i]; - if (e->accessor) want = "a plain object (this value has accessor properties)"; - else if (!e->writable || !e->enumerable || !e->configurable) + for (size_t i = 0; i < scr_dyn_obj_count(n) && !want; i++) { + ScrDynProp e = scr_dyn_obj_prop_at(n, i); + if (e.accessor) want = "a plain object (this value has accessor properties)"; + else if (!e.writable || !e.enumerable || !e.configurable) want = "a plain object (this value has non-default property attributes)"; } } diff --git a/packages/runtime/src/scr_dyn.h b/packages/runtime/src/scr_dyn.h index a71aaa244..ebb274a23 100644 --- a/packages/runtime/src/scr_dyn.h +++ b/packages/runtime/src/scr_dyn.h @@ -584,6 +584,67 @@ _Static_assert(sizeof(((ScrDyn *)0)->v.arr) <= sizeof(((ScrDyn *)0)->v.typed_ref _Static_assert(sizeof(void *) != 8 || offsetof(ScrDyn, v.obj.shape) == 80, "v.obj.shape offset"); _Static_assert(sizeof(void *) != 8 || offsetof(ScrDyn, v.obj.region) == 72, "v.obj.region offset"); +/* ── own properties of ordinary objects ────────────────────────────── + * Code outside the object core (scr_json.c and scr_js_object.c) reads and + * adjusts an SCR_DYN_OBJ's own string-keyed properties through these + * accessors, never through v.obj.entries, so the object's storage can + * change underneath (the single object model's generic objects). Index i + * runs over the properties in creation order, 0 <= i < scr_dyn_obj_count. + * Keys and values are borrowed: they stay valid until the object changes. */ +typedef struct { + const char *key; + size_t key_len; + ScrDyn *value; /* data property; the undefined value for an accessor */ + ScrDyn *getter; /* accessor property; NULL when absent */ + ScrDyn *setter; /* accessor property; NULL when absent */ + bool accessor; + bool writable; + bool enumerable; + bool configurable; +} ScrDynProp; + +static inline size_t scr_dyn_obj_count(const ScrDyn *o) { + return o->v.obj.len; +} + +static inline const char *scr_dyn_obj_key_at(const ScrDyn *o, size_t i, size_t *key_len) { + *key_len = o->v.obj.entries[i].key_len; + return o->v.obj.entries[i].key; +} + +/* The value of data property i (borrowed); the undefined value for an + * accessor property, whose functions scr_dyn_obj_prop_at answers. */ +static inline ScrDyn *scr_dyn_obj_value_at(const ScrDyn *o, size_t i) { + return o->v.obj.entries[i].value; +} + +static inline ScrDynProp scr_dyn_obj_prop_at(const ScrDyn *o, size_t i) { + const ScrDynEntry *e = &o->v.obj.entries[i]; + return (ScrDynProp){e->key, e->key_len, e->value, e->getter, e->setter, + e->accessor, e->writable, e->enumerable, e->configurable}; +} + +/* The index of own property `key`, or SIZE_MAX. A linear scan: for code + * that looks up options and headers, not for hot property reads. */ +static inline size_t scr_dyn_obj_index_of(const ScrDyn *o, const char *key, size_t key_len) { + for (size_t i = 0; i < o->v.obj.len; i++) { + const ScrDynEntry *e = &o->v.obj.entries[i]; + if (e->key_len == key_len && memcmp(e->key, key, key_len) == 0) return i; + } + return SIZE_MAX; +} + +/* Sets the attributes of property i (`writable` is ignored for an + * accessor property): builtin objects the runtime builds restrict their + * members this way right after adding them. */ +static inline void scr_dyn_obj_set_attrs_at(ScrDyn *o, size_t i, bool writable, bool enumerable, + bool configurable) { + ScrDynEntry *e = &o->v.obj.entries[i]; + if (!e->accessor) e->writable = writable; + e->enumerable = enumerable; + e->configurable = configurable; +} + /* ── intact parse regions ───────────────────────────────────────────── * Every heap dyn node carries a cycle header, so an ordinary release that * leaves an array or object alive buffers it as a possible cycle root, and @@ -748,6 +809,20 @@ ScrDyn *scr_dyn_get_own_property_symbols(const ScrDyn *value); /* The own property stored under a symbol key on an object (borrowed), or * NULL when it has none. */ ScrDynEntry *scr_dyn_own_symbol_entry(const ScrDyn *object, const ScrDyn *key); +/* The own symbol-keyed property `key` of `object` as a property view + * (scr_dyn_obj_prop_at; `key` and `key_len` name its private storage + * key); false when there is none. */ +static inline bool scr_dyn_own_symbol_prop(const ScrDyn *object, const ScrDyn *key, ScrDynProp *out) { + const ScrDynEntry *e = scr_dyn_own_symbol_entry(object, key); + if (!e) return false; + *out = (ScrDynProp){e->key, e->key_len, e->value, e->getter, e->setter, + e->accessor, e->writable, e->enumerable, e->configurable}; + return true; +} +/* Copies own property i of `source` to `target` (scr_dyn_copy_entry). */ +static inline void scr_dyn_copy_prop_at(ScrDyn *target, const ScrDyn *source, size_t i) { + scr_dyn_copy_entry(target, source, &source->v.obj.entries[i]); +} /* The private storage name of a symbol key (+1). With it, * scr_dyn_symbol_init_own gives an object that has no symbol properties * yet its first one, a writable, enumerable, configurable data property diff --git a/packages/runtime/src/scr_dyn_handle_props.c b/packages/runtime/src/scr_dyn_handle_props.c index c7de963f0..88677836f 100644 --- a/packages/runtime/src/scr_dyn_handle_props.c +++ b/packages/runtime/src/scr_dyn_handle_props.c @@ -60,17 +60,17 @@ static ScrDyn *hp_props_of(void *child) { return props; } -static ScrDynEntry *hp_entry(const ScrDyn *props, const char *key, size_t key_len) { - if (!props) return NULL; - for (size_t i = 0; i < props->v.obj.len; i++) { - ScrDynEntry *e = &props->v.obj.entries[i]; - if (e->key_len == key_len && !memcmp(e->key, key, key_len)) return e; - } - return NULL; +/* Own property `key` of the table object, in *out; false when absent. */ +static bool hp_entry(const ScrDyn *props, const char *key, size_t key_len, ScrDynProp *out) { + if (!props) return false; + size_t i = scr_dyn_obj_index_of(props, key, key_len); + if (i == SIZE_MAX) return false; + *out = scr_dyn_obj_prop_at(props, i); + return true; } static bool hp_has(const ScrDyn *props, const char *key, size_t key_len) { - return hp_entry(props, key, key_len) != NULL; + return props && scr_dyn_obj_index_of(props, key, key_len) != SIZE_MAX; } /* A property the program added, read with the child as `this`: an @@ -78,10 +78,10 @@ static bool hp_has(const ScrDyn *props, const char *key, size_t key_len) { * it (`Object.defineProperty(child, "x", { get() { return this.pid; } })`). * +1, or NULL with the exception pending. */ static ScrDyn *hp_read(ScrDyn *props, ScrDyn *self, const char *key, size_t key_len) { - ScrDynEntry *e = hp_entry(props, key, key_len); - if (!e || !e->accessor) return scr_dyn_obj_read(props, key, key_len); - if (!e->getter) return scr_dyn_retain(scr_dyn_undefined()); - ScrDyn *getter = scr_dyn_retain(e->getter); + ScrDynProp e; + if (!hp_entry(props, key, key_len, &e) || !e.accessor) return scr_dyn_obj_read(props, key, key_len); + if (!e.getter) return scr_dyn_retain(scr_dyn_undefined()); + ScrDyn *getter = scr_dyn_retain(e.getter); scr_dyn_this_push_dyn(self); ScrDyn *result = scr_dyn_call(getter, NULL, 0, "getter"); scr_dyn_this_pop(); @@ -132,7 +132,8 @@ static ScrDyn *hp_get(void *h, const char *key, size_t key_len) { static bool hp_set(void *h, const char *key, size_t key_len, const ScrDyn *value) { ScrDyn *props = hp_find(h); - ScrDynEntry *entry = hp_entry(props, key, key_len); + ScrDynProp found; + const ScrDynProp *entry = hp_entry(props, key, key_len, &found) ? &found : NULL; if (entry && entry->accessor) { /* An accessor's setter runs on the child. Without one the write throws * the strict-mode TypeError, as the other handle getters do. */ diff --git a/packages/runtime/src/scr_fetch.c b/packages/runtime/src/scr_fetch.c index d28707d5d..1b30c451d 100644 --- a/packages/runtime/src/scr_fetch.c +++ b/packages/runtime/src/scr_fetch.c @@ -4821,14 +4821,14 @@ static ScrArr *sf_headers_convert_init(const ScrDyn *headers, bool *canonical_ou return converted; } if (headers->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < headers->v.obj.len; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - ScrStr *raw_name = scr_str_new(e->key, e->key_len); + for (size_t i = 0; i < scr_dyn_obj_count(headers); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + ScrStr *raw_name = scr_str_new(e.key, e.key_len); ScrStr *name_bytes = sf_header_value_bytestring(raw_name); ScrStr *name = name_bytes ? sf_latin1_to_utf8(name_bytes) : NULL; scr_str_release(name_bytes); scr_str_release(raw_name); - ScrDyn *source = scr_dyn_retain(e->value); + ScrDyn *source = scr_dyn_retain(e.value); ScrStr *raw_value = name ? scr_dyn_string_coerce_js(source) : NULL; scr_dyn_release(source); ScrStr *value_bytes = raw_value @@ -6088,11 +6088,11 @@ static int8_t sf_light_header_form(const ScrDyn *headers, bool json_body, } int8_t form = 0; if (headers->kind == SCR_DYN_OBJ) { - ScrArr *out = scr_arr_new(SCR_ELEM_STR, 2 * headers->v.obj.len); + ScrArr *out = scr_arr_new(SCR_ELEM_STR, 2 * scr_dyn_obj_count(headers)); form = 3; - for (size_t i = 0; i < headers->v.obj.len && form == 3; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - const ScrDyn *value = e->value; + for (size_t i = 0; i < scr_dyn_obj_count(headers) && form == 3; i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + const ScrDyn *value = e.value; size_t count = value->kind == SCR_DYN_ARR ? value->v.arr.len : 1; for (size_t j = 0; j < count; j++) { const ScrDyn *item = value->kind == SCR_DYN_ARR ? value->v.arr.items[j] : value; @@ -6106,7 +6106,7 @@ static int8_t sf_light_header_form(const ScrDyn *headers, bool json_body, form = 0; break; } - scr_arr_push_ref(out, scr_str_new(e->key, e->key_len)); + scr_arr_push_ref(out, scr_str_new(e.key, e.key_len)); scr_arr_push_ref(out, text); } } @@ -6666,27 +6666,27 @@ ScrPromise *scr_fetch_static(ScrStr *url, ScrDyn *init) { return sf_reject_now(promise, "fetch failed"); } if (init && init->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < init->v.obj.len; i++) { - const ScrDynEntry *entry = &init->v.obj.entries[i]; - bool supported = sf_name(entry->key, entry->key_len, "method") || - sf_name(entry->key, entry->key_len, "headers") || - sf_name(entry->key, entry->key_len, "body") || - sf_name(entry->key, entry->key_len, "duplex") || - sf_name(entry->key, entry->key_len, "redirect") || - sf_name(entry->key, entry->key_len, "signal"); + for (size_t i = 0; i < scr_dyn_obj_count(init); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(init, i); + bool supported = sf_name(entry.key, entry.key_len, "method") || + sf_name(entry.key, entry.key_len, "headers") || + sf_name(entry.key, entry.key_len, "body") || + sf_name(entry.key, entry.key_len, "duplex") || + sf_name(entry.key, entry.key_len, "redirect") || + sf_name(entry.key, entry.key_len, "signal"); bool recognized_unsupported = - sf_name(entry->key, entry->key_len, "cache") || - sf_name(entry->key, entry->key_len, "credentials") || - sf_name(entry->key, entry->key_len, "dispatcher") || - sf_name(entry->key, entry->key_len, "integrity") || - sf_name(entry->key, entry->key_len, "keepalive") || - sf_name(entry->key, entry->key_len, "mode") || - sf_name(entry->key, entry->key_len, "priority") || - sf_name(entry->key, entry->key_len, "referrer") || - sf_name(entry->key, entry->key_len, "referrerPolicy") || - sf_name(entry->key, entry->key_len, "window"); - if (!supported && recognized_unsupported && entry->value && - entry->value->kind != SCR_DYN_UNDEF) { + sf_name(entry.key, entry.key_len, "cache") || + sf_name(entry.key, entry.key_len, "credentials") || + sf_name(entry.key, entry.key_len, "dispatcher") || + sf_name(entry.key, entry.key_len, "integrity") || + sf_name(entry.key, entry.key_len, "keepalive") || + sf_name(entry.key, entry.key_len, "mode") || + sf_name(entry.key, entry.key_len, "priority") || + sf_name(entry.key, entry.key_len, "referrer") || + sf_name(entry.key, entry.key_len, "referrerPolicy") || + sf_name(entry.key, entry.key_len, "window"); + if (!supported && recognized_unsupported && entry.value && + entry.value->kind != SCR_DYN_UNDEF) { return sf_reject_now(promise, "fetch failed"); } } diff --git a/packages/runtime/src/scr_http.c b/packages/runtime/src/scr_http.c index 3fb0fdcba..0284c31c4 100644 --- a/packages/runtime/src/scr_http.c +++ b/packages/runtime/src/scr_http.c @@ -2047,10 +2047,10 @@ void scr_http_res_write_head_dyn(ScrHttpRes *r, double status, const ScrDyn *hea scr_dyn_arg_type_fail("headers", "an instance of Object", headers); return; } - for (size_t i = 0; i < headers->v.obj.len; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - const ScrDyn *v = e->value; - ScrStr *name = scr_str_new(e->key, e->key_len); + for (size_t i = 0; i < scr_dyn_obj_count(headers); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + const ScrDyn *v = e.value; + ScrStr *name = scr_str_new(e.key, e.key_len); if (v->kind == SCR_DYN_STR) { scr_http_res_set_header(r, name, v->v.str); } else if (v->kind == SCR_DYN_NUM) { @@ -6200,10 +6200,10 @@ static bool scr_http_dynh_req_set(void *h, const char *key, size_t key_len, cons * consecutive same-name pairs). Numbers format through String(n). NULL = * a value this surface cannot honestly carry (exception pending). */ static ScrArr *scr_http_dynh_pairs(const ScrDyn *headers) { - ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, headers->v.obj.len * 2); - for (size_t i = 0; i < headers->v.obj.len; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - const ScrDyn *v = e->value; + ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, scr_dyn_obj_count(headers) * 2); + for (size_t i = 0; i < scr_dyn_obj_count(headers); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + const ScrDyn *v = e.value; size_t reps = v->kind == SCR_DYN_ARR ? v->v.arr.len : 1; for (size_t k = 0; k < reps; k++) { const ScrDyn *one = v->kind == SCR_DYN_ARR ? v->v.arr.items[k] : v; @@ -6218,7 +6218,7 @@ static ScrArr *scr_http_dynh_pairs(const ScrDyn *headers) { scr_jb_puts(&b, "Invalid value \""); scr_jb_puts(&b, one->kind == SCR_DYN_UNDEF ? "undefined" : "object"); scr_jb_puts(&b, "\" for header \""); - for (size_t c = 0; c < e->key_len; c++) scr_jb_putc(&b, e->key[c]); + for (size_t c = 0; c < e.key_len; c++) scr_jb_putc(&b, e.key[c]); scr_jb_putc(&b, '"'); ScrStr *msg = scr_jb_finish(&b); scr_throw_error_msg_code(SCR_ERR_TYPE, msg->data, msg->len, "ERR_HTTP_INVALID_HEADER_VALUE"); @@ -6226,7 +6226,7 @@ static ScrArr *scr_http_dynh_pairs(const ScrDyn *headers) { scr_arr_release(pairs); return NULL; } - scr_arr_push_ref(pairs, scr_str_new(e->key, e->key_len)); + scr_arr_push_ref(pairs, scr_str_new(e.key, e.key_len)); scr_arr_push_ref(pairs, vs); } } @@ -6945,8 +6945,8 @@ static ScrDyn *scr_http_dynh_agent_get(void *h, const char *key, size_t key_len) scr_dyn_arr_push(arr, item); } /* the list is most recent first: reverse each array into Node's order */ - for (size_t i = 0; i < obj->v.obj.len; i++) { - ScrDyn *arr = obj->v.obj.entries[i].value; + for (size_t i = 0; i < scr_dyn_obj_count(obj); i++) { + ScrDyn *arr = scr_dyn_obj_value_at(obj, i); for (size_t l = 0, r = arr->v.arr.len ? arr->v.arr.len - 1 : 0; l < r; l++, r--) { ScrDyn *t = arr->v.arr.items[l]; arr->v.arr.items[l] = arr->v.arr.items[r]; @@ -7449,13 +7449,13 @@ bool scr_http_req_opts_read(const ScrDyn *opts, bool secure, const char *api, Sc headers = headers_view; } if (headers != NULL && headers->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < headers->v.obj.len; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - ScrStr *name = scr_str_new(e->key, e->key_len); + for (size_t i = 0; i < scr_dyn_obj_count(headers); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + ScrStr *name = scr_str_new(e.key, e.key_len); ScrStr *label = scr_str_new("", 0); scr_http_validate_header_name(name, label); scr_str_release(label); - const ScrDyn *v = e->value; + const ScrDyn *v = e.value; if (!scr_exc_pending()) { if (v->kind == SCR_DYN_ARR) { for (size_t k = 0; k < v->v.arr.len && !scr_exc_pending(); k++) diff --git a/packages/runtime/src/scr_http2.c b/packages/runtime/src/scr_http2.c index 489946a12..30fddf8a4 100644 --- a/packages/runtime/src/scr_http2.c +++ b/packages/runtime/src/scr_http2.c @@ -2559,9 +2559,9 @@ ScrNetServer *scr_http2_create_secure_server_dyn(const ScrDyn *opts /*borrowed*/ ScrHttpReqFn fn) { bool allow_http1 = false; if (opts != NULL && opts->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < opts->v.obj.len; i++) { - if (strcmp(opts->v.obj.entries[i].key, "allowHTTP1") == 0) { - allow_http1 = scr_dyn_truthy(opts->v.obj.entries[i].value); + for (size_t i = 0; i < scr_dyn_obj_count(opts); i++) { + if (strcmp(scr_dyn_obj_prop_at(opts, i).key, "allowHTTP1") == 0) { + allow_http1 = scr_dyn_truthy(scr_dyn_obj_value_at(opts, i)); } } } @@ -3105,15 +3105,15 @@ static void scr_h2_dyn_fire_goaway(ScrClosure *cb, double code, double last) { /* Flatten a dyn headers object into the flat pairs array (numbers via * String(n); the :status/:method/... pseudo-headers pass through). */ static ScrArr *scr_h2_dom_to_pairs(const ScrDyn *headers) { - ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, headers->v.obj.len * 2); - for (size_t i = 0; i < headers->v.obj.len; i++) { - const ScrDynEntry *e = &headers->v.obj.entries[i]; - const ScrDyn *v = e->value; + ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, scr_dyn_obj_count(headers) * 2); + for (size_t i = 0; i < scr_dyn_obj_count(headers); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(headers, i); + const ScrDyn *v = e.value; ScrStr *vs; if (v->kind == SCR_DYN_STR) vs = scr_str_retain(v->v.str); else if (v->kind == SCR_DYN_NUM) vs = scr_f64_to_scrstr(v->v.num); else continue; /* undefined/null header: dropped, Node's stance */ - scr_arr_push_ref(pairs, scr_str_new(e->key, e->key_len)); + scr_arr_push_ref(pairs, scr_str_new(e.key, e.key_len)); scr_arr_push_ref(pairs, vs); } return pairs; diff --git a/packages/runtime/src/scr_inspect.c b/packages/runtime/src/scr_inspect.c index 1de583af5..b4994cd32 100644 --- a/packages/runtime/src/scr_inspect.c +++ b/packages/runtime/src/scr_inspect.c @@ -973,21 +973,23 @@ ScrStr *scr_insp_regex(ScrRegex *re) { * the k-th symbol key. */ static size_t insp_view_property_slots(const ScrDyn *props) { if (!props) return 0; - return props->v.obj.len + (props->symbol_keys ? props->symbol_keys->v.arr.len : 0); + return scr_dyn_obj_count(props) + (props->symbol_keys ? props->symbol_keys->v.arr.len : 0); } -static ScrStr *insp_accessor_tag(const ScrDynEntry *ent); +static ScrStr *insp_accessor_tag(const ScrDynProp *ent); static void insp_key_into(InspBuf *b, const char *key, size_t len); /* Slot k's `key: value` entry when it is enumerable, else NULL. */ static ScrStr *insp_view_property(ScrDyn *props, size_t k, double recurse, double depth) { - const ScrDynEntry *ent; + ScrDynProp found; + const ScrDynProp *ent = NULL; ScrStr *key = NULL; - if (k < props->v.obj.len) { - ent = &props->v.obj.entries[k]; + if (k < scr_dyn_obj_count(props)) { + found = scr_dyn_obj_prop_at(props, k); + ent = &found; } else { - ScrDyn *symbol = props->symbol_keys->v.arr.items[k - props->v.obj.len]; - ent = scr_dyn_own_symbol_entry(props, symbol); + ScrDyn *symbol = props->symbol_keys->v.arr.items[k - scr_dyn_obj_count(props)]; + if (scr_dyn_own_symbol_prop(props, symbol, &found)) ent = &found; if (ent && ent->enumerable) key = scr_insp_dyn(symbol, recurse, depth); } if (!ent || !ent->enumerable) return NULL; @@ -1005,10 +1007,11 @@ static ScrStr *insp_view_property(ScrDyn *props, size_t k, double recurse, doubl static size_t insp_view_property_count(ScrDyn *props) { size_t count = 0; for (size_t k = 0; k < insp_view_property_slots(props); k++) { - if (k < props->v.obj.len) count += props->v.obj.entries[k].enumerable; + if (k < scr_dyn_obj_count(props)) count += scr_dyn_obj_prop_at(props, k).enumerable; else { - const ScrDynEntry *ent = scr_dyn_own_symbol_entry(props, props->symbol_keys->v.arr.items[k - props->v.obj.len]); - count += ent && ent->enumerable; + ScrDynProp ent; + count += scr_dyn_own_symbol_prop(props, props->symbol_keys->v.arr.items[k - scr_dyn_obj_count(props)], &ent) && + ent.enumerable; } } return count; @@ -1157,11 +1160,11 @@ static ScrDyn *insp_error_cause(ScrError *e, bool *enumerable, ScrDyn **view) { ScrDyn *node = scr_errdyn_view_of(e); if (node != NULL && node->kind == SCR_DYN_TYPED_REF) node = *view = scr_dyn_typed_ref_materialize(node); if (node != NULL && node->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < node->v.obj.len; i++) { - ScrDynEntry *ent = &node->v.obj.entries[i]; - if (ent->key_len == 5 && memcmp(ent->key, "cause", 5) == 0) { - *enumerable = ent->enumerable; - return ent->value; + for (size_t i = 0; i < scr_dyn_obj_count(node); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(node, i); + if (ent.key_len == 5 && memcmp(ent.key, "cause", 5) == 0) { + *enumerable = ent.enumerable; + return ent.value; } } return NULL; @@ -1176,9 +1179,9 @@ static ScrDyn *insp_error_cause(ScrError *e, bool *enumerable, ScrDyn **view) { * other non-enumerable property of an error. */ bool scr_insp_error_bag_has_properties(ScrDyn *bag) { if (bag == NULL || bag->kind != SCR_DYN_OBJ) return false; - for (size_t i = 0; i < bag->v.obj.len; i++) { - const ScrDynEntry *ent = &bag->v.obj.entries[i]; - if (ent->enumerable && !(ent->key_len == 5 && memcmp(ent->key, "cause", 5) == 0)) return true; + for (size_t i = 0; i < scr_dyn_obj_count(bag); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(bag, i); + if (ent.enumerable && !(ent.key_len == 5 && memcmp(ent.key, "cause", 5) == 0)) return true; } return false; } @@ -1282,7 +1285,7 @@ ScrStr *scr_insp_error(ScrError *e, double recurse, double depth) { * (/^[a-zA-Z_][a-zA-Z_0-9]*$/, '__proto__' excepted), quoted otherwise. */ /* util.inspect's text for an accessor property: [Getter], [Setter] or * [Getter/Setter], by which of the two are functions. +1. */ -static ScrStr *insp_accessor_tag(const ScrDynEntry *ent) { +static ScrStr *insp_accessor_tag(const ScrDynProp *ent) { bool get = ent->getter && ent->getter->kind == SCR_DYN_FUNC; bool set = ent->setter && ent->setter->kind == SCR_DYN_FUNC; const char *text = get && set ? "[Getter/Setter]" : get ? "[Getter]" : set ? "[Setter]" : "undefined"; @@ -1371,12 +1374,12 @@ static ScrDyn *insp_assertion_ellipsis(ScrDyn *value) { * Node's `{ ... }` tail. */ /* An own non-enumerable entry of the error encoding, NULL when absent or * enumerable (an enumerable one prints with the other own keys). */ -static const ScrDynEntry *insp_error_hidden_entry(const ScrDyn *d, const char *key, size_t len) { - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *ent = &d->v.obj.entries[i]; - if (ent->key_len == len && memcmp(ent->key, key, len) == 0) return ent->enumerable ? NULL : ent; - } - return NULL; +static const ScrDynProp *insp_error_hidden_entry(const ScrDyn *d, const char *key, size_t len, + ScrDynProp *out) { + size_t i = scr_dyn_obj_index_of(d, key, len); + if (i == SIZE_MAX) return NULL; + *out = scr_dyn_obj_prop_at(d, i); + return out->enumerable ? NULL : out; } static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) { @@ -1390,19 +1393,20 @@ static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) { memcmp(name->v.str->data, "AssertionError", 14) == 0; double value_depth = assertion ? recurse : depth; size_t extra = 0; - for (size_t i = 0; i < d->v.obj.len; i++) { - ScrDynEntry *ent = &d->v.obj.entries[i]; - if (!ent->enumerable) continue; - if ((ent->key_len == 4 && memcmp(ent->key, "name", 4) == 0) || - (ent->key_len == 7 && memcmp(ent->key, "message", 7) == 0) || - (ent->key_len == 5 && memcmp(ent->key, "stack", 5) == 0)) + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); + if (!ent.enumerable) continue; + if ((ent.key_len == 4 && memcmp(ent.key, "name", 4) == 0) || + (ent.key_len == 7 && memcmp(ent.key, "message", 7) == 0) || + (ent.key_len == 5 && memcmp(ent.key, "stack", 5) == 0)) continue; extra++; } /* formatError shows a non-enumerable `cause`, and an AggregateError's * `errors` array, after the enumerable keys: `[cause]`, `[errors]`. */ - const ScrDynEntry *cause = insp_error_hidden_entry(d, "cause", 5); - const ScrDynEntry *errors = insp_error_hidden_entry(d, "errors", 6); + ScrDynProp cause_prop, errors_prop; + const ScrDynProp *cause = insp_error_hidden_entry(d, "cause", 5, &cause_prop); + const ScrDynProp *errors = insp_error_hidden_entry(d, "errors", 6, &errors_prop); if (errors && errors->value->kind != SCR_DYN_ARR) errors = NULL; extra += (cause != NULL) + (errors != NULL); InspBuf base = {0}; @@ -1426,20 +1430,20 @@ static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) { if (extra == 0) return base_str; scr_insp_begin(recurse + 1); scr_insp_seen_push(d); - for (size_t i = 0; i < d->v.obj.len; i++) { - ScrDynEntry *ent = &d->v.obj.entries[i]; - if (!ent->enumerable) continue; - if ((ent->key_len == 4 && memcmp(ent->key, "name", 4) == 0) || - (ent->key_len == 7 && memcmp(ent->key, "message", 7) == 0) || - (ent->key_len == 5 && memcmp(ent->key, "stack", 5) == 0)) + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); + if (!ent.enumerable) continue; + if ((ent.key_len == 4 && memcmp(ent.key, "name", 4) == 0) || + (ent.key_len == 7 && memcmp(ent.key, "message", 7) == 0) || + (ent.key_len == 5 && memcmp(ent.key, "stack", 5) == 0)) continue; - bool shortened = assertion && ((ent->key_len == 6 && memcmp(ent->key, "actual", 6) == 0) || - (ent->key_len == 8 && memcmp(ent->key, "expected", 8) == 0)); - ScrDyn *shown = shortened ? insp_assertion_ellipsis(ent->value) : scr_dyn_retain(ent->value); + bool shortened = assertion && ((ent.key_len == 6 && memcmp(ent.key, "actual", 6) == 0) || + (ent.key_len == 8 && memcmp(ent.key, "expected", 8) == 0)); + ScrDyn *shown = shortened ? insp_assertion_ellipsis(ent.value) : scr_dyn_retain(ent.value); ScrStr *val = scr_insp_dyn(shown, recurse + 1, value_depth); scr_dyn_release(shown); InspBuf eb = {0}; - insp_key_into(&eb, ent->key, ent->key_len); + insp_key_into(&eb, ent.key, ent.key_len); ib_cstr(&eb, ": "); ib_bytes(&eb, val->data, val->len); scr_str_release(val); @@ -1447,7 +1451,7 @@ static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) { scr_insp_entry(entry, false); scr_str_release(entry); } - const ScrDynEntry *hidden[] = {cause, errors}; + const ScrDynProp *hidden[] = {cause, errors}; for (size_t i = 0; i < 2; i++) { if (!hidden[i]) continue; ScrStr *val = scr_insp_dyn(hidden[i]->value, recurse + 1, value_depth); @@ -1690,9 +1694,9 @@ static ScrStr *scr_insp_dyn_collection(ScrDyn *d, double recurse, double depth, /* An own symbol-keyed property of `d` (borrowed) this render shows, or * NULL: enumerable ones, and under showHidden the non-enumerable ones too. */ -static ScrDynEntry *scr_insp_symbol_entry(ScrDyn *d, size_t i) { - ScrDynEntry *ent = scr_dyn_own_symbol_entry(d, d->symbol_keys->v.arr.items[i]); - return ent && (ent->enumerable || g_show_hidden) ? ent : NULL; +static bool scr_insp_symbol_entry(ScrDyn *d, size_t i, ScrDynProp *out) { + return scr_dyn_own_symbol_prop(d, d->symbol_keys->v.arr.items[i], out) && + (out->enumerable || g_show_hidden); } /* An ordinary object: `Name { key: value }` with its constructor name @@ -1700,15 +1704,21 @@ static ScrDynEntry *scr_insp_symbol_entry(ScrDyn *d, size_t i) { * enumerable string keys, then the own enumerable symbol keys. */ /* An own entry this render shows: enumerable ones, and under showHidden * the non-enumerable ones too (never the encoding's `%` markers). */ -static bool insp_obj_shows(const ScrDynEntry *ent) { +static bool insp_obj_shows(const ScrDynProp *ent) { return ent->enumerable || (g_show_hidden && !(ent->key_len && ent->key[0] == '%')); } static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char *cls) { size_t shown = 0; - for (size_t i = 0; i < d->v.obj.len; i++) shown += insp_obj_shows(&d->v.obj.entries[i]); + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); + shown += insp_obj_shows(&ent); + } size_t nsymbols = d->symbol_keys ? d->symbol_keys->v.arr.len : 0; - for (size_t i = 0; i < nsymbols; i++) shown += scr_insp_symbol_entry(d, i) != NULL; + for (size_t i = 0; i < nsymbols; i++) { + ScrDynProp ent; + shown += scr_insp_symbol_entry(d, i, &ent); + } if (shown == 0) { if (cls) return scr_insp_class_tag(cls, " {}"); return d->null_proto ? scr_str_new("[Object: null prototype] {}", 27) @@ -1720,17 +1730,17 @@ static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char : insp_styled_lit("[Object]", 8, INSP_SPECIAL); } scr_insp_begin(recurse + 1); - for (size_t i = 0; i < d->v.obj.len; i++) { - ScrDynEntry *ent = &d->v.obj.entries[i]; - if (!insp_obj_shows(ent)) continue; - ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(d, i); + if (!insp_obj_shows(&ent)) continue; + ScrStr *val = ent.accessor ? insp_accessor_tag(&ent) : scr_insp_dyn(ent.value, recurse + 1, depth); InspBuf eb = {0}; - if (ent->enumerable) { - insp_key_into(&eb, ent->key, ent->key_len); + if (ent.enumerable) { + insp_key_into(&eb, ent.key, ent.key_len); } else { /* formatProperty's hidden key: the key's rendering in brackets. */ ib_char(&eb, '['); - insp_key_into(&eb, ent->key, ent->key_len); + insp_key_into(&eb, ent.key, ent.key_len); ib_char(&eb, ']'); } ib_cstr(&eb, ": "); @@ -1742,8 +1752,9 @@ static ScrStr *scr_insp_obj(ScrDyn *d, double recurse, double depth, const char if (scr_exc_pending()) break; } for (size_t i = 0; i < nsymbols; i++) { - ScrDynEntry *ent = scr_insp_symbol_entry(d, i); - if (!ent) continue; + ScrDynProp found; + if (!scr_insp_symbol_entry(d, i, &found)) continue; + const ScrDynProp *ent = &found; ScrStr *key = scr_insp_dyn(d->symbol_keys->v.arr.items[i], recurse + 1, depth); ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); InspBuf eb = {0}; @@ -2063,7 +2074,7 @@ static ScrDyn *insp_inspect_value(void) { ScrDyn *set = scr_dyn_new_func(scr_closure_new((void *)members[i], 0), insp_inspect_member_call, 1, "native:function", members[i]); scr_dyn_obj_set_accessor(table, members[i], strlen(members[i]), get, set); - table->v.obj.entries[table->v.obj.len - 1].enumerable = false; + scr_dyn_obj_set_attrs_at(table, scr_dyn_obj_count(table) - 1, true, false, true); } scr_dyn_release(table); } @@ -2100,15 +2111,15 @@ static ScrDyn *insp_user_options(double depth) { insp_opt_set(options, "getters", scr_dyn_new_bool(false)); insp_opt_set(options, "numericSeparator", scr_dyn_new_bool(false)); if (g_user_options && g_user_options->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < g_user_options->v.obj.len; i++) { - const ScrDynEntry *ent = &g_user_options->v.obj.entries[i]; - if (!ent->enumerable) continue; - ScrDyn *value = scr_dyn_obj_read((ScrDyn *)g_user_options, ent->key, ent->key_len); + for (size_t i = 0; i < scr_dyn_obj_count(g_user_options); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(g_user_options, i); + if (!ent.enumerable) continue; + ScrDyn *value = scr_dyn_obj_read((ScrDyn *)g_user_options, ent.key, ent.key_len); if (!value) { scr_dyn_release(options); return NULL; } - scr_dyn_obj_set(options, ent->key, ent->key_len, value); + scr_dyn_obj_set(options, ent.key, ent.key_len, value); } } return options; @@ -2343,9 +2354,12 @@ static ScrStr *scr_insp_dyn_func(ScrDyn *d, double recurse, double depth) { } size_t shown = 0; if (table && table->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < table->v.obj.len; i++) shown += table->v.obj.entries[i].enumerable; + for (size_t i = 0; i < scr_dyn_obj_count(table); i++) shown += scr_dyn_obj_prop_at(table, i).enumerable; size_t nsymbols = table->symbol_keys ? table->symbol_keys->v.arr.len : 0; - for (size_t i = 0; i < nsymbols; i++) shown += scr_insp_symbol_entry(table, i) != NULL; + for (size_t i = 0; i < nsymbols; i++) { + ScrDynProp ent; + shown += scr_insp_symbol_entry(table, i, &ent); + } } if (shown == 0) { scr_dyn_release(table); @@ -2498,7 +2512,10 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { * own `length` comes first, and non-enumerable properties show too. */ const ScrDyn *props = d->v.arr.properties; size_t nprops = 0; - for (size_t i = 0; props && i < props->v.obj.len; i++) nprops += insp_obj_shows(&props->v.obj.entries[i]); + for (size_t i = 0; props && i < scr_dyn_obj_count(props); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(props, i); + nprops += insp_obj_shows(&ent); + } /* An instance of a class that extends Array: Node's `Name(length) ` * prefix (scr_arr_copy_metadata names the class). */ const char *cls = props ? props->v.obj.class_name : NULL; @@ -2562,15 +2579,15 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { scr_str_release(s); entries++; } - for (size_t k = 0; props && k < props->v.obj.len; k++) { - const ScrDynEntry *ent = &props->v.obj.entries[k]; - if (!insp_obj_shows(ent)) continue; - ScrStr *val = ent->accessor ? insp_accessor_tag(ent) : scr_insp_dyn(ent->value, recurse + 1, depth); + for (size_t k = 0; props && k < scr_dyn_obj_count(props); k++) { + ScrDynProp ent = scr_dyn_obj_prop_at(props, k); + if (!insp_obj_shows(&ent)) continue; + ScrStr *val = ent.accessor ? insp_accessor_tag(&ent) : scr_insp_dyn(ent.value, recurse + 1, depth); InspBuf eb = {0}; /* formatProperty's hidden key: the key's rendering in brackets. */ - if (!ent->enumerable) ib_char(&eb, '['); - insp_key_into(&eb, ent->key, ent->key_len); - if (!ent->enumerable) ib_char(&eb, ']'); + if (!ent.enumerable) ib_char(&eb, '['); + insp_key_into(&eb, ent.key, ent.key_len); + if (!ent.enumerable) ib_char(&eb, ']'); ib_cstr(&eb, ": "); ib_bytes(&eb, val->data, val->len); scr_str_release(val); @@ -2753,8 +2770,8 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { scr_str_release(iso); ScrStr *prefix = ib_take(&head); bool fields = false; - for (size_t i = 0; i < materialized->v.obj.len && !fields; i++) - fields = materialized->v.obj.entries[i].enumerable; + for (size_t i = 0; i < scr_dyn_obj_count(materialized) && !fields; i++) + fields = scr_dyn_obj_prop_at(materialized, i).enumerable; if (!fields || recurse > depth) { scr_dyn_release(materialized); return prefix; @@ -3323,12 +3340,12 @@ bool scr_insp_format_read_options(const ScrDyn *options, ScrInspFormatOptions *o "maxArrayLength", "maxStringLength", "breakLength", "compact", "sorted", "getters", "numericSeparator", "stylize", }; - for (size_t i = 0; i < options->v.obj.len && !o->user_options; i++) { - const ScrDynEntry *ent = &options->v.obj.entries[i]; - if (!ent->enumerable) continue; + for (size_t i = 0; i < scr_dyn_obj_count(options) && !o->user_options; i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(options, i); + if (!ent.enumerable) continue; bool read = false; for (size_t k = 0; k < sizeof known / sizeof *known && !read; k++) - read = ent->key_len == strlen(known[k]) && memcmp(ent->key, known[k], ent->key_len) == 0; + read = ent.key_len == strlen(known[k]) && memcmp(ent.key, known[k], ent.key_len) == 0; if (!read) o->user_options = options; } } @@ -3436,10 +3453,10 @@ static ScrStr *insp_table_cell(ScrDyn *v) { } const char *base = v->null_proto ? "[Object: null prototype]" : "Object"; size_t n = 0; - for (size_t i = 0; i < v->v.obj.len; i++) { - ScrDynEntry *ent = &v->v.obj.entries[i]; - if (!ent->enumerable) continue; - if (ent->accessor) { + for (size_t i = 0; i < scr_dyn_obj_count(v); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(v, i); + if (!ent.enumerable) continue; + if (ent.accessor) { static const char message[] = "console.table cells holding accessor properties have no lowering"; scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); return scr_str_new("", 0); @@ -3463,12 +3480,12 @@ static ScrStr *insp_table_cell(ScrDyn *v) { } ScrStr *items[2]; size_t count = 0; - for (size_t i = 0; i < v->v.obj.len; i++) { - ScrDynEntry *ent = &v->v.obj.entries[i]; - if (!ent->enumerable) continue; - ScrStr *val = scr_insp_dyn(ent->value, 1, 0); + for (size_t i = 0; i < scr_dyn_obj_count(v); i++) { + ScrDynProp ent = scr_dyn_obj_prop_at(v, i); + if (!ent.enumerable) continue; + ScrStr *val = scr_insp_dyn(ent.value, 1, 0); InspBuf eb = {0}; - insp_key_into(&eb, ent->key, ent->key_len); + insp_key_into(&eb, ent.key, ent.key_len); ib_cstr(&eb, ": "); ib_str(&eb, val); scr_str_release(val); diff --git a/packages/runtime/src/scr_lib.c b/packages/runtime/src/scr_lib.c index 928400da3..0b780603b 100644 --- a/packages/runtime/src/scr_lib.c +++ b/packages/runtime/src/scr_lib.c @@ -716,8 +716,9 @@ ScrDyn *scr_process_versions(void) { scr_dyn_obj_set(scr_versions_object, "openssl", 7, scr_dyn_new_str(openssl)); scr_str_release(node); scr_str_release(openssl); - for (size_t i = 0; i < scr_versions_object->v.obj.len; i++) { - scr_versions_object->v.obj.entries[i].writable = false; + for (size_t i = 0; i < scr_dyn_obj_count(scr_versions_object); i++) { + ScrDynProp prop = scr_dyn_obj_prop_at(scr_versions_object, i); + scr_dyn_obj_set_attrs_at(scr_versions_object, i, false, prop.enumerable, prop.configurable); } scr_atexit(&scr_versions_object_cleanup); } @@ -914,8 +915,8 @@ static void scr_env_store(ScrDyn *out, const char *text, size_t ks, size_t ke, } static int scr_env_entry_order(const void *left, const void *right) { - const ScrDynEntry *a = *(const ScrDynEntry *const *)left; - const ScrDynEntry *b = *(const ScrDynEntry *const *)right; + const ScrDynProp *a = left; + const ScrDynProp *b = right; size_t n = a->key_len < b->key_len ? a->key_len : b->key_len; int cmp = memcmp(a->key, b->key, n); return cmp ? cmp : (a->key_len > b->key_len) - (a->key_len < b->key_len); @@ -991,14 +992,14 @@ static ScrDyn *scr_env_parse_content(const ScrStr *input, bool ordinary) { /* Node's std::map orders UTF-8 keys before installing object properties. * Ordinary objects ignore string-valued __proto__ assignments; loading * the native environment retains that name as an ordinary variable. */ - size_t count = entries->v.obj.len; - const ScrDynEntry **sorted = count ? malloc(count * sizeof(*sorted)) : NULL; + size_t count = scr_dyn_obj_count(entries); + ScrDynProp *sorted = count ? malloc(count * sizeof(*sorted)) : NULL; if (count && !sorted) scr_trap("scriptc: out of memory\n"); - for (size_t i = 0; i < count; i++) sorted[i] = &entries->v.obj.entries[i]; + for (size_t i = 0; i < count; i++) sorted[i] = scr_dyn_obj_prop_at(entries, i); if (count > 1) qsort(sorted, count, sizeof(*sorted), scr_env_entry_order); ScrDyn *result = ordinary ? scr_dyn_new_obj() : scr_dyn_new_obj_null_proto(); for (size_t i = 0; i < count; i++) { - const ScrDynEntry *entry = sorted[i]; + const ScrDynProp *entry = &sorted[i]; if (ordinary && entry->key_len == 9 && memcmp(entry->key, "__proto__", 9) == 0) continue; if (!ordinary) { scr_dyn_obj_set(result, entry->key, entry->key_len, scr_dyn_retain(entry->value)); @@ -1103,15 +1104,15 @@ void scr_process_load_env_file(const ScrDyn *value) { if (!content) return; ScrDyn *entries = scr_env_parse_content(content, false); scr_str_release(content); - for (size_t i = 0; i < entries->v.obj.len; i++) { - const ScrDynEntry *entry = &entries->v.obj.entries[i]; + for (size_t i = 0; i < scr_dyn_obj_count(entries); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(entries, i); /* Node normalizes each raw sorted entry immediately before checking * the environment. Distinct invalid byte keys can normalize alike; * the first absent name wins, rather than the last parsed entry. */ - ScrStr *key = scr_str_from_utf8_lossy((const uint8_t *)entry->key, entry->key_len); + ScrStr *key = scr_str_from_utf8_lossy((const uint8_t *)entry.key, entry.key_len); ScrStr *existing = scr_env_get(key); if (!existing) { - const ScrStr *raw = entry->value->v.str; + const ScrStr *raw = entry.value->v.str; ScrStr *value = scr_str_from_utf8_lossy((const uint8_t *)raw->data, raw->len); scr_env_set(key, value); scr_str_release(value); @@ -2810,10 +2811,9 @@ ScrDyn *scr_process_release(void) { #endif #undef SCR_RELEASE_FILE #undef SCR_RELEASE_PREFIX - for (size_t i = 0; i < scr_release_object->v.obj.len; i++) { - scr_release_object->v.obj.entries[i].writable = false; - scr_release_object->v.obj.entries[i].configurable = false; - } + for (size_t i = 0; i < scr_dyn_obj_count(scr_release_object); i++) + scr_dyn_obj_set_attrs_at(scr_release_object, i, false, + scr_dyn_obj_prop_at(scr_release_object, i).enumerable, false); scr_atexit(&scr_release_object_cleanup); } return scr_dyn_retain(scr_release_object); diff --git a/packages/runtime/src/scr_message.c b/packages/runtime/src/scr_message.c index d4eb263cf..419dcea4b 100644 --- a/packages/runtime/src/scr_message.c +++ b/packages/runtime/src/scr_message.c @@ -636,9 +636,9 @@ static bool scr_publish_container_ok(const ScrDyn *d) { if (!scr_dyn_is_frozen(d)) return false; if (d->kind == SCR_DYN_ARR) return d->v.arr.properties == NULL; if (d->null_proto || d->v.obj.source_identity || d->v.obj.source_access) return false; - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *e = &d->v.obj.entries[i]; - if (e->accessor || !e->enumerable) return false; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(d, i); + if (e.accessor || !e.enumerable) return false; } return true; } @@ -666,8 +666,8 @@ static bool scr_publish_collect(ScrPublish *p, ScrDyn *root) { break; case SCR_DYN_OBJ: if (!scr_publish_container_ok(d)) return false; - for (size_t i = 0; i < d->v.obj.len; i++) { - ScrDyn *child = d->v.obj.entries[i].value; + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDyn *child = scr_dyn_obj_value_at(d, i); if (child && !scr_publish_seen(p, child)) scr_publish_push(p, child); } break; @@ -1033,14 +1033,15 @@ ScrDyn *scr_message_decode(const ScrMessage *message) { } else scr_dyn_obj_set(values[i], property->key, property->length, value); } if (node->kind == SCR_MESSAGE_ERROR) { - for (size_t j = 0; j < values[i]->v.obj.len; j++) { - ScrDynEntry *entry = &values[i]->v.obj.entries[j]; - entry->enumerable = false; + for (size_t j = 0; j < scr_dyn_obj_count(values[i]); j++) { + ScrDynProp entry = scr_dyn_obj_prop_at(values[i], j); + bool enumerable = false; for (size_t k = 0; k < node->count; k++) { const ScrMessageProperty *property = &node->properties[k]; - if (!property->hidden && property->length == entry->key_len && !memcmp(property->key, entry->key, entry->key_len)) - entry->enumerable = true; + if (!property->hidden && property->length == entry.key_len && !memcmp(property->key, entry.key, entry.key_len)) + enumerable = true; } + scr_dyn_obj_set_attrs_at(values[i], j, entry.writable, enumerable, entry.configurable); } ScrError *error = scr_errdyn_err_of(values[i]); scr_error_commit_dyn(error, values[i]); diff --git a/packages/runtime/src/scr_os.c b/packages/runtime/src/scr_os.c index 8bc476045..637bb7e61 100644 --- a/packages/runtime/src/scr_os.c +++ b/packages/runtime/src/scr_os.c @@ -745,9 +745,8 @@ double scr_os_cpus_field(double i, double field) { static void scr_os_constant(ScrDyn *o, const char *name, double value) { scr_dyn_obj_set(o, name, strlen(name), scr_dyn_new_num(value)); - ScrDynEntry *entry = &o->v.obj.entries[o->v.obj.len - 1]; - entry->writable = false; - entry->configurable = false; + size_t last = scr_dyn_obj_count(o) - 1; + scr_dyn_obj_set_attrs_at(o, last, false, scr_dyn_obj_prop_at(o, last).enumerable, false); } #define SCR_OS_CONSTANT(o, name) scr_os_constant((o), #name, (double)(name)) @@ -1514,10 +1513,10 @@ ScrDyn *scr_node_constants(const ScrDyn *crypto_table) { scr_os_signal_constants(o); scr_fs_constants(o); if (crypto && crypto->kind == SCR_DYN_OBJ) { - for (size_t i = 0; i < crypto->v.obj.len; i++) { - const ScrDynEntry *entry = &crypto->v.obj.entries[i]; - if (entry->accessor || !entry->value) continue; - scr_dyn_obj_set(o, entry->key, entry->key_len, scr_dyn_retain(entry->value)); + for (size_t i = 0; i < scr_dyn_obj_count(crypto); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(crypto, i); + if (entry.accessor || !entry.value) continue; + scr_dyn_obj_set(o, entry.key, entry.key_len, scr_dyn_retain(entry.value)); } } scr_dyn_release(crypto); @@ -1718,10 +1717,12 @@ double scr_util_signal_exit_code(const ScrDyn *code) { ScrJsonBuf reason; scr_jb_init(&reason); scr_jb_puts(&reason, "must be one of: "); - for (size_t i = 0; i < signals->v.obj.len; i++) { + for (size_t i = 0; i < scr_dyn_obj_count(signals); i++) { if (i > 0) scr_jb_puts(&reason, ", "); scr_jb_putc(&reason, '\''); - scr_jb_write(&reason, signals->v.obj.entries[i].key, signals->v.obj.entries[i].key_len); + size_t key_len; + const char *key = scr_dyn_obj_key_at(signals, i, &key_len); + scr_jb_write(&reason, key, key_len); scr_jb_putc(&reason, '\''); } ScrStr *text = scr_jb_finish(&reason); diff --git a/packages/runtime/src/scr_publish.c b/packages/runtime/src/scr_publish.c index 8d11d3806..1ba2b22cd 100644 --- a/packages/runtime/src/scr_publish.c +++ b/packages/runtime/src/scr_publish.c @@ -278,8 +278,8 @@ static bool scr_pub_dyn_ok(const ScrDyn *d, const char **why) { *why = "Cannot publish an Object object"; return false; } - for (size_t i = 0; i < d->v.obj.len; i++) - if (d->v.obj.entries[i].accessor) { + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) + if (scr_dyn_obj_prop_at(d, i).accessor) { *why = "Cannot publish an accessor property"; return false; } @@ -325,9 +325,9 @@ void scr_pub_dyn_fn(void *v, ScrPub *ctx) { case SCR_DYN_ARR: case SCR_DYN_OBJ: { if (!scr_pub_dyn_ok(d, &why)) break; - size_t n = d->kind == SCR_DYN_ARR ? d->v.arr.len : d->v.obj.len; + size_t n = d->kind == SCR_DYN_ARR ? d->v.arr.len : scr_dyn_obj_count(d); for (size_t i = 0; i < n; i++) { - ScrDyn *child = d->kind == SCR_DYN_ARR ? d->v.arr.items[i] : d->v.obj.entries[i].value; + ScrDyn *child = d->kind == SCR_DYN_ARR ? d->v.arr.items[i] : scr_dyn_obj_value_at(d, i); if (child && child->rc != SIZE_MAX) SCR_PUB_ADD((uintptr_t)child); } break; diff --git a/packages/runtime/src/scr_region.c b/packages/runtime/src/scr_region.c index 25bbcf7ae..dacd9f163 100644 --- a/packages/runtime/src/scr_region.c +++ b/packages/runtime/src/scr_region.c @@ -858,12 +858,12 @@ static void scr_rgn_walk_dyn(ScrRgnWalk *w, ScrDyn *d) { break; case SCR_DYN_OBJ: /* source_identity is owned through its access callback: not walked. */ - for (size_t i = 0; i < d->v.obj.len; i++) { - const ScrDynEntry *e = &d->v.obj.entries[i]; - if (e->accessor) { - scr_rgn_enter(w, e->getter, SCR_RGN_W_DYN, NULL); - scr_rgn_enter(w, e->setter, SCR_RGN_W_DYN, NULL); - } else scr_rgn_enter(w, e->value, SCR_RGN_W_DYN, NULL); + for (size_t i = 0; i < scr_dyn_obj_count(d); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(d, i); + if (e.accessor) { + scr_rgn_enter(w, e.getter, SCR_RGN_W_DYN, NULL); + scr_rgn_enter(w, e.setter, SCR_RGN_W_DYN, NULL); + } else scr_rgn_enter(w, e.value, SCR_RGN_W_DYN, NULL); } break; case SCR_DYN_FUNC: scr_rgn_enter(w, d->v.fn.clo, SCR_RGN_W_CLOSURE, NULL); break; diff --git a/packages/runtime/src/scr_symbol.c b/packages/runtime/src/scr_symbol.c index 7e573fc28..372e60702 100644 --- a/packages/runtime/src/scr_symbol.c +++ b/packages/runtime/src/scr_symbol.c @@ -333,7 +333,7 @@ static void scr_dirent_init(void) { scr_dyn_obj_set(proto, methods[i].name, strlen(methods[i].name), scr_dyn_new_func(scr_closure_new(NULL, 0), methods[i].thunk, 0, "native:fs.Dirent", methods[i].name)); - proto->v.obj.entries[proto->v.obj.len - 1].enumerable = false; + scr_dyn_obj_set_attrs_at(proto, scr_dyn_obj_count(proto) - 1, true, false, true); } ScrStr *desc = scr_str_new("type", 4); ScrSym *sym = scr_sym_new(desc); @@ -350,15 +350,14 @@ void scr_dyn_dirent_finish(ScrDyn *obj) { scr_dyn_region_store(obj, NULL); /* a prototype store (ScrDynRegion) */ scr_dyn_release(obj->prototype); obj->prototype = scr_dyn_retain(scr_dirent_prototype); - for (size_t i = 0; i < obj->v.obj.len; i++) { - ScrDynEntry *ent = &obj->v.obj.entries[i]; - if (ent->key_len != 6 || memcmp(ent->key, "%dtype", 6) != 0) continue; - ent->enumerable = false; + size_t i = scr_dyn_obj_index_of(obj, "%dtype", 6); + if (i != SIZE_MAX) { + ScrDynProp ent = scr_dyn_obj_prop_at(obj, i); + scr_dyn_obj_set_attrs_at(obj, i, ent.writable, false, ent.configurable); if (!obj->symbol_properties && !obj->symbol_keys) - scr_dyn_symbol_init_own(obj, scr_dirent_type_key, scr_dirent_type_storage, ent->value); + scr_dyn_symbol_init_own(obj, scr_dirent_type_key, scr_dirent_type_storage, ent.value); else - scr_dyn_symbol_key_set(obj, scr_dirent_type_key, ent->value); - break; + scr_dyn_symbol_key_set(obj, scr_dirent_type_key, ent.value); } } diff --git a/packages/runtime/src/scr_tls.c b/packages/runtime/src/scr_tls.c index 547fef27a..76ec48e6c 100644 --- a/packages/runtime/src/scr_tls.c +++ b/packages/runtime/src/scr_tls.c @@ -2597,21 +2597,21 @@ static bool scr_tls_srv_opts_walk_ex(const ScrDyn *opts, const char *api, ScrByt } if (!scr_tls_opts_validate(opts)) return false; /* Node's typed errors first */ bool ok = true; - for (size_t i = 0; ok && i < opts->v.obj.len; i++) { - const ScrDynEntry *e = &opts->v.obj.entries[i]; - const ScrDyn *v = e->value; + for (size_t i = 0; ok && i < scr_dyn_obj_count(opts); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(opts, i); + const ScrDyn *v = e.value; if (v->kind == SCR_DYN_UNDEF) continue; /* an undefined member reads as absent, like Node */ - if (strcmp(e->key, "cert") == 0 || strcmp(e->key, "key") == 0) { + if (strcmp(e.key, "cert") == 0 || strcmp(e.key, "key") == 0) { char what[96]; - snprintf(what, sizeof what, "a %s '%s' option", api, e->key); + snprintf(what, sizeof what, "a %s '%s' option", api, e.key); ScrBytes *b = scr_tls_pem_dyn(v, what, false); if (b == NULL) { ok = false; break; } - ScrBytes **slot = e->key[0] == 'c' ? cert_out : key_out; + ScrBytes **slot = e.key[0] == 'c' ? cert_out : key_out; scr_bytes_release(*slot); *slot = b; continue; } - if (strcmp(e->key, "ca") == 0) { + if (strcmp(e.key, "ca") == 0) { /* the server's `ca` verifies client certificates and completes the * server's own chain (scr_tls_srv_client_auth); a walk without * client certificates drops it */ @@ -2623,7 +2623,7 @@ static bool scr_tls_srv_opts_walk_ex(const ScrDyn *opts, const char *api, ScrByt } continue; } - if (strcmp(e->key, "requestCert") == 0) { + if (strcmp(e.key, "requestCert") == 0) { if (auth != NULL) { auth->request = scr_tls_dyn_truthy(v); continue; @@ -2635,22 +2635,22 @@ static bool scr_tls_srv_opts_walk_ex(const ScrDyn *opts, const char *api, ScrByt } continue; } - if (strcmp(e->key, "rejectUnauthorized") == 0) { + if (strcmp(e.key, "rejectUnauthorized") == 0) { if (auth != NULL) auth->reject = scr_tls_dyn_not_false(v); continue; /* without client certificates it has no effect */ } - if (server_ok && (strcmp(e->key, "ALPNProtocols") == 0 || strcmp(e->key, "minVersion") == 0 || - strcmp(e->key, "maxVersion") == 0)) + if (server_ok && (strcmp(e.key, "ALPNProtocols") == 0 || strcmp(e.key, "minVersion") == 0 || + strcmp(e.key, "maxVersion") == 0)) continue; - if (strcmp(e->key, "SNICallback") == 0) { + if (strcmp(e.key, "SNICallback") == 0) { scr_tls_opt_fence(api, "SNICallback", "SNICallback lowers only on http2.createSecureServer({ allowHTTP1: true, ... }) with a literal options object"); ok = false; continue; } for (size_t k = 0; SCR_TLS_SRV_FENCED_OPTIONS[k] != NULL; k++) { - if (strcmp(e->key, SCR_TLS_SRV_FENCED_OPTIONS[k]) == 0) { - scr_tls_opt_fence(api, e->key, + if (strcmp(e.key, SCR_TLS_SRV_FENCED_OPTIONS[k]) == 0) { + scr_tls_opt_fence(api, e.key, "cert and key (PEM strings or Buffers) are the honestly-implemented members of a runtime options record"); ok = false; break; @@ -2957,17 +2957,17 @@ static const char *const SCR_TLS_WRAP_FENCED_OPTIONS[] = { * certificates are not modeled), SNICallback and ALPNCallback fence. * False with the exception pending. */ static bool scr_tls_wrap_socket_opts_check(const ScrDyn *record, const char *api) { - for (size_t i = 0; record != NULL && i < record->v.obj.len; i++) { - const ScrDynEntry *e = &record->v.obj.entries[i]; - if (e->value->kind == SCR_DYN_UNDEF) continue; - if (strcmp(e->key, "requestCert") == 0) { - if (!scr_tls_dyn_truthy(e->value)) continue; + for (size_t i = 0; record != NULL && i < scr_dyn_obj_count(record); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(record, i); + if (e.value->kind == SCR_DYN_UNDEF) continue; + if (strcmp(e.key, "requestCert") == 0) { + if (!scr_tls_dyn_truthy(e.value)) continue; scr_tls_opt_fence(api, "requestCert", "requestCert lowers with cert and key options, not beside a secureContext"); return false; } - if (strcmp(e->key, "SNICallback") == 0 || strcmp(e->key, "ALPNCallback") == 0) { - scr_tls_opt_fence(api, e->key, + if (strcmp(e.key, "SNICallback") == 0 || strcmp(e.key, "ALPNCallback") == 0) { + scr_tls_opt_fence(api, e.key, "isServer, cert, key, secureContext, ALPNProtocols, minVersion and maxVersion are the implemented members"); return false; } @@ -2980,21 +2980,21 @@ ScrNetSocket *scr_tls_wrap_socket(ScrNetSocket *raw, const ScrDyn *opts /*borrow static const char api[] = "new tls.TLSSocket"; bool is_server = false; const ScrDyn *record = opts != NULL && opts->kind == SCR_DYN_OBJ ? opts : NULL; - for (size_t i = 0; record != NULL && i < record->v.obj.len; i++) { - const ScrDynEntry *e = &record->v.obj.entries[i]; - if (e->value->kind == SCR_DYN_UNDEF) continue; - if (strcmp(e->key, "isServer") == 0) { - is_server = scr_tls_dyn_truthy(e->value); + for (size_t i = 0; record != NULL && i < scr_dyn_obj_count(record); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(record, i); + if (e.value->kind == SCR_DYN_UNDEF) continue; + if (strcmp(e.key, "isServer") == 0) { + is_server = scr_tls_dyn_truthy(e.value); continue; } - if (strcmp(e->key, "secureContext") == 0) { + if (strcmp(e.key, "secureContext") == 0) { scr_tls_opt_fence(api, "secureContext", "secureContext lowers as a member of an options object literal"); return NULL; } for (size_t k = 0; SCR_TLS_WRAP_FENCED_OPTIONS[k] != NULL; k++) { - if (strcmp(e->key, SCR_TLS_WRAP_FENCED_OPTIONS[k]) == 0) { - scr_tls_opt_fence(api, e->key, + if (strcmp(e.key, SCR_TLS_WRAP_FENCED_OPTIONS[k]) == 0) { + scr_tls_opt_fence(api, e.key, "isServer, cert, key, secureContext, ALPNProtocols, minVersion and maxVersion are the implemented members"); return NULL; } @@ -3267,10 +3267,10 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, /* createSecureContext's version checks come before its other options * (Node's SecureContext constructor). */ if (ok && !scr_tls_versions_dyn(opts, &min_version, &max_version)) ok = false; - for (size_t i = 0; ok && i < opts->v.obj.len; i++) { - const ScrDynEntry *e = &opts->v.obj.entries[i]; - const ScrDyn *v = e->value; - if (strcmp(e->key, "checkServerIdentity") == 0) { + for (size_t i = 0; ok && i < scr_dyn_obj_count(opts); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(opts, i); + const ScrDyn *v = e.value; + if (strcmp(e.key, "checkServerIdentity") == 0) { /* Node spreads user options over the defaults, so a PRESENT key * replaces the builtin verifier even when its value is undefined * — validateFunction then throws for anything non-callable. A @@ -3287,7 +3287,7 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, continue; } if (v->kind == SCR_DYN_UNDEF) continue; - if (strcmp(e->key, "port") == 0) { + if (strcmp(e.key, "port") == 0) { if (port >= 0) continue; /* the argument form wins, like Node */ if (v->kind == SCR_DYN_NUM) port = v->v.num; else if (v->kind == SCR_DYN_STR) port = strtod(v->v.str->data, NULL); /* Node coerces numeric strings */ @@ -3297,7 +3297,7 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, } continue; } - if (strcmp(e->key, "host") == 0) { + if (strcmp(e.key, "host") == 0) { if (v->kind == SCR_DYN_STR) opt_host = v->v.str; else { scr_dyn_arg_type_fail("options.host", "of type string", v); @@ -3305,34 +3305,34 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, } continue; } - if (strcmp(e->key, "rejectUnauthorized") == 0) { + if (strcmp(e.key, "rejectUnauthorized") == 0) { reject = scr_tls_dyn_not_false(v); continue; } - if (strcmp(e->key, "ca") == 0) { + if (strcmp(e.key, "ca") == 0) { scr_bytes_release(ca); if (!scr_tls_ca_dyn(v, "a tls.connect 'ca' option", &ca)) ok = false; continue; } - if (strcmp(e->key, "cert") == 0 || strcmp(e->key, "key") == 0) { - ScrBytes **slot = e->key[0] == 'c' ? &own_cert : &own_key; + if (strcmp(e.key, "cert") == 0 || strcmp(e.key, "key") == 0) { + ScrBytes **slot = e.key[0] == 'c' ? &own_cert : &own_key; scr_bytes_release(*slot); - *slot = scr_tls_pem_dyn(v, e->key[0] == 'c' ? "a tls.connect 'cert' option" : "a tls.connect 'key' option", + *slot = scr_tls_pem_dyn(v, e.key[0] == 'c' ? "a tls.connect 'cert' option" : "a tls.connect 'key' option", false); if (*slot == NULL) ok = false; continue; } - if (strcmp(e->key, "ALPNProtocols") == 0) { + if (strcmp(e.key, "ALPNProtocols") == 0) { alpn_value = v; /* converted after the context options, as TLSSocket does */ continue; } - if (strcmp(e->key, "minVersion") == 0 || strcmp(e->key, "maxVersion") == 0) continue; - if (strcmp(e->key, "timeout") == 0) { + if (strcmp(e.key, "minVersion") == 0 || strcmp(e.key, "maxVersion") == 0) continue; + if (strcmp(e.key, "timeout") == 0) { /* `if (options.timeout) tlssock.setTimeout(options.timeout)` */ timeout_value = scr_tls_dyn_truthy(v) ? v : NULL; continue; } - if (strcmp(e->key, "socket") == 0) { + if (strcmp(e.key, "socket") == 0) { if (v->kind == SCR_DYN_HANDLE && v->v.handle.tag == SCR_DYNH_NET_SOCKET) { wrap = (ScrNetSocket *)v->v.handle.ptr; } else { @@ -3341,7 +3341,7 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, } continue; } - if (strcmp(e->key, "servername") == 0) { + if (strcmp(e.key, "servername") == 0) { if (v->kind == SCR_DYN_STR) servername = v->v.str; else { scr_dyn_arg_type_fail("options.servername", "of type string", v); @@ -3350,8 +3350,8 @@ static ScrNetSocket *scr_tls_connect_plain(double port, ScrStr *host /*borrowed, continue; } for (size_t k = 0; SCR_TLS_CONNECT_FENCED_OPTIONS[k] != NULL; k++) { - if (strcmp(e->key, SCR_TLS_CONNECT_FENCED_OPTIONS[k]) == 0) { - scr_tls_opt_fence("tls.connect", e->key, + if (strcmp(e.key, SCR_TLS_CONNECT_FENCED_OPTIONS[k]) == 0) { + scr_tls_opt_fence("tls.connect", e.key, "port/host/socket/rejectUnauthorized/ca/servername/ALPNProtocols/minVersion/maxVersion/timeout are the implemented members of a runtime options record"); ok = false; break; diff --git a/packages/runtime/src/scr_util_compare.c b/packages/runtime/src/scr_util_compare.c index 5a15b662f..4c2cb9d16 100644 --- a/packages/runtime/src/scr_util_compare.c +++ b/packages/runtime/src/scr_util_compare.c @@ -26,15 +26,15 @@ static bool util_deep_equal(const ScrDyn *, const ScrDyn *, bool, const ScrDeepF static size_t util_deep_key_count(const ScrDyn *table) { size_t count = 0; - if (table) for (size_t i = 0; i < table->v.obj.len; i++) count += table->v.obj.entries[i].enumerable; + if (table) for (size_t i = 0; i < scr_dyn_obj_count(table); i++) count += scr_dyn_obj_prop_at(table, i).enumerable; return count; } static bool util_deep_same_keys(const ScrDyn *a, const ScrDyn *b) { if (util_deep_key_count(a) != util_deep_key_count(b)) return false; - if (a) for (size_t i = 0; i < a->v.obj.len; i++) { - const ScrDynEntry *e = &a->v.obj.entries[i]; - if (e->enumerable && (!b || !scr_dyn_obj_enumerable(b, e->key, e->key_len))) return false; + if (a) for (size_t i = 0; i < scr_dyn_obj_count(a); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(a, i); + if (e.enumerable && (!b || !scr_dyn_obj_enumerable(b, e.key, e.key_len))) return false; } return true; } @@ -42,14 +42,14 @@ static bool util_deep_same_keys(const ScrDyn *a, const ScrDyn *b) { static ScrDyn *util_deep_constructor(const ScrDyn *value) { for (const ScrDyn *p = value; p; p = p->prototype) { const ScrDyn *table = p->kind == SCR_DYN_OBJ ? p : p->kind == SCR_DYN_ARR ? p->v.arr.properties : NULL; - if (table) for (size_t i = 0; i < table->v.obj.len; i++) { - const ScrDynEntry *e = &table->v.obj.entries[i]; - if (e->key_len == 11 && !memcmp(e->key, "constructor", 11)) { - if (e->accessor) { util_deep_refused("native accessor properties"); return NULL; } - if (p == value && e->value->kind == SCR_DYN_FUNC) { + if (table) for (size_t i = 0; i < scr_dyn_obj_count(table); i++) { + ScrDynProp e = scr_dyn_obj_prop_at(table, i); + if (e.key_len == 11 && !memcmp(e.key, "constructor", 11)) { + if (e.accessor) { util_deep_refused("native accessor properties"); return NULL; } + if (p == value && e.value->kind == SCR_DYN_FUNC) { util_deep_refused("native own constructor functions"); return NULL; } - return scr_dyn_retain(e->value); + return scr_dyn_retain(e.value); } } if (!p->prototype) { @@ -77,8 +77,8 @@ static ScrDyn *util_deep_tag(const ScrDyn *value) { if (!scr_dyn_same_value(keys->v.arr.items[i], key)) continue; // General native getters retain an explicit boundary, including // non-enumerable Symbol.toStringTag descriptors read by Node. - for (size_t j = 0; j < p->symbol_properties->v.obj.len; j++) { - if (p->symbol_properties->v.obj.entries[j].accessor) { + for (size_t j = 0; j < scr_dyn_obj_count(p->symbol_properties); j++) { + if (scr_dyn_obj_prop_at(p->symbol_properties, j).accessor) { scr_dyn_release(key); util_deep_refused("native accessor properties"); return NULL; } } @@ -95,20 +95,21 @@ static bool util_deep_properties(const ScrDyn *a, const ScrDyn *b, bool skip, const ScrDeepFrame *frame) { if (util_deep_key_count(a) != util_deep_key_count(b)) return false; if (!a) return true; - for (size_t i = 0; i < a->v.obj.len; i++) { - const ScrDynEntry *x = &a->v.obj.entries[i]; - if (!x->enumerable) continue; - const ScrDynEntry *y = NULL; - if (b) for (size_t j = 0; j < b->v.obj.len; j++) { - const ScrDynEntry *candidate = &b->v.obj.entries[j]; - if (candidate->enumerable && candidate->key_len == x->key_len && - !memcmp(candidate->key, x->key, x->key_len)) { y = candidate; break; } + for (size_t i = 0; i < scr_dyn_obj_count(a); i++) { + ScrDynProp x = scr_dyn_obj_prop_at(a, i); + if (!x.enumerable) continue; + ScrDynProp yp; + const ScrDynProp *y = NULL; + if (b) for (size_t j = 0; j < scr_dyn_obj_count(b); j++) { + ScrDynProp candidate = scr_dyn_obj_prop_at(b, j); + if (candidate.enumerable && candidate.key_len == x.key_len && + !memcmp(candidate.key, x.key, x.key_len)) { yp = candidate; y = &yp; break; } } if (!y) return false; - if (x->accessor || y->accessor) return util_deep_refused("native accessor properties"); + if (x.accessor || y->accessor) return util_deep_refused("native accessor properties"); // Materializing a cyclic typed child can refresh its ancestor table. // Retain the values before that refresh replaces descriptor storage. - ScrDyn *xv = scr_dyn_retain(x->value), *yv = scr_dyn_retain(y->value); + ScrDyn *xv = scr_dyn_retain(x.value), *yv = scr_dyn_retain(y->value); bool same = util_deep_equal(xv, yv, skip, frame); scr_dyn_release(xv); scr_dyn_release(yv); if (!same) return false; diff --git a/packages/runtime/src/scr_worker_events.c b/packages/runtime/src/scr_worker_events.c index a23395c91..7669c4fb9 100644 --- a/packages/runtime/src/scr_worker_events.c +++ b/packages/runtime/src/scr_worker_events.c @@ -1385,17 +1385,17 @@ static ScrDyn *scr_worker_new_options(double root, ScrStr *filename, ScrDyn *opt scr_dyn_arg_type_fail("options", "object", options); return NULL; } - for (size_t i = 0; i < options->v.obj.len; i++) { - const ScrDynEntry *entry = &options->v.obj.entries[i]; - if (entry->accessor) return scr_worker_refusal("Worker option accessors are not supported yet"); - if (entry->key_len == 10 && !memcmp(entry->key, "workerData", 10)) data = entry->value; - else if (entry->key_len == 4 && !memcmp(entry->key, "argv", 4)) argv = entry->value; - else if (entry->key_len == 12 && !memcmp(entry->key, "transferList", 12)) transfer_list = entry->value; - else if (entry->key_len == 14 && !memcmp(entry->key, "resourceLimits", 14)) limits = entry->value; - else if (entry->key_len == 4 && !memcmp(entry->key, "name", 4)) name = entry->value; - else if (entry->key_len == 6 && !memcmp(entry->key, "stdout", 6)) capture |= scr_dyn_truthy(entry->value) ? 1 : 0; - else if (entry->key_len == 6 && !memcmp(entry->key, "stderr", 6)) capture |= scr_dyn_truthy(entry->value) ? 2 : 0; - else if (entry->key_len == 5 && !memcmp(entry->key, "stdin", 5)) capture |= scr_dyn_truthy(entry->value) ? 4 : 0; + for (size_t i = 0; i < scr_dyn_obj_count(options); i++) { + ScrDynProp entry = scr_dyn_obj_prop_at(options, i); + if (entry.accessor) return scr_worker_refusal("Worker option accessors are not supported yet"); + if (entry.key_len == 10 && !memcmp(entry.key, "workerData", 10)) data = entry.value; + else if (entry.key_len == 4 && !memcmp(entry.key, "argv", 4)) argv = entry.value; + else if (entry.key_len == 12 && !memcmp(entry.key, "transferList", 12)) transfer_list = entry.value; + else if (entry.key_len == 14 && !memcmp(entry.key, "resourceLimits", 14)) limits = entry.value; + else if (entry.key_len == 4 && !memcmp(entry.key, "name", 4)) name = entry.value; + else if (entry.key_len == 6 && !memcmp(entry.key, "stdout", 6)) capture |= scr_dyn_truthy(entry.value) ? 1 : 0; + else if (entry.key_len == 6 && !memcmp(entry.key, "stderr", 6)) capture |= scr_dyn_truthy(entry.value) ? 2 : 0; + else if (entry.key_len == 5 && !memcmp(entry.key, "stdin", 5)) capture |= scr_dyn_truthy(entry.value) ? 4 : 0; else return scr_worker_refusal("This Worker constructor option is not supported yet"); } } diff --git a/packages/runtime/test/object-core.test.ts b/packages/runtime/test/object-core.test.ts new file mode 100644 index 000000000..04f7cfb38 --- /dev/null +++ b/packages/runtime/test/object-core.test.ts @@ -0,0 +1,23 @@ +import { readdirSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { expect, test } from "vitest"; + +const src = join(import.meta.dirname, "..", "src"); + +/* Ordinary objects keep their properties behind the object core: units + * outside it read and adjust them through the scr_dyn.h own-property + * accessors (scr_dyn_obj_count, scr_dyn_obj_prop_at, ...), so the core can + * change how it stores them. */ +const CORE = new Set(["scr_json.c", "scr_js_object.c", "scr_dyn.h"]); + +test("only the object core reads ordinary objects' entry storage", () => { + const offenders: string[] = []; + for (const name of readdirSync(src)) { + if (!/\.(c|h)$/.test(name) || name.endsWith(".test.c") || CORE.has(name)) continue; + const lines = readFileSync(join(src, name), "utf8").split("\n"); + lines.forEach((line, i) => { + if (/\bv\.obj\.(entries|len)\b/.test(line)) offenders.push(`${name}:${i + 1}: ${line.trim()}`); + }); + } + expect(offenders).toEqual([]); +});