summaryrefslogtreecommitdiff
path: root/vm_eval.c
diff options
context:
space:
mode:
authorMatt Valentine-House <[email protected]>2024-02-01 15:00:50 +0000
committerKevin Newton <[email protected]>2024-02-13 21:19:12 -0500
commitc2af974e6751b9a4c533a8c6855efd9897b09c5f (patch)
tree29eeaee40c15223f511c6dd88c1fd4685eff0286 /vm_eval.c
parentfd3f776a05505d71c818620fe9c6ca8fb3d132ea (diff)
[PRISM] Build wrapper scopes for eval
- Don't use `build_options_scopes` We can inline the code here instead and avoid allocating all the extra arrays. - Create `pm_scope_node_t` objects with the correct local table, for the scope node returned from the parser. Co-Authored-By: Kevin Newton <[email protected]>
Diffstat (limited to 'vm_eval.c')
-rw-r--r--vm_eval.c84
1 files changed, 76 insertions, 8 deletions
diff --git a/vm_eval.c b/vm_eval.c
index 4c607f76ca..ecbe790ae2 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1646,26 +1646,94 @@ static const rb_iseq_t *
pm_eval_make_iseq(VALUE src, VALUE fname, int line,
const struct rb_block *base_block)
{
- rb_iseq_t *iseq = NULL;
const rb_iseq_t *const parent = vm_block_iseq(base_block);
const rb_iseq_t *iseq = parent;
VALUE name = rb_fstring_lit("<compiled>");
fname = rb_fstring_lit("<compiled>");
pm_parse_result_t result = { 0 };
- VALUE error;
-
- error = pm_parse_string(&result, src, fname);
+ // Cout scopes, one for each parent iseq, plus one for our local scope
+ int scopes_count = 0;
+ do {
+ scopes_count++;
+ } while ((iseq = ISEQ_BODY(iseq)->parent_iseq) && (ISEQ_BODY(iseq)->type != ISEQ_TYPE_TOP));
+ pm_options_scopes_init(&result.options, scopes_count + 1);
+
+ // Walk over the scope tree, adding known locals at the correct depths. The
+ // scope array should be deepest -> shallowest. so lower indexes in the
+ // scopes array refer to root nodes on the tree, and higher indexes are the
+ // leaf nodes.
+ iseq = parent;
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ pm_options_scope_init(options_scope, locals_count);
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ pm_string_t *scope_local = &options_scope->locals[local_index];
+ const char *name = rb_id2name(ISEQ_BODY(iseq)->local_table[local_index]);
+ if (name) pm_string_constant_init(scope_local, name, strlen(name));
+ }
- if (error == Qnil) {
- iseq = pm_iseq_new_eval(&result.node, name, fname, fname, ln, parent, 0);
- pm_parse_result_free(&result);
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
}
- else {
+
+ // Add our empty local scope at the very end of the array for our eval
+ // scope's locals.
+ pm_options_scope_init(&result.options.scopes[scopes_count], 0);
+ VALUE error = pm_parse_string(&result, src, fname);
+
+ // If the parse failed, clean up and raise.
+ if (error != Qnil) {
pm_parse_result_free(&result);
rb_exc_raise(error);
}
+ // Create one scope node for each scope passed in, initialize the local
+ // lookup table with all the local variable information attached to the
+ // scope used by the parser.
+ pm_scope_node_t *node = &result.node;
+ iseq = parent;
+
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ pm_scope_node_t *parent_scope = ruby_xcalloc(1, sizeof(pm_scope_node_t));
+ if (parent_scope == NULL) abort();
+
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ parent_scope->parser = &result.parser;
+ parent_scope->index_lookup_table = st_init_numtable();
+
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+ parent_scope->local_table_for_iseq_size = locals_count;
+ pm_constant_id_list_init(&parent_scope->locals);
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ const pm_string_t *scope_local = &options_scope->locals[local_index];
+
+ pm_constant_id_t constant_id = 0;
+ if (pm_string_length(scope_local) > 0) {
+ constant_id = pm_constant_pool_insert_constant(
+ &result.parser.constant_pool, pm_string_source(scope_local),
+ pm_string_length(scope_local));
+ st_insert(parent_scope->index_lookup_table, (st_data_t)constant_id, (st_data_t)local_index);
+ }
+ pm_constant_id_list_append(&parent_scope->locals, constant_id);
+ }
+
+ node->previous = parent_scope;
+ node = parent_scope;
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+ }
+
+ iseq = pm_iseq_new_eval(&result.node, name, fname, fname, line, parent, 0);
+
+ pm_scope_node_t *prev = result.node.previous;
+ while (prev) {
+ pm_scope_node_t *next = prev->previous;
+ ruby_xfree(prev);
+ prev = next;
+ }
+ pm_parse_result_free(&result);
return iseq;
}