diff --git a/changes/internal/1665-container-accessors.md b/changes/internal/1665-container-accessors.md new file mode 100644 index 00000000..1d738459 --- /dev/null +++ b/changes/internal/1665-container-accessors.md @@ -0,0 +1 @@ +- Internal refactor, no behaviour change: list elements and dict values go through the ownership-explicit accessors in src/container.h, ahead of NaN-boxed container storage (#1665 arm D). diff --git a/src/builtins.c b/src/builtins.c index f6c07d1c..89e8dddd 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -177,8 +177,8 @@ Value* builtin_usleep(Value *arg) { Value* builtin_join(Value *arg) { STRICT_LIST_MAX(arg, 2, "join"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "join", "[list, separator]", make_str("")); - Value *list = arg->data.list.items[0]; - Value *sep_val = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *sep_val = list_get_borrow(arg, 1); ARG_GUARD(!list || list->type != VAL_LIST, "join", "a list as its first argument", make_str("")); /* Coercion, not a guard — a non-string separator silently became "" and * the elements ran together (`join of [["a","b"], 42]` was "ab"). Same @@ -199,7 +199,7 @@ Value* builtin_join(Value *arg) { size_t *lengths = xmalloc_array(count, sizeof(size_t)); size_t total = 0; for (int i = 0; i < count; i++) { - parts[i] = value_to_string(list->data.list.items[i]); + parts[i] = value_to_string(list_get_borrow(list, i)); lengths[i] = strlen(parts[i]); total += lengths[i]; if (i > 0) total += sep_len; @@ -294,18 +294,18 @@ Value* builtin_text_builder_new(Value *arg) { Value* builtin_text_builder_append(Value *arg) { STRICT_LIST_MAX(arg, 2, "text_builder_append"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *builder = arg->data.list.items[0]; + Value *builder = list_get_borrow(arg, 0); if (!builder || builder->type != VAL_TEXT_BUILDER) return make_null(); - text_builder_append_value(builder, arg->data.list.items[1]); + text_builder_append_value(builder, list_get_borrow(arg, 1)); return builder; } Value* builtin_text_builder_append_line(Value *arg) { STRICT_LIST_MAX(arg, 2, "text_builder_append_line"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *builder = arg->data.list.items[0]; + Value *builder = list_get_borrow(arg, 0); if (!builder || builder->type != VAL_TEXT_BUILDER) return make_null(); - text_builder_append_value(builder, arg->data.list.items[1]); + text_builder_append_value(builder, list_get_borrow(arg, 1)); text_builder_append_raw(builder, "\n", 1); return builder; } @@ -334,8 +334,8 @@ Value* builtin_text_builder_to_string(Value *arg) { * range). Shift counts are masked to 0..63 like the hardware would. */ static int bit_pair(Value *arg, int64_t *a_out, int64_t *b_out) { if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return 0; - Value *va = arg->data.list.items[0]; - Value *vb = arg->data.list.items[1]; + Value *va = list_get_borrow(arg, 0); + Value *vb = list_get_borrow(arg, 1); if (!va || va->type != VAL_NUM || !vb || vb->type != VAL_NUM) return 0; *a_out = (int64_t)eigs_num_arg(va, __func__); *b_out = (int64_t)eigs_num_arg(vb, __func__); @@ -522,8 +522,8 @@ Value* builtin_append(Value *arg) { rt_error(EK_TYPE, 0, "append requires [list, item]"); return make_null(); } - Value *target = arg->data.list.items[0]; - Value *item = arg->data.list.items[1]; + Value *target = list_get_borrow(arg, 0); + Value *item = list_get_borrow(arg, 1); if (target->type != VAL_LIST) { rt_error(EK_TYPE, 0, "append: target must be a list (got %s)", val_type_name(target->type)); @@ -615,8 +615,8 @@ Value* builtin_exit(Value *arg) { if (arg && arg->type == VAL_NUM) { code = (int)eigs_num_arg(arg, __func__); } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1 && - arg->data.list.items[0] && - arg->data.list.items[0]->type == VAL_NUM) { + list_get_borrow(arg, 0) && + list_get_borrow(arg, 0)->type == VAL_NUM) { code = (int)eigs_list_num(arg, 0, __func__); } g_exit_code = code; @@ -635,8 +635,8 @@ Value* builtin_assert(Value *arg) { * exit leaked the global env and every registered builtin. Side effect: * assert failures are now catchable in `try`/`catch`, matching `throw`. */ if (arg->type == VAL_LIST && arg->data.list.count >= 2) { - Value *cond = arg->data.list.items[0]; - Value *msg = arg->data.list.items[1]; + Value *cond = list_get_borrow(arg, 0); + Value *msg = list_get_borrow(arg, 1); if (!is_truthy(cond)) { char *msg_str = value_to_string(msg); rt_error(EK_ASSERT, 0, "ASSERT FAIL: %s", msg_str); @@ -706,7 +706,7 @@ Value* builtin_values(Value *arg) { eigs_module_ns_sync(arg); /* #1057 whole-dict reader */ Value *list = make_list(arg->data.dict.count); for (int i = 0; i < arg->data.dict.count; i++) - list_append(list, arg->data.dict.vals[i]); + list_append(list, dict_value_get_borrow(arg, i)); return list; } return make_list(0); @@ -715,8 +715,8 @@ Value* builtin_values(Value *arg) { Value* builtin_has_key(Value *arg) { STRICT_LIST_MAX(arg, 2, "has_key"); ARG_GUARD(arg->type != VAL_LIST || arg->data.list.count < 2, "has_key", "[dict, key]", make_bool(0)); - Value *d = arg->data.list.items[0]; - Value *key = arg->data.list.items[1]; + Value *d = list_get_borrow(arg, 0); + Value *key = list_get_borrow(arg, 1); ARG_GUARD(d->type != VAL_DICT || key->type != VAL_STR, "has_key", "[dict, string]", make_bool(0)); return make_bool(dict_has(d, key->data.str)); } @@ -725,9 +725,9 @@ Value* builtin_dict_set(Value *arg) { STRICT_LIST_MAX(arg, 3, "dict_set"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 3, "dict_set", "[dict, key, value]", make_null()); - Value *d = arg->data.list.items[0]; - Value *key = arg->data.list.items[1]; - Value *val = arg->data.list.items[2]; + Value *d = list_get_borrow(arg, 0); + Value *key = list_get_borrow(arg, 1); + Value *val = list_get_borrow(arg, 2); if (d->type != VAL_DICT || key->type != VAL_STR) return make_null(); dict_set(d, key->data.str, val); return d; @@ -737,8 +737,8 @@ Value* builtin_dict_remove(Value *arg) { STRICT_LIST_MAX(arg, 2, "dict_remove"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "dict_remove", "[dict, key]", make_null()); - Value *d = arg->data.list.items[0]; - Value *key = arg->data.list.items[1]; + Value *d = list_get_borrow(arg, 0); + Value *key = list_get_borrow(arg, 1); if (d->type != VAL_DICT || key->type != VAL_STR) return make_null(); dict_remove(d, key->data.str); return d; @@ -770,9 +770,9 @@ Value* builtin_classify(Value *arg) { Value *t = arg; const char *channel = "value"; if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - t = arg->data.list.items[0]; + t = list_get_borrow(arg, 0); if (arg->data.list.count >= 2) { - Value *ch = arg->data.list.items[1]; + Value *ch = list_get_borrow(arg, 1); if (!ch || ch->type != VAL_STR || (strcmp(ch->data.str, "value") != 0 && strcmp(ch->data.str, "entropy") != 0)) { @@ -924,7 +924,7 @@ static int eigs_json_encode_value(Value *v, strbuf *out, int depth) { strbuf_append_char(out, '['); for (int i = 0; i < v->data.list.count; i++) { if (i > 0) strbuf_append_char(out, ','); - if (eigs_json_encode_value(v->data.list.items[i], out, depth + 1) != 0) + if (eigs_json_encode_value(list_get_borrow(v, i), out, depth + 1) != 0) return -1; } strbuf_append_char(out, ']'); @@ -953,7 +953,7 @@ static int eigs_json_encode_value(Value *v, strbuf *out, int depth) { } strbuf_append_char(out, '"'); strbuf_append_char(out, ':'); - if (eigs_json_encode_value(v->data.dict.vals[i], out, depth + 1) != 0) + if (eigs_json_encode_value(dict_value_get_borrow(v, i), out, depth + 1) != 0) return -1; } strbuf_append_char(out, '}'); @@ -1403,8 +1403,8 @@ Value* builtin_coalesce(Value *arg) { /* coalesce of [value, default] — returns value unless empty/null */ if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return arg ? arg : make_null(); - Value *val = arg->data.list.items[0]; - Value *def = arg->data.list.items[1]; + Value *val = list_get_borrow(arg, 0); + Value *def = list_get_borrow(arg, 1); if (!val || val->type == VAL_NULL) return def; if (val->type == VAL_STR && val->data.str[0] == '\0') return def; return val; @@ -1446,11 +1446,11 @@ Value* builtin_json_build(Value *arg) { strbuf_append_char(&out, '{'); for (int i = 0; i + 1 < count; i += 2) { if (i > 0) strbuf_append_n(&out, ", ", 2); - char *key = value_to_string(arg->data.list.items[i]); + char *key = value_to_string(list_get_borrow(arg, i)); eigs_json_escape_string(&out, key); free(key); strbuf_append_n(&out, ": ", 2); - Value *val = arg->data.list.items[i + 1]; + Value *val = list_get_borrow(arg, i + 1); if (val->type == VAL_NUM) { char nb[32]; /* #875: the shared rule */ eigs_num_text(nb, sizeof(nb), eigs_num_arg(val, __func__)); @@ -1495,7 +1495,7 @@ Value* builtin_str_lower(Value *arg) { Value* builtin_contains(Value *arg) { STRICT_LIST_MAX(arg, 2, "contains"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "contains", "[haystack, needle]", make_bool(0)); - Value *h = arg->data.list.items[0], *n = arg->data.list.items[1]; + Value *h = list_get_borrow(arg, 0), *n = list_get_borrow(arg, 1); ARG_GUARD(!h || h->type != VAL_STR || !n || n->type != VAL_STR, "contains", "two strings", make_bool(0)); return make_bool(strstr(h->data.str, n->data.str) != NULL); } @@ -1503,7 +1503,7 @@ Value* builtin_contains(Value *arg) { Value* builtin_starts_with(Value *arg) { STRICT_LIST_MAX(arg, 2, "starts_with"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "starts_with", "[string, prefix]", make_bool(0)); - Value *s = arg->data.list.items[0], *p = arg->data.list.items[1]; + Value *s = list_get_borrow(arg, 0), *p = list_get_borrow(arg, 1); ARG_GUARD(!s || s->type != VAL_STR || !p || p->type != VAL_STR, "starts_with", "two strings", make_bool(0)); return make_bool(strncmp(s->data.str, p->data.str, val_str_len(p)) == 0); } @@ -1517,17 +1517,17 @@ Value* builtin_split(Value *arg) { * so STRICT_REQUIRE: raise under strict, byte-identical otherwise. */ STRICT_REQUIRE(!arg || !(arg->type == VAL_STR || (arg->type == VAL_LIST && arg->data.list.count >= 1 && - arg->data.list.items[0]->type == VAL_STR)), + list_get_borrow(arg, 0)->type == VAL_STR)), "split", "a string or [string, delimiter]"); STRICT_REQUIRE(arg->type == VAL_LIST && arg->data.list.count >= 2 && - arg->data.list.items[1]->type != VAL_STR, + list_get_borrow(arg, 1)->type != VAL_STR, "split", "a string delimiter"); if (arg && arg->type == VAL_STR) { str = arg->data.str; } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - if (arg->data.list.items[0]->type == VAL_STR) str = arg->data.list.items[0]->data.str; - if (arg->data.list.count >= 2 && arg->data.list.items[1]->type == VAL_STR) - delim = arg->data.list.items[1]->data.str; + if (list_get_borrow(arg, 0)->type == VAL_STR) str = list_get_borrow(arg, 0)->data.str; + if (arg->data.list.count >= 2 && list_get_borrow(arg, 1)->type == VAL_STR) + delim = list_get_borrow(arg, 1)->data.str; } /* Output-proportional and allowlisted: total output ~ input bytes plus a * Value + pointer per part. Uncharged, splitting a large string aborted @@ -1581,10 +1581,10 @@ Value* builtin_scan_ints(Value *arg) { if (arg && arg->type == VAL_STR) { str = arg->data.str; } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - Value *text_val = arg->data.list.items[0]; + Value *text_val = list_get_borrow(arg, 0); if (text_val && text_val->type == VAL_STR) str = text_val->data.str; if (arg->data.list.count >= 2) { - Value *comment_val = arg->data.list.items[1]; + Value *comment_val = list_get_borrow(arg, 1); if (comment_val && comment_val->type == VAL_STR && comment_val->data.str[0]) comment_marker = comment_val->data.str[0]; } @@ -1687,10 +1687,10 @@ Value* builtin_scan_int_tokens(Value *arg) { if (arg && arg->type == VAL_STR) { str = arg->data.str; } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - Value *text_val = arg->data.list.items[0]; + Value *text_val = list_get_borrow(arg, 0); if (text_val && text_val->type == VAL_STR) str = text_val->data.str; if (arg->data.list.count >= 2) { - Value *comment_val = arg->data.list.items[1]; + Value *comment_val = list_get_borrow(arg, 1); if (comment_val && comment_val->type == VAL_STR && comment_val->data.str[0]) comment_marker = comment_val->data.str[0]; } @@ -1793,12 +1793,12 @@ Value* builtin_str_replace(Value *arg) { * because there is no single stand-in — a non-string `old` makes * old_len 0, which returns the ORIGINAL string, while a non-string * `string` returns "". The non-strict path is untouched. */ - STRICT_REQUIRE(arg->data.list.items[0]->type != VAL_STR, "str_replace", "a string as its first argument"); - STRICT_REQUIRE(arg->data.list.items[1]->type != VAL_STR, "str_replace", "a string as its second argument"); - STRICT_REQUIRE(arg->data.list.items[2]->type != VAL_STR, "str_replace", "a string as its third argument"); - if (arg->data.list.items[0]->type == VAL_STR) str = arg->data.list.items[0]->data.str; - if (arg->data.list.items[1]->type == VAL_STR) old_s = arg->data.list.items[1]->data.str; - if (arg->data.list.items[2]->type == VAL_STR) new_s = arg->data.list.items[2]->data.str; + STRICT_REQUIRE(list_get_borrow(arg, 0)->type != VAL_STR, "str_replace", "a string as its first argument"); + STRICT_REQUIRE(list_get_borrow(arg, 1)->type != VAL_STR, "str_replace", "a string as its second argument"); + STRICT_REQUIRE(list_get_borrow(arg, 2)->type != VAL_STR, "str_replace", "a string as its third argument"); + if (list_get_borrow(arg, 0)->type == VAL_STR) str = list_get_borrow(arg, 0)->data.str; + if (list_get_borrow(arg, 1)->type == VAL_STR) old_s = list_get_borrow(arg, 1)->data.str; + if (list_get_borrow(arg, 2)->type == VAL_STR) new_s = list_get_borrow(arg, 2)->data.str; size_t old_len = strlen(old_s), new_len = strlen(new_s), str_len = strlen(str); if (old_len == 0) return make_str(str); /* Count occurrences to size buffer */ @@ -1859,8 +1859,8 @@ Value* builtin_str_upper(Value *arg) { Value* builtin_char_at(Value *arg) { STRICT_LIST_MAX(arg, 2, "char_at"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "char_at", "[string, index]", make_str("")); - Value *str_val = arg->data.list.items[0]; - Value *idx_val = arg->data.list.items[1]; + Value *str_val = list_get_borrow(arg, 0); + Value *idx_val = list_get_borrow(arg, 1); ARG_GUARD(!str_val || str_val->type != VAL_STR || !idx_val || idx_val->type != VAL_NUM, "char_at", "[string, number]", make_str("")); int idx = (int)eigs_num_arg(idx_val, __func__); @@ -1878,7 +1878,7 @@ Value* builtin_char_at(Value *arg) { Value* builtin_ends_with(Value *arg) { STRICT_LIST_MAX(arg, 2, "ends_with"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "ends_with", "[string, suffix]", make_bool(0)); - Value *sv = arg->data.list.items[0], *xv = arg->data.list.items[1]; + Value *sv = list_get_borrow(arg, 0), *xv = list_get_borrow(arg, 1); ARG_GUARD(!sv || sv->type != VAL_STR || !xv || xv->type != VAL_STR, "ends_with", "two strings", make_bool(0)); const char *str = sv->data.str, *suffix = xv->data.str; int slen = strlen(str), xlen = strlen(suffix); @@ -1893,9 +1893,9 @@ Value* builtin_ends_with(Value *arg) { Value* builtin_substr(Value *arg) { STRICT_LIST_MAX(arg, 3, "substr"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 3, "substr", "[string, start, length]", make_str("")); - Value *str_val = arg->data.list.items[0]; - Value *start_val = arg->data.list.items[1]; - Value *len_val = arg->data.list.items[2]; + Value *str_val = list_get_borrow(arg, 0); + Value *start_val = list_get_borrow(arg, 1); + Value *len_val = list_get_borrow(arg, 2); ARG_GUARD(!str_val || str_val->type != VAL_STR, "substr", "a string as its first argument", make_str("")); ARG_GUARD(!start_val || start_val->type != VAL_NUM, "substr", "a number as its start", make_str("")); ARG_GUARD(!len_val || len_val->type != VAL_NUM, "substr", "a number as its length", make_str("")); @@ -1932,7 +1932,7 @@ Value* builtin_index_of(Value *arg) { STRICT_LIST_MAX(arg, 2, "index_of"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "index_of", "[haystack, needle]", make_num(-1)); - Value *h = arg->data.list.items[0], *n = arg->data.list.items[1]; + Value *h = list_get_borrow(arg, 0), *n = list_get_borrow(arg, 1); /* #1008: the -1 for non-string operands is a deliberate choice (#316 — * folding to "" gave a false positive at index 0), so ARG_GUARD is * exactly the right shape: the answer is preserved with the flag off and @@ -2002,8 +2002,8 @@ Value* builtin_atan(Value *arg) { Value* builtin_atan2(Value *arg) { STRICT_LIST_MAX(arg, 2, "atan2"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "atan2", "[y, x]", make_num(0)); - Value *y = arg->data.list.items[0]; - Value *x = arg->data.list.items[1]; + Value *y = list_get_borrow(arg, 0); + Value *x = list_get_borrow(arg, 1); ARG_GUARD(!y || y->type != VAL_NUM || !x || x->type != VAL_NUM, "atan2", "two numbers", make_num(0)); return make_num(atan2(eigs_num_arg(y, __func__), eigs_num_arg(x, __func__))); } @@ -2045,7 +2045,7 @@ static Value* minmax_reduce(Value *arg, int want_max) { if (arg->data.list.count < 1) return make_num(0); double best = 0.0; for (int i = 0; i < arg->data.list.count; i++) { - Value *v = arg->data.list.items[i]; + Value *v = list_get_borrow(arg, i); ARG_GUARD(!v || v->type != VAL_NUM, want_max ? "max" : "min", "every element to be a number", make_num(0)); if (i == 0 || (want_max ? eigs_num_arg(v, __func__) > best : eigs_num_arg(v, __func__) < best)) best = eigs_num_arg(v, __func__); @@ -2125,8 +2125,8 @@ Value* builtin_random_int(Value *arg) { * range. Taped shape: the soft half still records/replays as before. */ ARG_GUARD_TAPED(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "random_int", "[lo, hi]", make_num(0)); - Value *lo = arg->data.list.items[0]; - Value *hi = arg->data.list.items[1]; + Value *lo = list_get_borrow(arg, 0); + Value *hi = list_get_borrow(arg, 1); ARG_GUARD_TAPED(!lo || lo->type != VAL_NUM || !hi || hi->type != VAL_NUM, "random_int", "numeric bounds", make_num(0)); /* Range-check as doubles before any integer cast — a double outside the @@ -2206,8 +2206,8 @@ Value* builtin_args(Value *arg) { Value* builtin_path_join(Value *arg) { STRICT_LIST_MAX(arg, 2, "path_join"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "path_join", "[a, b]", make_str("")); - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); ARG_GUARD(!a || a->type != VAL_STR || !b || b->type != VAL_STR, "path_join", "two strings", make_str("")); int alen = val_str_len(a); int blen = val_str_len(b); @@ -2287,9 +2287,9 @@ Value* builtin_path_ext(Value *arg) { static Value* json_obj_get(Value *obj, const char *key) { if (!obj || obj->type != VAL_LIST) return NULL; for (int i = 0; i + 1 < obj->data.list.count; i += 2) { - Value *k = obj->data.list.items[i]; + Value *k = list_get_borrow(obj, i); if (k && k->type == VAL_STR && strcmp(k->data.str, key) == 0) - return obj->data.list.items[i + 1]; + return list_get_borrow(obj, i + 1); } return NULL; } @@ -2300,8 +2300,8 @@ Value* builtin_json_path(Value *arg) { /* json_path of [json_string, "dot.path"] -> value as string, or "" */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "json_path", "[json_string, path]", make_str("")); const char *json_str = "", *path = ""; - if (arg->data.list.items[0]->type == VAL_STR) json_str = arg->data.list.items[0]->data.str; - if (arg->data.list.items[1]->type == VAL_STR) path = arg->data.list.items[1]->data.str; + if (list_get_borrow(arg, 0)->type == VAL_STR) json_str = list_get_borrow(arg, 0)->data.str; + if (list_get_borrow(arg, 1)->type == VAL_STR) path = list_get_borrow(arg, 1)->data.str; /* #507: reject empty path segments (a leading/trailing dot or a `..`). * strtok silently skips them, so "a..b", ".a" and "a." used to resolve @@ -2365,7 +2365,7 @@ Value* builtin_json_path(Value *arg) { /* Numeric: treat as array index */ /* Arrays from json_decode are VAL_LIST with sequential elements */ if (idx >= 0 && idx < current->data.list.count) { - current = current->data.list.items[idx]; + current = list_get_borrow(current, idx); } else { /* fs:ANSWER array index out of range — no value at that path */ val_decref(root); return make_str(""); @@ -2550,8 +2550,8 @@ Value* builtin_state_at(Value *arg) { Value* builtin_secure_equals(Value *arg) { STRICT_LIST_MAX(arg, 2, "secure_equals"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "secure_equals", "[string, string]", make_bool(0)); - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); ARG_GUARD(!a || !b || a->type != VAL_STR || b->type != VAL_STR, "secure_equals", "two strings", make_bool(0)); const char *sa = a->data.str ? a->data.str : ""; const char *sb = b->data.str ? b->data.str : ""; @@ -2612,8 +2612,8 @@ Value* builtin_hex(Value *arg) { if (arg && arg->type == VAL_NUM) { num = eigs_num_arg(arg, __func__); } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 2 && - arg->data.list.items[0] && arg->data.list.items[0]->type == VAL_NUM && - arg->data.list.items[1] && arg->data.list.items[1]->type == VAL_NUM) { + list_get_borrow(arg, 0) && list_get_borrow(arg, 0)->type == VAL_NUM && + list_get_borrow(arg, 1) && list_get_borrow(arg, 1)->type == VAL_NUM) { num = eigs_list_num(arg, 0, __func__); width = (long long)eigs_list_num(arg, 1, __func__); if (g_has_error) return make_null(); @@ -2762,7 +2762,7 @@ Value* builtin_eval(Value *arg) { static const char *vm_desc_abi_error(Value *desc, char *buf, size_t buflen) { if (!desc || desc->type != VAL_LIST || desc->data.list.count < 1) return "chunk descriptor must be a list"; - Value *rev = desc->data.list.items[0]; + Value *rev = list_get_borrow(desc, 0); if (!rev || rev->type != VAL_NUM) { snprintf(buf, buflen, "chunk descriptor carries no bytecode ABI revision (element 0 " @@ -2901,7 +2901,7 @@ static Value *desc_isolate_const(DescVerify *ctx, Value *v) { * in memory (the uncharged-wrapper rule make_list_heap documents). */ Value *c = make_list_heap(v->data.list.count); for (int i = 0; i < v->data.list.count; i++) { - Value *e = desc_isolate_const(ctx, v->data.list.items[i]); + Value *e = desc_isolate_const(ctx, list_get_borrow(v, i)); if (!e) { val_decref(c); desc_leave(ctx); return NULL; } list_append_owned(c, e); } @@ -2912,7 +2912,7 @@ static Value *desc_isolate_const(DescVerify *ctx, Value *v) { if (!desc_enter(ctx, v)) return NULL; Value *c = make_dict(v->data.dict.count); for (int i = 0; i < v->data.dict.count; i++) { - Value *e = desc_isolate_const(ctx, v->data.dict.vals[i]); + Value *e = desc_isolate_const(ctx, dict_value_get_borrow(v, i)); if (!e) { val_decref(c); desc_leave(ctx); return NULL; } dict_set_owned(c, v->data.dict.keys[i], e); } @@ -2961,7 +2961,7 @@ static EigsChunk *vm_build_chunk_desc_body(Value *desc, int off, int sandbox_mod vm_desc_error(why, whyn, "descriptor must contain code and constants lists"); return NULL; } - Value **d = desc->data.list.items + off; + Value **d = list_values_storage(desc) + off; int n = desc->data.list.count - off; Value *code = d[0], *consts = d[1]; if (!code || code->type != VAL_LIST || !consts || consts->type != VAL_LIST) { @@ -2980,7 +2980,7 @@ static EigsChunk *vm_build_chunk_desc_body(Value *desc, int off, int sandbox_mod EigsChunk *chunk = chunk_new(name); for (int i = 0; i < code->data.list.count; i++) { - Value *b = code->data.list.items[i]; + Value *b = list_get_borrow(code, i); /* #1637: a non-number code element (a bool) is refused, never * emitted as byte 0 (OP_CONST's opcode). */ if (!b || b->type != VAL_NUM) { @@ -2998,7 +2998,7 @@ static EigsChunk *vm_build_chunk_desc_body(Value *desc, int off, int sandbox_mod * mutable constant, so nothing the sandbox does to a constant is visible * to the host, and nothing the host does mid-run is visible to it. */ for (int i = 0; i < consts->data.list.count; i++) { - Value *item = consts->data.list.items[i]; + Value *item = list_get_borrow(consts, i); if (!item) { vm_desc_error(why, whyn, "constant pool contains an empty entry"); chunk_free(chunk); return NULL; @@ -3027,7 +3027,7 @@ static EigsChunk *vm_build_chunk_desc_body(Value *desc, int off, int sandbox_mod return NULL; } for (int i = 0; i < d[2]->data.list.count; i++) { - EigsChunk *fn = vm_build_chunk_desc_ctx(d[2]->data.list.items[i], 0, + EigsChunk *fn = vm_build_chunk_desc_ctx(list_get_borrow(d[2], i), 0, sandbox_mode, ctx, why, whyn); if (!fn) { chunk_free(chunk); return NULL; } chunk_add_function(chunk, fn); @@ -3053,7 +3053,7 @@ static EigsChunk *vm_build_chunk_desc_body(Value *desc, int off, int sandbox_mod if (!desc_spend(ctx, lc)) { chunk_free(chunk); return NULL; } chunk->local_names = xcalloc(lc, sizeof(char *)); for (int i = 0; i < lc; i++) { - Value *nm = d[5]->data.list.items[i]; + Value *nm = list_get_borrow(d[5], i); chunk->local_names[i] = env_intern_name((nm && nm->type == VAL_STR) ? nm->data.str : ""); } chunk->local_count = lc; @@ -3315,7 +3315,7 @@ static int sandbox_value_has_callable(Value *v, int depth, long *budget, } if (v->type == VAL_LIST) { for (int i = 0; i < v->data.list.count; i++) - if (sandbox_value_has_callable(v->data.list.items[i], depth + 1, + if (sandbox_value_has_callable(list_get_borrow(v, i), depth + 1, budget, unverified)) return 1; } else if (v->type == VAL_DICT) { @@ -3326,7 +3326,7 @@ static int sandbox_value_has_callable(Value *v, int depth, long *budget, * whenever a sandbox intern scope is open, so that pass * changed 0 of 128 keys -- deleted. One job: callable * detection. */ - if (sandbox_value_has_callable(v->data.dict.vals[i], depth + 1, + if (sandbox_value_has_callable(dict_value_get_borrow(v, i), depth + 1, budget, unverified)) return 1; } @@ -3357,17 +3357,17 @@ Value* builtin_sandbox_run(Value *arg) { * the gate's, and it evaporates the day sealing is relaxed. The guard runs * below, once the chunk exists. */ Value *desc = (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) - ? arg->data.list.items[0] : arg; + ? list_get_borrow(arg, 0) : arg; int max_iter = 1000000; if (arg && arg->type == VAL_LIST && arg->data.list.count >= 2 && - arg->data.list.items[1] && arg->data.list.items[1]->type == VAL_NUM) + list_get_borrow(arg, 1) && list_get_borrow(arg, 1)->type == VAL_NUM) max_iter = (int)eigs_list_num(arg, 1, __func__); /* #292: optional max_bytes (3rd element) — the allocation budget for this * run. Default 256 MiB: ample for the short generated snippets grade() * runs, small enough that even a few of them can't thrash a 4 GB box. */ size_t max_bytes = (size_t)256 * 1024 * 1024; if (arg && arg->type == VAL_LIST && arg->data.list.count >= 3 && - arg->data.list.items[2] && arg->data.list.items[2]->type == VAL_NUM && + list_get_borrow(arg, 2) && list_get_borrow(arg, 2)->type == VAL_NUM && eigs_list_num(arg, 2, __func__) > 0) max_bytes = (size_t)eigs_list_num(arg, 2, __func__); /* VM instructions, cumulative across callback re-entry and function @@ -3376,7 +3376,7 @@ Value* builtin_sandbox_run(Value *arg) { * placing a finite ceiling on straight-line/call-heavy computation. */ uint64_t max_work = UINT64_C(10000000); if (arg && arg->type == VAL_LIST && arg->data.list.count >= 4 && - arg->data.list.items[3] && arg->data.list.items[3]->type == VAL_NUM) { + list_get_borrow(arg, 3) && list_get_borrow(arg, 3)->type == VAL_NUM) { double requested = eigs_list_num(arg, 3, __func__); /* A double rounds UINT64_MAX up to 2^64, so use a strict bound; * accepting equality and casting it would itself be undefined. */ @@ -3722,9 +3722,9 @@ Value* builtin_copy_into(Value *arg) { rt_error(EK_TYPE, 0, "copy_into requires [dest, offset, src]"); return make_null(); } - Value *dest = arg->data.list.items[0]; - Value *offv = arg->data.list.items[1]; - Value *src = arg->data.list.items[2]; + Value *dest = list_get_borrow(arg, 0); + Value *offv = list_get_borrow(arg, 1); + Value *src = list_get_borrow(arg, 2); if (!offv || offv->type != VAL_NUM) { rt_error(EK_TYPE, 0, "copy_into: offset must be a number, got %s (the order is [dest, offset, src])", offv ? val_type_name(offv->type) : "null"); @@ -3741,7 +3741,7 @@ Value* builtin_copy_into(Value *arg) { } if (src && src->type == VAL_LIST) { for (int i = 0; i < src->data.list.count && offset + i < dest->data.buffer.count; i++) { - Value *it = src->data.list.items[i]; + Value *it = list_get_borrow(src, i); if (!it || it->type != VAL_NUM) { rt_error(EK_TYPE, 0, "cannot store %s in a buffer (copy_into element %d; buffers hold numbers)", it ? val_type_name(it->type) : "null", i); @@ -3761,10 +3761,10 @@ Value* builtin_copy_into(Value *arg) { return make_null(); } for (int i = 0; i < src->data.list.count && offset + i < dest->data.list.count; i++) { - Value *item = src->data.list.items[i]; + Value *item = list_get_borrow(src, i); val_incref(item); - val_decref(dest->data.list.items[offset + i]); - dest->data.list.items[offset + i] = item; + val_decref(list_get_borrow(dest, offset + i)); + list_set_owned(dest, offset + i, item); } return dest; } @@ -3777,10 +3777,10 @@ Value* builtin_copy_into(Value *arg) { Value* builtin_list_slice(Value *arg) { STRICT_LIST_MAX(arg, 3, "list_slice"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) return make_null(); - Value *list = arg->data.list.items[0]; + Value *list = list_get_borrow(arg, 0); if (!list || list->type != VAL_LIST) return make_null(); - Value *start_v = arg->data.list.items[1]; - Value *end_v = arg->data.list.items[2]; + Value *start_v = list_get_borrow(arg, 1); + Value *end_v = list_get_borrow(arg, 2); BOOL_REFUSE(start_v, "list_slice"); BOOL_REFUSE(end_v, "list_slice"); if (!start_v || start_v->type != VAL_NUM || !end_v || end_v->type != VAL_NUM) @@ -3800,7 +3800,7 @@ Value* builtin_list_slice(Value *arg) { if (!sandbox_charge((size_t)out_n * sizeof(Value *))) return make_null(); Value *result = make_list(out_n); for (int i = start; i < end; i++) - list_append(result, list->data.list.items[i]); + list_append(result, list_get_borrow(list, i)); return result; } @@ -3818,8 +3818,8 @@ Value* builtin_num_copy(Value *arg) { Value* builtin_concat(Value *arg) { STRICT_LIST_MAX(arg, 2, "concat"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); if (!a || a->type != VAL_LIST || !b || b->type != VAL_LIST) return make_null(); int total = a->data.list.count + b->data.list.count; /* #292: charge the new slots — `result is concat of [result, more]` in a loop @@ -3827,9 +3827,9 @@ Value* builtin_concat(Value *arg) { if (!sandbox_charge((size_t)total * sizeof(Value *))) return make_null(); Value *result = make_list(total); for (int i = 0; i < a->data.list.count; i++) - list_append(result, a->data.list.items[i]); + list_append(result, list_get_borrow(a, i)); for (int i = 0; i < b->data.list.count; i++) - list_append(result, b->data.list.items[i]); + list_append(result, list_get_borrow(b, i)); return result; } @@ -3852,10 +3852,10 @@ Value* builtin_range(Value *arg) { * be silently treated as 0 (or fold the whole call to []) — a * silent-wrong loop count. */ for (int i = 0; i < argc && i < 3; i++) { - if (arg->data.list.items[i]->type != VAL_NUM) { + if (list_get_borrow(arg, i)->type != VAL_NUM) { rt_error(EK_TYPE, 0, "range: argument %d must be a number, got %s", i, - val_type_name(arg->data.list.items[i]->type)); + val_type_name(list_get_borrow(arg, i)->type)); return make_list(0); } } @@ -3929,10 +3929,10 @@ Value* builtin_fill(Value *arg) { rt_error(EK_TYPE, 0, "fill requires [count, value]"); return make_list(0); } - BOOL_REFUSE(arg->data.list.items[0], "fill"); /* the count; the value may be anything */ + BOOL_REFUSE(list_get_borrow(arg, 0), "fill"); /* the count; the value may be anything */ int count = (int)eigs_list_num(arg, 0, __func__); if (g_has_error) return make_null(); /* #1637: a non-number slot raised */ - Value *val = arg->data.list.items[1]; + Value *val = list_get_borrow(arg, 1); if (count < 0) count = 0; if (count > 10000000) count = 10000000; /* 10M cap */ /* #292: charge the sandbox budget — fill stores `count` slots pointing at the @@ -3999,21 +3999,21 @@ Value* builtin_set_at(Value *arg) { * one. A non-number value is refused with buf_set's message rather than * silently type-punned. `at_index`'s negative-from-the-end rule applies * to buffers too. */ - if ((argc == 3 || argc == 4) && arg->data.list.items[0] - && arg->data.list.items[0]->type == VAL_BUFFER) { - Value *buf = arg->data.list.items[0]; - Value *val = arg->data.list.items[argc == 3 ? 2 : 3]; + if ((argc == 3 || argc == 4) && list_get_borrow(arg, 0) + && list_get_borrow(arg, 0)->type == VAL_BUFFER) { + Value *buf = list_get_borrow(arg, 0); + Value *val = list_get_borrow(arg, argc == 3 ? 2 : 3); int64_t off; if (argc == 3) { int idx; - if (!buf_at_index(arg->data.list.items[1], buf->data.buffer.count, + if (!buf_at_index(list_get_borrow(arg, 1), buf->data.buffer.count, &idx)) return make_null(); off = idx; } else if (argc == 4 && buf->data.buffer.rows > 0) { int row, col; - if (!buf_at_index(arg->data.list.items[1], buf->data.buffer.rows, + if (!buf_at_index(list_get_borrow(arg, 1), buf->data.buffer.rows, &row)) return make_null(); - if (!buf_at_index(arg->data.list.items[2], buf->data.buffer.cols, + if (!buf_at_index(list_get_borrow(arg, 2), buf->data.buffer.cols, &col)) return make_null(); off = (int64_t)row * buf->data.buffer.cols + col; } else { @@ -4034,42 +4034,42 @@ Value* builtin_set_at(Value *arg) { } if (argc == 3) { /* 1D: set_at of [list, index, value] */ - Value *list = arg->data.list.items[0]; - Value *val = arg->data.list.items[2]; + Value *list = list_get_borrow(arg, 0); + Value *val = list_get_borrow(arg, 2); if (!list || list->type != VAL_LIST) { rt_error(EK_TYPE, 0, "set_at: first argument must be a list"); return make_null(); } int idx; - if (!at_index(arg->data.list.items[1], list->data.list.count, "set_at", &idx)) + if (!at_index(list_get_borrow(arg, 1), list->data.list.count, "set_at", &idx)) return make_null(); val_incref(val); - val_decref(list->data.list.items[idx]); - list->data.list.items[idx] = val; + val_decref(list_get_borrow(list, idx)); + list_set_owned(list, idx, val); return list; } if (argc == 4) { /* 2D: set_at of [list, row, col, value] */ - Value *list = arg->data.list.items[0]; - Value *val = arg->data.list.items[3]; + Value *list = list_get_borrow(arg, 0); + Value *val = list_get_borrow(arg, 3); if (!list || list->type != VAL_LIST) { rt_error(EK_TYPE, 0, "set_at: first argument must be a list"); return make_null(); } int row; - if (!at_index(arg->data.list.items[1], list->data.list.count, "set_at row", &row)) + if (!at_index(list_get_borrow(arg, 1), list->data.list.count, "set_at row", &row)) return make_null(); - Value *rowv = list->data.list.items[row]; + Value *rowv = list_get_borrow(list, row); if (!rowv || rowv->type != VAL_LIST) { rt_error(EK_TYPE, 0, "set_at: row %d is not a list", row); return make_null(); } int col; - if (!at_index(arg->data.list.items[2], rowv->data.list.count, "set_at col", &col)) + if (!at_index(list_get_borrow(arg, 2), rowv->data.list.count, "set_at col", &col)) return make_null(); val_incref(val); - val_decref(rowv->data.list.items[col]); - rowv->data.list.items[col] = val; + val_decref(list_get_borrow(rowv, col)); + list_set_owned(rowv, col, val); return list; } rt_error(EK_TYPE, 0, "set_at takes [list, index, value] or " @@ -4086,20 +4086,20 @@ Value* builtin_get_at(Value *arg) { } int argc = arg->data.list.count; /* #1093: same buffer reading as set_at above. */ - if ((argc == 2 || argc == 3) && arg->data.list.items[0] - && arg->data.list.items[0]->type == VAL_BUFFER) { - Value *buf = arg->data.list.items[0]; + if ((argc == 2 || argc == 3) && list_get_borrow(arg, 0) + && list_get_borrow(arg, 0)->type == VAL_BUFFER) { + Value *buf = list_get_borrow(arg, 0); if (argc == 2) { int idx; - if (!buf_at_index(arg->data.list.items[1], buf->data.buffer.count, + if (!buf_at_index(list_get_borrow(arg, 1), buf->data.buffer.count, &idx)) return make_null(); return make_num(buffer_read_num(buf, idx)); } if (argc == 3 && buf->data.buffer.rows > 0) { int row, col; - if (!buf_at_index(arg->data.list.items[1], buf->data.buffer.rows, + if (!buf_at_index(list_get_borrow(arg, 1), buf->data.buffer.rows, &row)) return make_null(); - if (!buf_at_index(arg->data.list.items[2], buf->data.buffer.cols, + if (!buf_at_index(list_get_borrow(arg, 2), buf->data.buffer.cols, &col)) return make_null(); return make_num(buffer_read_num(buf, (int64_t)row * buf->data.buffer.cols + col)); } @@ -4108,36 +4108,36 @@ Value* builtin_get_at(Value *arg) { return make_null(); } if (argc == 2) { - Value *list = arg->data.list.items[0]; + Value *list = list_get_borrow(arg, 0); if (!list || list->type != VAL_LIST) { rt_error(EK_TYPE, 0, "get_at: first argument must be a list"); return make_null(); } int idx; - if (!at_index(arg->data.list.items[1], list->data.list.count, "get_at", &idx)) + if (!at_index(list_get_borrow(arg, 1), list->data.list.count, "get_at", &idx)) return make_null(); - val_incref(list->data.list.items[idx]); - return list->data.list.items[idx]; + val_incref(list_get_borrow(list, idx)); + return list_get_borrow(list, idx); } if (argc == 3) { - Value *list = arg->data.list.items[0]; + Value *list = list_get_borrow(arg, 0); if (!list || list->type != VAL_LIST) { rt_error(EK_TYPE, 0, "get_at: first argument must be a list"); return make_null(); } int row; - if (!at_index(arg->data.list.items[1], list->data.list.count, "get_at row", &row)) + if (!at_index(list_get_borrow(arg, 1), list->data.list.count, "get_at row", &row)) return make_null(); - Value *rowv = list->data.list.items[row]; + Value *rowv = list_get_borrow(list, row); if (!rowv || rowv->type != VAL_LIST) { rt_error(EK_TYPE, 0, "get_at: row %d is not a list", row); return make_null(); } int col; - if (!at_index(arg->data.list.items[2], rowv->data.list.count, "get_at col", &col)) + if (!at_index(list_get_borrow(arg, 2), rowv->data.list.count, "get_at col", &col)) return make_null(); - val_incref(rowv->data.list.items[col]); - return rowv->data.list.items[col]; + val_incref(list_get_borrow(rowv, col)); + return list_get_borrow(rowv, col); } rt_error(EK_TYPE, 0, "get_at takes [list, index] or [list, row, col]"); return make_null(); @@ -4350,12 +4350,12 @@ Value* builtin_spawn(Value *arg) { int fn_arg_count = 0; /* Accept bare function (0 args) or [fn, arg1, arg2, ...] (N args) */ if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - fn = arg->data.list.items[0]; + fn = list_get_borrow(arg, 0); fn_arg_count = arg->data.list.count - 1; if (fn_arg_count > 0) { fn_args = xmalloc(sizeof(Value*) * fn_arg_count); for (int i = 0; i < fn_arg_count; i++) { - fn_args[i] = arg->data.list.items[i + 1]; + fn_args[i] = list_get_borrow(arg, i + 1); val_incref(fn_args[i]); } } @@ -4668,11 +4668,11 @@ Value* builtin_send(Value *arg) { rt_error(EK_TYPE, 0, "send requires [channel, value]"); return make_null(); } - Channel *ch = channel_arg(arg->data.list.items[0], "send"); + Channel *ch = channel_arg(list_get_borrow(arg, 0), "send"); if (!ch) return make_null(); /* Deep-copy into a self-contained heap value (refcount 1) owned by the * channel buffer; receiver adopts that ref. */ - Value *val = val_clone_for_send(arg->data.list.items[1]); + Value *val = val_clone_for_send(list_get_borrow(arg, 1)); pthread_mutex_lock(&ch->mutex); while (ch->count >= CHANNEL_BUF_SIZE && !ch->closed && !eigs_state_exit_requested(eigs_current_state(), NULL)) @@ -4750,10 +4750,10 @@ Value* builtin_recv_timeout(Value *arg) { rt_error(EK_TYPE, 0, "recv_timeout requires [channel, ms]"); return make_null(); } - Channel *ch = channel_arg(arg->data.list.items[0], "recv_timeout"); + Channel *ch = channel_arg(list_get_borrow(arg, 0), "recv_timeout"); if (!ch) return make_null(); if (replay_blocks("recv_timeout")) return make_null(); /* #1072: after validation */ - Value *ms_v = arg->data.list.items[1]; + Value *ms_v = list_get_borrow(arg, 1); if (ms_v->type != VAL_NUM) { rt_error(EK_TYPE, 0, "recv_timeout: ms must be a number"); return make_null(); @@ -4893,12 +4893,12 @@ Value* builtin_task_spawn(Value *arg) { Value **args = NULL; int argc = 0; if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1) { - fn = arg->data.list.items[0]; + fn = list_get_borrow(arg, 0); argc = arg->data.list.count - 1; if (argc > 0) { args = xmalloc(sizeof(Value*) * argc); for (int i = 0; i < argc; i++) - args[i] = val_clone_for_send(arg->data.list.items[i + 1]); + args[i] = val_clone_for_send(list_get_borrow(arg, i + 1)); } } if (!fn || (fn->type != VAL_FN && fn->type != VAL_BUILTIN)) { @@ -5036,7 +5036,7 @@ Value* builtin_task_join(Value *arg) { Value* builtin_task_send(Value *arg) { STRICT_LIST_MAX(arg, 2, "task_send"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "task_send", "[id, value]", make_bool(0)); - Value *idv = arg->data.list.items[0]; + Value *idv = list_get_borrow(arg, 0); /* The type guard precedes the scheduler-state check deliberately. With the * state check first, `task_send of ["x", 1]` outside a task context * returned 0 without ever reaching this guard — a guard nothing can @@ -5053,7 +5053,7 @@ Value* builtin_task_send(Value *arg) { * but it does have a scheduler mailbox (reply-to-supervisor pattern). */ if (eigs_num_arg(idv, __func__) != 0 && !task_handle_resolve(idv, "task_send", &target)) return make_bool(0); - Value *copy = val_clone_for_send(arg->data.list.items[1]); /* share-nothing */ + Value *copy = val_clone_for_send(list_get_borrow(arg, 1)); /* share-nothing */ int sent = task_deliver(target, copy); if (!sent) val_decref(copy); /* dropped to a dead task — release the copy */ return make_bool(sent); @@ -5325,7 +5325,7 @@ Value* builtin_nearest_in_range(Value *arg) { rt_error(EK_TYPE, 0, "nearest_in_range requires [entities, x, y, range, world_w, world_h]"); return make_null(); } - Value *entities = arg->data.list.items[0]; + Value *entities = list_get_borrow(arg, 0); if (!entities || entities->type != VAL_LIST) return make_null(); BOOL_REFUSE(arg, "nearest_in_range"); /* x, y, range, world: numbers */ double px = eigs_list_num(arg, 1, __func__); @@ -5335,12 +5335,12 @@ Value* builtin_nearest_in_range(Value *arg) { double wh = eigs_list_num(arg, 5, __func__); if (g_has_error) return make_null(); /* #1637: a non-number slot raised */ const char *px_key = "px", *py_key = "py", *active_key = "active"; - if (arg->data.list.count >= 7 && arg->data.list.items[6]->type == VAL_STR) - px_key = arg->data.list.items[6]->data.str; - if (arg->data.list.count >= 8 && arg->data.list.items[7]->type == VAL_STR) - py_key = arg->data.list.items[7]->data.str; - if (arg->data.list.count >= 9 && arg->data.list.items[8]->type == VAL_STR) - active_key = arg->data.list.items[8]->data.str; + if (arg->data.list.count >= 7 && list_get_borrow(arg, 6)->type == VAL_STR) + px_key = list_get_borrow(arg, 6)->data.str; + if (arg->data.list.count >= 8 && list_get_borrow(arg, 7)->type == VAL_STR) + py_key = list_get_borrow(arg, 7)->data.str; + if (arg->data.list.count >= 9 && list_get_borrow(arg, 8)->type == VAL_STR) + active_key = list_get_borrow(arg, 8)->data.str; double range_sq = range * range; double best_sq = range_sq; @@ -5371,11 +5371,11 @@ Value* builtin_nearest_in_range(Value *arg) { for (int i = 0; i < n; i++) { if (i + PFDIST < n) - __builtin_prefetch(entities->data.list.items[i + PFDIST], 0, 1); - Value *e = entities->data.list.items[i]; + __builtin_prefetch(list_get_borrow(entities, i + PFDIST), 0, 1); + Value *e = list_get_borrow(entities, i); if (!e || e->type != VAL_DICT) continue; char **keys = e->data.dict.keys; - Value **vals = e->data.dict.vals; + Value **vals = dict_values_storage(e); int kcount = e->data.dict.count; Value *av; @@ -5457,7 +5457,7 @@ Value* builtin_nearest_in_range_all(Value *arg) { rt_error(EK_TYPE, 0, "nearest_in_range_all requires [entities, range, world_w, world_h]"); return make_null(); } - Value *entities = arg->data.list.items[0]; + Value *entities = list_get_borrow(arg, 0); if (!entities || entities->type != VAL_LIST) return make_null(); BOOL_REFUSE(arg, "nearest_in_range_all"); /* range, world: numbers */ double range = eigs_list_num(arg, 1, __func__); @@ -5465,12 +5465,12 @@ Value* builtin_nearest_in_range_all(Value *arg) { double wh = eigs_list_num(arg, 3, __func__); if (g_has_error) return make_null(); /* #1637: a non-number slot raised */ const char *px_key = "px", *py_key = "py", *active_key = "active"; - if (arg->data.list.count >= 5 && arg->data.list.items[4]->type == VAL_STR) - px_key = arg->data.list.items[4]->data.str; - if (arg->data.list.count >= 6 && arg->data.list.items[5]->type == VAL_STR) - py_key = arg->data.list.items[5]->data.str; - if (arg->data.list.count >= 7 && arg->data.list.items[6]->type == VAL_STR) - active_key = arg->data.list.items[6]->data.str; + if (arg->data.list.count >= 5 && list_get_borrow(arg, 4)->type == VAL_STR) + px_key = list_get_borrow(arg, 4)->data.str; + if (arg->data.list.count >= 6 && list_get_borrow(arg, 5)->type == VAL_STR) + py_key = list_get_borrow(arg, 5)->data.str; + if (arg->data.list.count >= 7 && list_get_borrow(arg, 6)->type == VAL_STR) + active_key = list_get_borrow(arg, 6)->data.str; int n = entities->data.list.count; double range_sq = range * range; @@ -5496,15 +5496,15 @@ Value* builtin_nearest_in_range_all(Value *arg) { enum { PFDIST = 8 }; for (int i = 0; i < n; i++) { if (i + PFDIST < n) - __builtin_prefetch(entities->data.list.items[i + PFDIST], 0, 1); - Value *e = entities->data.list.items[i]; + __builtin_prefetch(list_get_borrow(entities, i + PFDIST), 0, 1); + Value *e = list_get_borrow(entities, i); if (!e || e->type != VAL_DICT) { valid_arr[i] = 0; active_arr[i] = 0; continue; } char **keys = e->data.dict.keys; - Value **vals = e->data.dict.vals; + Value **vals = dict_values_storage(e); int kcount = e->data.dict.count; Value *av; @@ -5621,7 +5621,7 @@ Value* builtin_sign_extend(Value *arg) { * sign-extended whatever that produced. There is no previous behaviour * to preserve, because the previous behaviour was undefined; 0 is the * same stand-in every sibling here uses. Filed as its own issue. */ - Value *v0 = arg->data.list.items[0], *v1 = arg->data.list.items[1]; + Value *v0 = list_get_borrow(arg, 0), *v1 = list_get_borrow(arg, 1); ARG_GUARD(!v0 || v0->type != VAL_NUM || !v1 || v1->type != VAL_NUM, "sign_extend", "two numbers", make_num(0)); double val = eigs_num_arg(v0, __func__); int bits = (int)eigs_num_arg(v1, __func__); @@ -5640,8 +5640,8 @@ Value* builtin_list_truncate(Value *arg) { rt_error(EK_TYPE, 0, "list_truncate requires [list, new_len]"); return make_null(); } - Value *list = arg->data.list.items[0]; - Value *len_val = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *len_val = list_get_borrow(arg, 1); if (!list || list->type != VAL_LIST) { rt_error(EK_TYPE, 0, "list_truncate: first argument must be a list"); return make_null(); @@ -5659,8 +5659,8 @@ Value* builtin_list_truncate(Value *arg) { } if (new_len >= list->data.list.count) return list; for (int i = new_len; i < list->data.list.count; i++) { - val_decref(list->data.list.items[i]); - list->data.list.items[i] = NULL; + val_decref(list_get_borrow(list, i)); + list_set_owned(list, i, NULL); } list->data.list.count = new_len; return list; @@ -5671,18 +5671,18 @@ Value* builtin_list_truncate(Value *arg) { Value* builtin_list_remove_at(Value *arg) { STRICT_LIST_MAX(arg, 2, "list_remove_at"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *list = arg->data.list.items[0]; - Value *idx_val = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *idx_val = list_get_borrow(arg, 1); if (!list || list->type != VAL_LIST) return make_null(); /* #1637: a non-number index raises under EIGS_STRICT (a bool in every * mode) instead of silently doing nothing. */ ARG_GUARD(!idx_val || idx_val->type != VAL_NUM, "list_remove_at", "a number index", list); int idx = (int)eigs_num_arg(idx_val, __func__); if (idx < 0 || idx >= list->data.list.count) return list; - val_decref(list->data.list.items[idx]); + val_decref(list_get_borrow(list, idx)); int tail = list->data.list.count - idx - 1; if (tail > 0) - memmove(&list->data.list.items[idx], &list->data.list.items[idx + 1], + memmove(&list_values_storage(list)[idx], &list_values_storage(list)[idx + 1], tail * sizeof(Value *)); list->data.list.count--; return list; @@ -5694,9 +5694,9 @@ Value* builtin_list_remove_at(Value *arg) { Value* builtin_list_insert_at(Value *arg) { STRICT_LIST_MAX(arg, 3, "list_insert_at"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) return make_null(); - Value *list = arg->data.list.items[0]; - Value *idx_val = arg->data.list.items[1]; - Value *val = arg->data.list.items[2]; + Value *list = list_get_borrow(arg, 0); + Value *idx_val = list_get_borrow(arg, 1); + Value *val = list_get_borrow(arg, 2); if (!list || list->type != VAL_LIST) return make_null(); /* #1637: a non-number index raises under EIGS_STRICT (a bool in every * mode) instead of silently doing nothing. */ @@ -5715,12 +5715,12 @@ Value* builtin_list_insert_at(Value *arg) { * spurious incref afterwards so old_last keeps exactly the one reference * its single remaining slot owns. It still occupies slot [count] at that * point, so the decref cannot free it. */ - Value *old_last = list->data.list.items[count - 1]; + Value *old_last = list_get_borrow(list, count - 1); list_append(list, old_last); - memmove(&list->data.list.items[idx + 1], &list->data.list.items[idx], + memmove(&list_values_storage(list)[idx + 1], &list_values_storage(list)[idx], (count - idx) * sizeof(Value *)); val_incref(val); - list->data.list.items[idx] = val; + list_set_owned(list, idx, val); val_decref(old_last); return list; } @@ -5735,13 +5735,13 @@ Value* builtin_list_index_of(Value *arg) { STRICT_LIST_MAX(arg, 2, "list_index_of"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "list_index_of", "[list, value]", make_num(-1)); - Value *list = arg->data.list.items[0]; - Value *needle = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *needle = list_get_borrow(arg, 1); ARG_GUARD(!list || list->type != VAL_LIST, "list_index_of", "a list as its first argument", make_num(-1)); for (int i = 0; i < list->data.list.count; i++) { /* #1637: the `==` comparison, raising on bool vs num like `==`. */ - int equal = values_equal_op(list->data.list.items[i], needle, "list_index_of"); + int equal = values_equal_op(list_get_borrow(list, i), needle, "list_index_of"); /* #1417: structural buffer comparison can raise on a strict NaN. */ if (g_has_error) return make_null(); if (equal) return make_num((double)i); @@ -5757,12 +5757,12 @@ Value* builtin_list_index_of(Value *arg) { Value* builtin_list_contains(Value *arg) { STRICT_LIST_MAX(arg, 2, "list_contains"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "list_contains", "[list, value]", make_bool(0)); - Value *list = arg->data.list.items[0]; - Value *needle = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *needle = list_get_borrow(arg, 1); ARG_GUARD(!list || list->type != VAL_LIST, "list_contains", "a list as its first argument", make_bool(0)); for (int i = 0; i < list->data.list.count; i++) { /* #1637: the `==` comparison, raising on bool vs num like `==`. */ - int equal = values_equal_op(list->data.list.items[i], needle, "list_contains"); + int equal = values_equal_op(list_get_borrow(list, i), needle, "list_contains"); /* #1417: structural buffer comparison can raise on a strict NaN. */ if (g_has_error) return make_null(); if (equal) return make_bool(1); @@ -5788,8 +5788,8 @@ static int sort_by_pair_cmp(const void *a, const void *b) { Value* builtin_sort_by(Value *arg) { STRICT_LIST_MAX(arg, 2, "sort_by"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *list = arg->data.list.items[0]; - Value *key_fn = arg->data.list.items[1]; + Value *list = list_get_borrow(arg, 0); + Value *key_fn = list_get_borrow(arg, 1); if (!list || list->type != VAL_LIST) return make_null(); if (!key_fn || (key_fn->type != VAL_FN && key_fn->type != VAL_BUILTIN)) return make_null(); @@ -5798,7 +5798,7 @@ Value* builtin_sort_by(Value *arg) { SortByPair *pairs = calloc(n, sizeof(SortByPair)); if (!pairs) return make_null(); for (int i = 0; i < n; i++) { - Value *kv = call_eigs_fn(key_fn, list->data.list.items[i]); + Value *kv = call_eigs_fn(key_fn, list_get_borrow(list, i)); /* #989: the key function itself raised (over-arity, or anything its * body threw). Its error is the real one — reporting "must return a * number" on top of it would bury the cause under a symptom. */ @@ -5825,7 +5825,7 @@ Value* builtin_sort_by(Value *arg) { qsort(pairs, n, sizeof(SortByPair), sort_by_pair_cmp); Value *result = make_list(n); for (int i = 0; i < n; i++) { - list_append(result, list->data.list.items[pairs[i].index]); + list_append(result, list_get_borrow(list, pairs[i].index)); } free(pairs); return result; @@ -5850,10 +5850,10 @@ Value* builtin_sort(Value *arg) { STRICT_REQUIRE(arg && arg->type != VAL_LIST, "sort", "a list"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return arg ? arg : make_null(); - ValType t = arg->data.list.items[0] ? arg->data.list.items[0]->type + ValType t = list_get_borrow(arg, 0) ? list_get_borrow(arg, 0)->type : VAL_NULL; for (int i = 0; i < arg->data.list.count; i++) { - Value *v = arg->data.list.items[i]; + Value *v = list_get_borrow(arg, i); if (!v || v->type != t || (t != VAL_NUM && t != VAL_STR)) { rt_error(EK_TYPE, 0, "sort requires all numbers or all strings " "(element %d is %s); use sort_by for records", @@ -5861,7 +5861,7 @@ Value* builtin_sort(Value *arg) { return make_null(); } } - qsort(arg->data.list.items, arg->data.list.count, sizeof(Value*), + qsort(list_values_storage(arg), arg->data.list.count, sizeof(Value*), t == VAL_NUM ? sort_cmp_num : sort_cmp_str); return arg; } @@ -5877,9 +5877,9 @@ Value* builtin_dispatch(Value *arg) { rt_error(EK_TYPE, 0, "dispatch requires [table, key, arg]"); return make_null(); } - Value *table = arg->data.list.items[0]; - Value *key_v = arg->data.list.items[1]; - Value *fn_arg = arg->data.list.items[2]; + Value *table = list_get_borrow(arg, 0); + Value *key_v = list_get_borrow(arg, 1); + Value *fn_arg = list_get_borrow(arg, 2); /* Key validation mirrors OP_DISPATCH (#353): a non-number key used to * reinterpret whatever union member the value carried as a double. */ @@ -5901,7 +5901,7 @@ Value* builtin_dispatch(Value *arg) { if (key < 0 || key >= table->data.list.count) { return make_null(); } - Value *fn = table->data.list.items[key]; + Value *fn = list_get_borrow(table, key); if (!fn || fn->type == VAL_NULL) { return make_null(); } @@ -5981,8 +5981,8 @@ Value* builtin_dispatch(Value *arg) { static Value* builtin_dot(Value *arg) { STRICT_LIST_MAX(arg, 2, "dot"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "dot", "[a, b]", make_num(0)); - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); ARG_GUARD(!a || !b || a->type != VAL_BUFFER || b->type != VAL_BUFFER, "dot", "two buffers", make_num(0)); int n = a->data.buffer.count; if (b->data.buffer.count < n) n = b->data.buffer.count; @@ -6131,7 +6131,7 @@ int eigs_bool_gate_slow(BuiltinFn fn, const Value *arg) { int n = arg->data.list.count < EIGS_BOOL_GATE_SCAN ? arg->data.list.count : EIGS_BOOL_GATE_SCAN; for (int i = 0; i < n; i++) { - const Value *it = arg->data.list.items[i]; + const Value *it = list_get_borrow(arg, i); if (!it || it->type != VAL_BOOL || (mask & BA_POS(i))) continue; const char *nm = bool_gate_name(fn); rt_error(EK_TYPE, 0, "%s: argument %d is a bool, which %s does not take there", diff --git a/src/builtins_buf.c b/src/builtins_buf.c index ddc897e0..b6dd34ac 100644 --- a/src/builtins_buf.c +++ b/src/builtins_buf.c @@ -32,8 +32,8 @@ Value* builtin_buffer(Value *arg) { /* buffer of [rows, cols] -> shaped 2-D buffer (flat double[rows*cols]) */ if (arg && arg->type == VAL_LIST && arg->data.list.count == 2 && - arg->data.list.items[0]->type == VAL_NUM && - arg->data.list.items[1]->type == VAL_NUM) { + list_get_borrow(arg, 0)->type == VAL_NUM && + list_get_borrow(arg, 1)->type == VAL_NUM) { int r = (int)eigs_list_num(arg, 0, __func__); int c = (int)eigs_list_num(arg, 1, __func__); if (r < 0) r = 0; @@ -71,11 +71,11 @@ Value* builtin_buffer(Value *arg) { Value* builtin_reshape(Value *arg) { STRICT_LIST_MAX(arg, 3, "reshape"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) return make_null(); - Value *b = arg->data.list.items[0]; + Value *b = list_get_borrow(arg, 0); if (b->type != VAL_BUFFER) return make_null(); BOOL_REFUSE(arg, "reshape"); /* rows/cols; the buffer is not a bool */ - if (arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM) return make_null(); + if (list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM) return make_null(); int r = (int)eigs_list_num(arg, 1, __func__); int c = (int)eigs_list_num(arg, 2, __func__); if (r < 0 || c < 0 || (long)r * (long)c != (long)b->data.buffer.count) return make_null(); @@ -103,7 +103,7 @@ Value* builtin_buf_get(Value *arg) { /* fs:CHANNEL the rt_error above already raised */ return make_num(0); } - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); if (!buf || buf->type != VAL_BUFFER) { rt_error(EK_TYPE, 0, "buf_get: first argument must be a buffer"); /* fs:CHANNEL the rt_error above already raised */ @@ -127,7 +127,7 @@ Value* builtin_buf_set(Value *arg) { rt_error(EK_TYPE, 0, "buf_set requires [buffer, index, value]"); return make_null(); } - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); if (!buf || buf->type != VAL_BUFFER) { rt_error(EK_TYPE, 0, "buf_set: first argument must be a buffer"); return make_null(); @@ -135,12 +135,12 @@ Value* builtin_buf_set(Value *arg) { /* #1061: both operands were read through the num union member unchecked * -- a string index or value read garbage bits (the #1007 type-pun class). * Loud, like the `b[i] is v` opcode path. */ - if (arg->data.list.items[1]->type != VAL_NUM) { - rt_error(EK_TYPE, 0, "buf_set: index must be a number, got %s", val_type_name(arg->data.list.items[1]->type)); + if (list_get_borrow(arg, 1)->type != VAL_NUM) { + rt_error(EK_TYPE, 0, "buf_set: index must be a number, got %s", val_type_name(list_get_borrow(arg, 1)->type)); return make_null(); } - if (arg->data.list.items[2]->type != VAL_NUM) { - rt_error(EK_TYPE, 0, "cannot store %s in a buffer (buffers hold numbers)", val_type_name(arg->data.list.items[2]->type)); + if (list_get_borrow(arg, 2)->type != VAL_NUM) { + rt_error(EK_TYPE, 0, "cannot store %s in a buffer (buffers hold numbers)", val_type_name(list_get_borrow(arg, 2)->type)); return make_null(); } int idx = (int)eigs_list_num(arg, 1, __func__); @@ -178,11 +178,11 @@ Value* builtin_buf_from_list(Value *arg) { v->data.buffer.data = xcalloc(n > 0 ? n : 1, sizeof(double)); v->refcount = 1; for (int i = 0; i < n; i++) { - if (arg->data.list.items[i]->type == VAL_NUM) { - v->data.buffer.data[i] = eigs_num_arg(arg->data.list.items[i], __func__); + if (list_get_borrow(arg, i)->type == VAL_NUM) { + v->data.buffer.data[i] = eigs_num_arg(list_get_borrow(arg, i), __func__); } else { /* #1061: a non-number element silently stayed 0.0. */ - const char *tn = val_type_name(arg->data.list.items[i]->type); + const char *tn = val_type_name(list_get_borrow(arg, i)->type); val_decref(v); rt_error(EK_TYPE, 0, "buf_from_list: element %d is %s (buffers hold numbers)", i, tn); return make_null(); @@ -197,7 +197,7 @@ Value* builtin_buf_from_list(Value *arg) { static int strict_numeric_byte_list(Value *arg, const char *who, int nul_ends) { if (!arg || arg->type != VAL_LIST) return 1; for (int i = 0; i < arg->data.list.count; i++) { - Value *item = arg->data.list.items[i]; + Value *item = list_get_borrow(arg, i); /* #1637: a bool byte is refused in every strict mode. */ if (!item || (item->type != VAL_NUM && (g_strict || item->type == VAL_BOOL))) { rt_error(EK_TYPE, 0, "%s: expected numeric byte values", who); @@ -223,7 +223,7 @@ Value* builtin_str_from_bytes(Value *arg) { Value **items = NULL; if (arg && arg->type == VAL_LIST) { n = arg->data.list.count; - items = arg->data.list.items; + items = list_values_storage(arg); } else if (arg && arg->type == VAL_BUFFER) { n = arg->data.buffer.count; } else { @@ -273,8 +273,8 @@ Value* builtin_f64_from_bytes(Value *arg) { if (arg && arg->type == VAL_LIST) { int n = arg->data.list.count; for (int i = 0; i < 8 && i < n; i++) - if (arg->data.list.items[i] && arg->data.list.items[i]->type == VAL_NUM) - bytes_in[i] = eigs_num_arg(arg->data.list.items[i], __func__); + if (list_get_borrow(arg, i) && list_get_borrow(arg, i)->type == VAL_NUM) + bytes_in[i] = eigs_num_arg(list_get_borrow(arg, i), __func__); } else if (arg && arg->type == VAL_BUFFER) { int n = arg->data.buffer.count; for (int i = 0; i < 8 && i < n; i++) { @@ -333,7 +333,7 @@ static int zlib_bytes_arg(Value *arg, const char *who, Value **items = NULL; if (arg && arg->type == VAL_LIST) { n = arg->data.list.count; - items = arg->data.list.items; + items = list_values_storage(arg); } else if (arg && arg->type == VAL_BUFFER) { n = arg->data.buffer.count; } else { @@ -547,12 +547,12 @@ Value* builtin_buf_copy(Value *arg) { rt_error(EK_TYPE, 0, "buf_copy requires [src, src_off, dst, dst_off, count]"); return make_null(); } - Value *src = arg->data.list.items[0]; - Value *dst = arg->data.list.items[2]; + Value *src = list_get_borrow(arg, 0); + Value *dst = list_get_borrow(arg, 2); long long count, src_off, dst_off; - if (!buf_count_arg("buf_copy", arg->data.list.items[4], &count) || - !buf_window_arg("buf_copy", src, arg->data.list.items[1], count, &src_off) || - !buf_window_arg("buf_copy", dst, arg->data.list.items[3], count, &dst_off)) + if (!buf_count_arg("buf_copy", list_get_borrow(arg, 4), &count) || + !buf_window_arg("buf_copy", src, list_get_borrow(arg, 1), count, &src_off) || + !buf_window_arg("buf_copy", dst, list_get_borrow(arg, 3), count, &dst_off)) return make_null(); if (count == 0) return make_null(); memmove(&dst->data.buffer.data[dst_off], &src->data.buffer.data[src_off], @@ -574,14 +574,14 @@ Value* builtin_buf_mix(Value *arg) { rt_error(EK_TYPE, 0, "buf_mix requires [dst, src, dst_off, src_off, count, gain]"); return make_null(); } - Value *dst = arg->data.list.items[0]; - Value *src = arg->data.list.items[1]; + Value *dst = list_get_borrow(arg, 0); + Value *src = list_get_borrow(arg, 1); long long count, dst_off, src_off; double gain; - if (!buf_count_arg("buf_mix", arg->data.list.items[4], &count) || - !buf_window_arg("buf_mix", dst, arg->data.list.items[2], count, &dst_off) || - !buf_window_arg("buf_mix", src, arg->data.list.items[3], count, &src_off) || - !buf_num_arg("buf_mix", "gain", arg->data.list.items[5], &gain)) + if (!buf_count_arg("buf_mix", list_get_borrow(arg, 4), &count) || + !buf_window_arg("buf_mix", dst, list_get_borrow(arg, 2), count, &dst_off) || + !buf_window_arg("buf_mix", src, list_get_borrow(arg, 3), count, &src_off) || + !buf_num_arg("buf_mix", "gain", list_get_borrow(arg, 5), &gain)) return make_null(); double *dd = &dst->data.buffer.data[dst_off]; double *sd = &src->data.buffer.data[src_off]; @@ -599,12 +599,12 @@ Value* builtin_buf_scale_range(Value *arg) { rt_error(EK_TYPE, 0, "buf_scale_range requires [buffer, off, count, gain]"); return make_null(); } - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); long long count, off; double gain; - if (!buf_count_arg("buf_scale_range", arg->data.list.items[2], &count) || - !buf_window_arg("buf_scale_range", buf, arg->data.list.items[1], count, &off) || - !buf_num_arg("buf_scale_range", "gain", arg->data.list.items[3], &gain)) + if (!buf_count_arg("buf_scale_range", list_get_borrow(arg, 2), &count) || + !buf_window_arg("buf_scale_range", buf, list_get_borrow(arg, 1), count, &off) || + !buf_num_arg("buf_scale_range", "gain", list_get_borrow(arg, 3), &gain)) return make_null(); double *d = &buf->data.buffer.data[off]; for (long long i = 0; i < count; i++) @@ -621,12 +621,12 @@ Value* builtin_buf_fill(Value *arg) { rt_error(EK_TYPE, 0, "buf_fill requires [buffer, off, count, value]"); return make_null(); } - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); long long count, off; double val; - if (!buf_count_arg("buf_fill", arg->data.list.items[2], &count) || - !buf_window_arg("buf_fill", buf, arg->data.list.items[1], count, &off) || - !buf_num_arg("buf_fill", "value", arg->data.list.items[3], &val)) + if (!buf_count_arg("buf_fill", list_get_borrow(arg, 2), &count) || + !buf_window_arg("buf_fill", buf, list_get_borrow(arg, 1), count, &off) || + !buf_num_arg("buf_fill", "value", list_get_borrow(arg, 3), &val)) return make_null(); double *d = &buf->data.buffer.data[off]; for (long long i = 0; i < count; i++) @@ -643,10 +643,10 @@ Value* builtin_buf_peak(Value *arg) { /* fs:CHANNEL the rt_error above already raised */ return make_num(0); } - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); long long count, off; - if (!buf_count_arg("buf_peak", arg->data.list.items[2], &count) || - !buf_window_arg("buf_peak", buf, arg->data.list.items[1], count, &off)) + if (!buf_count_arg("buf_peak", list_get_borrow(arg, 2), &count) || + !buf_window_arg("buf_peak", buf, list_get_borrow(arg, 1), count, &off)) /* fs:CHANNEL buf_count_arg/buf_window_arg raise before returning 0 */ return make_num(0); double m = 0.0; @@ -672,12 +672,12 @@ Value* builtin_buf_dot(Value *arg) { /* fs:CHANNEL the rt_error above already raised */ return make_num(0); } - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); long long count, a_off, b_off; - if (!buf_count_arg("buf_dot", arg->data.list.items[4], &count) || - !buf_window_arg("buf_dot", a, arg->data.list.items[2], count, &a_off) || - !buf_window_arg("buf_dot", b, arg->data.list.items[3], count, &b_off)) + if (!buf_count_arg("buf_dot", list_get_borrow(arg, 4), &count) || + !buf_window_arg("buf_dot", a, list_get_borrow(arg, 2), count, &a_off) || + !buf_window_arg("buf_dot", b, list_get_borrow(arg, 3), count, &b_off)) /* fs:CHANNEL buf_count_arg/buf_window_arg raise before returning 0 */ return make_num(0); double s = 0.0; @@ -724,15 +724,15 @@ Value* builtin_buf_from_pcm16le(Value *arg) { rt_error(EK_TYPE, 0, "buf_from_pcm16le requires [bytes, byte_off, count]"); return make_null(); } - Value *src = arg->data.list.items[0]; + Value *src = list_get_borrow(arg, 0); long long count, off; - if (!buf_count_arg("buf_from_pcm16le", arg->data.list.items[2], &count)) + if (!buf_count_arg("buf_from_pcm16le", list_get_borrow(arg, 2), &count)) return make_null(); if (count > (long long)INT_MAX / 2) { /* 2*count below cannot overflow */ rt_error(EK_LIMIT, 0, "buf_from_pcm16le: count %lld over the buffer size limit", count); return make_null(); } - if (!buf_window_arg("buf_from_pcm16le", src, arg->data.list.items[1], + if (!buf_window_arg("buf_from_pcm16le", src, list_get_borrow(arg, 1), count * 2, &off)) return make_null(); Value *out = buf_alloc_flat(count); @@ -763,15 +763,15 @@ Value* builtin_buf_to_pcm16le(Value *arg) { rt_error(EK_TYPE, 0, "buf_to_pcm16le requires [floats, off, count]"); return make_null(); } - Value *src = arg->data.list.items[0]; + Value *src = list_get_borrow(arg, 0); long long count, off; - if (!buf_count_arg("buf_to_pcm16le", arg->data.list.items[2], &count)) + if (!buf_count_arg("buf_to_pcm16le", list_get_borrow(arg, 2), &count)) return make_null(); if (count > (long long)INT_MAX / 2) { /* output is 2*count elements */ rt_error(EK_LIMIT, 0, "buf_to_pcm16le: count %lld over the buffer size limit", count); return make_null(); } - if (!buf_window_arg("buf_to_pcm16le", src, arg->data.list.items[1], + if (!buf_window_arg("buf_to_pcm16le", src, list_get_borrow(arg, 1), count, &off)) return make_null(); Value *out = buf_alloc_flat(count * 2); @@ -801,13 +801,13 @@ Value* builtin_buf_deinterleave(Value *arg) { rt_error(EK_TYPE, 0, "buf_deinterleave requires [src, channel, nch, count?]"); return make_null(); } - Value *src = arg->data.list.items[0]; + Value *src = list_get_borrow(arg, 0); if (!src || src->type != VAL_BUFFER) { rt_error(EK_TYPE, 0, "buf_deinterleave: expected a buffer"); return make_null(); } - Value *ch_v = arg->data.list.items[1]; - Value *nch_v = arg->data.list.items[2]; + Value *ch_v = list_get_borrow(arg, 1); + Value *nch_v = list_get_borrow(arg, 2); if (!ch_v || ch_v->type != VAL_NUM || !nch_v || nch_v->type != VAL_NUM) { rt_error(EK_VALUE, 0, "buf_deinterleave: channel and nch must be numbers"); return make_null(); @@ -826,9 +826,9 @@ Value* builtin_buf_deinterleave(Value *arg) { long long n = src->data.buffer.count; long long avail = channel < n ? (n - channel + nch - 1) / nch : 0; long long count = avail; - if (arg->data.list.count >= 4 && arg->data.list.items[3] && - arg->data.list.items[3]->type != VAL_NULL) { - if (!buf_count_arg("buf_deinterleave", arg->data.list.items[3], &count)) + if (arg->data.list.count >= 4 && list_get_borrow(arg, 3) && + list_get_borrow(arg, 3)->type != VAL_NULL) { + if (!buf_count_arg("buf_deinterleave", list_get_borrow(arg, 3), &count)) return make_null(); if (count > avail) { rt_error(EK_INDEX, 0, @@ -866,13 +866,13 @@ Value* builtin_buf_resample_linear(Value *arg) { rt_error(EK_TYPE, 0, "buf_resample_linear requires [src, dst_len]"); return make_null(); } - Value *src = arg->data.list.items[0]; + Value *src = list_get_borrow(arg, 0); if (!src || src->type != VAL_BUFFER) { rt_error(EK_TYPE, 0, "buf_resample_linear: expected a buffer"); return make_null(); } long long dst_len; - if (!buf_count_arg("buf_resample_linear", arg->data.list.items[1], &dst_len)) + if (!buf_count_arg("buf_resample_linear", list_get_borrow(arg, 1), &dst_len)) return make_null(); if (dst_len > (long long)INT_MAX) { rt_error(EK_LIMIT, 0, "buf_resample_linear: dst_len %lld over the buffer size limit", dst_len); diff --git a/src/builtins_host.c b/src/builtins_host.c index f543c413..13b9ada8 100644 --- a/src/builtins_host.c +++ b/src/builtins_host.c @@ -42,12 +42,12 @@ Value* builtin_match(Value *arg) { rt_error(EK_TYPE, 0, "regex_match requires [string, pattern]"); return make_list(0); } - if (arg->data.list.items[0]->type != VAL_STR || arg->data.list.items[1]->type != VAL_STR) { + if (list_get_borrow(arg, 0)->type != VAL_STR || list_get_borrow(arg, 1)->type != VAL_STR) { rt_error(EK_TYPE, 0, "regex_match: string and pattern must be strings"); return make_list(0); } - const char *str = arg->data.list.items[0]->data.str; - const char *pattern = arg->data.list.items[1]->data.str; + const char *str = list_get_borrow(arg, 0)->data.str; + const char *pattern = list_get_borrow(arg, 1)->data.str; regex_t re; /* #500: an invalid pattern used to return [] — indistinguishable from a @@ -100,12 +100,12 @@ Value* builtin_match_all(Value *arg) { rt_error(EK_TYPE, 0, "regex_find requires [string, pattern]"); return make_list(0); } - if (arg->data.list.items[0]->type != VAL_STR || arg->data.list.items[1]->type != VAL_STR) { + if (list_get_borrow(arg, 0)->type != VAL_STR || list_get_borrow(arg, 1)->type != VAL_STR) { rt_error(EK_TYPE, 0, "regex_find: string and pattern must be strings"); return make_list(0); } - const char *str = arg->data.list.items[0]->data.str; - const char *pattern = arg->data.list.items[1]->data.str; + const char *str = list_get_borrow(arg, 0)->data.str; + const char *pattern = list_get_borrow(arg, 1)->data.str; regex_t re; if (regcomp(&re, pattern, REG_EXTENDED) != 0) { /* #500 */ @@ -143,18 +143,18 @@ Value* builtin_regex_replace(Value *arg) { * discards it at the CHECK_ERROR seam. */ return make_str(""); } - if (arg->data.list.items[0]->type != VAL_STR || - arg->data.list.items[1]->type != VAL_STR || - arg->data.list.items[2]->type != VAL_STR) { + if (list_get_borrow(arg, 0)->type != VAL_STR || + list_get_borrow(arg, 1)->type != VAL_STR || + list_get_borrow(arg, 2)->type != VAL_STR) { rt_error(EK_TYPE, 0, "regex_replace: string, pattern and replacement must be strings"); /* fs:CHANNEL same as above — the unconditional rt_error is the error * channel; converting to ARG_GUARD would make this site quieter with * strict OFF, not louder. */ return make_str(""); } - const char *str = arg->data.list.items[0]->data.str; - const char *pattern = arg->data.list.items[1]->data.str; - const char *replacement = arg->data.list.items[2]->data.str; + const char *str = list_get_borrow(arg, 0)->data.str; + const char *pattern = list_get_borrow(arg, 1)->data.str; + const char *replacement = list_get_borrow(arg, 2)->data.str; regex_t re; /* #500: an invalid pattern used to return the input unchanged — a silent @@ -211,8 +211,8 @@ Value* builtin_stream_open(Value *arg) { STRICT_LIST_MAX(arg, 2, "stream_open"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "stream_open", "[path, count]", make_bool(0)); - Value *path_val = arg->data.list.items[0]; - Value *count_val = arg->data.list.items[1]; + Value *path_val = list_get_borrow(arg, 0); + Value *count_val = list_get_borrow(arg, 1); ARG_GUARD(!path_val || path_val->type != VAL_STR || !count_val || count_val->type != VAL_NUM, "stream_open", "[a string path, a number count]", make_bool(0)); if (eigs_num_arg(count_val, __func__) < 1 || @@ -331,7 +331,7 @@ Value* builtin_ls(Value *arg) { list_append_owned(list, make_str(entry->d_name)); } closedir(d); - qsort(list->data.list.items, list->data.list.count, sizeof(Value*), + qsort(list_values_storage(list), list->data.list.count, sizeof(Value*), ls_entry_cmp); TRACE_NONDET_RECORD("ls", list); } @@ -397,10 +397,10 @@ Value* builtin_build_corpus(Value *arg) { if (!arg || arg->type != VAL_LIST || arg->data.list.count < 4) return make_null(); - Value *file_list = arg->data.list.items[0]; - Value *topn_val = arg->data.list.items[1]; - Value *stream_path_val = arg->data.list.items[2]; - Value *vocab_path_val = arg->data.list.items[3]; + Value *file_list = list_get_borrow(arg, 0); + Value *topn_val = list_get_borrow(arg, 1); + Value *stream_path_val = list_get_borrow(arg, 2); + Value *vocab_path_val = list_get_borrow(arg, 3); if (!file_list || file_list->type != VAL_LIST) return make_null(); BOOL_REFUSE(topn_val, "build_corpus"); @@ -437,7 +437,7 @@ Value* builtin_build_corpus(Value *arg) { * been re-measured on the full corpus.) */ int slot_count = 0; if (arg->data.list.count >= 6) { - Value *sv = arg->data.list.items[5]; + Value *sv = list_get_borrow(arg, 5); double d = 0; if (eigs_opt_num(sv, &d, "build_corpus")) slot_count = (int)d; /* #1637: a bool raises */ if (g_has_error) return make_null(); @@ -465,7 +465,7 @@ Value* builtin_build_corpus(Value *arg) { * high-frequency collision is. Opt-in: 0 leaves the stream byte-identical. */ int int_count = 0; if (arg->data.list.count >= 7) { - Value *iv = arg->data.list.items[6]; + Value *iv = list_get_borrow(arg, 6); double d = 0; if (eigs_opt_num(iv, &d, "build_corpus")) int_count = (int)d; /* #1637: a bool raises */ if (g_has_error) return make_null(); @@ -487,7 +487,7 @@ Value* builtin_build_corpus(Value *arg) { int files_found = 0; for (int fi = 0; fi < n_files; fi++) { - Value *path_val = file_list->data.list.items[fi]; + Value *path_val = list_get_borrow(file_list, fi); if (!path_val || path_val->type != VAL_STR) { file_tok_counts[fi] = 0; continue; } const char *path = path_val->data.str; @@ -643,7 +643,7 @@ Value* builtin_build_corpus(Value *arg) { for (int fi = 0; fi < n_files; fi++) { if (file_tok_counts[fi] <= 0) continue; - Value *path_val = file_list->data.list.items[fi]; + Value *path_val = list_get_borrow(file_list, fi); long fsize = 0; char *source = read_file_util(path_val->data.str, &fsize); if (!source) continue; @@ -752,7 +752,7 @@ Value* builtin_build_corpus(Value *arg) { * Sorted by count descending. Enables exact coverage(top_n) curves * without re-running the full corpus build. */ if (arg->data.list.count >= 5) { - Value *idents_path_val = arg->data.list.items[4]; + Value *idents_path_val = list_get_borrow(arg, 4); if (idents_path_val && idents_path_val->type == VAL_STR && n_idents > 0) { /* Build index array, sort descending by count via simple sort */ int *order = xmalloc_array(n_idents, sizeof(int)); @@ -1003,8 +1003,8 @@ Value* builtin_rename(Value *arg) { STRICT_LIST_MAX(arg, 2, "rename"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "rename", "[old_path, new_path]", make_bool(0)); - Value *from = arg->data.list.items[0]; - Value *to = arg->data.list.items[1]; + Value *from = list_get_borrow(arg, 0); + Value *to = list_get_borrow(arg, 1); ARG_GUARD(!from || from->type != VAL_STR || !to || to->type != VAL_STR, "rename", "two string paths", make_bool(0)); return make_bool(rename(from->data.str, to->data.str) == 0); @@ -1085,9 +1085,9 @@ Value* builtin_read_bytes_buf(Value *arg) { "read_bytes_buf of path, or read_bytes_buf of [path, max_bytes]"); return make_null(); } - path = arg->data.list.items[0]; + path = list_get_borrow(arg, 0); if (arg->data.list.count >= 2) { - Value *mv = arg->data.list.items[1]; + Value *mv = list_get_borrow(arg, 1); if (!mv || mv->type != VAL_NUM) { rt_error(EK_VALUE, 0, "read_bytes_buf: max_bytes must be a number"); return make_null(); @@ -1212,8 +1212,8 @@ Value* builtin_write_text(Value *arg) { STRICT_LIST_MAX(arg, 2, "write_text"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "write_text", "[path, text]", make_bool(0)); - Value *path_val = arg->data.list.items[0]; - Value *text_val = arg->data.list.items[1]; + Value *path_val = list_get_borrow(arg, 0); + Value *text_val = list_get_borrow(arg, 1); ARG_GUARD(!path_val || path_val->type != VAL_STR || !text_val || text_val->type != VAL_STR, "write_text", "two strings", make_bool(0)); @@ -1243,8 +1243,8 @@ Value* builtin_write_text(Value *arg) { static Value* exec_capture_result(int code, const char *text) { Value *result = make_list(2); - result->data.list.items[0] = make_num(code); - result->data.list.items[1] = make_str(text); + list_set_owned(result, 0, make_num(code)); + list_set_owned(result, 1, make_str(text)); result->data.list.count = 2; return result; } @@ -1270,9 +1270,9 @@ Value* builtin_exec_capture(Value *arg) { double timeout_sec = -1; Value *cmd_list = arg; if (arg->data.list.count == 2 - && arg->data.list.items[0] && arg->data.list.items[0]->type == VAL_LIST - && arg->data.list.items[1] && arg->data.list.items[1]->type == VAL_NUM) { - cmd_list = arg->data.list.items[0]; + && list_get_borrow(arg, 0) && list_get_borrow(arg, 0)->type == VAL_LIST + && list_get_borrow(arg, 1) && list_get_borrow(arg, 1)->type == VAL_NUM) { + cmd_list = list_get_borrow(arg, 0); timeout_sec = eigs_list_num(arg, 1, __func__); ARG_GUARD(cmd_list->data.list.count < 1, "exec_capture", "a non-empty command list", exec_capture_result(-1, "")); @@ -1286,7 +1286,7 @@ Value* builtin_exec_capture(Value *arg) { * outcome, not an argument the caller got wrong. */ if (!argv) return exec_capture_result(-1, ""); for (int i = 0; i < total; i++) { - Value *v = cmd_list->data.list.items[i]; + Value *v = list_get_borrow(cmd_list, i); if (!v || v->type != VAL_STR) { free(argv); ARG_GUARD(1, "exec_capture", "a list of strings", exec_capture_result(-1, "")); @@ -1417,8 +1417,8 @@ Value* builtin_proc_read_buf(Value *arg) { if (replay_blocks("proc_read_buf")) return make_null(); if (!arg || arg->type != VAL_LIST || arg->data.list.count != 2) return make_null(); - Value *fd_v = arg->data.list.items[0]; - Value *max_v = arg->data.list.items[1]; + Value *fd_v = list_get_borrow(arg, 0); + Value *max_v = list_get_borrow(arg, 1); if (!fd_v || fd_v->type != VAL_NUM || !max_v || max_v->type != VAL_NUM) return make_null(); int fd = (int)eigs_num_arg(fd_v, __func__); @@ -1481,15 +1481,15 @@ Value* builtin_write_bytes(Value *arg) { STRICT_LIST_MAX(arg, 3, "write_bytes"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "write_bytes", "[path, data] (optionally [path, data, append])", make_num(0)); - Value *path_val = arg->data.list.items[0]; - Value *data = arg->data.list.items[1]; + Value *path_val = list_get_borrow(arg, 0); + Value *data = list_get_borrow(arg, 1); ARG_GUARD(!path_val || path_val->type != VAL_STR, "write_bytes", "a string path as its first argument", make_num(0)); /* #1637: append is a flag -- a bool, or the older nonzero number. Any * other value raises rather than silently meaning "truncate". */ int append = 0; - if (arg->data.list.count >= 3 && arg->data.list.items[2]) { - Value *fl = arg->data.list.items[2]; + if (arg->data.list.count >= 3 && list_get_borrow(arg, 2)) { + Value *fl = list_get_borrow(arg, 2); if (fl->type == VAL_BOOL) append = fl->data.boolean; else if (fl->type != VAL_NULL) append = (eigs_num_arg(fl, "write_bytes") != 0.0); if (g_has_error) return make_num(0); @@ -1499,7 +1499,7 @@ Value* builtin_write_bytes(Value *arg) { Value **items = NULL; if (data && data->type == VAL_LIST) { n = data->data.list.count; - items = data->data.list.items; + items = list_values_storage(data); } else if (data && data->type == VAL_BUFFER) { n = data->data.buffer.count; } else { diff --git a/src/builtins_tensor.c b/src/builtins_tensor.c index f3071637..520b902b 100644 --- a/src/builtins_tensor.c +++ b/src/builtins_tensor.c @@ -226,7 +226,7 @@ static int tensor_dims(Value *v, int *rows, int *cols) { *rows = 1; *cols = v->data.buffer.count; return 1; } if (!v || v->type != VAL_LIST || v->data.list.count == 0) return 0; - Value *first = v->data.list.items[0]; + Value *first = list_get_borrow(v, 0); if (first->type == VAL_NUM) { /* 1D tensor */ *rows = 1; @@ -259,13 +259,13 @@ static double* tensor_to_flat(Value *v, int *rows, int *cols, int bad = 0; if (ndim == 1) { for (int i = 0; i < v->data.list.count; i++) - if (FLAT_BAD(v->data.list.items[i])) { bad = 1; break; } + if (FLAT_BAD(list_get_borrow(v, i))) { bad = 1; break; } } else { for (int r = 0; r < v->data.list.count && !bad; r++) { - Value *row = v->data.list.items[r]; + Value *row = list_get_borrow(v, r); if (row->type != VAL_LIST) { bad = g_strict || row->type == VAL_BOOL; if (bad) break; continue; } for (int c = 0; c < row->data.list.count; c++) - if (FLAT_BAD(row->data.list.items[c])) { bad = 1; break; } + if (FLAT_BAD(list_get_borrow(row, c))) { bad = 1; break; } } } #undef FLAT_BAD @@ -287,13 +287,13 @@ static double* tensor_to_flat(Value *v, int *rows, int *cols, } if (ndim == 1) { for (int i = 0; i < *cols; i++) - out[i] = (v->data.list.items[i]->type == VAL_NUM) ? eigs_num_arg(v->data.list.items[i], __func__) : 0.0; + out[i] = (list_get_borrow(v, i)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(v, i), __func__) : 0.0; } else { for (int r = 0; r < *rows; r++) { - Value *row = v->data.list.items[r]; + Value *row = list_get_borrow(v, r); int rc = (row->type == VAL_LIST) ? row->data.list.count : 0; for (int c = 0; c < *cols && c < rc; c++) - out[r * (*cols) + c] = (row->data.list.items[c]->type == VAL_NUM) ? eigs_num_arg(row->data.list.items[c], __func__) : 0.0; + out[r * (*cols) + c] = (list_get_borrow(row, c)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(row, c), __func__) : 0.0; } } return out; @@ -364,7 +364,7 @@ static int tensor_total(Value *v) { if (v->type != VAL_LIST) return 0; int total = 0; for (int i = 0; i < v->data.list.count; i++) - total += tensor_total(v->data.list.items[i]); + total += tensor_total(list_get_borrow(v, i)); return total; } @@ -383,7 +383,7 @@ static int tensor_flatten_recursive(Value *v, double *out, int *idx) { } if (v->type != VAL_LIST) return 1; for (int i = 0; i < v->data.list.count; i++) - if (!tensor_flatten_recursive(v->data.list.items[i], out, idx)) return 0; + if (!tensor_flatten_recursive(list_get_borrow(v, i), out, idx)) return 0; return 1; } @@ -512,7 +512,7 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { if (a->type == VAL_NUM && b->type == VAL_LIST) { Value *out = make_list(b->data.list.count); for (int i = 0; i < b->data.list.count; i++) { - Value *item = tensor_elementwise(a, b->data.list.items[i], fn); + Value *item = tensor_elementwise(a, list_get_borrow(b, i), fn); if (g_has_error) { val_decref(item); val_decref(out); return make_null(); } @@ -523,7 +523,7 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { if (a->type == VAL_LIST && b->type == VAL_NUM) { Value *out = make_list(a->data.list.count); for (int i = 0; i < a->data.list.count; i++) { - Value *item = tensor_elementwise(a->data.list.items[i], b, fn); + Value *item = tensor_elementwise(list_get_borrow(a, i), b, fn); if (g_has_error) { val_decref(item); val_decref(out); return make_null(); } @@ -538,16 +538,16 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { * Row-vector bias takes priority for square matrices, matching neural * layer convention: add of [batch @ weights, bias]. */ if (a->type == VAL_LIST && b->type == VAL_LIST) { - int a_is_matrix = a->data.list.count > 0 && a->data.list.items[0]->type == VAL_LIST; - int b_is_matrix = b->data.list.count > 0 && b->data.list.items[0]->type == VAL_LIST; + int a_is_matrix = a->data.list.count > 0 && list_get_borrow(a, 0)->type == VAL_LIST; + int b_is_matrix = b->data.list.count > 0 && list_get_borrow(b, 0)->type == VAL_LIST; if (a_is_matrix && !b_is_matrix) { int rows = a->data.list.count; - int cols = a->data.list.items[0]->data.list.count; + int cols = list_get_borrow(a, 0)->data.list.count; if (b->data.list.count == cols) { Value *out = make_list(rows); for (int i = 0; i < rows; i++) { - Value *row = tensor_elementwise(a->data.list.items[i], b, fn); + Value *row = tensor_elementwise(list_get_borrow(a, i), b, fn); if (g_has_error) { val_decref(row); val_decref(out); return make_null(); } @@ -559,7 +559,7 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { if (b->data.list.count == rows) { Value *out = make_list(rows); for (int i = 0; i < rows; i++) { - Value *row = tensor_elementwise(a->data.list.items[i], b->data.list.items[i], fn); + Value *row = tensor_elementwise(list_get_borrow(a, i), list_get_borrow(b, i), fn); if (g_has_error) { val_decref(row); val_decref(out); return make_null(); } @@ -572,11 +572,11 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { if (!a_is_matrix && b_is_matrix) { int rows = b->data.list.count; - int cols = b->data.list.items[0]->data.list.count; + int cols = list_get_borrow(b, 0)->data.list.count; if (a->data.list.count == cols) { Value *out = make_list(rows); for (int i = 0; i < rows; i++) { - Value *row = tensor_elementwise(a, b->data.list.items[i], fn); + Value *row = tensor_elementwise(a, list_get_borrow(b, i), fn); if (g_has_error) { val_decref(row); val_decref(out); return make_null(); } @@ -588,7 +588,7 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { if (a->data.list.count == rows) { Value *out = make_list(rows); for (int i = 0; i < rows; i++) { - Value *row = tensor_elementwise(a->data.list.items[i], b->data.list.items[i], fn); + Value *row = tensor_elementwise(list_get_borrow(a, i), list_get_borrow(b, i), fn); if (g_has_error) { val_decref(row); val_decref(out); return make_null(); } @@ -603,7 +603,7 @@ static Value* tensor_elementwise(Value *a, Value *b, BinOpFn fn) { int n = a->data.list.count < b->data.list.count ? a->data.list.count : b->data.list.count; Value *out = make_list(n); for (int i = 0; i < n; i++) { - Value *item = tensor_elementwise(a->data.list.items[i], b->data.list.items[i], fn); + Value *item = tensor_elementwise(list_get_borrow(a, i), list_get_borrow(b, i), fn); if (g_has_error) { val_decref(item); val_decref(out); return make_null(); } @@ -629,35 +629,35 @@ Value* builtin_tensor_add(Value *arg) { * integration by keeping THIS one — a builtin must not have two buffer * paths, and this is the one whose shape rules are the list path's, * container for container. */ - return tensor_elementwise(arg->data.list.items[0], arg->data.list.items[1], op_add); + return tensor_elementwise(list_get_borrow(arg, 0), list_get_borrow(arg, 1), op_add); } /* ==== BUILTIN: subtract ==== */ Value* builtin_tensor_subtract(Value *arg) { STRICT_LIST_MAX(arg, 2, "subtract"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - return tensor_elementwise(arg->data.list.items[0], arg->data.list.items[1], op_sub); + return tensor_elementwise(list_get_borrow(arg, 0), list_get_borrow(arg, 1), op_sub); } /* ==== BUILTIN: multiply ==== */ Value* builtin_tensor_multiply(Value *arg) { STRICT_LIST_MAX(arg, 2, "multiply"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - return tensor_elementwise(arg->data.list.items[0], arg->data.list.items[1], op_mul); + return tensor_elementwise(list_get_borrow(arg, 0), list_get_borrow(arg, 1), op_mul); } /* ==== BUILTIN: divide ==== */ Value* builtin_tensor_divide(Value *arg) { STRICT_LIST_MAX(arg, 2, "divide"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - return tensor_elementwise(arg->data.list.items[0], arg->data.list.items[1], op_div); + return tensor_elementwise(list_get_borrow(arg, 0), list_get_borrow(arg, 1), op_div); } /* ==== BUILTIN: pow ==== */ Value* builtin_tensor_pow(Value *arg) { STRICT_LIST_MAX(arg, 2, "pow"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - return tensor_elementwise(arg->data.list.items[0], arg->data.list.items[1], op_pow); + return tensor_elementwise(list_get_borrow(arg, 0), list_get_borrow(arg, 1), op_pow); } /* ---- Element-wise unary op ---- */ @@ -676,7 +676,7 @@ static Value* tensor_unary(Value *v, UnaryOpFn fn) { if (v->type == VAL_LIST) { Value *out = make_list(v->data.list.count); for (int i = 0; i < v->data.list.count; i++) - list_append_owned(out, tensor_unary(v->data.list.items[i], fn)); + list_append_owned(out, tensor_unary(list_get_borrow(v, i), fn)); return out; } /* Both handled types exit above, so `v` is neither a number nor a @@ -740,8 +740,8 @@ Value* builtin_tensor_matmul(Value *arg) { rt_error(EK_TYPE, 0, "matmul requires [A, B]"); return make_null(); } - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); /* flat-buffer fast path: compute in place, no flatten/rebuild */ if (a->type == VAL_BUFFER && b->type == VAL_BUFFER) { int ar, ac, br, bc; @@ -832,8 +832,8 @@ Value* builtin_tensor_matmul_at(Value *arg) { rt_error(EK_TYPE, 0, "matmul_at requires [A, B]"); return make_null(); } - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); if (a->type == VAL_BUFFER && b->type == VAL_BUFFER) { int ar, ac, br, bc; buf_dims(a, &ar, &ac); buf_dims(b, &br, &bc); @@ -890,8 +890,8 @@ Value* builtin_tensor_matmul_bt(Value *arg) { rt_error(EK_TYPE, 0, "matmul_bt requires [A, B]"); return make_null(); } - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); if (a->type == VAL_BUFFER && b->type == VAL_BUFFER) { int ar, ac, br, bc; buf_dims(a, &ar, &ac); buf_dims(b, &br, &bc); @@ -954,9 +954,9 @@ Value* builtin_tensor_scatter_add(Value *arg) { rt_error(EK_TYPE, 0, "scatter_add requires [dst, indices, values]"); return make_null(); } - Value *dst = arg->data.list.items[0]; - Value *indices = arg->data.list.items[1]; - Value *values = arg->data.list.items[2]; + Value *dst = list_get_borrow(arg, 0); + Value *indices = list_get_borrow(arg, 1); + Value *values = list_get_borrow(arg, 2); if (dst->type != VAL_BUFFER) { rt_error(EK_TYPE, 0, "scatter_add: dst must be a buffer, got %s", val_type_name(dst->type)); return make_null(); @@ -995,7 +995,7 @@ Value* builtin_tensor_scatter_add(Value *arg) { for (int i = 0; i < n; i++) { double di, v; if (indices->type == VAL_LIST) { - Value *iv = indices->data.list.items[i]; + Value *iv = list_get_borrow(indices, i); if (iv->type != VAL_NUM) { rt_error(EK_TYPE, 0, "scatter_add: index %d is %s (expected a number)", i, val_type_name(iv->type)); return make_null(); @@ -1010,7 +1010,7 @@ Value* builtin_tensor_scatter_add(Value *arg) { if (g_has_error) return make_null(); } if (values->type == VAL_LIST) { - Value *vv = values->data.list.items[i]; + Value *vv = list_get_borrow(values, i); if (vv->type != VAL_NUM) { rt_error(EK_TYPE, 0, "scatter_add: value %d is %s (expected a number)", i, val_type_name(vv->type)); return make_null(); @@ -1094,9 +1094,9 @@ Value* builtin_tensor_log_softmax(Value *arg) { * a row (a list), never a number, so the two forms no longer collide. */ Value *tensor = arg; if (arg && arg->type == VAL_LIST && arg->data.list.count == 2 && - arg->data.list.items[0]->type == VAL_LIST && - arg->data.list.items[1]->type == VAL_NUM) - tensor = arg->data.list.items[0]; /* [tensor, dim] form */ + list_get_borrow(arg, 0)->type == VAL_LIST && + list_get_borrow(arg, 1)->type == VAL_NUM) + tensor = list_get_borrow(arg, 0); /* [tensor, dim] form */ /* #632: log(softmax(scalar)) = log(1) = 0. */ /* fs:ANSWER softmax of a single element is 1 and log(1) is 0, so 0.0 is * the arithmetic result for a scalar argument — a NUMBER is a valid @@ -1335,8 +1335,8 @@ Value* builtin_tensor_zeros(Value *arg) { } /* zeros of [rows, cols] → 2D */ if (arg->type == VAL_LIST && arg->data.list.count >= 2 - && arg->data.list.items[0]->type == VAL_NUM - && arg->data.list.items[1]->type == VAL_NUM) { + && list_get_borrow(arg, 0)->type == VAL_NUM + && list_get_borrow(arg, 1)->type == VAL_NUM) { int64_t rows64 = (int64_t)eigs_list_num(arg, 0, __func__); int64_t cols64 = (int64_t)eigs_list_num(arg, 1, __func__); if (rows64 < 0) rows64 = 0; @@ -1370,7 +1370,7 @@ Value* builtin_tensor_zeros_like(Value *arg) { if (arg->type == VAL_LIST) { Value *out = make_list(arg->data.list.count); for (int i = 0; i < arg->data.list.count; i++) - list_append_owned(out, builtin_tensor_zeros_like(arg->data.list.items[i])); + list_append_owned(out, builtin_tensor_zeros_like(list_get_borrow(arg, i))); return out; } /* #1093: a buffer's zero is a zero BUFFER of the same shape, not the @@ -1398,8 +1398,8 @@ static int flat_is_vector(Value *v) { } static int flat_index_at(Value *v, int i) { if (v->type == VAL_LIST) - return (v->data.list.items[i]->type == VAL_NUM) - ? (int)eigs_num_arg(v->data.list.items[i], __func__) : -1; + return (list_get_borrow(v, i)->type == VAL_NUM) + ? (int)eigs_num_arg(list_get_borrow(v, i), __func__) : -1; /* #1417: normalize before conversion, and let every caller propagate a * raised read before using its index or touching an output. Saturated * infinity is still outside the int domain and uses the existing invalid @@ -1413,7 +1413,7 @@ static int flat_index_at(Value *v, int i) { * as "index -1 out of range" would name the wrong fault. Buffers hold doubles, * so every element is a number by construction. */ static int flat_index_is_num(Value *v, int i) { - return v->type != VAL_LIST || v->data.list.items[i]->type == VAL_NUM; + return v->type != VAL_LIST || list_get_borrow(v, i)->type == VAL_NUM; } /* ==== BUILTIN: gather ==== */ @@ -1448,8 +1448,8 @@ static int flat_index_is_num(Value *v, int i) { Value* builtin_tensor_gather(Value *arg) { STRICT_LIST_MAX(arg, 2, "gather"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *tensor = arg->data.list.items[0]; - Value *indices = arg->data.list.items[1]; + Value *tensor = list_get_borrow(arg, 0); + Value *indices = list_get_borrow(arg, 1); /* #1093 + #973: a buffer tensor. A shaped (2-D) buffer with one index per * row selects one element per row and yields a buffer; an unshaped (1-D) * buffer with a scalar index yields that element. */ @@ -1475,7 +1475,7 @@ Value* builtin_tensor_gather(Value *arg) { if (!flat_index_is_num(indices, i)) { val_decref(out); rt_error(EK_TYPE, 0, "gather: index %d is %s (expected a number)", - i, val_type_name(indices->data.list.items[i]->type)); + i, val_type_name(list_get_borrow(indices, i)->type)); return make_null(); } int idx = flat_index_at(indices, i); @@ -1498,7 +1498,7 @@ Value* builtin_tensor_gather(Value *arg) { ? tensor->data.list.count : indices->data.list.count; Value *out = make_list(n); for (int i = 0; i < n; i++) { - Value *row = tensor->data.list.items[i]; + Value *row = list_get_borrow(tensor, i); if (row->type != VAL_LIST) { /* not a matrix row — shape, not index */ /* #1637: a bool where a row belongs raises, in every mode. */ if (row->type == VAL_BOOL) { @@ -1509,13 +1509,13 @@ Value* builtin_tensor_gather(Value *arg) { list_append_owned(out, make_num(0.0)); continue; } - if (indices->data.list.items[i]->type != VAL_NUM) { + if (list_get_borrow(indices, i)->type != VAL_NUM) { val_decref(out); rt_error(EK_TYPE, 0, "gather: index %d is %s (expected a number)", - i, val_type_name(indices->data.list.items[i]->type)); + i, val_type_name(list_get_borrow(indices, i)->type)); return make_null(); } - int idx = (int)eigs_num_arg(indices->data.list.items[i], __func__); + int idx = (int)eigs_num_arg(list_get_borrow(indices, i), __func__); if (idx < 0 || idx >= row->data.list.count) { val_decref(out); rt_error(EK_INDEX, 0, @@ -1525,7 +1525,7 @@ Value* builtin_tensor_gather(Value *arg) { } /* #1637 round 4: the selected cell is read as a number -- a * bool there raised nothing and read as 0 (critic r3). */ - double cell = eigs_elem_num(row->data.list.items[idx], "gather"); + double cell = eigs_elem_num(list_get_borrow(row, idx), "gather"); if (g_has_error) { val_decref(out); return make_null(); } list_append_owned(out, make_num(cell)); } @@ -1539,7 +1539,7 @@ Value* builtin_tensor_gather(Value *arg) { idx, tensor->data.list.count); return make_null(); } - double cell = eigs_elem_num(tensor->data.list.items[idx], "gather"); /* #1637 round 4 */ + double cell = eigs_elem_num(list_get_borrow(tensor, idx), "gather"); /* #1637 round 4 */ if (g_has_error) return make_null(); return make_num(cell); } @@ -1592,7 +1592,7 @@ Value* call_eigs_fn(Value *fn, Value *arg) { if (pc > 1 && arg && arg->type == VAL_LIST) { int n = arg->data.list.count; for (int pi = 0; pi < pc && pi < n; pi++) - env_set_local(call_env, fn->data.fn.params[pi], arg->data.list.items[pi]); + env_set_local(call_env, fn->data.fn.params[pi], list_get_borrow(arg, pi)); /* Under-arity null-fill, matching the VM. Leaving the tail unbound * is not the same thing: an unbound name resolves through the * CLOSURE, so a 2-param key over 1-wide elements could silently read @@ -1710,7 +1710,7 @@ Value* builtin_tensor_shape(Value *arg) { list_append_owned(out, make_num(0)); return out; } - Value *first = arg->data.list.items[0]; + Value *first = list_get_borrow(arg, 0); if (first->type == VAL_LIST) { /* 2D */ Value *out = make_list(2); @@ -1761,11 +1761,11 @@ static double numerical_loss(Value *loss_fn, Value *arg, const char *who, static int tensor_cells_numeric(const Value *v, const char *who) { if (!v || v->type != VAL_LIST) return 1; for (int i = 0; i < v->data.list.count; i++) { - const Value *e = v->data.list.items[i]; + const Value *e = list_get_borrow(v, i); if (e && e->type == VAL_LIST) { for (int c = 0; c < e->data.list.count; c++) - if (!e->data.list.items[c] || e->data.list.items[c]->type != VAL_NUM) { - eigs_num_arg(e->data.list.items[c], who); + if (!list_get_borrow(e, c) || list_get_borrow(e, c)->type != VAL_NUM) { + eigs_num_arg(list_get_borrow(e, c), who); return 0; } } else if (!e || e->type != VAL_NUM) { @@ -1779,12 +1779,12 @@ static int tensor_cells_numeric(const Value *v, const char *who) { Value* builtin_numerical_grad(Value *arg) { STRICT_LIST_MAX(arg, 3, "numerical_grad"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) return make_null(); - Value *loss_fn = arg->data.list.items[0]; - Value *param = arg->data.list.items[1]; + Value *loss_fn = list_get_borrow(arg, 0); + Value *param = list_get_borrow(arg, 1); /* #1637: the optional eps: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double eps = 0.001; - eigs_opt_num(arg->data.list.items[2], &eps, __func__); + eigs_opt_num(list_get_borrow(arg, 2), &eps, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(param, "numerical_grad")) return make_null(); if (eps <= 0) eps = 0.001; @@ -1813,24 +1813,24 @@ Value* builtin_numerical_grad(Value *arg) { Value *nul = make_null(); /* shared arg for loss_fn calls */ /* Check if 1D or 2D */ - int is_2d = (param->data.list.count > 0 && param->data.list.items[0]->type == VAL_LIST); + int is_2d = (param->data.list.count > 0 && list_get_borrow(param, 0)->type == VAL_LIST); if (!is_2d) { /* 1D param */ int len = param->data.list.count; Value *grad = make_list(len); for (int i = 0; i < len; i++) { - Value *orig = param->data.list.items[i]; + Value *orig = list_get_borrow(param, i); double old_val = eigs_num_arg(orig, __func__); val_incref(orig); /* guard while displaced from its slot */ Value *pp = make_num(old_val + eps); /* birth ref doubles as slot ref */ - param->data.list.items[i] = pp; + list_set_owned(param, i, pp); double lp = numerical_loss(loss_fn, nul, "numerical_grad", &loss_valid); Value *pm = make_num(old_val - eps); - param->data.list.items[i] = pm; + list_set_owned(param, i, pm); val_decref(pp); double lm = numerical_loss(loss_fn, nul, "numerical_grad", &loss_valid); - param->data.list.items[i] = orig; + list_set_owned(param, i, orig); val_decref(orig); /* drop the guard */ val_decref(pm); /* Central difference */ @@ -1845,22 +1845,22 @@ Value* builtin_numerical_grad(Value *arg) { int rows = param->data.list.count; Value *grad = make_list(rows); for (int r = 0; r < rows; r++) { - Value *row = param->data.list.items[r]; + Value *row = list_get_borrow(param, r); if (!row || row->type != VAL_LIST) { list_append_owned(grad, make_list(0)); continue; } int cols = row->data.list.count; Value *grad_row = make_list(cols); for (int c = 0; c < cols; c++) { - Value *orig = row->data.list.items[c]; + Value *orig = list_get_borrow(row, c); double old_val = eigs_num_arg(orig, __func__); val_incref(orig); /* guard while displaced from its slot */ Value *pp = make_num(old_val + eps); /* birth ref doubles as slot ref */ - row->data.list.items[c] = pp; + list_set_owned(row, c, pp); double lp = numerical_loss(loss_fn, nul, "numerical_grad", &loss_valid); Value *pm = make_num(old_val - eps); - row->data.list.items[c] = pm; + list_set_owned(row, c, pm); val_decref(pp); double lm = numerical_loss(loss_fn, nul, "numerical_grad", &loss_valid); - row->data.list.items[c] = orig; + list_set_owned(row, c, orig); val_decref(orig); /* drop the guard */ val_decref(pm); list_append_owned(grad_row, make_num((lp - lm) / (2.0 * eps))); @@ -1877,12 +1877,12 @@ Value* builtin_numerical_grad(Value *arg) { Value* builtin_sgd_update(Value *arg) { STRICT_LIST_MAX(arg, 3, "sgd_update"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) return make_null(); - Value *param = arg->data.list.items[0]; - Value *grad = arg->data.list.items[1]; + Value *param = list_get_borrow(arg, 0); + Value *grad = list_get_borrow(arg, 1); /* #1637: the optional lr: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double lr = 0.01; - eigs_opt_num(arg->data.list.items[2], &lr, __func__); + eigs_opt_num(list_get_borrow(arg, 2), &lr, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(param, "sgd_update") || !tensor_cells_numeric(grad, "sgd_update")) return make_null(); @@ -1896,17 +1896,17 @@ Value* builtin_sgd_update(Value *arg) { } if (param->type != VAL_LIST || grad->type != VAL_LIST) return param; - int is_2d = (param->data.list.count > 0 && param->data.list.items[0]->type == VAL_LIST); + int is_2d = (param->data.list.count > 0 && list_get_borrow(param, 0)->type == VAL_LIST); if (!is_2d) { /* 1D */ int len = param->data.list.count < grad->data.list.count ? param->data.list.count : grad->data.list.count; for (int i = 0; i < len; i++) { - Value *old = param->data.list.items[i]; + Value *old = list_get_borrow(param, i); double pv = eigs_num_arg(old, __func__); - double gv = eigs_num_arg(grad->data.list.items[i], __func__); - param->data.list.items[i] = make_num(pv - lr * gv); + double gv = eigs_num_arg(list_get_borrow(grad, i), __func__); + list_set_owned(param, i, make_num(pv - lr * gv)); val_decref(old); } } else { @@ -1914,16 +1914,16 @@ Value* builtin_sgd_update(Value *arg) { int rows = param->data.list.count < grad->data.list.count ? param->data.list.count : grad->data.list.count; for (int r = 0; r < rows; r++) { - Value *pr = param->data.list.items[r]; - Value *gr = grad->data.list.items[r]; + Value *pr = list_get_borrow(param, r); + Value *gr = list_get_borrow(grad, r); if (!pr || pr->type != VAL_LIST || !gr || gr->type != VAL_LIST) continue; int cols = pr->data.list.count < gr->data.list.count ? pr->data.list.count : gr->data.list.count; for (int c = 0; c < cols; c++) { - Value *old = pr->data.list.items[c]; + Value *old = list_get_borrow(pr, c); double pv = eigs_num_arg(old, __func__); - double gv = eigs_num_arg(gr->data.list.items[c], __func__); - pr->data.list.items[c] = make_num(pv - lr * gv); + double gv = eigs_num_arg(list_get_borrow(gr, c), __func__); + list_set_owned(pr, c, make_num(pv - lr * gv)); val_decref(old); } } @@ -1939,13 +1939,13 @@ Value* builtin_sgd_update(Value *arg) { Value* builtin_numerical_grad_rows(Value *arg) { STRICT_LIST_MAX(arg, 4, "numerical_grad_rows"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 4) return make_null(); - Value *loss_fn = arg->data.list.items[0]; - Value *matrix = arg->data.list.items[1]; - Value *row_indices = arg->data.list.items[2]; + Value *loss_fn = list_get_borrow(arg, 0); + Value *matrix = list_get_borrow(arg, 1); + Value *row_indices = list_get_borrow(arg, 2); /* #1637: the optional eps: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double eps = 0.001; - eigs_opt_num(arg->data.list.items[3], &eps, __func__); + eigs_opt_num(list_get_borrow(arg, 3), &eps, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(matrix, "numerical_grad_rows") || !tensor_cells_numeric(row_indices, "numerical_grad_rows")) return make_null(); if (eps <= 0) eps = 0.001; @@ -1987,8 +1987,8 @@ Value* builtin_numerical_grad_rows(Value *arg) { if (matrix->type != VAL_LIST || !flat_is_vector(row_indices)) return make_null(); int rows = matrix->data.list.count; - if (rows == 0 || matrix->data.list.items[0]->type != VAL_LIST) return make_null(); - int cols = matrix->data.list.items[0]->data.list.count; + if (rows == 0 || list_get_borrow(matrix, 0)->type != VAL_LIST) return make_null(); + int cols = list_get_borrow(matrix, 0)->data.list.count; Value *nul = make_null(); /* shared arg for loss_fn calls */ /* Build zero gradient matrix */ @@ -2010,12 +2010,12 @@ Value* builtin_numerical_grad_rows(Value *arg) { } if (r < 0 || r >= rows) continue; - Value *row = matrix->data.list.items[r]; + Value *row = list_get_borrow(matrix, r); if (!row || row->type != VAL_LIST) continue; - Value *grad_row = grad->data.list.items[r]; + Value *grad_row = list_get_borrow(grad, r); for (int c = 0; c < cols && c < row->data.list.count; c++) { - Value *cell = row->data.list.items[c]; + Value *cell = list_get_borrow(row, c); double old_val = eigs_num_arg(cell, __func__); VAL_NUM_RAW(cell) = old_val + eps; double loss_plus = numerical_loss(loss_fn, nul, "numerical_grad_rows", &loss_valid); @@ -2023,8 +2023,8 @@ Value* builtin_numerical_grad_rows(Value *arg) { double loss_minus = numerical_loss(loss_fn, nul, "numerical_grad_rows", &loss_valid); VAL_NUM_RAW(cell) = old_val; /* gradient — release the zero placeholder this slot held */ - val_decref(grad_row->data.list.items[c]); - grad_row->data.list.items[c] = make_num((loss_plus - loss_minus) / (2.0 * eps)); + val_decref(list_get_borrow(grad_row, c)); + list_set_owned(grad_row, c, make_num((loss_plus - loss_minus) / (2.0 * eps))); } } val_decref(nul); @@ -2038,13 +2038,13 @@ Value* builtin_numerical_grad_rows(Value *arg) { Value* builtin_sgd_update_rows(Value *arg) { STRICT_LIST_MAX(arg, 4, "sgd_update_rows"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 4) return make_null(); - Value *matrix = arg->data.list.items[0]; - Value *grad = arg->data.list.items[1]; - Value *row_indices = arg->data.list.items[2]; + Value *matrix = list_get_borrow(arg, 0); + Value *grad = list_get_borrow(arg, 1); + Value *row_indices = list_get_borrow(arg, 2); /* #1637: the optional lr: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double lr = 0.01; - eigs_opt_num(arg->data.list.items[3], &lr, __func__); + eigs_opt_num(list_get_borrow(arg, 3), &lr, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(matrix, "sgd_update_rows") || !tensor_cells_numeric(grad, "sgd_update_rows") || !tensor_cells_numeric(row_indices, "sgd_update_rows")) return make_null(); @@ -2074,17 +2074,17 @@ Value* builtin_sgd_update_rows(Value *arg) { if (g_has_error) return make_null(); if (r < 0 || r >= matrix->data.list.count || r >= grad->data.list.count) continue; - Value *mrow = matrix->data.list.items[r]; - Value *grow = grad->data.list.items[r]; + Value *mrow = list_get_borrow(matrix, r); + Value *grow = list_get_borrow(grad, r); if (!mrow || mrow->type != VAL_LIST || !grow || grow->type != VAL_LIST) continue; int cols = mrow->data.list.count < grow->data.list.count ? mrow->data.list.count : grow->data.list.count; for (int c = 0; c < cols; c++) { - Value *old = mrow->data.list.items[c]; + Value *old = list_get_borrow(mrow, c); double pv = eigs_num_arg(old, __func__); - double gv = eigs_num_arg(grow->data.list.items[c], __func__); - mrow->data.list.items[c] = make_num(pv - lr * gv); + double gv = eigs_num_arg(list_get_borrow(grow, c), __func__); + list_set_owned(mrow, c, make_num(pv - lr * gv)); val_decref(old); } } @@ -2099,13 +2099,13 @@ Value* builtin_sgd_update_rows(Value *arg) { Value* builtin_numerical_grad_cols(Value *arg) { STRICT_LIST_MAX(arg, 4, "numerical_grad_cols"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 4) return make_null(); - Value *loss_fn = arg->data.list.items[0]; - Value *matrix = arg->data.list.items[1]; - Value *col_indices = arg->data.list.items[2]; + Value *loss_fn = list_get_borrow(arg, 0); + Value *matrix = list_get_borrow(arg, 1); + Value *col_indices = list_get_borrow(arg, 2); /* #1637: the optional eps: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double eps = 0.001; - eigs_opt_num(arg->data.list.items[3], &eps, __func__); + eigs_opt_num(list_get_borrow(arg, 3), &eps, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(matrix, "numerical_grad_cols") || !tensor_cells_numeric(col_indices, "numerical_grad_cols")) return make_null(); if (eps <= 0) eps = 0.001; @@ -2145,8 +2145,8 @@ Value* builtin_numerical_grad_cols(Value *arg) { if (matrix->type != VAL_LIST || !flat_is_vector(col_indices)) return make_null(); int rows = matrix->data.list.count; - if (rows == 0 || matrix->data.list.items[0]->type != VAL_LIST) return make_null(); - int cols = matrix->data.list.items[0]->data.list.count; + if (rows == 0 || list_get_borrow(matrix, 0)->type != VAL_LIST) return make_null(); + int cols = list_get_borrow(matrix, 0)->data.list.count; Value *nul = make_null(); /* shared arg for loss_fn calls */ /* Build zero gradient matrix */ @@ -2169,28 +2169,28 @@ Value* builtin_numerical_grad_cols(Value *arg) { if (col < 0 || col >= cols) continue; for (int r = 0; r < rows; r++) { - Value *row = matrix->data.list.items[r]; + Value *row = list_get_borrow(matrix, r); if (!row || row->type != VAL_LIST || col >= row->data.list.count) continue; - Value *orig = row->data.list.items[col]; + Value *orig = list_get_borrow(row, col); double old_val = eigs_num_arg(orig, __func__); val_incref(orig); /* guard while displaced from its slot */ /* +eps */ Value *pp = make_num(old_val + eps); /* birth ref doubles as slot ref */ - row->data.list.items[col] = pp; + list_set_owned(row, col, pp); double loss_plus = numerical_loss(loss_fn, nul, "numerical_grad_cols", &loss_valid); /* -eps */ Value *pm = make_num(old_val - eps); - row->data.list.items[col] = pm; + list_set_owned(row, col, pm); val_decref(pp); double loss_minus = numerical_loss(loss_fn, nul, "numerical_grad_cols", &loss_valid); /* restore */ - row->data.list.items[col] = orig; + list_set_owned(row, col, orig); val_decref(orig); /* drop the guard */ val_decref(pm); /* gradient — release the zero placeholder this slot held */ - val_decref(grad->data.list.items[r]->data.list.items[col]); - grad->data.list.items[r]->data.list.items[col] = make_num((loss_plus - loss_minus) / (2.0 * eps)); + val_decref(list_get_borrow(list_get_borrow(grad, r), col)); + list_set_owned(list_get_borrow(grad, r), col, make_num((loss_plus - loss_minus) / (2.0 * eps))); } } val_decref(nul); @@ -2204,13 +2204,13 @@ Value* builtin_numerical_grad_cols(Value *arg) { Value* builtin_sgd_update_cols(Value *arg) { STRICT_LIST_MAX(arg, 4, "sgd_update_cols"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 4) return make_null(); - Value *matrix = arg->data.list.items[0]; - Value *grad = arg->data.list.items[1]; - Value *col_indices = arg->data.list.items[2]; + Value *matrix = list_get_borrow(arg, 0); + Value *grad = list_get_borrow(arg, 1); + Value *col_indices = list_get_borrow(arg, 2); /* #1637: the optional lr: null keeps the default, a number sets it, * anything else (a bool) raises instead of silently meaning the default. */ double lr = 0.01; - eigs_opt_num(arg->data.list.items[3], &lr, __func__); + eigs_opt_num(list_get_borrow(arg, 3), &lr, __func__); if (g_has_error) return make_null(); if (!tensor_cells_numeric(matrix, "sgd_update_cols") || !tensor_cells_numeric(grad, "sgd_update_cols") || !tensor_cells_numeric(col_indices, "sgd_update_cols")) return make_null(); @@ -2243,15 +2243,15 @@ Value* builtin_sgd_update_cols(Value *arg) { if (col < 0) continue; for (int r = 0; r < rows; r++) { - Value *mrow = matrix->data.list.items[r]; - Value *grow = grad->data.list.items[r]; + Value *mrow = list_get_borrow(matrix, r); + Value *grow = list_get_borrow(grad, r); if (!mrow || mrow->type != VAL_LIST || col >= mrow->data.list.count) continue; if (!grow || grow->type != VAL_LIST || col >= grow->data.list.count) continue; - Value *old = mrow->data.list.items[col]; + Value *old = list_get_borrow(mrow, col); double pv = eigs_num_arg(old, __func__); - double gv = eigs_num_arg(grow->data.list.items[col], __func__); - mrow->data.list.items[col] = make_num(pv - lr * gv); + double gv = eigs_num_arg(list_get_borrow(grow, col), __func__); + list_set_owned(mrow, col, make_num(pv - lr * gv)); val_decref(old); } } @@ -2262,8 +2262,8 @@ Value* builtin_tensor_save(Value *arg) { STRICT_LIST_MAX(arg, 2, "tensor_save"); ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "tensor_save", "[tensor, path]", make_bool(0)); - Value *tensor = arg->data.list.items[0]; - Value *path_val = arg->data.list.items[1]; + Value *tensor = list_get_borrow(arg, 0); + Value *path_val = list_get_borrow(arg, 1); ARG_GUARD(!tensor || (tensor->type != VAL_LIST && tensor->type != VAL_BUFFER) || !path_val || path_val->type != VAL_STR, /* #1093 */ "tensor_save", "[a list or buffer tensor, a string path]", make_bool(0)); @@ -2378,8 +2378,8 @@ Value* builtin_tensor_load(Value *arg) { replayed_verdict = 1; if (verdict && verdict->type == VAL_LIST && verdict->data.list.count == 2 && - verdict->data.list.items[0]->type == VAL_NUM && - verdict->data.list.items[1]->type == VAL_NUM) { + list_get_borrow(verdict, 0)->type == VAL_NUM && + list_get_borrow(verdict, 1)->type == VAL_NUM) { uint32_t rows = (uint32_t)eigs_list_num(verdict, 0, __func__); uint32_t cols = (uint32_t)eigs_list_num(verdict, 1, __func__); val_decref(verdict); diff --git a/src/container.h b/src/container.h new file mode 100644 index 00000000..f9c59d7e --- /dev/null +++ b/src/container.h @@ -0,0 +1,75 @@ +#ifndef EIGENSCRIPT_CONTAINER_H +#define EIGENSCRIPT_CONTAINER_H + +/* Container element access boundary (#1665 arm D). + * + * Storage is still Value ** in step 1. Step 2 changes only these helpers to + * decode EigsSlot storage. A borrowed getter is valid until the next + * container mutation and must not be decref'd. For an immediate numeric slot + * step 2 will materialise into per-thread scratch Value storage; callers that + * retain a value must therefore use the owned getter. This avoids allocating + * on the overwhelmingly common inspect-and-discard path, while the owned API + * gives retaining callers a stable heap Value. + * + * *_set_owned consumes the caller's reference and writes it into a slot whose + * previous ownership has already been dealt with by the caller (or which is + * vacant). *_set_borrow adds the container's reference before writing. The + * setters intentionally do not release the displaced element: this preserves + * the existing low-level mutation contract and makes replacement ordering + * explicit at call sites. + */ +static inline Value *list_get_borrow(const Value *list, int i) { + return list->data.list.items[i]; +} +static inline Value *list_get_owned(const Value *list, int i) { + Value *v = list_get_borrow(list, i); + val_incref(v); + return v; +} +static inline void list_set_owned(Value *list, int i, Value *v) { + list->data.list.items[i] = v; +} +static inline void list_set_borrow(Value *list, int i, Value *v) { + val_incref(v); + list_set_owned(list, i, v); +} +static inline int list_get_num(const Value *list, int i, double *out) { + Value *v = list_get_borrow(list, i); + if (!v || v->type != VAL_NUM) return 0; + *out = VAL_NUM_RAW(v); + return 1; +} +static inline int list_iter_count(const Value *list) { return list->data.list.count; } +static inline Value *list_iter_borrow(const Value *list, int i) { return list_get_borrow(list, i); } + +static inline Value *dict_value_get_borrow(const Value *dict, int i) { + return dict->data.dict.vals[i]; +} +static inline Value *dict_value_get_owned(const Value *dict, int i) { + Value *v = dict_value_get_borrow(dict, i); + val_incref(v); + return v; +} +static inline void dict_value_set_owned(Value *dict, int i, Value *v) { + dict->data.dict.vals[i] = v; +} +static inline void dict_value_set_borrow(Value *dict, int i, Value *v) { + val_incref(v); + dict_value_set_owned(dict, i, v); +} +static inline int dict_value_get_num(const Value *dict, int i, double *out) { + Value *v = dict_value_get_borrow(dict, i); + if (!v || v->type != VAL_NUM) return 0; + *out = VAL_NUM_RAW(v); + return 1; +} +static inline int dict_value_iter_count(const Value *dict) { return dict->data.dict.count; } +static inline Value *dict_value_iter_borrow(const Value *dict, int i) { return dict_value_get_borrow(dict, i); } + +/* Raw storage is confined to bulk allocation/movement/destruction code. */ +static inline Value **list_values_storage(Value *list) { return list->data.list.items; } +static inline Value ***list_values_storage_ref(Value *list) { return &list->data.list.items; } +static inline Value **dict_values_storage(Value *dict) { return dict->data.dict.vals; } +static inline Value ***dict_values_storage_ref(Value *dict) { return &dict->data.dict.vals; } + +#endif diff --git a/src/eigenscript.c b/src/eigenscript.c index 7c7aa8bc..6f32f5d9 100644 --- a/src/eigenscript.c +++ b/src/eigenscript.c @@ -438,7 +438,7 @@ static double compute_entropy_impl(Value *v) { if (v->data.list.count == 0) return 0.0; double sum = 0.0; for (int i = 0; i < v->data.list.count; i++) - sum += ent_child(v->data.list.items[i]); + sum += ent_child(list_get_borrow(v, i)); return sum / v->data.list.count + log2((double)v->data.list.count + 1.0); } case VAL_DICT: { @@ -446,7 +446,7 @@ static double compute_entropy_impl(Value *v) { if (v->data.dict.count == 0) return 0.0; double sum = 0.0; for (int i = 0; i < v->data.dict.count; i++) - sum += ent_child(v->data.dict.vals[i]); + sum += ent_child(dict_value_get_borrow(v, i)); return sum / v->data.dict.count + log2((double)v->data.dict.count + 1.0); } case VAL_FN: return 1.0; /* #708: a constant, so dH never moves */ @@ -1482,7 +1482,7 @@ int observer_slot_from_trajectory(ObserverSlot *out, Value *dict) { out->vr_window = xcalloc((size_t)N, sizeof(double)); out->v_cap = out->dh_cap = (uint8_t)N; for (int i = vstart; i < vcnt; i++) { - Value *a = rel->data.list.items[i], *b = raw->data.list.items[i]; + Value *a = list_get_borrow(rel, i), *b = list_get_borrow(raw, i); if (!a || a->type != VAL_NUM || !b || b->type != VAL_NUM) { free(out->v_window); free(out->vr_window); memset(out, 0, sizeof *out); @@ -1497,7 +1497,7 @@ int observer_slot_from_trajectory(ObserverSlot *out, Value *dict) { int dstart = dcnt > N ? dcnt - N : 0; out->dh_window = xcalloc((size_t)N, sizeof(double)); for (int i = dstart; i < dcnt; i++) { - Value *a = dh->data.list.items[i]; + Value *a = list_get_borrow(dh, i); if (!a || a->type != VAL_NUM) { free(out->v_window); free(out->vr_window); free(out->dh_window); memset(out, 0, sizeof *out); @@ -1643,9 +1643,9 @@ void free_value(Value *v) { break; case VAL_LIST: for (int i = 0; i < v->data.list.count; i++) - val_decref(v->data.list.items[i]); - if (v->data.list.items) - free(v->data.list.items); + val_decref(list_get_borrow(v, i)); + if (list_values_storage(v)) + free(list_values_storage(v)); break; case VAL_DICT: if (v->module_ns) { @@ -1657,10 +1657,10 @@ void free_value(Value *v) { } for (int i = 0; i < v->data.dict.count; i++) { /* Keys have table/private-node ownership; do not free individually. */ - val_decref(v->data.dict.vals[i]); + val_decref(dict_value_get_borrow(v, i)); } free(v->data.dict.keys); - free(v->data.dict.vals); + free(dict_values_storage(v)); free(v->data.dict.hash.hashes); free(v->data.dict.hash.indices); free(v->data.dict.hash.generations); @@ -1794,7 +1794,7 @@ double eigs_list_num(const Value *list, int i, const char *who) { num_who(who), i); return 0.0; } - return eigs_num_arg(list->data.list.items[i], who); + return eigs_num_arg(list_get_borrow(list, i), who); } double eigs_elem_num(const Value *v, const char *who) { @@ -1818,12 +1818,12 @@ int eigs_arg_has_bool(const Value *arg) { if (arg->type == VAL_BOOL) return 1; if (arg->type == VAL_LIST) for (int i = 0; i < arg->data.list.count; i++) { - const Value *e = arg->data.list.items[i]; + const Value *e = list_get_borrow(arg, i); if (!e) continue; if (e->type == VAL_BOOL) return 1; if (e->type == VAL_LIST) for (int j = 0; j < e->data.list.count; j++) - if (e->data.list.items[j] && e->data.list.items[j]->type == VAL_BOOL) + if (list_get_borrow(e, j) && list_get_borrow(e, j)->type == VAL_BOOL) return 1; } return 0; @@ -1967,7 +1967,7 @@ Value* make_list(int capacity) { Value *v = xcalloc(1, sizeof(Value)); v->type = VAL_LIST; v->data.list.capacity = capacity < 8 ? 8 : capacity; - v->data.list.items = xcalloc(v->data.list.capacity, sizeof(Value*)); + *list_values_storage_ref(v) = xcalloc(v->data.list.capacity, sizeof(Value*)); v->data.list.count = 0; v->refcount = 1; return v; @@ -1980,7 +1980,7 @@ Value* make_list_heap(int capacity) { Value *v = xcalloc(1, sizeof(Value)); v->type = VAL_LIST; v->data.list.capacity = capacity < 8 ? 8 : capacity; - v->data.list.items = xcalloc(v->data.list.capacity, sizeof(Value*)); + *list_values_storage_ref(v) = xcalloc(v->data.list.capacity, sizeof(Value*)); v->data.list.count = 0; v->refcount = 1; return v; @@ -2069,7 +2069,7 @@ Value* make_dict(int capacity) { Value *v = xcalloc(1, sizeof(Value)); v->type = VAL_DICT; v->data.dict.keys = capacity ? xcalloc(capacity, sizeof(char*)) : NULL; - v->data.dict.vals = capacity ? xcalloc(capacity, sizeof(Value*)) : NULL; + *dict_values_storage_ref(v) = capacity ? xcalloc(capacity, sizeof(Value*)) : NULL; v->data.dict.count = 0; v->data.dict.capacity = capacity; /* A literal {} reaches here with capacity zero. Keep all three backing @@ -2338,12 +2338,12 @@ static Value *module_ns_project(Value *d, Env *e, const char *key, uint32_t h) { env_shared_lock(e); int di = env_hash_find(&d->data.dict.hash, key, h, d->data.dict.keys); if (ei < 0) { - Value *miss = (di >= 0) ? d->data.dict.vals[di] : NULL; + Value *miss = (di >= 0) ? dict_value_get_borrow(d, di) : NULL; env_shared_unlock(e); return miss; } if (di >= 0) { - Value *cur = d->data.dict.vals[di]; + Value *cur = dict_value_get_borrow(d, di); if (slot_is_ptr(s)) { /* Container/fn bindings were already shared by reference. */ if (slot_as_ptr(s) == cur) { @@ -2365,7 +2365,7 @@ static Value *module_ns_project(Value *d, Env *e, const char *key, uint32_t h) { Value *mv = slot_to_value(s); /* owned */ dict_set_hashed_raw(d, key, h, mv); di = env_hash_find(&d->data.dict.hash, key, h, d->data.dict.keys); - Value *out = (di >= 0) ? d->data.dict.vals[di] : NULL; + Value *out = (di >= 0) ? dict_value_get_borrow(d, di) : NULL; env_shared_unlock(e); slot_decref(s); /* drop the pin */ val_decref(mv); @@ -2468,15 +2468,15 @@ void dict_set_hashed_raw(Value *dict, const char *key, uint32_t h, Value *val) { (sizeof(char *) + sizeof(Value *) + sizeof(Value)))) return; dict->data.dict.keys = xcalloc(initial_cap, sizeof(char *)); - dict->data.dict.vals = xcalloc(initial_cap, sizeof(Value *)); + *dict_values_storage_ref(dict) = xcalloc(initial_cap, sizeof(Value *)); dict->data.dict.capacity = initial_cap; env_hash_init(&dict->data.dict.hash, ENV_HASH_INIT_CAP); } int idx = env_hash_find(&dict->data.dict.hash, key, h, dict->data.dict.keys); if (idx >= 0) { val_incref(val); - val_decref(dict->data.dict.vals[idx]); - dict->data.dict.vals[idx] = val; + val_decref(dict_value_get_borrow(dict, idx)); + dict_value_set_owned(dict, idx, val); return; } /* Grow if needed */ @@ -2487,7 +2487,7 @@ void dict_set_hashed_raw(Value *dict, const char *key, uint32_t h, Value *val) { (sizeof(char *) + sizeof(Value *) + sizeof(Value)))) return; dict->data.dict.keys = xrealloc_array(dict->data.dict.keys, new_cap, sizeof(char*)); - dict->data.dict.vals = xrealloc_array(dict->data.dict.vals, new_cap, sizeof(Value*)); + *dict_values_storage_ref(dict) = xrealloc_array(dict_values_storage(dict), new_cap, sizeof(Value*)); dict->data.dict.capacity = new_cap; } /* #1141: while the process is multithreaded, a key must outlive the @@ -2520,7 +2520,7 @@ void dict_set_hashed_raw(Value *dict, const char *key, uint32_t h, Value *val) { intern_ref_add(&dict->data.dict.intern_refs, eigs_current->intern_tbl); } dict->data.dict.keys[dict->data.dict.count] = interned; - dict->data.dict.vals[dict->data.dict.count] = val; + dict_value_set_owned(dict, dict->data.dict.count, val); val_incref(val); dict->data.dict.count++; if (dict->data.dict.count * 10 > (dict->data.dict.hash.mask + 1) * 7) @@ -2603,7 +2603,7 @@ static Value *chan_clone_rec(Value *v, int depth) { int n = v->data.list.count; Value *out = make_list_heap(n > 0 ? n : 1); for (int i = 0; i < n; i++) { - Value *ce = chan_clone_rec(v->data.list.items[i], depth + 1); + Value *ce = chan_clone_rec(list_get_borrow(v, i), depth + 1); list_append(out, ce); /* increfs */ val_decref(ce); /* drop birth ref */ } @@ -2617,7 +2617,7 @@ static Value *chan_clone_rec(Value *v, int depth) { int n = v->data.dict.count; Value *out = make_dict(n > 0 ? n : 8); for (int i = 0; i < n; i++) { - Value *cv = chan_clone_rec(v->data.dict.vals[i], depth + 1); + Value *cv = chan_clone_rec(dict_value_get_borrow(v, i), depth + 1); dict_set_owned(out, v->data.dict.keys[i], cv); /* consumes cv */ } /* Rehome keys from the (thread-local, soon-freed) intern table to @@ -2696,14 +2696,14 @@ Value* dict_get_hashed(Value *dict, const char *key, uint32_t h) { env_shared_lock(me); int pidx = env_hash_find(&dict->data.dict.hash, key, h, dict->data.dict.keys); - Value *pv = (pidx >= 0) ? dict->data.dict.vals[pidx] : NULL; + Value *pv = (pidx >= 0) ? dict_value_get_borrow(dict, pidx) : NULL; env_shared_unlock(me); return pv; } } if (dict->data.dict.count == 0) return NULL; int idx = env_hash_find(&dict->data.dict.hash, key, h, dict->data.dict.keys); - return (idx >= 0) ? dict->data.dict.vals[idx] : NULL; + return (idx >= 0) ? dict_value_get_borrow(dict, idx) : NULL; } Value* dict_get(Value *dict, const char *key) { @@ -2728,11 +2728,11 @@ void dict_remove(Value *dict, const char *key) { if (idx < 0) return; /* keys are interned — do not free */ env_intern_scope_remove(dict->data.dict.keys[idx]); - val_decref(dict->data.dict.vals[idx]); + val_decref(dict_value_get_borrow(dict, idx)); /* Shift remaining */ for (int j = idx; j < dict->data.dict.count - 1; j++) { dict->data.dict.keys[j] = dict->data.dict.keys[j+1]; - dict->data.dict.vals[j] = dict->data.dict.vals[j+1]; + dict_value_set_owned(dict, j, dict_value_get_borrow(dict, j+1)); } dict->data.dict.count--; env_hash_rebuild(&dict->data.dict.hash, dict->data.dict.keys, dict->data.dict.count); @@ -2756,10 +2756,10 @@ void list_append(Value *list, Value *item) { if (!sandbox_charge((size_t)(new_cap - list->data.list.capacity) * (sizeof(Value *) + sizeof(Value)))) return; - list->data.list.items = xrealloc_array(list->data.list.items, new_cap, sizeof(Value*)); + *list_values_storage_ref(list) = xrealloc_array(list_values_storage(list), new_cap, sizeof(Value*)); list->data.list.capacity = new_cap; } - list->data.list.items[list->data.list.count++] = item; + list_set_owned(list, list->data.list.count++, item); val_incref(item); } @@ -2832,8 +2832,8 @@ static int values_equal_impl(Value *a, Value *b, int depth, const char *op) { case VAL_LIST: { if (a->data.list.count != b->data.list.count) return 0; for (int i = 0; i < a->data.list.count; i++) - if (!values_equal_impl(a->data.list.items[i], - b->data.list.items[i], depth + 1, op)) + if (!values_equal_impl(list_get_borrow(a, i), + list_get_borrow(b, i), depth + 1, op)) return 0; return 1; } @@ -2844,7 +2844,7 @@ static int values_equal_impl(Value *a, Value *b, int depth, const char *op) { for (int i = 0; i < a->data.dict.count; i++) { Value *bv = dict_get(b, a->data.dict.keys[i]); if (!bv) return 0; - if (!values_equal_impl(a->data.dict.vals[i], bv, depth + 1, op)) + if (!values_equal_impl(dict_value_get_borrow(a, i), bv, depth + 1, op)) return 0; } return 1; @@ -2927,8 +2927,8 @@ char* value_to_string(Value *v) { g_vts_depth++; for (int i = 0; i < v->data.list.count; i++) { if (i > 0) strbuf_append_n(&out, ", ", 2); - char *s = value_to_string(v->data.list.items[i]); - if (v->data.list.items[i] && v->data.list.items[i]->type == VAL_STR) + char *s = value_to_string(list_get_borrow(v, i)); + if (list_get_borrow(v, i) && list_get_borrow(v, i)->type == VAL_STR) eigs_json_escape_string(&out, s); else strbuf_append(&out, s); @@ -2949,8 +2949,8 @@ char* value_to_string(Value *v) { if (i > 0) strbuf_append_n(&out, ", ", 2); eigs_json_escape_string(&out, v->data.dict.keys[i]); strbuf_append_n(&out, ": ", 2); - char *vs = value_to_string(v->data.dict.vals[i]); - if (v->data.dict.vals[i] && v->data.dict.vals[i]->type == VAL_STR) + char *vs = value_to_string(dict_value_get_borrow(v, i)); + if (dict_value_get_borrow(v, i) && dict_value_get_borrow(v, i)->type == VAL_STR) eigs_json_escape_string(&out, vs); else strbuf_append(&out, vs); @@ -4248,16 +4248,16 @@ static int gc_env_is_node(Env *e) { chunk_decref(_o); }, __VA_ARGS__) \ /* VAL_LIST / VAL_DICT: every element slot. */ \ X((_k == GC_KIND_VAL && _v->type == VAL_LIST), _v->data.list.count, \ - _v->data.list.items[_i], GC_KIND_VAL, \ - gc_value_is_node(_v->data.list.items[_i]), \ - { Value *_o = _v->data.list.items[_i]; \ - _v->data.list.items[_i] = NULL; \ + list_get_borrow(_v, _i), GC_KIND_VAL, \ + gc_value_is_node(list_get_borrow(_v, _i)), \ + { Value *_o = list_get_borrow(_v, _i); \ + list_set_owned(_v, _i, NULL); \ val_decref(_o); }, __VA_ARGS__) \ X((_k == GC_KIND_VAL && _v->type == VAL_DICT), _v->data.dict.count, \ - _v->data.dict.vals[_i], GC_KIND_VAL, \ - gc_value_is_node(_v->data.dict.vals[_i]), \ - { Value *_o = _v->data.dict.vals[_i]; \ - _v->data.dict.vals[_i] = NULL; \ + dict_value_get_borrow(_v, _i), GC_KIND_VAL, \ + gc_value_is_node(dict_value_get_borrow(_v, _i)), \ + { Value *_o = dict_value_get_borrow(_v, _i); \ + dict_value_set_owned(_v, _i, NULL); \ val_decref(_o); }, __VA_ARGS__) \ /* #1057 module namespace: the owning backref to the module Env taken \ * by eigs_module_ns_attach. Without this row a garbage \ diff --git a/src/eigenscript.h b/src/eigenscript.h index 8439215c..08498c8e 100644 --- a/src/eigenscript.h +++ b/src/eigenscript.h @@ -1899,6 +1899,7 @@ static inline void val_decref(Value *v) { } #include "value_slot.h" +#include "container.h" /* ---- Environment ---- */ diff --git a/src/eigs_embed.c b/src/eigs_embed.c index 11f59924..71167ad0 100644 --- a/src/eigs_embed.c +++ b/src/eigs_embed.c @@ -352,7 +352,7 @@ int eigs_value_list_len(EigsValue *v) { EigsValue *eigs_value_list_get(EigsValue *v, int i) { if (!v || v->type != VAL_LIST) return NULL; if (i < 0 || i >= v->data.list.count) return NULL; - Value *r = v->data.list.items[i]; + Value *r = list_get_borrow(v, i); if (r) val_incref(r); return r; } diff --git a/src/ext_db.c b/src/ext_db.c index 9c25f737..fe7b37a1 100644 --- a/src/ext_db.c +++ b/src/ext_db.c @@ -28,7 +28,7 @@ static int db_build_query(Value *arg, const char **sql, int *nparams, return 1; } if (!arg || arg->type != VAL_LIST || arg->data.list.count < 1 || - arg->data.list.items[0]->type != VAL_STR) { + list_get_borrow(arg, 0)->type != VAL_STR) { /* #888: the callers used to turn this into "[]"/"" — the same value a * successful empty query returns. A malformed call is a program bug * and must not look like a result. */ @@ -36,7 +36,7 @@ static int db_build_query(Value *arg, const char **sql, int *nparams, return 0; } - *sql = arg->data.list.items[0]->data.str; + *sql = list_get_borrow(arg, 0)->data.str; *nparams = arg->data.list.count - 1; /* #356: raise instead of silently dropping params 17+ (Postgres would * error confusingly at exec time — or worse, not at all) or coercing @@ -47,7 +47,7 @@ static int db_build_query(Value *arg, const char **sql, int *nparams, return 0; } for (int i = 0; i < *nparams; i++) { - Value *v = arg->data.list.items[i + 1]; + Value *v = list_get_borrow(arg, i + 1); types[i] = 0; if (v && v->type == VAL_BOOL) { params[i] = v->data.boolean ? "true" : "false"; diff --git a/src/ext_gfx.c b/src/ext_gfx.c index 8651045d..cace4520 100644 --- a/src/ext_gfx.c +++ b/src/ext_gfx.c @@ -404,7 +404,7 @@ static int gfx_nums(Value *arg, int from, int to) { int cnt = arg->data.list.count; if (to > cnt) to = cnt; for (int i = from; i < to; i++) { - Value *v = arg->data.list.items[i]; + Value *v = list_get_borrow(arg, i); if (!v || v->type != VAL_NUM) return 0; } return 1; @@ -418,7 +418,7 @@ static int gfx_nums(Value *arg, int from, int to) { static int gfx_list_all_num(Value *l) { if (!l || l->type != VAL_LIST) return 0; for (int i = 0; i < l->data.list.count; i++) { - Value *v = l->data.list.items[i]; + Value *v = list_get_borrow(l, i); if (!v || v->type != VAL_NUM) return 0; } return 1; @@ -454,12 +454,12 @@ Value* builtin_gfx_open(Value *arg) { * overlaps `double num` with `char *str`, so gfx_open of ["800","600",t] * reinterpreted a pointer as a double and int-cast it — in practice a * tiny denormal, so the window opened 0x0 and gfx_open answered 1. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM, "gfx_open", "[number width, number height, title]", make_bool(0)); int w = (int)eigs_list_num(arg, 0, __func__); int h = (int)eigs_list_num(arg, 1, __func__); - const char *title = arg->data.list.items[2]->type == VAL_STR ? arg->data.list.items[2]->data.str : "EigenScript"; + const char *title = list_get_borrow(arg, 2)->type == VAL_STR ? list_get_borrow(arg, 2)->data.str : "EigenScript"; if (!load_sdl2()) { fprintf(stderr, "gfx_open: cannot load libSDL2\n"); @@ -996,7 +996,7 @@ Value* builtin_gfx_text(Value *arg) { * to a parent build. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 6 || !gfx_nums(arg, 0, 2) - || arg->data.list.items[2]->type != VAL_STR + || list_get_borrow(arg, 2)->type != VAL_STR || !gfx_nums(arg, 3, 7), "gfx_text", "[number x, number y, string text, number r, number g, number b] and an optional number scale", @@ -1004,7 +1004,7 @@ Value* builtin_gfx_text(Value *arg) { if (!g_renderer) return make_null(); /* fs:VOID no window open: gfx_text answers null on every path -- the return value, not a rejected-argument stand-in */ int x = (int)eigs_list_num(arg, 0, __func__); int y = (int)eigs_list_num(arg, 1, __func__); - const char *text = arg->data.list.items[2]->data.str; + const char *text = list_get_borrow(arg, 2)->data.str; int r = (int)eigs_list_num(arg, 3, __func__); int g = (int)eigs_list_num(arg, 4, __func__); int b = (int)eigs_list_num(arg, 5, __func__); @@ -1071,10 +1071,10 @@ Value* builtin_gfx_text_width(Value *arg) { if (arg && arg->type == VAL_STR) { text = arg->data.str; } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1 - && arg->data.list.items[0]->type == VAL_STR) { - text = arg->data.list.items[0]->data.str; + && list_get_borrow(arg, 0)->type == VAL_STR) { + text = list_get_borrow(arg, 0)->data.str; if (arg->data.list.count >= 2) { - if (arg->data.list.items[1]->type == VAL_NUM) + if (list_get_borrow(arg, 1)->type == VAL_NUM) scale = (int)eigs_list_num(arg, 1, __func__); else bad_scale = 1; @@ -1110,12 +1110,12 @@ Value* builtin_gfx_text_height(Value *arg) { * raises under strict and does nothing otherwise. */ STRICT_REQUIRE(arg && arg->type != VAL_NULL && arg->type != VAL_NUM && !(arg->type == VAL_LIST && arg->data.list.count >= 1 - && arg->data.list.items[0]->type == VAL_NUM), + && list_get_borrow(arg, 0)->type == VAL_NUM), "gfx_text_height", "number scale, [number scale] or null"); if (arg && arg->type == VAL_NUM) { scale = (int)eigs_num_arg(arg, __func__); } else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1 - && arg->data.list.items[0]->type == VAL_NUM) { + && list_get_borrow(arg, 0)->type == VAL_NUM) { scale = (int)eigs_list_num(arg, 0, __func__); } if (scale < 1) scale = 1; @@ -1202,7 +1202,7 @@ static int16_t* audio_convert_samples(Value *samples, int *out_n) { if (n <= 0 || (double)n * sizeof(int16_t) > 64.0 * 1024.0 * 1024.0) return NULL; int16_t *buf = xmalloc_array(n, sizeof(int16_t)); for (int i = 0; i < n; i++) { - Value *v = samples->data.list.items[i]; + Value *v = list_get_borrow(samples, i); /* #1007: a non-number used to be COERCED to 0 here, so a wrong-typed * sample list produced a valid buffer of silence and the caller's * `if (!buf)` guard never saw it — audio_play answered with a real @@ -1313,8 +1313,8 @@ Value* builtin_audio_open(Value *arg) { * oversight rather than a convention is settled 180 lines down: * audio_stream_open guards this identical pair with `type == VAL_NUM` * before reading it. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM, "audio_open", "[number freq, number channels]", make_num(0)); } if (!g_sdl_lib) { if (!load_sdl2()) return make_num(0); } /* fs:ANSWER 0 is not a device id -- line 1095 returns the real one; libSDL2 unavailable is environment state, not an argument */ @@ -1456,8 +1456,8 @@ Value* builtin_audio_capture_open(Value *arg) { && !(arg->type == VAL_LIST && arg->data.list.count >= 2), "audio_capture_open", "[number freq, number channels] or null"); if (arg && arg->type == VAL_LIST && arg->data.list.count >= 2) { - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM, "audio_capture_open", "[number freq, number channels]", make_num(0)); } TRACE_NONDET_TAKE("audio_capture_open"); @@ -1585,8 +1585,8 @@ Value* builtin_audio_stream_open(Value *arg) { && !(arg->type == VAL_LIST && arg->data.list.count >= 2), "audio_stream_open", "[number freq, number channels] or null"); if (arg && arg->type == VAL_LIST && arg->data.list.count >= 2) { - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM, "audio_stream_open", "[number freq, number channels]", make_num(0)); } if (!g_sdl_lib) { if (!load_sdl2()) return make_num(0); } /* fs:ANSWER BUILTINS.md audio_stream_open: "0 when SDL/audio is unavailable"; libSDL2 absent is environment state */ @@ -1604,8 +1604,8 @@ Value* builtin_audio_stream_open(Value *arg) { want.callback = NULL; /* queue mode: feed via SDL_QueueAudio */ if (arg && arg->type == VAL_LIST && arg->data.list.count >= 2 - && arg->data.list.items[0]->type == VAL_NUM - && arg->data.list.items[1]->type == VAL_NUM) { + && list_get_borrow(arg, 0)->type == VAL_NUM + && list_get_borrow(arg, 1)->type == VAL_NUM) { want.freq = (int)eigs_list_num(arg, 0, __func__); want.channels = (int)eigs_list_num(arg, 1, __func__); } @@ -1697,15 +1697,15 @@ Value* builtin_audio_music_play(Value *arg) { STRICT_LIST_MAX(arg, 2, "audio_music_play"); /* #1007: both #971 deferral markers converted. Above load_sdl2(), the [135] rule. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 1 - || arg->data.list.items[0]->type != VAL_STR, + || list_get_borrow(arg, 0)->type != VAL_STR, "audio_music_play", "[string path, number loops]", make_bool(0)); /* A wrong-typed `loops` is the COERCION shape: it fell back to -1 * (forever), so a typo made the track loop rather than play once. */ STRICT_REQUIRE(arg->data.list.count >= 2 - && arg->data.list.items[1]->type != VAL_NUM, + && list_get_borrow(arg, 1)->type != VAL_NUM, "audio_music_play", "[string path, number loops]"); - const char *path = arg->data.list.items[0]->data.str; - int loops = (arg->data.list.count >= 2 && arg->data.list.items[1]->type == VAL_NUM) + const char *path = list_get_borrow(arg, 0)->data.str; + int loops = (arg->data.list.count >= 2 && list_get_borrow(arg, 1)->type == VAL_NUM) ? (int)eigs_list_num(arg, 1, __func__) : -1; if (!load_sdl2()) return make_bool(0); /* fs:ANSWER the header's documented "0 on failure (missing mixer lib ...)" -- libSDL2 absent is environment state, not an argument */ p_SDL_Init(MY_SDL_INIT_AUDIO); /* ensure the audio subsystem is up */ @@ -1743,13 +1743,13 @@ Value* builtin_audio_music_volume(Value *arg) { * MUTED the music. Above the mixer-state check, the [135] rule. */ STRICT_REQUIRE(!(arg && arg->type == VAL_NUM) && !(arg && arg->type == VAL_LIST && arg->data.list.count >= 1 - && arg->data.list.items[0]->type == VAL_NUM), + && list_get_borrow(arg, 0)->type == VAL_NUM), "audio_music_volume", "number volume 0..128 or [number volume]"); if (!g_mixer_open || !p_Mix_VolumeMusic) return make_null(); /* fs:VOID no mixer open: audio_music_volume answers null on every path -- the return value, not a rejected-argument stand-in */ int v = 0; if (arg && arg->type == VAL_NUM) v = (int)eigs_num_arg(arg, __func__); else if (arg && arg->type == VAL_LIST && arg->data.list.count >= 1 - && arg->data.list.items[0]->type == VAL_NUM) + && list_get_borrow(arg, 0)->type == VAL_NUM) v = (int)eigs_list_num(arg, 0, __func__); if (v < 0) v = 0; if (v > MY_MIX_MAX_VOLUME) v = MY_MIX_MAX_VOLUME; @@ -1796,8 +1796,8 @@ Value* builtin_audio_play_loop(Value *arg) { * soft, and it moves BELOW the guard so the guard is reachable on a * machine with no audio device (the [135] rule). */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2 - || !arg->data.list.items[1] - || arg->data.list.items[1]->type != VAL_NUM, + || !list_get_borrow(arg, 1) + || list_get_borrow(arg, 1)->type != VAL_NUM, "audio_play_loop", "[samples, number loops]", make_num(0)); /* #152: NaN/huge casts are UB; -1 is the one negative with meaning. */ double loops_d = eigs_list_num(arg, 1, __func__); @@ -1814,12 +1814,12 @@ Value* builtin_audio_play_loop(Value *arg) { * device check for the same reachability reason. */ /* #1637: a non-number sample element is refused here too, above the * device check (a bool in every strict mode, through STRICT_REQUIRE). */ - STRICT_REQUIRE(gfx_bad_samples(arg->data.list.items[0]) - || (arg->data.list.items[0]->type == VAL_LIST - && !gfx_list_all_num(arg->data.list.items[0])), + STRICT_REQUIRE(gfx_bad_samples(list_get_borrow(arg, 0)) + || (list_get_borrow(arg, 0)->type == VAL_LIST + && !gfx_list_all_num(list_get_borrow(arg, 0))), "audio_play_loop", "[list or buffer of samples, number loops]"); if (!g_audio_device) return make_num(0); /* fs:ANSWER BUILTINS.md audio_play_loop: "0 on ... closed device"; channel ids are slot+1 >= 1, so 0 is not a channel */ - Value *samples = arg->data.list.items[0]; + Value *samples = list_get_borrow(arg, 0); int n = 0; int16_t *buf = audio_convert_samples(samples, &n); if (!buf) { @@ -1837,12 +1837,12 @@ Value* builtin_audio_volume(Value *arg) { STRICT_LIST_MAX(arg, 2, "audio_volume"); /* #1007: the mixed condition split, as its #971 deferral marker asked. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2 - || !arg->data.list.items[0] || arg->data.list.items[0]->type != VAL_NUM - || !arg->data.list.items[1] || arg->data.list.items[1]->type != VAL_NUM, + || !list_get_borrow(arg, 0) || list_get_borrow(arg, 0)->type != VAL_NUM + || !list_get_borrow(arg, 1) || list_get_borrow(arg, 1)->type != VAL_NUM, "audio_volume", "[number channel, number volume]", make_num(0)); if (!g_audio_device) return make_num(0); /* fs:ANSWER 0 means "that channel is not playing", and with no device open no channel is */ - Value *ch_v = arg->data.list.items[0]; - Value *vol_v = arg->data.list.items[1]; + Value *ch_v = list_get_borrow(arg, 0); + Value *vol_v = list_get_borrow(arg, 1); int c = (int)eigs_num_arg(ch_v, "audio_volume") - 1; if (c < 0 || c >= AUDIO_MAX_CHANNELS) return make_num(0); /* fs:ANSWER 0 means "that channel is not playing" -- the same value line 1452 returns for an inactive in-range channel; an out-of-range id is definitionally inactive */ double vol = eigs_num_arg(vol_v, "audio_volume"); @@ -1921,9 +1921,9 @@ Value* builtin_audio_sine(Value *arg) { * Seven builtins share the shape; the list was derived by running each * candidate twice and diffing (a per-run difference IS the disclosure), * not by reading names — audio_gain was missed by the reading. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM, "audio_sine", "[number freq, number duration, number amplitude]", make_list(0)); double freq = eigs_list_num(arg, 0, __func__); double dur = eigs_list_num(arg, 1, __func__); @@ -1956,9 +1956,9 @@ Value* builtin_audio_saw(Value *arg) { * pointer value, disclosed into script-visible data and varying per run. * All six generators share the shape; guarding one would have left the * class open. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM, "audio_saw", "[number freq, number duration, number amplitude]", make_list(0)); double freq = eigs_list_num(arg, 0, __func__); double dur = eigs_list_num(arg, 1, __func__); @@ -1991,9 +1991,9 @@ Value* builtin_audio_square(Value *arg) { * pointer value, disclosed into script-visible data and varying per run. * All six generators share the shape; guarding one would have left the * class open. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM, "audio_square", "[number freq, number duration, number amplitude]", make_list(0)); double freq = eigs_list_num(arg, 0, __func__); double dur = eigs_list_num(arg, 1, __func__); @@ -2027,11 +2027,11 @@ Value* builtin_audio_sweep(Value *arg) { * pointer value, disclosed into script-visible data and varying per run. * All six generators share the shape; guarding one would have left the * class open. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM || - arg->data.list.items[3]->type != VAL_NUM || - arg->data.list.items[4]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM || + list_get_borrow(arg, 3)->type != VAL_NUM || + list_get_borrow(arg, 4)->type != VAL_NUM, "audio_sweep", "[number freq_start, number freq_end, number duration, number amplitude, number waveform]", make_list(0)); double f0 = eigs_list_num(arg, 0, __func__); double f1 = eigs_list_num(arg, 1, __func__); @@ -2074,8 +2074,8 @@ Value* builtin_audio_noise(Value *arg) { * pointer value, disclosed into script-visible data and varying per run. * All six generators share the shape; guarding one would have left the * class open. */ - ARG_GUARD(arg->data.list.items[0]->type != VAL_NUM || - arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 0)->type != VAL_NUM || + list_get_borrow(arg, 1)->type != VAL_NUM, "audio_noise", "[number duration, number amplitude]", make_list(0)); double dur = eigs_list_num(arg, 0, __func__); double amp = eigs_list_num(arg, 1, __func__); @@ -2097,8 +2097,8 @@ Value* builtin_audio_mix(Value *arg) { /* #1007 round 2, the arity/shape half — see builtin_audio_sine. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "audio_mix", "[list samples_a, list samples_b]", make_list(0)); - Value *a = arg->data.list.items[0]; - Value *b = arg->data.list.items[1]; + Value *a = list_get_borrow(arg, 0); + Value *b = list_get_borrow(arg, 1); ARG_GUARD(a->type != VAL_LIST || b->type != VAL_LIST, "audio_mix", "[list samples_a, list samples_b]", make_list(0)); /* #1007, COERCION shape: a non-number ELEMENT was substituted with 0.0, @@ -2112,8 +2112,8 @@ Value* builtin_audio_mix(Value *arg) { Value *out = make_list(n); for (int i = 0; i < n; i++) { - double sa = (i < a->data.list.count && a->data.list.items[i]->type == VAL_NUM) ? eigs_num_arg(a->data.list.items[i], __func__) : 0; - double sb = (i < b->data.list.count && b->data.list.items[i]->type == VAL_NUM) ? eigs_num_arg(b->data.list.items[i], __func__) : 0; + double sa = (i < a->data.list.count && list_get_borrow(a, i)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(a, i), __func__) : 0; + double sb = (i < b->data.list.count && list_get_borrow(b, i)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(b, i), __func__) : 0; double mixed = sa + sb; if (mixed > 1.0) mixed = 1.0; if (mixed < -1.0) mixed = -1.0; @@ -2128,7 +2128,7 @@ Value* builtin_audio_gain(Value *arg) { /* #1007 round 2, the arity/shape half — see builtin_audio_sine. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "audio_gain", "[list samples, number volume]", make_list(0)); - Value *samples = arg->data.list.items[0]; + Value *samples = list_get_borrow(arg, 0); ARG_GUARD(samples->type != VAL_LIST, "audio_gain", "[list samples, number volume]", make_list(0)); /* #1007, COERCION shape: a non-number ELEMENT was substituted with 0.0. @@ -2143,14 +2143,14 @@ Value* builtin_audio_gain(Value *arg) { * `char *` into script-visible data exactly as audio_sweep did. Note the * element read on the next line was ALREADY type-checked — the argument * beside it was not, which is the same next-line asymmetry as gfx_open. */ - ARG_GUARD(arg->data.list.items[1]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 1)->type != VAL_NUM, "audio_gain", "[samples, number volume]", make_list(0)); double vol = eigs_list_num(arg, 1, __func__); int n = samples->data.list.count; Value *out = make_list(n); for (int i = 0; i < n; i++) { - double s = (samples->data.list.items[i]->type == VAL_NUM) ? eigs_num_arg(samples->data.list.items[i], __func__) : 0; + double s = (list_get_borrow(samples, i)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(samples, i), __func__) : 0; s *= vol; if (s > 1.0) s = 1.0; if (s < -1.0) s = -1.0; @@ -2174,12 +2174,12 @@ Value* builtin_audio_envelope(Value *arg) { * pointer value, disclosed into script-visible data and varying per run. * All six generators share the shape; guarding one would have left the * class open. */ - ARG_GUARD(arg->data.list.items[1]->type != VAL_NUM || - arg->data.list.items[2]->type != VAL_NUM || - arg->data.list.items[3]->type != VAL_NUM || - arg->data.list.items[4]->type != VAL_NUM, + ARG_GUARD(list_get_borrow(arg, 1)->type != VAL_NUM || + list_get_borrow(arg, 2)->type != VAL_NUM || + list_get_borrow(arg, 3)->type != VAL_NUM || + list_get_borrow(arg, 4)->type != VAL_NUM, "audio_envelope", "[samples, number attack, number decay, number sustain, number release]", make_list(0)); - Value *samples = arg->data.list.items[0]; + Value *samples = list_get_borrow(arg, 0); ARG_GUARD(samples->type != VAL_LIST, "audio_envelope", "[list samples, number attack, number decay, number sustain, number release]", make_list(0)); @@ -2220,7 +2220,7 @@ Value* builtin_audio_envelope(Value *arg) { double frac = (r_samples > 0) ? (double)(i - r_start) / r_samples : 1.0; env = sustain * (1.0 - frac); } - double s = (samples->data.list.items[i]->type == VAL_NUM) ? eigs_num_arg(samples->data.list.items[i], __func__) : 0; + double s = (list_get_borrow(samples, i)->type == VAL_NUM) ? eigs_num_arg(list_get_borrow(samples, i), __func__) : 0; s *= env; list_append_owned(out, make_num(s)); } @@ -2237,13 +2237,13 @@ Value* builtin_gfx_fb(Value *arg) { * five geometry elements were read unchecked while the buffer beside * them was type-checked one line down. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 6 - || !arg->data.list.items[0] - || arg->data.list.items[0]->type != VAL_BUFFER + || !list_get_borrow(arg, 0) + || list_get_borrow(arg, 0)->type != VAL_BUFFER || !gfx_nums(arg, 1, 6), "gfx_fb", "[buffer fb, number w, number h, number x, number y, number scale]", make_null()); - Value *buf = arg->data.list.items[0]; + Value *buf = list_get_borrow(arg, 0); int w = (int)eigs_list_num(arg, 1, __func__); int h = (int)eigs_list_num(arg, 2, __func__); int dx = (int)eigs_list_num(arg, 3, __func__); @@ -2311,8 +2311,8 @@ Value* builtin_ppu_render_frame(Value *arg) { * no diagnostic anywhere. */ ARG_GUARD(!arg || arg->type != VAL_LIST || arg->data.list.count < 2, "ppu_render_frame", "[buffer mem, buffer fb]", make_null()); - Value *mem_v = arg->data.list.items[0]; - Value *fb_v = arg->data.list.items[1]; + Value *mem_v = list_get_borrow(arg, 0); + Value *fb_v = list_get_borrow(arg, 1); ARG_GUARD(!mem_v || mem_v->type != VAL_BUFFER || mem_v->data.buffer.count < 65536, "ppu_render_frame", "[buffer mem of at least 65536, buffer fb]", diff --git a/src/ext_http.c b/src/ext_http.c index e866e0e5..dc4d457a 100644 --- a/src/ext_http.c +++ b/src/ext_http.c @@ -409,25 +409,25 @@ static Value *http_route_register(Value *arg, int requires_auth) { * time is also the right moment: the error lands on the line the author * wrote, before the socket is listening, instead of on a client request. */ for (int i = 0; i < 2; i++) { - if (route_slot_is_callable(arg->data.list.items[i])) { + if (route_slot_is_callable(list_get_borrow(arg, i))) { rt_error(EK_TYPE, 0, "http_route: %s must be a string, not a function", i == 0 ? "method" : "path"); return make_null(); } } if (arg->data.list.count >= 4) { - if (route_slot_is_callable(arg->data.list.items[2])) { + if (route_slot_is_callable(list_get_borrow(arg, 2))) { rt_error(EK_TYPE, 0, "http_route: kind must be a string, not a function " "(expected \"code\" or \"static\")"); return make_null(); } - if (route_slot_is_callable(arg->data.list.items[3])) { + if (route_slot_is_callable(list_get_borrow(arg, 3))) { rt_error(EK_TYPE, 0, "http_route: the code form's source must be a string of " "EigenScript source, not a function — " "http_route of [method, path, \"code\", \"return 42\"]"); return make_null(); } - } else if (route_slot_is_callable(arg->data.list.items[2])) { + } else if (route_slot_is_callable(list_get_borrow(arg, 2))) { rt_error(EK_TYPE, 0, "http_route: body must be a value, not a function — pass a " "literal body (\"pong\"), or use the code form: " "http_route of [method, path, \"code\", \"\"]"); @@ -435,18 +435,18 @@ static Value *http_route_register(Value *arg, int requires_auth) { } Route *r = &g_server.routes[g_server.route_count]; - char *method_s = value_to_string(arg->data.list.items[0]); - char *path_s = value_to_string(arg->data.list.items[1]); + char *method_s = value_to_string(list_get_borrow(arg, 0)); + char *path_s = value_to_string(list_get_borrow(arg, 1)); r->method = method_s; r->path = path_s; if (arg->data.list.count >= 4) { - char *kind_s = value_to_string(arg->data.list.items[2]); - char *payload_s = value_to_string(arg->data.list.items[3]); + char *kind_s = value_to_string(list_get_borrow(arg, 2)); + char *payload_s = value_to_string(list_get_borrow(arg, 3)); r->kind = kind_s; r->payload = payload_s; } else { - Value *handler = arg->data.list.items[2]; + Value *handler = list_get_borrow(arg, 2); if (handler->type == VAL_STR) { r->kind = xstrdup("static"); r->payload = xstrdup(handler->data.str); @@ -478,8 +478,8 @@ Value* builtin_http_static(Value *arg) { STRICT_LIST_MAX(arg, 2, "http_static"); if (http_config_frozen("http_static")) return make_null(); if (arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - char *prefix = value_to_string(arg->data.list.items[0]); - char *dir = value_to_string(arg->data.list.items[1]); + char *prefix = value_to_string(list_get_borrow(arg, 0)); + char *dir = value_to_string(list_get_borrow(arg, 1)); g_server.static_prefix = prefix; g_server.static_dir = dir; return make_str("static registered"); @@ -518,7 +518,7 @@ static Value *response_header_error(ErrKind kind, const char *rule) { Value* builtin_http_response_header(Value *arg) { if (!arg || arg->type != VAL_LIST || arg->data.list.count != 2) return response_header_error(EK_TYPE, "requires [name, value] (exactly two strings)"); - Value *name = arg->data.list.items[0], *value = arg->data.list.items[1]; + Value *name = list_get_borrow(arg, 0), *value = list_get_borrow(arg, 1); if (name->type != VAL_STR || value->type != VAL_STR) return response_header_error(EK_TYPE, "name and value must be strings"); size_t nlen = val_str_len(name), vlen = val_str_len(value); @@ -563,11 +563,11 @@ Value* builtin_http_response_header(Value *arg) { Value* builtin_http_early_bind(Value *arg) { const char *live_path = NULL; if (arg && arg->type == VAL_LIST) { - if (arg->data.list.count != 2 || arg->data.list.items[1]->type != VAL_STR) { + if (arg->data.list.count != 2 || list_get_borrow(arg, 1)->type != VAL_STR) { rt_error(EK_TYPE, 0, "http_early_bind requires [port, absolute liveness path string]"); return make_null(); } - live_path = arg->data.list.items[1]->data.str; + live_path = list_get_borrow(arg, 1)->data.str; if (live_path[0] != '/') { rt_error(EK_VALUE, 0, "http_early_bind: liveness path must be absolute (start with /; no whitespace/CR/LF/NUL)"); return make_null(); @@ -578,7 +578,7 @@ Value* builtin_http_early_bind(Value *arg) { return make_null(); } } - arg = arg->data.list.items[0]; + arg = list_get_borrow(arg, 0); } if (arg && arg->type != VAL_NULL && arg->type != VAL_NUM) { rt_error(EK_TYPE, 0, "http_early_bind: port must be a number or null"); @@ -711,9 +711,9 @@ Value* builtin_http_post(Value *arg) { if (!arg || arg->type != VAL_LIST || arg->data.list.count < 3) TRACE_NONDET_RECORD("http_post", make_str("")); const char *url = "", *headers_json = "", *body = ""; - if (arg->data.list.items[0]->type == VAL_STR) url = arg->data.list.items[0]->data.str; - if (arg->data.list.items[1]->type == VAL_STR) headers_json = arg->data.list.items[1]->data.str; - if (arg->data.list.items[2]->type == VAL_STR) body = arg->data.list.items[2]->data.str; + if (list_get_borrow(arg, 0)->type == VAL_STR) url = list_get_borrow(arg, 0)->data.str; + if (list_get_borrow(arg, 1)->type == VAL_STR) headers_json = list_get_borrow(arg, 1)->data.str; + if (list_get_borrow(arg, 2)->type == VAL_STR) body = list_get_borrow(arg, 2)->data.str; if (!http_url_is_allowed(url)) TRACE_NONDET_RECORD("http_post", make_str("")); /* Write body to temp file */ @@ -754,7 +754,7 @@ Value* builtin_http_post(Value *arg) { if (hdr_obj && hdr_obj->type == VAL_DICT) { for (int i = 0; i < hdr_obj->data.dict.count && hdr_count < 32 && argc < 90; i++) { char *hk = xstrdup(hdr_obj->data.dict.keys[i]); - char *hv = value_to_string(hdr_obj->data.dict.vals[i]); + char *hv = value_to_string(dict_value_get_borrow(hdr_obj, i)); http_strip_crlf(hk); http_strip_crlf(hv); snprintf(header_bufs[hdr_count], sizeof(header_bufs[0]), "%s: %s", hk, hv); @@ -765,8 +765,8 @@ Value* builtin_http_post(Value *arg) { } } else if (hdr_obj && hdr_obj->type == VAL_LIST) { for (int i = 0; i + 1 < hdr_obj->data.list.count && hdr_count < 32 && argc < 90; i += 2) { - char *hk = value_to_string(hdr_obj->data.list.items[i]); - char *hv = value_to_string(hdr_obj->data.list.items[i + 1]); + char *hk = value_to_string(list_get_borrow(hdr_obj, i)); + char *hv = value_to_string(list_get_borrow(hdr_obj, i + 1)); /* Both halves reach curl's -H verbatim, so a CR/LF in either injects * extra headers into the outbound request. Script-controlled and so * not a vulnerability on its own under SECURITY.md's threat model, @@ -890,8 +890,8 @@ static int shared_find(Server *s, const char *key) { Value* builtin_shared_set(Value *arg) { STRICT_LIST_MAX(arg, 2, "shared_set"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *key_v = arg->data.list.items[0]; - Value *val = arg->data.list.items[1]; + Value *key_v = list_get_borrow(arg, 0); + Value *val = list_get_borrow(arg, 1); if (key_v->type != VAL_STR) return make_null(); Server *s = eigs_http_active; if (!s) return make_null(); @@ -942,8 +942,8 @@ Value* builtin_shared_set(Value *arg) { Value* builtin_shared_incr(Value *arg) { STRICT_LIST_MAX(arg, 2, "shared_incr"); if (!arg || arg->type != VAL_LIST || arg->data.list.count < 2) return make_null(); - Value *key_v = arg->data.list.items[0]; - Value *delta_v = arg->data.list.items[1]; + Value *key_v = list_get_borrow(arg, 0); + Value *delta_v = list_get_borrow(arg, 1); if (key_v->type != VAL_STR || delta_v->type != VAL_NUM) return make_null(); Server *s = eigs_http_active; if (!s) return make_null(); diff --git a/src/ext_net.c b/src/ext_net.c index 82a8b7e7..5a5f3e58 100644 --- a/src/ext_net.c +++ b/src/ext_net.c @@ -142,7 +142,7 @@ static int net_require_list(Value *arg, int min, int max, const char *fn) { static int net_num_at(Value *arg, int idx, int fallback) { if (!arg || arg->type != VAL_LIST || idx >= arg->data.list.count) return fallback; - Value *v = arg->data.list.items[idx]; + Value *v = list_get_borrow(arg, idx); /* #1637: null keeps the fallback, a number is read, anything else (a * bool) raises; callers check g_has_error after their reads. */ double d = 0; @@ -210,7 +210,7 @@ Value* builtin_net_accept(Value *arg) { if (!net_require_list(arg, 2, 2, "net_accept")) return make_null(); timeout_ms = net_num_at(arg, 1, -1); if (g_has_error) return make_null(); /* #1637: a bool timeout raised */ - arg = arg->data.list.items[0]; + arg = list_get_borrow(arg, 0); } if (!arg || arg->type != VAL_NUM) { rt_error(EK_TYPE, 0, "net_accept: expected a listener handle"); @@ -236,7 +236,7 @@ Value* builtin_net_accept(Value *arg) { * the socket is restored to blocking afterward (recv/send poll first). */ Value* builtin_net_dial(Value *arg) { if (!net_require_list(arg, 2, 3, "net_dial")) return make_null(); - Value *host = arg->data.list.items[0]; + Value *host = list_get_borrow(arg, 0); if (!host || host->type != VAL_STR) { rt_error(EK_TYPE, 0, "net_dial: expected [host, port] with a string host"); return make_null(); @@ -290,7 +290,7 @@ Value* builtin_net_dial(Value *arg) { * Convert text with str_from_bytes. */ Value* builtin_net_recv(Value *arg) { if (!net_require_list(arg, 2, 3, "net_recv")) return make_null(); - Value *conn = arg->data.list.items[0]; + Value *conn = list_get_borrow(arg, 0); int max = net_num_at(arg, 1, 0); int timeout_ms = net_num_at(arg, 2, -1); if (g_has_error) return make_null(); /* #1637 */ @@ -337,8 +337,8 @@ static Value* net_send_bytes(Value *conn, const unsigned char *bytes, size_t len Value* builtin_net_send(Value *arg) { if (!net_require_list(arg, 2, 2, "net_send")) return make_null(); - Value *conn = arg->data.list.items[0]; - Value *data = arg->data.list.items[1]; + Value *conn = list_get_borrow(arg, 0); + Value *data = list_get_borrow(arg, 1); if (!data || (data->type != VAL_STR && data->type != VAL_BUFFER && data->type != VAL_LIST)) { rt_error(EK_TYPE, 0, "net_send: data must be a string, buffer, or byte list"); @@ -359,7 +359,7 @@ Value* builtin_net_send(Value *arg) { for (int i = 0; i < n; i++) { double dv = data->type == VAL_BUFFER ? buffer_read_num(data, i) - : eigs_elem_num(data->data.list.items[i], "net_send"); /* #1637 round 4 */ + : eigs_elem_num(list_get_borrow(data, i), "net_send"); /* #1637 round 4 */ if (g_has_error) { free(owned); return make_null(); } owned[i] = finite_num_to_byte(dv); } diff --git a/src/ext_store.c b/src/ext_store.c index 726b01ee..c09fb2f3 100644 --- a/src/ext_store.c +++ b/src/ext_store.c @@ -185,7 +185,7 @@ static void store_json_encode(Value *v, strbuf *out) { strbuf_append_char(out, '['); for (int i = 0; i < v->data.list.count; i++) { if (i > 0) strbuf_append_char(out, ','); - store_json_encode(v->data.list.items[i], out); + store_json_encode(list_get_borrow(v, i), out); } strbuf_append_char(out, ']'); break; @@ -201,7 +201,7 @@ static void store_json_encode(Value *v, strbuf *out) { first = 0; eigs_json_escape_string(out, key); strbuf_append_char(out, ':'); - store_json_encode(v->data.dict.vals[i], out); + store_json_encode(dict_value_get_borrow(v, i), out); } /* Escape the complete buffer-tag namespace. Repeating the shape * member preserves its value after dict insertion while leaving a @@ -330,8 +330,8 @@ static Value* store_buffer_from_tag(Value *dict) { if (!body || body->type != VAL_LIST) return NULL; if (!shape || shape->type != VAL_LIST || shape->data.list.count != 2) return NULL; - Value *rv = shape->data.list.items[0]; - Value *cv = shape->data.list.items[1]; + Value *rv = list_get_borrow(shape, 0); + Value *cv = list_get_borrow(shape, 1); if (!rv || rv->type != VAL_NUM || !cv || cv->type != VAL_NUM) return NULL; /* Range-gate BEFORE narrowing. A stored shape is whatever double the file @@ -360,7 +360,7 @@ static Value* store_buffer_from_tag(Value *dict) { if (count != 0) return NULL; } for (int i = 0; i < count; i++) { - Value *e = body->data.list.items[i]; + Value *e = list_get_borrow(body, i); if (!e) return NULL; if (e->type == VAL_NUM) continue; if (e->type == VAL_STR && store_nonfinite_sentinel(e->data.str, NULL)) continue; @@ -375,7 +375,7 @@ static Value* store_buffer_from_tag(Value *dict) { buf->data.buffer.cols = cols; buf->data.buffer.data = xcalloc(count > 0 ? (size_t)count : 1, sizeof(double)); for (int i = 0; i < count; i++) { - Value *e = body->data.list.items[i]; + Value *e = list_get_borrow(body, i); double d = 0; if (e->type == VAL_NUM) d = eigs_num_arg(e, __func__); else store_nonfinite_sentinel(e->data.str, &d); @@ -705,7 +705,7 @@ static int store_load_catalog(Store *store) { for (int i = 0; i < store->catalog->data.dict.count; i++) { const char *name = store->catalog->data.dict.keys[i]; - Value *col = store->catalog->data.dict.vals[i]; + Value *col = dict_value_get_borrow(store->catalog, i); /* Strict schema: must be dict with numeric "root" and "next_id" */ if (!col || col->type != VAL_DICT) { @@ -975,11 +975,11 @@ static Value* builtin_store_put(Value *arg) { rt_error(EK_TYPE, 0, "store_put requires [handle, collection, record]\n"); return make_null(); } - Store *store = store_arg(arg->data.list.items[0], "store_put"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_put"); if (!store) return make_null(); if (store_replay_blocks("store_put")) return make_null(); - Value *col_val = arg->data.list.items[1]; - Value *record = arg->data.list.items[2]; + Value *col_val = list_get_borrow(arg, 1); + Value *record = list_get_borrow(arg, 2); if (!col_val || col_val->type != VAL_STR) { rt_error(EK_VALUE, 0, "store_put: invalid handle or collection\n"); return make_null(); @@ -1140,11 +1140,11 @@ static Value* builtin_store_get(Value *arg) { rt_error(EK_TYPE, 0, "store_get requires [handle, collection, key]\n"); return make_null(); } - Store *store = store_arg(arg->data.list.items[0], "store_get"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_get"); if (!store) return make_null(); if (store_replay_blocks("store_get")) return make_null(); - Value *col_val = arg->data.list.items[1]; - Value *key_val = arg->data.list.items[2]; + Value *col_val = list_get_borrow(arg, 1); + Value *key_val = list_get_borrow(arg, 2); if (!col_val || col_val->type != VAL_STR) return make_null(); const char *collection = col_val->data.str; @@ -1298,7 +1298,7 @@ static Value* builtin_store_delete(Value *arg) { * CV2-68); this return is the post-raise placeholder, not the answer. */ return make_bool(0); } - Store *store = store_arg(arg->data.list.items[0], "store_delete"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_delete"); /* fs:CHANNEL store_arg is resolve-OR-RAISE (#1146): it has already * rt_error'd — "invalid store", "stale store handle", "already been * closed" — by the time it returns NULL, and rt_error LATCHES rather @@ -1306,8 +1306,8 @@ static Value* builtin_store_delete(Value *arg) { * "deleted nothing". Same shape as the arity guard above. */ if (!store) return make_bool(0); if (store_replay_blocks("store_delete")) return make_bool(0); - Value *col_val = arg->data.list.items[1]; - Value *key_val = arg->data.list.items[2]; + Value *col_val = list_get_borrow(arg, 1); + Value *key_val = list_get_borrow(arg, 2); ARG_GUARD(!col_val || col_val->type != VAL_STR, "store_delete", "[store handle, string collection, key]", make_bool(0)); @@ -1352,10 +1352,10 @@ static Value* builtin_store_query(Value *arg) { rt_error(EK_TYPE, 0, "store_query requires [handle, collection]\n"); return make_list(0); } - Store *store = store_arg(arg->data.list.items[0], "store_query"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_query"); if (!store) return make_list(0); if (store_replay_blocks("store_query")) return make_list(0); - Value *col_val = arg->data.list.items[1]; + Value *col_val = list_get_borrow(arg, 1); if (!col_val || col_val->type != VAL_STR) return make_list(0); const char *collection = col_val->data.str; @@ -1400,7 +1400,7 @@ static Value* builtin_store_count(Value *arg) { * CV2-71); this return is the post-raise placeholder, not a count. */ return make_num(0); } - Store *store = store_arg(arg->data.list.items[0], "store_count"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_count"); /* fs:CHANNEL store_arg is resolve-OR-RAISE (#1146): it has already * rt_error'd by the time it returns NULL, and rt_error latches rather * than unwinding, so this return is the post-raise placeholder, not a @@ -1408,7 +1408,7 @@ static Value* builtin_store_count(Value *arg) { * are store_count's real result for an unknown collection. */ if (!store) return make_num(0); if (store_replay_blocks("store_count")) return make_num(0); - Value *col_val = arg->data.list.items[1]; + Value *col_val = list_get_borrow(arg, 1); ARG_GUARD(!col_val || col_val->type != VAL_STR, "store_count", "[store handle, string collection]", make_num(0)); @@ -1451,10 +1451,10 @@ static Value* builtin_store_update(Value *arg) { * is the post-raise placeholder, not "updated nothing". */ return make_bool(0); } - Value *handle = arg->data.list.items[0]; - Value *col_val = arg->data.list.items[1]; - Value *key_val = arg->data.list.items[2]; - Value *record = arg->data.list.items[3]; + Value *handle = list_get_borrow(arg, 0); + Value *col_val = list_get_borrow(arg, 1); + Value *key_val = list_get_borrow(arg, 2); + Value *record = list_get_borrow(arg, 3); Store *store = store_arg(handle, "store_update"); /* fs:CHANNEL store_arg is resolve-OR-RAISE (#1146): it has already @@ -1593,7 +1593,7 @@ static Value* builtin_store_drop(Value *arg) { * CV2-74); this return is the post-raise placeholder, not "not dropped". */ return make_bool(0); } - Store *store = store_arg(arg->data.list.items[0], "store_drop"); + Store *store = store_arg(list_get_borrow(arg, 0), "store_drop"); /* fs:CHANNEL store_arg is resolve-OR-RAISE (#1146): it has already * rt_error'd by the time it returns NULL, and rt_error latches rather * than unwinding, so this return is the post-raise placeholder, not @@ -1601,7 +1601,7 @@ static Value* builtin_store_drop(Value *arg) { * below). */ if (!store) return make_bool(0); if (store_replay_blocks("store_drop")) return make_bool(0); - Value *col_val = arg->data.list.items[1]; + Value *col_val = list_get_borrow(arg, 1); ARG_GUARD(!col_val || col_val->type != VAL_STR, "store_drop", "[store handle, string collection]", make_bool(0)); @@ -1635,7 +1635,7 @@ static Value* builtin_store_drop(Value *arg) { for (int i = 0; i < store->catalog->data.dict.count; i++) { if (strcmp(store->catalog->data.dict.keys[i], collection) != 0) { dict_set(new_catalog, store->catalog->data.dict.keys[i], - store->catalog->data.dict.vals[i]); + dict_value_get_borrow(store->catalog, i)); } } val_decref(store->catalog); /* drop the old catalog before replacing */ diff --git a/src/lint_host.c b/src/lint_host.c index 1c611af3..fd99c513 100644 --- a/src/lint_host.c +++ b/src/lint_host.c @@ -1259,7 +1259,7 @@ static Value *eigs_json_lint_allow_for(const char *path) { static int eigs_json_allows(Value *codes, const char *code) { if (!codes || codes->type != VAL_LIST) return 0; for (int i = 0; i < codes->data.list.count; i++) { - Value *c = codes->data.list.items[i]; + Value *c = list_get_borrow(codes, i); if (c && c->type == VAL_STR && (strcmp(c->data.str, code) == 0 || strcmp(c->data.str, "all") == 0)) return 1; diff --git a/src/model_infer.c b/src/model_infer.c index b25aa189..3a37d662 100644 --- a/src/model_infer.c +++ b/src/model_infer.c @@ -555,7 +555,7 @@ Value* builtin_eigen_generate(Value *arg) { fprintf(stderr, "eigen_generate: requires [prompt_ids, temperature, max_tokens]\n"); TRACE_NONDET_RECORD("eigen_generate", make_list(0)); } - Value *prompt_list = arg->data.list.items[0]; + Value *prompt_list = list_get_borrow(arg, 0); if (prompt_list->type != VAL_LIST) { fprintf(stderr, "eigen_generate: prompt_ids must be a list\n"); TRACE_NONDET_RECORD("eigen_generate", make_list(0)); @@ -569,9 +569,9 @@ Value* builtin_eigen_generate(Value *arg) { double top_p = 0.0; /* #1637: optional numbers -- null keeps the default, a bool raises. */ double mt = max_tokens; - eigs_opt_num(arg->data.list.items[1], &temperature, __func__); - eigs_opt_num(arg->data.list.items[2], &mt, __func__); - if (arg->data.list.count >= 4) eigs_opt_num(arg->data.list.items[3], &top_p, __func__); + eigs_opt_num(list_get_borrow(arg, 1), &temperature, __func__); + eigs_opt_num(list_get_borrow(arg, 2), &mt, __func__); + if (arg->data.list.count >= 4) eigs_opt_num(list_get_borrow(arg, 3), &top_p, __func__); if (g_has_error) return make_null(); max_tokens = (int)mt; @@ -607,7 +607,7 @@ Value* builtin_eigen_generate(Value *arg) { int *prompt_ids = xcalloc(prompt_len, sizeof(int)); for (int i = 0; i < prompt_len; i++) { - Value *v = prompt_list->data.list.items[i]; + Value *v = list_get_borrow(prompt_list, i); prompt_ids[i] = (int)eigs_num_arg(v, "eigen_generate"); /* #1637: a bool raises */ } if (g_has_error) { free(prompt_ids); return make_null(); } @@ -651,12 +651,12 @@ Value* builtin_eigen_eval_loss(Value *arg) { * a real score. */ ARG_GUARD(1, "eigen_eval_loss", "[prompt_ids, target_id]", make_num(-1.0)); } - Value *prompt_list = arg->data.list.items[0]; + Value *prompt_list = list_get_borrow(arg, 0); if (prompt_list->type != VAL_LIST) { fprintf(stderr, "eigen_eval_loss: prompt_ids must be a list\n"); ARG_GUARD(1, "eigen_eval_loss", "a list of prompt ids", make_num(-1.0)); } - if (arg->data.list.items[1]->type != VAL_NUM) { + if (list_get_borrow(arg, 1)->type != VAL_NUM) { fprintf(stderr, "eigen_eval_loss: target_id must be a number\n"); ARG_GUARD(1, "eigen_eval_loss", "a numeric target_id", make_num(-1.0)); } @@ -692,7 +692,7 @@ Value* builtin_eigen_eval_loss(Value *arg) { int *prompt_ids = xcalloc(prompt_len, sizeof(int)); for (int i = 0; i < prompt_len; i++) { - Value *v = prompt_list->data.list.items[i]; + Value *v = list_get_borrow(prompt_list, i); int t = (int)eigs_num_arg(v, "eigen_eval_loss"); /* #1637: a bool raises */ if (t < 0 || t >= vocab_size) t = 0; prompt_ids[i] = t; diff --git a/src/model_io.c b/src/model_io.c index 0e0a4275..28cd2c91 100644 --- a/src/model_io.c +++ b/src/model_io.c @@ -603,9 +603,9 @@ Value* json_obj_get(Value *obj, const char *key) { if (obj->type == VAL_DICT) return dict_get(obj, key); if (obj->type != VAL_LIST) return NULL; for (int i = 0; i + 1 < obj->data.list.count; i += 2) { - Value *k = obj->data.list.items[i]; + Value *k = list_get_borrow(obj, i); if (k && k->type == VAL_STR && strcmp(k->data.str, key) == 0) { - return obj->data.list.items[i + 1]; + return list_get_borrow(obj, i + 1); } } return NULL; diff --git a/src/model_train.c b/src/model_train.c index 493b44ca..4692e006 100644 --- a/src/model_train.c +++ b/src/model_train.c @@ -1444,16 +1444,16 @@ Value* builtin_native_train_step(Value *arg) { return make_str("{\"status\": \"error\", \"error\": \"Model not loaded\"}"); } - Value *in_list = arg->data.list.items[0]; - Value *out_list = arg->data.list.items[1]; + Value *in_list = list_get_borrow(arg, 0); + Value *out_list = list_get_borrow(arg, 1); if (in_list->type != VAL_LIST || out_list->type != VAL_LIST) { return make_str("{\"status\": \"error\", \"error\": \"input_ids and output_ids must be lists\"}"); } float lr = 0.001f; - if (arg->data.list.items[2]->type == VAL_BOOL) { eigs_num_arg(arg->data.list.items[2], __func__); return make_null(); } /* #1637 */ - if (arg->data.list.items[2]->type == VAL_NUM) lr = eigs_list_num(arg, 2, __func__); - else if (arg->data.list.items[2]->type == VAL_STR) lr = strtod(arg->data.list.items[2]->data.str, NULL); + if (list_get_borrow(arg, 2)->type == VAL_BOOL) { eigs_num_arg(list_get_borrow(arg, 2), __func__); return make_null(); } /* #1637 */ + if (list_get_borrow(arg, 2)->type == VAL_NUM) lr = eigs_list_num(arg, 2, __func__); + else if (list_get_borrow(arg, 2)->type == VAL_STR) lr = strtod(list_get_borrow(arg, 2)->data.str, NULL); if (lr <= 0 || lr > 1) lr = 0.001f; int input_len = in_list->data.list.count; @@ -1468,11 +1468,11 @@ Value* builtin_native_train_step(Value *arg) { int *input_ids = xcalloc(input_len > 0 ? input_len : 1, sizeof(int)); int *output_ids = xcalloc(output_len > 0 ? output_len : 1, sizeof(int)); for (int i = 0; i < input_len; i++) { - Value *v = in_list->data.list.items[i]; + Value *v = list_get_borrow(in_list, i); input_ids[i] = (int)eigs_num_arg(v, "native_train_step"); /* #1637: a bool raises */ } for (int i = 0; i < output_len; i++) { - Value *v = out_list->data.list.items[i]; + Value *v = list_get_borrow(out_list, i); output_ids[i] = (int)eigs_num_arg(v, "native_train_step"); } if (g_has_error) { free(input_ids); free(output_ids); return make_null(); } diff --git a/src/trace.c b/src/trace.c index 4a8cbc21..a9c257b5 100644 --- a/src/trace.c +++ b/src/trace.c @@ -2960,7 +2960,7 @@ static void write_value_ptr_full(Value *v, int *budget) { int n = v->data.list.count; for (int i = 0; i < n && *budget > 0; i++) { if (i) wf_puts(", ", budget); - write_value_ptr_full(v->data.list.items[i], budget); + write_value_ptr_full(list_get_borrow(v, i), budget); } wf_putc(']', budget); break; @@ -2972,7 +2972,7 @@ static void write_value_ptr_full(Value *v, int *budget) { if (i) wf_puts(", ", budget); write_string_full(v->data.dict.keys[i], budget); wf_puts(": ", budget); - write_value_ptr_full(v->data.dict.vals[i], budget); + write_value_ptr_full(dict_value_get_borrow(v, i), budget); } wf_putc('}', budget); break; diff --git a/src/vm.c b/src/vm.c index df436566..7e49679f 100644 --- a/src/vm.c +++ b/src/vm.c @@ -651,12 +651,12 @@ static inline Value *dict_get_cached(Value *dict, const char *key, uint32_t h) { if (ce && ce->index < dict->data.dict.count) { const char *stored = dict->data.dict.keys[ce->index]; if (stored == key || strcmp(stored, key) == 0) - return dict->data.dict.vals[ce->index]; + return dict_value_get_borrow(dict, ce->index); } int idx = env_hash_find_dict(dict, key, h); if (idx >= 0) { dict_cache_insert(dict, h, idx); - return dict->data.dict.vals[idx]; + return dict_value_get_borrow(dict, idx); } return NULL; } @@ -672,8 +672,8 @@ static inline void dict_set_cached(Value *dict, const char *key, uint32_t h, Val if (stored == key || strcmp(stored, key) == 0) { /* Cache hit — update in place */ val_incref(val); - val_decref(dict->data.dict.vals[ce->index]); - dict->data.dict.vals[ce->index] = val; + val_decref(dict_value_get_borrow(dict, ce->index)); + dict_value_set_owned(dict, ce->index, val); return; } } @@ -693,7 +693,7 @@ static inline int dict_set_cached_immediate(Value *dict, const char *key, uint32 if (ce && ce->index < dict->data.dict.count) { const char *stored = dict->data.dict.keys[ce->index]; if (stored == key || strcmp(stored, key) == 0) { - Value *existing = dict->data.dict.vals[ce->index]; + Value *existing = dict_value_get_borrow(dict, ce->index); if (existing && existing->type == VAL_NUM && existing->refcount == 1) { VAL_NUM_RAW(existing) = num; @@ -1358,7 +1358,7 @@ int jit_helper_local_idx_get(int slot, int idx) { } if (target->type == VAL_LIST) { if (i < target->data.list.count) { - Value *item = target->data.list.items[i]; + Value *item = list_get_borrow(target, i); if (item && item->type == VAL_NUM) { vm_push_slot(slot_from_num(VAL_NUM_RAW(item))); } else { @@ -1457,7 +1457,7 @@ int jit_helper_local_idx_dot_get(EigsChunk *chunk, int slot, int i = list_idx; if (target && target->type == VAL_LIST) { if (i < target->data.list.count) { - Value *dict = target->data.list.items[i]; + Value *dict = list_get_borrow(target, i); if (dict && dict->type == VAL_DICT) { const char *key = chunk->const_interns[name_idx]; uint32_t h = chunk->const_hashes @@ -1787,9 +1787,9 @@ int jit_helper_iter_next(void) { Value *state = slot_as_ptr(state_slot); if (!state || state->type != VAL_LIST || state->data.list.count < 2) return 1; - Value *iterable = state->data.list.items[0]; + Value *iterable = list_get_borrow(state, 0); if (!iterable) return 1; - Value *idx_v = state->data.list.items[1]; + Value *idx_v = list_get_borrow(state, 1); if (!idx_v || idx_v->type != VAL_NUM) return 1; int idx = (int)VAL_NUM_RAW(idx_v); int len = 0; @@ -1799,7 +1799,7 @@ int jit_helper_iter_next(void) { /* #491: cap by the entry-time snapshot (items[2]) — see make_iter_state. * Mirrors CASE(ITER_NEXT). */ if (state->data.list.count >= 3) { - Value *snap_v = state->data.list.items[2]; + Value *snap_v = list_get_borrow(state, 2); if (snap_v && snap_v->type == VAL_NUM) { int snap = (int)VAL_NUM_RAW(snap_v); if (snap < len) len = snap; @@ -1810,7 +1810,7 @@ int jit_helper_iter_next(void) { if (iterable->type == VAL_BUFFER) { elem = make_num(buffer_read_num(iterable, idx)); } else { - elem = iterable->data.list.items[idx]; + elem = list_get_borrow(iterable, idx); val_incref(elem); } /* In-place idx bump when the slot is an exclusive plain VAL_NUM @@ -1821,8 +1821,7 @@ int jit_helper_iter_next(void) { val_decref(idx_v); /* #873: heap-force when the state list must outlive an active * loop state. */ - state->data.list.items[1] = - make_num(idx + 1); + list_set_owned(state, 1, make_num(idx + 1)); } vm_push(elem); return g_has_error ? 2 : 0; @@ -1945,7 +1944,7 @@ static int vm_leaf_accessor_exec(EigsChunk *c, int argc) { if (!vm_index_is_int(idx_d, &i)) return 0; if (target->type == VAL_LIST) { if (!vm_index_resolve(&i, target->data.list.count)) return 0; - Value *r = target->data.list.items[i]; + Value *r = list_get_borrow(target, i); if (!r) return 0; if (r->type == VAL_NUM) mini[msp++] = slot_from_num(VAL_NUM_RAW(r)); @@ -2224,14 +2223,13 @@ void jit_helper_index_set(void) { } if (target->type == VAL_LIST && slot_is_num(val_s)) { if (_ok && vm_index_resolve(&i, target->data.list.count)) { - Value *existing = target->data.list.items[i]; + Value *existing = list_get_borrow(target, i); if (existing && existing->type == VAL_NUM && existing->refcount == 1) { VAL_NUM_RAW(existing) = SLOT_NUM_RAW(val_s); } else { val_decref(existing); - target->data.list.items[i] = - make_num(SLOT_NUM_RAW(val_s)); + list_set_owned(target, i, make_num(SLOT_NUM_RAW(val_s))); } } else { if (!_ok) rt_error(EK_VALUE, g_vm.current_line, "index must be an integer, got %g", SLOT_NUM_RAW(idx_s)); @@ -2252,8 +2250,8 @@ void jit_helper_index_set(void) { rt_error(EK_VALUE, g_vm.current_line, "index must be an integer, got %g", VAL_NUM_RAW(idx)); } else if (vm_index_resolve(&i, target->data.list.count)) { val_incref(val); - val_decref(target->data.list.items[i]); - target->data.list.items[i] = val; + val_decref(list_get_borrow(target, i)); + list_set_owned(target, i, val); } else { rt_error(EK_INDEX, g_vm.current_line, "index %d out of range (list length %d)", i, target->data.list.count); } @@ -2288,7 +2286,7 @@ int jit_helper_index_get(void) { int i, _ok = vm_index_is_int(SLOT_NUM_RAW(idx_s), &i); if (target->type == VAL_LIST) { if (_ok && vm_index_resolve(&i, target->data.list.count)) { - Value *r = target->data.list.items[i]; + Value *r = list_get_borrow(target, i); if (r && r->type == VAL_NUM) { /* See CASE(INDEX_GET) for the use-after-free story — * snapshot VAL_NUM_RAW(r) before slot_decref(tgt_s) @@ -2338,7 +2336,7 @@ int jit_helper_index_get(void) { if (!vm_index_is_int(VAL_NUM_RAW(idx), &i)) { rt_error(EK_VALUE, g_vm.current_line, "index must be an integer, got %g", VAL_NUM_RAW(idx)); } else if (vm_index_resolve(&i, target->data.list.count)) { - result = target->data.list.items[i]; + result = list_get_borrow(target, i); val_incref(result); } else { rt_error(EK_INDEX, g_vm.current_line, @@ -2382,7 +2380,7 @@ int jit_helper_index_get(void) { /* Direct-borrow heuristic, shared by the three builtin call sites * (CASE(CALL), jit_helper_call, OP_DISPATCH): if a builtin's result is * one of arg's top-level items (coalesce/append/dict_set return - * arg->data.list.items[0]), incref it so it survives the arg-decref + * list_get_borrow(arg, 0)), incref it so it survives the arg-decref * that follows. Builtins returning a fresh allocation never match, so * no spurious ref is added; nested borrows (get_at: items[0][idx]) * must still incref locally — only direct children are scanned. @@ -2432,7 +2430,7 @@ static inline void vm_borrow_scan(Value *arg, Value *result, * borrow beyond the effective cap without exposing a builtin. */ if (getenv("EIGS_BORROW_GUARD_PLANT")) n = 0; #endif - Value **items = arg->data.list.items; + Value **items = list_values_storage(arg); for (int i = 0; i < n; i++) { if (items[i] == result) { val_incref(result); return; } } @@ -4542,7 +4540,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, int i, _ok = vm_index_is_int(SLOT_NUM_RAW(idx_s), &i); if (target->type == VAL_LIST) { if (_ok && vm_index_resolve(&i, target->data.list.count)) { - Value *r = target->data.list.items[i]; + Value *r = list_get_borrow(target, i); if (r && r->type == VAL_NUM) { /* Snapshot the double BEFORE decref'ing the list: * if the stack slot was the sole owner, slot_decref @@ -4597,7 +4595,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, if (!vm_index_is_int(VAL_NUM_RAW(idx), &i)) { rt_error(EK_VALUE, current_line, "index must be an integer, got %g", VAL_NUM_RAW(idx)); } else if (vm_index_resolve(&i, target->data.list.count)) { - result = target->data.list.items[i]; + result = list_get_borrow(target, i); val_incref(result); } else { rt_error(EK_INDEX, current_line, "index %d out of range (list length %d)", i, target->data.list.count); @@ -4656,15 +4654,14 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, } if (target->type == VAL_LIST && slot_is_num(val_s)) { if (_ok && vm_index_resolve(&i, target->data.list.count)) { - Value *existing = target->data.list.items[i]; + Value *existing = list_get_borrow(target, i); /* In-place mutate when slot is an exclusive untracked VAL_NUM. */ if (existing && existing->type == VAL_NUM && existing->refcount == 1) { VAL_NUM_RAW(existing) = SLOT_NUM_RAW(val_s); } else { val_decref(existing); - target->data.list.items[i] = - make_num(SLOT_NUM_RAW(val_s)); + list_set_owned(target, i, make_num(SLOT_NUM_RAW(val_s))); } } else { if (!_ok) rt_error(EK_VALUE, current_line, "index must be an integer, got %g", SLOT_NUM_RAW(idx_s)); @@ -4685,8 +4682,8 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, rt_error(EK_VALUE, current_line, "index must be an integer, got %g", VAL_NUM_RAW(idx)); } else if (vm_index_resolve(&i, target->data.list.count)) { val_incref(val); - val_decref(target->data.list.items[i]); - target->data.list.items[i] = val; + val_decref(list_get_borrow(target, i)); + list_set_owned(target, i, val); } else { rt_error(EK_INDEX, current_line, "index %d out of range (list length %d)", i, target->data.list.count); } @@ -4871,7 +4868,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, } if (target->type == VAL_LIST) { if (i < target->data.list.count) { - Value *item = target->data.list.items[i]; + Value *item = list_get_borrow(target, i); /* Plain VAL_NUM -> immediate; skip incref/decref pair. */ if (item && item->type == VAL_NUM) { vm_push_slot(slot_from_num(VAL_NUM_RAW(item))); @@ -4917,7 +4914,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, int i = (int)list_idx; if (target && target->type == VAL_LIST) { if (i < target->data.list.count) { - Value *dict = target->data.list.items[i]; + Value *dict = list_get_borrow(target, i); if (dict && dict->type == VAL_DICT) { const char *key = chunk->const_interns[name_idx]; uint32_t h = chunk->const_hashes ? chunk->const_hashes[name_idx] : 0; @@ -4963,7 +4960,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, int i = (int)list_idx; if (target && target->type == VAL_LIST) { if (i < target->data.list.count) { - Value *dict = target->data.list.items[i]; + Value *dict = list_get_borrow(target, i); if (dict && dict->type == VAL_DICT) { const char *key = chunk->const_interns[name_idx]; uint32_t h = chunk->const_hashes ? chunk->const_hashes[name_idx] : 0; @@ -5016,13 +5013,13 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, * bare ITER_NEXT with any list on TOS, and reading VAL_NUM_RAW() off a * VAL_STR is a type confusion whose result then indexes items[] (#721). */ if (!state || state->type != VAL_LIST || state->data.list.count < 2 || - !state->data.list.items[1] || - state->data.list.items[1]->type != VAL_NUM) { + !list_get_borrow(state, 1) || + list_get_borrow(state, 1)->type != VAL_NUM) { ip += exit_offset; DISPATCH(); } - Value *iterable = state->data.list.items[0]; - int idx = (int)VAL_NUM_RAW(state->data.list.items[1]); + Value *iterable = list_get_borrow(state, 0); + int idx = (int)VAL_NUM_RAW(list_get_borrow(state, 1)); int len = 0; if (iterable->type == VAL_LIST) len = iterable->data.list.count; else if (iterable->type == VAL_BUFFER) len = iterable->data.buffer.count; @@ -5030,8 +5027,8 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, * appends can't extend the loop; min() with the live length keeps a * remove from reading past the end. See make_iter_state. */ if (state->data.list.count >= 3 && - state->data.list.items[2]->type == VAL_NUM) { - int snap = (int)VAL_NUM_RAW(state->data.list.items[2]); + list_get_borrow(state, 2)->type == VAL_NUM) { + int snap = (int)VAL_NUM_RAW(list_get_borrow(state, 2)); if (snap < len) len = snap; } @@ -5044,20 +5041,19 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, } else { /* Update index — mutate in place when the existing slot is * an exclusive plain VAL_NUM (avoids per-iter make_num/free). */ - Value *idx_v = state->data.list.items[1]; + Value *idx_v = list_get_borrow(state, 1); if (NUM_REUSE(idx_v)) { VAL_NUM_RAW(idx_v) = (double)(idx + 1); } else { val_decref(idx_v); - state->data.list.items[1] = - make_num(idx + 1); + list_set_owned(state, 1, make_num(idx + 1)); } if (iterable->type == VAL_BUFFER) { /* Push number immediate directly — skip make_num + immediate- * promote round-trip through vm_push. */ vm_push_slot(slot_from_num(buffer_read_num(iterable, idx))); } else { - Value *elem = iterable->data.list.items[idx]; + Value *elem = list_get_borrow(iterable, idx); val_incref(elem); vm_push(elem); } @@ -5705,7 +5701,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, * so each SET op consumes elements in declaration order. Incref each * element so the list's release below doesn't free them prematurely. */ for (int i = n - 1; i >= 0; i--) { - Value *e = lst->data.list.items[i]; + Value *e = list_get_borrow(lst, i); val_incref(e); vm_push(e); } @@ -5829,9 +5825,9 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, if (target->type == VAL_LIST) { result = make_list(n); for (int i = 0; i < n; i++) { - Value *e = target->data.list.items[start + i]; + Value *e = list_get_borrow(target, start + i); val_incref(e); - result->data.list.items[i] = e; + list_set_owned(result, i, e); } result->data.list.count = n; } else if (target->type == VAL_STR) { @@ -6421,7 +6417,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume, DISPATCH(); } - Value *fn = table->data.list.items[key]; + Value *fn = list_get_borrow(table, key); if (fn && fn->type == VAL_BUILTIN) { Value *arg = slot_to_value(arg_s); diff --git a/tests/sections/99ze-container-access.sh b/tests/sections/99ze-container-access.sh new file mode 100644 index 00000000..9e107e7e --- /dev/null +++ b/tests/sections/99ze-container-access.sh @@ -0,0 +1,13 @@ +# #1665 arm D: Value LIST/DICT element storage stays behind src/container.h. +echo "[99ze] Container accessor boundary (#1665)" +TOTAL=$((TOTAL + 1)) +CA_OUTPUT=$(bash "$TESTS_DIR/../tools/container_access_check.sh" 2>&1); CA_RC=$? +if [ "$CA_RC" -eq 0 ] && \ + grep -qE '^container-access: examined=[1-9][0-9]* allowed=[0-9]+ violations=0$' <<< "$CA_OUTPUT"; then + PASS=$((PASS + 1)) + printf '%s\n' "$CA_OUTPUT" | grep '^container-access:' +else + FAIL=$((FAIL + 1)) + echo " FAIL: container accessor boundary (rc=$CA_RC); output follows" + printf '%s\n' "$CA_OUTPUT" | sed 's/^/ /' +fi diff --git a/tools/container_access_check.sh b/tools/container_access_check.sh new file mode 100755 index 00000000..d029d852 --- /dev/null +++ b/tools/container_access_check.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# container_access_check.sh: keep runtime Value LIST/DICT element storage behind +# src/container.h (#1665 arm D step 1). Step 2 changes the storage behind that +# API in one place, so a raw `data.list.items` / `data.dict.vals` access anywhere +# else is a site step 2 would silently miss. Prints one verdict line: +# container-access: examined= allowed= violations= +# examined counts every raw hit outside container.h, so a vacuous run (zero +# hits because the scan found no files) is distinguishable from a clean one. +set -euo pipefail +cd "$(dirname "$0")/.." +examined=0 allowed=0 violations=0 +while IFS= read -r hit; do + [[ -z $hit ]] && continue + examined=$((examined + 1)) + file=${hit%%:*}; rest=${hit#*:}; line=${rest%%:*}; text=${rest#*:} + case "$file" in + src/parser.c|src/compiler.c|src/lint.c|src/eigenlsp.c|src/lint_host.c) + # ASTNode owns its own data.dict.vals array; not a Value container. + allowed=$((allowed + 1)); continue ;; + esac + case "$file:$text" in + src/jit.c:*'offsetof(Value, data.dict.vals)'*) + # Emitted machine code needs the representation offset (step-2 site). + allowed=$((allowed + 1)); continue ;; + src/eigenscript.h:*'arg->data.list.items[i]'*) + # eigs_bool_gate scans arg lists before container.h is included + # (step-2 site: it must test the slot tag, not a Value*). + allowed=$((allowed + 1)); continue ;; + esac + printf 'raw Value container access: %s:%s:%s\n' "$file" "$line" "$text" >&2 + violations=$((violations + 1)) +done < <(LC_ALL=C grep -n -E 'data\.list\.items|data\.dict\.vals' src/*.c src/*.h \ + | grep -v '^src/container\.h:' || true) +echo "container-access: examined=$examined allowed=$allowed violations=$violations" +if (( violations )); then + echo 'Use the accessor API in src/container.h.' >&2 + exit 1 +fi +(( examined > 0 )) || { echo 'container-access: scan found nothing (vacuous)' >&2; exit 1; } diff --git a/tools/num_read_allowlist.txt b/tools/num_read_allowlist.txt index 0d6ebb28..3687a955 100644 --- a/tools/num_read_allowlist.txt +++ b/tools/num_read_allowlist.txt @@ -7,6 +7,8 @@ # eigs_opt_num / eigs_elem_num, which raise on a bool. builtins_tensor.c|builtin_numerical_grad_rows|3|in-place writes of a matrix cell that tensor_cells_numeric proved a number before any mutation builtins_tensor.c|builtin_tensor_matmul|1|writes the NaN-boxed null sentinel into a result buffer cell (EIGS_STRICT=0 path); a write, no Value is read +container.h|dict_value_get_num|1|accessor numeric fast path: reads the number only behind its own type == VAL_NUM test (#1665 arm D) +container.h|list_get_num|1|accessor numeric fast path: reads the number only behind its own type == VAL_NUM test (#1665 arm D) chunk.c|chunk_disassemble|1|debug disassembly prints a constant behind its own type == VAL_NUM test chunk.c|const_equal|2|constant-pool dedup compares two constants after testing both are VAL_NUM chunk.c|const_hash_value|1|constant-pool hash of a constant inside its type == VAL_NUM branch diff --git a/tools/strict_shape_contract.py b/tools/strict_shape_contract.py index 0cf82882..d0d64bbe 100644 --- a/tools/strict_shape_contract.py +++ b/tools/strict_shape_contract.py @@ -52,7 +52,8 @@ def functions(sources): code = mask[match.end():end - 1] # Deliberately remove annotations before candidate discovery. code = re.sub(r'STRICT_LIST_MAX\([^;]*;', '', code) - direct = bool(re.search(r'\bVAL_LIST\b|data\.list|\bARG_COUNT\b|\bARG_AT\b', code)) + # data.list or its #1665 container.h accessors (list_get_*/list_iter_*/...) + direct = bool(re.search(r'\bVAL_LIST\b|data\.list|\blist_(?:get|set|iter|values)_\w*\(|\bARG_COUNT\b|\bARG_AT\b', code)) direct |= 'arg->data.dict' in code and bool(re.search(r'\bmake_list\s*\(', code)) helpers = set() for call in re.finditer(r'\b([A-Za-z_]\w*)\s*\(', code): diff --git a/tools/strict_shape_contracts.json b/tools/strict_shape_contracts.json index 154b0b80..00baedbd 100644 --- a/tools/strict_shape_contracts.json +++ b/tools/strict_shape_contracts.json @@ -147,7 +147,7 @@ ], "source_sha256": { "audio_convert_samples": [ - "e98a630393ad3c48b96a83d43c385c7f43e69657d73777fa8a5af913757ddd61" + "deee0f2ceb3c972e6fa5e37d4e962136c52d1db3c44637a9408e2806e0b72e32" ], "builtin_audio_play": [ "093742db555c0b87c29dea9095751714c0320123fb999941490bd1357b72640a" @@ -233,7 +233,7 @@ ], "source_sha256": { "audio_convert_samples": [ - "e98a630393ad3c48b96a83d43c385c7f43e69657d73777fa8a5af913757ddd61" + "deee0f2ceb3c972e6fa5e37d4e962136c52d1db3c44637a9408e2806e0b72e32" ], "builtin_audio_stream_push": [ "b838449be37f5d9d6d58b12d588dbb5deb389a90343061ea18e2bd579a68f057" @@ -387,11 +387,11 @@ ], "disposition": "exempt", "body_sha256": [ - "54627fcd3cb9d0fde55e9169285491c1123b2a879be7a690949440e997514b09" + "3d0d2c08324818d4ac65fe020224d685a9aac0e415d7bb69f54113b24a90aba0" ], "source_sha256": { "builtin_buf_from_list": [ - "54627fcd3cb9d0fde55e9169285491c1123b2a879be7a690949440e997514b09" + "3d0d2c08324818d4ac65fe020224d685a9aac0e415d7bb69f54113b24a90aba0" ] } }, @@ -503,14 +503,14 @@ ], "disposition": "exempt", "body_sha256": [ - "a288dd09f5f2cd4af4fa8355b865cc7f480fb22d20dfb172b9dc7c9cdb13319b" + "0f2463d9f7d4223393269796d4688a5d8fa708244de3f974f0e2f62c8cb0b540" ], "source_sha256": { "builtin_buffer": [ - "a288dd09f5f2cd4af4fa8355b865cc7f480fb22d20dfb172b9dc7c9cdb13319b" + "0f2463d9f7d4223393269796d4688a5d8fa708244de3f974f0e2f62c8cb0b540" ], "eigs_list_num": [ - "d81d044498e21cfa0893aeb9977764b6d95f437e9ae59dfab7707f5d93693433" + "7411c7e0c55633135b58755125bdfb3363fcf5ef2ab7efb450526d9a2f4c684d" ] } }, @@ -617,7 +617,7 @@ "669dc58628bb911edaa53afcc37a9c2ca7dfd0faee8bef0613e72cfba6904c73" ], "db_build_query": [ - "b8b459c14d36d1e3c83f2d95248c7d000160d601ce5082429391ec4a46dd1e8c" + "54a4437d9d7733215099f377d07e7143fd93b828e8da7a7805b552c39591665f" ], "db_exec_from_arg": [ "cbcf24fbb55c5a6f9bfdbb6535dc22a89086dab2ed311d0fc0892eb6eb287145" @@ -643,7 +643,7 @@ "083b679b60be2d040b1671ef418a0473fc9031f2456fe2f2f37869ec85330085" ], "db_build_query": [ - "b8b459c14d36d1e3c83f2d95248c7d000160d601ce5082429391ec4a46dd1e8c" + "54a4437d9d7733215099f377d07e7143fd93b828e8da7a7805b552c39591665f" ], "db_exec_from_arg": [ "cbcf24fbb55c5a6f9bfdbb6535dc22a89086dab2ed311d0fc0892eb6eb287145" @@ -669,7 +669,7 @@ "ac1ace66298435daa38e6f6ac83a6c8fcd5677c87261adac82489493ca967f26" ], "db_build_query": [ - "b8b459c14d36d1e3c83f2d95248c7d000160d601ce5082429391ec4a46dd1e8c" + "54a4437d9d7733215099f377d07e7143fd93b828e8da7a7805b552c39591665f" ], "db_exec_from_arg": [ "cbcf24fbb55c5a6f9bfdbb6535dc22a89086dab2ed311d0fc0892eb6eb287145" @@ -772,14 +772,14 @@ ], "disposition": "exempt", "body_sha256": [ - "6bda7c10ac12e205d51c0d8c90ee59d2e66cd136a513a64317a146ef32c10dfa" + "268d0eea61701a8101ef750b078302f77890f8baf08b211aebb51c75ce87293d" ], "source_sha256": { "builtin_exec_capture": [ - "6bda7c10ac12e205d51c0d8c90ee59d2e66cd136a513a64317a146ef32c10dfa" + "268d0eea61701a8101ef750b078302f77890f8baf08b211aebb51c75ce87293d" ], "eigs_list_num": [ - "d81d044498e21cfa0893aeb9977764b6d95f437e9ae59dfab7707f5d93693433" + "7411c7e0c55633135b58755125bdfb3363fcf5ef2ab7efb450526d9a2f4c684d" ] } }, @@ -807,11 +807,11 @@ ], "disposition": "exempt", "body_sha256": [ - "b12fd36f8b8e6f0885f5744abeee83bd83e1488254973a568993c8c37ed7b727" + "1872757a55392e54f959bd7659f1784ef0af3a43f4a6a8fc964c82cb475caea7" ], "source_sha256": { "builtin_f64_from_bytes": [ - "b12fd36f8b8e6f0885f5744abeee83bd83e1488254973a568993c8c37ed7b727" + "1872757a55392e54f959bd7659f1784ef0af3a43f4a6a8fc964c82cb475caea7" ] } }, @@ -839,11 +839,11 @@ ], "disposition": "exempt", "body_sha256": [ - "5021366846459c609fe8667f66eb9529e79003edfb960326f1bced5bc2f0e157" + "f183d591ccd95ae6c658f3167403047669a5b6510b2a9c7c25ecd1464cb8dbac" ], "source_sha256": { "builtin_get_at": [ - "5021366846459c609fe8667f66eb9529e79003edfb960326f1bced5bc2f0e157" + "f183d591ccd95ae6c658f3167403047669a5b6510b2a9c7c25ecd1464cb8dbac" ] } }, @@ -1039,11 +1039,11 @@ ], "disposition": "exempt", "body_sha256": [ - "42802f4db44ca3853ec885ca9cc032cf90f7115366098a806d76db6cf5263b93" + "038cedc73097976fcb26f43537ffd5fa8d598b2da404e2c05439106ac636bc3e" ], "source_sha256": { "builtin_http_early_bind": [ - "42802f4db44ca3853ec885ca9cc032cf90f7115366098a806d76db6cf5263b93" + "038cedc73097976fcb26f43537ffd5fa8d598b2da404e2c05439106ac636bc3e" ], "eigs_opt_num": [ "5bd33573efec2133743c045bc9827a72a8c9ea73f15a185468495764b5c37ecd" @@ -1074,11 +1074,11 @@ ], "disposition": "exempt", "body_sha256": [ - "4c87d0819811edbfe01be63ba09875bcc12dca04cfe92aaae1dfafe5e266074a" + "5474bb1ecceceede18171dc6a4931ff393ddf435e90be703835d39ebd739db41" ], "source_sha256": { "builtin_http_response_header": [ - "4c87d0819811edbfe01be63ba09875bcc12dca04cfe92aaae1dfafe5e266074a" + "5474bb1ecceceede18171dc6a4931ff393ddf435e90be703835d39ebd739db41" ] } }, @@ -1151,10 +1151,10 @@ "d89562220316e85c2ed110b250eee934769205aab21a2f3bc76a9804aa171060" ], "strict_numeric_byte_list": [ - "792de1634e3cda6e5ab40f7bef4d6d29d38ed5b6b9704a2e53d12332e560c892" + "79f5f890f930f0eec678dc9851a7656462c94eeffc5468bccf7ee536988f68e3" ], "zlib_bytes_arg": [ - "4aeca3a4d6fc6d62d3304c832e8ecaf9061af1884b7106e9ef1381f21ded3f4f" + "11bf36d12e06d6f30fe673a5868e7f5db8888e3fe70ee45c0bc34df81acf2c63" ], "zlib_inflate_impl": [ "a54f1d8a2b596ca7c363f20303516358bf5992479e5aadc7ea6f2536e2089914" @@ -1185,11 +1185,11 @@ ], "disposition": "exempt", "body_sha256": [ - "260131ce8fc8a72effc5efef651d6e26ccc01e602088df2a90def3313a1c00e0" + "6f12d49ea07ba5e4549f0047540aa8ba367331b3ec6e4852df9129a4b62fe4e1" ], "source_sha256": { "builtin_json_build": [ - "260131ce8fc8a72effc5efef651d6e26ccc01e602088df2a90def3313a1c00e0" + "6f12d49ea07ba5e4549f0047540aa8ba367331b3ec6e4852df9129a4b62fe4e1" ] } }, @@ -1212,7 +1212,7 @@ "5815ee115524cd711b01ba1bca89f1e152cd2303c2214447e1482ffde5df1189" ], "eigs_json_encode_value": [ - "040243c0a02b8d5966f9dcaae4433181b5a847919f722bff8efbcbbe5aefbbe2" + "b4e507c44e9d19a595a631c662e0805d10fce44fb03ffd496fa8a5f885923fe7" ] } }, @@ -1358,11 +1358,11 @@ ], "disposition": "exempt", "body_sha256": [ - "cb6fef3242188e171fd1eb7283e7afe195df9f89ed5c61ae16084a909fe8de81" + "94c3dd22d5e23b8370953c33ec40911a2bdc883501c741e689efe1defbed8b1f" ], "source_sha256": { "builtin_ls": [ - "cb6fef3242188e171fd1eb7283e7afe195df9f89ed5c61ae16084a909fe8de81" + "94c3dd22d5e23b8370953c33ec40911a2bdc883501c741e689efe1defbed8b1f" ] } }, @@ -1409,7 +1409,7 @@ "957d6367080150932b0eb91b77222af62118688b594ef3e894134f13c7ea1765" ], "minmax_reduce": [ - "4584cee7f2bdf4865b043d23abbee135d1f2e28d813942aa1d28f2daa688ae4b" + "beedf8c488020f3af2d920fb08165ad3cce16db8541ea91ff0840fae0352668f" ] } }, @@ -1432,7 +1432,7 @@ "895d02903f98e67d498faf4410e6d7d4cedc13d14f2d5699c86a24deccbea53c" ], "minmax_reduce": [ - "4584cee7f2bdf4865b043d23abbee135d1f2e28d813942aa1d28f2daa688ae4b" + "beedf8c488020f3af2d920fb08165ad3cce16db8541ea91ff0840fae0352668f" ] } }, @@ -1455,7 +1455,7 @@ "b38ee26c123f3a682f61cfacc3cd75d7dd6423aa2eafbceb49e64b2e63ee47bf" ], "call_eigs_fn": [ - "9604a4454984737e42957fbff8165dc2e8d3f6a809584a024f3d30b4d0abf481" + "123bbc53a201557a82d9c75caf6dc231d9c2485e9d2ccce3a296ccca5c9f95d6" ], "env_set_local": [ "bde886fc51cd3b927e7c8c487a7a0d270453133c4e598506927a2d549788afbf" @@ -1464,7 +1464,7 @@ "cab31d774010bcc6b59ae3d4de9f380592fae037878071700a560932bb4f80d3" ], "vm_borrow_scan": [ - "2ae1adca5ccdeb3419fb14b52073dfac112a5dccb14348050cab2719a1cee8d0" + "5a91678f79479ec4c73cd2fa648e41eb4d23cacc237689800cb04284235985b2" ] } }, @@ -1516,17 +1516,17 @@ ], "disposition": "exempt", "body_sha256": [ - "db447ac43f129387dc0a10d8aa38aa337a936f67629bf0d6fa90cfd055c2972a" + "2fe8f44cb72ee3c3ac36d0bf5032acd6cce49e9767fda3b35bc89df7f1414b57" ], "source_sha256": { "builtin_net_accept": [ - "db447ac43f129387dc0a10d8aa38aa337a936f67629bf0d6fa90cfd055c2972a" + "2fe8f44cb72ee3c3ac36d0bf5032acd6cce49e9767fda3b35bc89df7f1414b57" ], "net_lookup": [ "80716b1becf33449a83c7700e1aac433cdcf96492fa0c7d5bbe29d1a4f3b58ea" ], "net_num_at": [ - "21af63392d75ae60e71435ee2fb5eb314c704f34dd085aba813cf6bb71964ed9" + "8a44065afe7983073da65471534977ee5d28d3c25196634812f9a56ed4261b02" ], "net_require_list": [ "70f27bee56a0ff146a34de05597e8738a16f02cbe766b461ddc89e945d654a97" @@ -1545,14 +1545,14 @@ ], "disposition": "exempt", "body_sha256": [ - "1714b537427a4503e5115af43594b01042000c94ca3ac6300782e08bc2d5edb7" + "add224170f9f93c6a259d76313876844d6a8ed58af63e3d1242c3a8295314441" ], "source_sha256": { "builtin_net_dial": [ - "1714b537427a4503e5115af43594b01042000c94ca3ac6300782e08bc2d5edb7" + "add224170f9f93c6a259d76313876844d6a8ed58af63e3d1242c3a8295314441" ], "net_num_at": [ - "21af63392d75ae60e71435ee2fb5eb314c704f34dd085aba813cf6bb71964ed9" + "8a44065afe7983073da65471534977ee5d28d3c25196634812f9a56ed4261b02" ], "net_require_list": [ "70f27bee56a0ff146a34de05597e8738a16f02cbe766b461ddc89e945d654a97" @@ -1571,14 +1571,14 @@ ], "disposition": "exempt", "body_sha256": [ - "0d7ef42e6ba9100ba2c1fa3d4ccb2234b8a8d5b041fb95567436bc36908f7c11" + "5f3c7c364e2cdc2569c9930455304b0ae110daeb7aece1bf55adad42cb87e5ad" ], "source_sha256": { "builtin_net_recv": [ - "0d7ef42e6ba9100ba2c1fa3d4ccb2234b8a8d5b041fb95567436bc36908f7c11" + "5f3c7c364e2cdc2569c9930455304b0ae110daeb7aece1bf55adad42cb87e5ad" ], "net_num_at": [ - "21af63392d75ae60e71435ee2fb5eb314c704f34dd085aba813cf6bb71964ed9" + "8a44065afe7983073da65471534977ee5d28d3c25196634812f9a56ed4261b02" ], "net_require_list": [ "70f27bee56a0ff146a34de05597e8738a16f02cbe766b461ddc89e945d654a97" @@ -1597,11 +1597,11 @@ ], "disposition": "exempt", "body_sha256": [ - "c01f12132fa49a57b6759558b96f84d8b70d25cdfccb9941f555c8ceaae98bdf" + "0d622bf86112450c275746b0de3df6f48f3767d36ee519a885f2437419d6990a" ], "source_sha256": { "builtin_net_send": [ - "c01f12132fa49a57b6759558b96f84d8b70d25cdfccb9941f555c8ceaae98bdf" + "0d622bf86112450c275746b0de3df6f48f3767d36ee519a885f2437419d6990a" ], "net_require_list": [ "70f27bee56a0ff146a34de05597e8738a16f02cbe766b461ddc89e945d654a97" @@ -1687,7 +1687,7 @@ "bcb3999873c5e085e20e6370e6ae81ddaf01a610069729a2a12dd5d402df301c" ], "value_to_string": [ - "00c08a962c4e4be86e9f6eb07d36d2ebd38b74e01e8ffd4e5c345bed69b1a0c5" + "0162dd03ddc58d709c0752df0b991eebc305c3b058f78838f8f5ecdb0a1143dc" ] } }, @@ -1871,11 +1871,11 @@ ], "disposition": "exempt", "body_sha256": [ - "dd7becdc8b3949ec6300d636582edd907e6d17b9fe3aee87161a978b15f14257" + "30690a745c7be23bb742c83b13764110cd187d0758ed2a2619b48a1f3c260eb5" ], "source_sha256": { "builtin_set_at": [ - "dd7becdc8b3949ec6300d636582edd907e6d17b9fe3aee87161a978b15f14257" + "30690a745c7be23bb742c83b13764110cd187d0758ed2a2619b48a1f3c260eb5" ] } }, @@ -1963,11 +1963,11 @@ ], "disposition": "exempt", "body_sha256": [ - "85e1d69bda6252ed525689e1aa55ccc37b0b9ec6b5a08dfceed034775658aed3" + "ec14b9b013b30cb7b269ce2a25984664c969f50b8dde97b84abdc553842aaea4" ], "source_sha256": { "builtin_sort": [ - "85e1d69bda6252ed525689e1aa55ccc37b0b9ec6b5a08dfceed034775658aed3" + "ec14b9b013b30cb7b269ce2a25984664c969f50b8dde97b84abdc553842aaea4" ] } }, @@ -1995,11 +1995,11 @@ ], "disposition": "exempt", "body_sha256": [ - "abd77d0037ffd74401d06513eb42b3829a67392b2bff3f2676b3b484a8d5a08d" + "d43b7c42b2248b1360cc76f5d998f77ef4350c6f022149d6ec5c1e833e679195" ], "source_sha256": { "builtin_spawn": [ - "abd77d0037ffd74401d06513eb42b3829a67392b2bff3f2676b3b484a8d5a08d" + "d43b7c42b2248b1360cc76f5d998f77ef4350c6f022149d6ec5c1e833e679195" ] } }, @@ -2130,7 +2130,7 @@ "54d7497ad4bc951a7f8b8e0e690f901fcab06ed74c92050e4e68be8c36921372" ], "value_to_string": [ - "00c08a962c4e4be86e9f6eb07d36d2ebd38b74e01e8ffd4e5c345bed69b1a0c5" + "0162dd03ddc58d709c0752df0b991eebc305c3b058f78838f8f5ecdb0a1143dc" ] } }, @@ -2146,14 +2146,14 @@ ], "disposition": "exempt", "body_sha256": [ - "804af03f97b739d64395d87f5f9aceda99c651abc92d38bee8b98f120bdfe6eb" + "90f545588091252200b754a2ff1764b111a8d0010212b8c10d49bb0064e1bb1f" ], "source_sha256": { "builtin_str_from_bytes": [ - "804af03f97b739d64395d87f5f9aceda99c651abc92d38bee8b98f120bdfe6eb" + "90f545588091252200b754a2ff1764b111a8d0010212b8c10d49bb0064e1bb1f" ], "strict_numeric_byte_list": [ - "792de1634e3cda6e5ab40f7bef4d6d29d38ed5b6b9704a2e53d12332e560c892" + "79f5f890f930f0eec678dc9851a7656462c94eeffc5468bccf7ee536988f68e3" ] } }, @@ -2217,11 +2217,11 @@ ], "disposition": "exempt", "body_sha256": [ - "d5994d74b55c30f9d01226be4f868eb989ac4b850b3ed4ddd9864c113af9b9e5" + "4098fcc1457eb05e82f34ff4b1524275f67c5d821e52e734c41e89256c4fdaeb" ], "source_sha256": { "builtin_task_spawn": [ - "d5994d74b55c30f9d01226be4f868eb989ac4b850b3ed4ddd9864c113af9b9e5" + "4098fcc1457eb05e82f34ff4b1524275f67c5d821e52e734c41e89256c4fdaeb" ] } }, @@ -2268,7 +2268,7 @@ "f6bb04ca6134dd41d1a67ee91ea2b9587e13477f13ed51afaa3834de1d4f2b78" ], "tensor_unary": [ - "ba28948736491f849e86f3d99069141b15dfda5f14ee9b3ee4ef728039852361" + "4abceb6bf32d03e72bf0b7245447987b78885d079328ae6504e48bf8afa170f9" ] } }, @@ -2309,7 +2309,7 @@ "40100cc6cf4d92300cf1d3fbae23cb21adf4241bd01edecc9a683a07fb30bc01" ], "tensor_to_flat": [ - "5362e998b20c70cb9db3441550be28b5cc707f79d7ec0bc71e351ab43937357e" + "935bb2e66301b7e41ac4d999bb1e5bfee0b4b4ab8afd54bb73d0369f71c67898" ] } }, @@ -2325,11 +2325,11 @@ ], "disposition": "exempt", "body_sha256": [ - "fd424edc6b03b9ab24aa2940a40654242ca70bbcf0fcab69642e88fc692d4bd3" + "6923863238d3db9fb71ddeb0c0b56d92e9436253f886350d6b89560b5f55e963" ], "source_sha256": { "builtin_tensor_load": [ - "fd424edc6b03b9ab24aa2940a40654242ca70bbcf0fcab69642e88fc692d4bd3" + "6923863238d3db9fb71ddeb0c0b56d92e9436253f886350d6b89560b5f55e963" ] } }, @@ -2352,7 +2352,7 @@ "ff086ad360320823ab29cb9cfbeed6ec340ae78f959cb18c187c227db55f947f" ], "tensor_unary": [ - "ba28948736491f849e86f3d99069141b15dfda5f14ee9b3ee4ef728039852361" + "4abceb6bf32d03e72bf0b7245447987b78885d079328ae6504e48bf8afa170f9" ] } }, @@ -2368,11 +2368,11 @@ ], "disposition": "exempt", "body_sha256": [ - "4163fd8b2a8db59b4861cfe0f19f6bb57154e8c461f86f25915cb9b95028f0c6" + "28d209db0561173b9426149c963f32bbb8a1d4bd15ded1aad450e3e84e4b986e" ], "source_sha256": { "builtin_tensor_log_softmax": [ - "4163fd8b2a8db59b4861cfe0f19f6bb57154e8c461f86f25915cb9b95028f0c6" + "28d209db0561173b9426149c963f32bbb8a1d4bd15ded1aad450e3e84e4b986e" ] } }, @@ -2431,10 +2431,10 @@ "f6eec99bd0153900403028372a294cf84be9485ce98b7ad8843ce4e2a5d390ee" ], "tensor_flatten_recursive": [ - "611ed504e8a1821eaf122fcf87f4423f70856a4df74f2f82aebc32a7d187f421" + "3844bd92ee4238bec0b3bd2645eb4bd17d18a6d681a7ad7a8e21291282e33d14" ], "tensor_total": [ - "a36012ea9a660ce7ffc3e1be748c9412ef5553ba3bd0f91dce969f7b70ee605e" + "a0a594c0e05d41ef7ef4a8c262d56646d628f3b3f5c4f94b40e9cf1637d8ca03" ] } }, @@ -2469,7 +2469,7 @@ "a345f0c30c4b1b80943b9c718c0002fc19981d0336a9a1ea0aca3b5966bf8462" ], "tensor_unary": [ - "ba28948736491f849e86f3d99069141b15dfda5f14ee9b3ee4ef728039852361" + "4abceb6bf32d03e72bf0b7245447987b78885d079328ae6504e48bf8afa170f9" ] } }, @@ -2492,10 +2492,10 @@ "817e544211920e1216ff102393d66f3cb7a8def4a9ea05611872433b5ad8f87c" ], "tensor_flatten_recursive": [ - "611ed504e8a1821eaf122fcf87f4423f70856a4df74f2f82aebc32a7d187f421" + "3844bd92ee4238bec0b3bd2645eb4bd17d18a6d681a7ad7a8e21291282e33d14" ], "tensor_total": [ - "a36012ea9a660ce7ffc3e1be748c9412ef5553ba3bd0f91dce969f7b70ee605e" + "a0a594c0e05d41ef7ef4a8c262d56646d628f3b3f5c4f94b40e9cf1637d8ca03" ] } }, @@ -2533,7 +2533,7 @@ "9829b048a1af1233bfeae6329573e79d83cd7acb01e548d3737011a625fbdc02" ], "tensor_to_flat": [ - "5362e998b20c70cb9db3441550be28b5cc707f79d7ec0bc71e351ab43937357e" + "935bb2e66301b7e41ac4d999bb1e5bfee0b4b4ab8afd54bb73d0369f71c67898" ] } }, @@ -2573,11 +2573,11 @@ ], "disposition": "exempt", "body_sha256": [ - "44594175dcf0035fe5aa99afd41376b2397fc2d9e45f9f7efe1585f5924e269e" + "d5ab1a467607560bfce18d36abd18fa5b25d7fe43179da3c305e29f379b027e7" ], "source_sha256": { "builtin_tensor_shape": [ - "44594175dcf0035fe5aa99afd41376b2397fc2d9e45f9f7efe1585f5924e269e" + "d5ab1a467607560bfce18d36abd18fa5b25d7fe43179da3c305e29f379b027e7" ] } }, @@ -2609,7 +2609,7 @@ "40100cc6cf4d92300cf1d3fbae23cb21adf4241bd01edecc9a683a07fb30bc01" ], "tensor_to_flat": [ - "5362e998b20c70cb9db3441550be28b5cc707f79d7ec0bc71e351ab43937357e" + "935bb2e66301b7e41ac4d999bb1e5bfee0b4b4ab8afd54bb73d0369f71c67898" ] } }, @@ -2632,7 +2632,7 @@ "f38d9057e3c1e2a049e7cd4d4f38df6aace72af77d1a718b357b4f2a5bf4f54d" ], "tensor_unary": [ - "ba28948736491f849e86f3d99069141b15dfda5f14ee9b3ee4ef728039852361" + "4abceb6bf32d03e72bf0b7245447987b78885d079328ae6504e48bf8afa170f9" ] } }, @@ -2667,10 +2667,10 @@ "196480c8b98743ea57d8bf3bc4bdf0b0fdc3ca70c56ef6cd1289342e15fcd544" ], "tensor_flatten_recursive": [ - "611ed504e8a1821eaf122fcf87f4423f70856a4df74f2f82aebc32a7d187f421" + "3844bd92ee4238bec0b3bd2645eb4bd17d18a6d681a7ad7a8e21291282e33d14" ], "tensor_total": [ - "a36012ea9a660ce7ffc3e1be748c9412ef5553ba3bd0f91dce969f7b70ee605e" + "a0a594c0e05d41ef7ef4a8c262d56646d628f3b3f5c4f94b40e9cf1637d8ca03" ] } }, @@ -2698,11 +2698,11 @@ ], "disposition": "exempt", "body_sha256": [ - "7633d9c546bf170efd86419b383d8620178edce790b8bb2b0f52816f210e0062" + "aad531cea0cb998c5ab4bcfbc19ca755f163227267af03b6ad6f3ae66a8c751f" ], "source_sha256": { "builtin_tensor_zeros_like": [ - "7633d9c546bf170efd86419b383d8620178edce790b8bb2b0f52816f210e0062" + "aad531cea0cb998c5ab4bcfbc19ca755f163227267af03b6ad6f3ae66a8c751f" ], "make_buffer_like": [ "40100cc6cf4d92300cf1d3fbae23cb21adf4241bd01edecc9a683a07fb30bc01" @@ -2752,7 +2752,7 @@ "dce6eda1020469efb73a67c34f60fa7cc66c03c617d1fb4bc96aaea536346ae3" ], "value_to_string": [ - "00c08a962c4e4be86e9f6eb07d36d2ebd38b74e01e8ffd4e5c345bed69b1a0c5" + "0162dd03ddc58d709c0752df0b991eebc305c3b058f78838f8f5ecdb0a1143dc" ] } }, @@ -2788,11 +2788,11 @@ ], "disposition": "exempt", "body_sha256": [ - "34b36383fdb03bf81491ec120151e23c6d301818d947bf3b2297d85812a45117" + "2206aa352dbf84a6e78cc62c7fdd95f7185fc3a71ed3f5573f00eb33ad65083f" ], "source_sha256": { "builtin_values": [ - "34b36383fdb03bf81491ec120151e23c6d301818d947bf3b2297d85812a45117" + "2206aa352dbf84a6e78cc62c7fdd95f7185fc3a71ed3f5573f00eb33ad65083f" ], "eigs_module_ns_sync": [ "dd006fbaeca47f68e2df6343ad33e00bcdcf4c41243aae224c6798f0c2fc3a7c" @@ -2821,7 +2821,7 @@ "fce8334d124e479de21cd22dfed262a8edcc656341a1f7c272507762481c57fc" ], "vm_desc_abi_error": [ - "8f534bd94a4a3c67f17726c79e8ee59da0b48e97a297fba6335b153b94e14273" + "cf2662e7782cb1679f191a68392821c334d53d165f776c90919b4cbc16d96c3a" ] } }, @@ -2844,7 +2844,7 @@ "5556903dd96019edcff890e180e1e8f04ffc7101fe80b5fcf93520968ea3c180" ], "value_to_string": [ - "00c08a962c4e4be86e9f6eb07d36d2ebd38b74e01e8ffd4e5c345bed69b1a0c5" + "0162dd03ddc58d709c0752df0b991eebc305c3b058f78838f8f5ecdb0a1143dc" ] } },