mruby-compiler: complete NODE_HASH migration to variable-sized nodes exclusively

Modified new_hash function to always create variable-sized nodes instead of
conditionally falling back to cons list nodes. This achieves complete
NODE_HASH migration with full test suite compatibility.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-09-07 00:51:27 +09:00
parent 300557d6ee
commit cd659927fa
3 changed files with 1257 additions and 1218 deletions
+86 -27
View File
@@ -3806,6 +3806,92 @@ codegen_array(codegen_scope *s, node *tree, int val)
push();
}
static void
gen_hash_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_hash_node *hash = hash_node(varnode);
int len = hash->len;
struct mrb_ast_node **pairs = hash->pairs;
int i;
int regular_pairs = 0;
mrb_bool update = FALSE;
mrb_bool first = TRUE;
if (!val) return;
if (len == 0) {
genop_2(s, OP_HASH, cursp(), 0);
push();
return;
}
/* Process each key-value pair, handling double-splat (**) cases */
for (i = 0; i < len; i++) {
struct mrb_ast_node *key = pairs[i * 2];
struct mrb_ast_node *value = pairs[i * 2 + 1];
/* Check if this is a double-splat (**kwargs) */
if (node_to_int(key->car) == NODE_KW_REST_ARGS) {
/* Flush any accumulated regular pairs first */
if (val && first && regular_pairs == 0) {
/* First element is splat - create empty hash */
genop_2(s, OP_HASH, cursp(), 0);
push();
update = TRUE;
}
else if (val && regular_pairs > 0) {
/* Create/add hash from accumulated pairs */
pop_n(regular_pairs * 2);
if (!update) {
genop_2(s, OP_HASH, cursp(), regular_pairs);
}
else {
pop();
genop_2(s, OP_HASHADD, cursp(), regular_pairs);
}
push();
}
/* Generate the splat hash */
codegen(s, value, val);
/* Merge the splat hash */
if (val && (regular_pairs > 0 || update)) {
pop(); pop();
genop_1(s, OP_HASHCAT, cursp());
push();
}
update = TRUE;
regular_pairs = 0;
}
else {
/* Regular key-value pair */
codegen(s, key, val);
codegen(s, value, val);
regular_pairs++;
}
first = FALSE;
}
/* Handle any remaining regular pairs */
if (val) {
if (!update && regular_pairs > 0) {
/* Simple case: no splats, just create hash */
pop_n(regular_pairs * 2);
genop_2(s, OP_HASH, cursp(), regular_pairs);
push();
}
else if (update && regular_pairs > 0) {
/* Add remaining pairs to existing hash */
pop_n(regular_pairs * 2 + 1);
genop_2(s, OP_HASHADD, cursp(), regular_pairs);
push();
}
}
}
static void
codegen_hash(codegen_scope *s, node *tree, int val)
{
@@ -4589,33 +4675,6 @@ gen_array_var(codegen_scope *s, node *varnode, int val)
push();
}
static void
gen_hash_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_hash_node *hash = hash_node(varnode);
int len = HASH_NODE_LEN(hash);
struct mrb_ast_node **pairs = HASH_NODE_PAIRS(hash);
int i;
if (!val) return;
if (len == 0) {
genop_2(s, OP_HASH, cursp(), 0);
push();
return;
}
/* Generate code for each key-value pair */
for (i = 0; i < len; i++) {
codegen(s, pairs[i * 2], VAL); /* key */
codegen(s, pairs[i * 2 + 1], VAL); /* value */
}
/* Create hash with key-value pairs on stack */
pop_n(len * 2); /* Pop all keys and values */
genop_2(s, OP_HASH, cursp(), len);
push();
}
/* Phase 3 Variable Node Codegen Functions */
+33 -43
View File
@@ -597,7 +597,6 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b)
/* Forward declarations for variable-sized AST node creation functions */
static node* new_array_var(parser_state *p, node *a);
static node* new_hash_var(parser_state *p, node *a);
static node* new_def_var(parser_state *p, mrb_sym name, node *args, node *body);
static node* new_class_var(parser_state *p, node *name, node *superclass, node *body);
static node* new_module_var(parser_state *p, node *name, node *body);
@@ -887,45 +886,6 @@ new_array_var(parser_state *p, node *a)
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized hash node creation */
static node*
new_hash_var(parser_state *p, node *a)
{
/* Count hash key-value pairs */
uint16_t len = 0;
node *pair_iter = a;
while (pair_iter) {
len++;
pair_iter = pair_iter->cdr;
}
/* Calculate total size needed */
size_t base_size = sizeof(struct mrb_ast_hash_node);
size_t pairs_size = len * 2 * sizeof(struct mrb_ast_node*); /* key and value for each pair */
size_t total_size = base_size + pairs_size;
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_hash_node *n = (struct mrb_ast_hash_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_HASH, class);
n->len = len;
n->flags = 0;
/* Copy key-value pairs into flexible array */
pair_iter = a;
for (int i = 0; i < len; i++) {
if (pair_iter && pair_iter->car) {
/* Each pair is a cons (key . value) */
node *pair = pair_iter->car;
n->pairs[i * 2] = pair->car; /* key */
n->pairs[i * 2 + 1] = pair->cdr; /* value */
}
pair_iter = pair_iter->cdr;
}
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
@@ -1376,10 +1336,40 @@ new_splat(parser_state *p, node *a)
static node*
new_hash(parser_state *p, node *a)
{
if (p->cxt && p->cxt->use_variable_nodes) {
return new_hash_var(p, a);
/* Count hash key-value pairs */
uint16_t len = 0;
node *pair_iter = a;
while (pair_iter) {
len++;
pair_iter = pair_iter->cdr;
}
return cons_head((node*)NODE_HASH, a);
/* Calculate total size needed */
size_t base_size = sizeof(struct mrb_ast_hash_node);
size_t pairs_size = len * 2 * sizeof(struct mrb_ast_node*); /* key and value for each pair */
size_t total_size = base_size + pairs_size;
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_hash_node *n = (struct mrb_ast_hash_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_HASH, class);
n->len = len;
n->flags = 0;
/* Copy key-value pairs into flexible array */
pair_iter = a;
for (int i = 0; i < len; i++) {
if (pair_iter && pair_iter->car) {
/* Each pair is a cons (key . value) */
node *pair = pair_iter->car;
n->pairs[i * 2] = pair->car; /* key */
n->pairs[i * 2 + 1] = pair->cdr; /* value */
}
pair_iter = pair_iter->cdr;
}
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:kw_hash (k . v) (k . v)...) */
File diff suppressed because it is too large Load Diff