[PRISM] Restructure parameters on ScopeNodes

This commit completely restructures how we handle parameters. The
motivation for this commit was the fix compilation of MultiTargetNodes
within parameters, including nested MultiTargetNodes. A subsequent
commit will actually do the compilation for the MultiTargetNodes.

This commit's main accomplishment is restructuring the locals table
and how we account for it on the ScopeNode, specifically with regards
to hidden variables.

It has multiple steps, all commented within the code, to calculate
the locals table correctly and compile the parameters:
- Step 1: Caculate the table size for the locals
- Step 2: Populate iv index table and local table
- Step 3: Fill in parameter names of MultiTargetNodes on local table
- Step 4: Fill in method body locals on local table
- Step 5: Compile any locals
This commit is contained in:
Jemma Issroff 2023-12-06 10:05:14 -05:00
parent e1d995a96c
commit 85ad5be9ee
2 changed files with 406 additions and 135 deletions

View file

@ -727,7 +727,7 @@ pm_lookup_local_index_any_scope(rb_iseq_t *iseq, pm_scope_node_t *scope_node, pm
return pm_lookup_local_index_any_scope(iseq, scope_node->previous, constant_id);
}
return scope_node->hidden_variable_count + (int)scope_node->index_lookup_table->num_entries - (int)local_index;
return scope_node->local_table_for_iseq_size - (int)local_index;
}
static int
@ -735,13 +735,11 @@ pm_lookup_local_index(rb_iseq_t *iseq, pm_scope_node_t *scope_node, pm_constant_
{
st_data_t local_index;
int locals_size = (int) scope_node->locals.size;
if (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
rb_bug("This local does not exist");
}
return scope_node->hidden_variable_count + locals_size - (int)local_index;
return scope_node->local_table_for_iseq_size - (int)local_index;
}
static int
@ -765,7 +763,7 @@ static ID
pm_constant_id_lookup(pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
{
if (constant_id < 1 || constant_id > scope_node->parser->constant_pool.size) {
rb_raise(rb_eArgError, "[PRISM] constant_id out of range: %u", (unsigned int)constant_id);
rb_bug("[PRISM] constant_id out of range: %u", (unsigned int)constant_id);
}
return scope_node->constants[constant_id - 1];
}
@ -1393,7 +1391,8 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
scope->body = NULL;
scope->constants = NULL;
scope->local_depth_offset = 0;
scope->hidden_variable_count = 0;
scope->local_table_for_iseq_size = 0;
if (previous) {
scope->constants = previous->constants;
scope->local_depth_offset = previous->local_depth_offset;
@ -1405,12 +1404,10 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
switch (PM_NODE_TYPE(node)) {
case PM_BLOCK_NODE: {
pm_block_node_t *cast = (pm_block_node_t *) node;
if (cast->parameters != NULL && PM_NODE_TYPE_P(cast->parameters, PM_BLOCK_PARAMETERS_NODE)) {
scope->parameters = ((pm_block_parameters_node_t *) cast->parameters)->parameters;
}
scope->body = cast->body;
scope->locals = cast->locals;
scope->local_depth_offset = 0;
scope->parameters = cast->parameters;
break;
}
case PM_CLASS_NODE: {
@ -1421,7 +1418,7 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
}
case PM_DEF_NODE: {
pm_def_node_t *cast = (pm_def_node_t *) node;
scope->parameters = cast->parameters;
scope->parameters = (pm_node_t *)cast->parameters;
scope->body = cast->body;
scope->locals = cast->locals;
break;
@ -1444,9 +1441,7 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
}
case PM_LAMBDA_NODE: {
pm_lambda_node_t *cast = (pm_lambda_node_t *) node;
if (cast->parameters != NULL && PM_NODE_TYPE_P(cast->parameters, PM_BLOCK_PARAMETERS_NODE)) {
scope->parameters = ((pm_block_parameters_node_t *) cast->parameters)->parameters;
}
scope->parameters = cast->parameters;
scope->body = cast->body;
scope->locals = cast->locals;
break;
@ -1892,6 +1887,69 @@ pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, const
ADD_SEQ(ret, ensure);
}
static void
pm_insert_local_index(pm_constant_id_t constant_id, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
{
ID local = pm_constant_id_lookup(scope_node, constant_id);
local_table_for_iseq->ids[local_index] = local;
st_insert(index_lookup_table, constant_id, local_index);
}
static int
pm_compile_multi_assign_params(pm_multi_target_node_t *multi, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
{
for (size_t m = 0; m < multi->lefts.size; m++) {
pm_node_t *multi_node = multi->lefts.nodes[m];
switch (PM_NODE_TYPE(multi_node)) {
case PM_REQUIRED_PARAMETER_NODE: {
pm_required_parameter_node_t *req = (pm_required_parameter_node_t *)multi_node;
pm_insert_local_index(req->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
break;
}
case PM_MULTI_TARGET_NODE: {
local_index = pm_compile_multi_assign_params((pm_multi_target_node_t *)multi_node, index_lookup_table, local_table_for_iseq, scope_node, local_index);
break;
}
default: {
rb_bug("Parameter within a MultiTargetNode isn't allowed");
}
}
}
if (multi->rest && PM_NODE_TYPE_P(multi->rest, PM_SPLAT_NODE)) {
pm_splat_node_t *rest = (pm_splat_node_t *)multi->rest;
if (rest->expression && PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE)) {
pm_required_parameter_node_t *req = (pm_required_parameter_node_t *)rest->expression;
pm_insert_local_index(req->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
}
}
for (size_t m = 0; m < multi->rights.size; m++) {
pm_node_t *multi_node = multi->rights.nodes[m];
switch (PM_NODE_TYPE(multi_node)) {
case PM_REQUIRED_PARAMETER_NODE: {
pm_required_parameter_node_t *req = (pm_required_parameter_node_t *)multi_node;
pm_insert_local_index(req->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
break;
}
case PM_MULTI_TARGET_NODE: {
local_index = pm_compile_multi_assign_params((pm_multi_target_node_t *)multi_node, index_lookup_table, local_table_for_iseq, scope_node, local_index);
break;
}
default: {
rb_bug("Parameter within a MultiTargetNode isn't allowed");
}
}
}
return local_index;
}
/*
* Compiles a prism node into instruction sequences
*
@ -2873,8 +2931,6 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
pm_constant_id_list_t locals;
pm_constant_id_list_init(&locals);
pm_constant_id_list_append(&locals, TEMP_CONSTANT_IDENTIFIER);
next_scope_node.locals = locals;
ADD_LABEL(ret, retry_label);
@ -4185,29 +4241,54 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
pm_scope_node_t *scope_node = (pm_scope_node_t *)node;
pm_constant_id_list_t *locals = &scope_node->locals;
pm_parameters_node_t *parameters_node = (pm_parameters_node_t *) scope_node->parameters;
pm_parameters_node_t *parameters_node = NULL;
pm_node_list_t *keywords_list = NULL;
pm_node_list_t *optionals_list = NULL;
pm_node_list_t *posts_list = NULL;
pm_node_list_t *requireds_list = NULL;
struct rb_iseq_param_keyword *keyword = NULL;
pm_node_list_t *block_locals = NULL;
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
if (scope_node->parameters) {
switch (PM_NODE_TYPE(scope_node->parameters)) {
case PM_BLOCK_PARAMETERS_NODE: {
pm_block_parameters_node_t *block_parameters_node = (pm_block_parameters_node_t *)scope_node->parameters;
parameters_node = block_parameters_node->parameters;
block_locals = &block_parameters_node->locals;
break;
}
case PM_PARAMETERS_NODE: {
parameters_node = (pm_parameters_node_t *) scope_node->parameters;
break;
}
case PM_NUMBERED_PARAMETERS_NODE: {
body->param.lead_num = ((pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
break;
}
default:
rb_bug("Unexpected node type for parameters: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
}
}
struct rb_iseq_param_keyword *keyword = NULL;
if (parameters_node) {
optionals_list = &parameters_node->optionals;
requireds_list = &parameters_node->requireds;
keywords_list = &parameters_node->keywords;
posts_list = &parameters_node->posts;
} else if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
body->param.lead_num = 1;
} else if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
body->param.opt_num = 0;
} else {
}
else {
body->param.lead_num = 0;
body->param.opt_num = 0;
}
//********STEP 1**********
// Goal: calculate the table size for the locals, accounting for
// hidden variables and multi target nodes
size_t locals_size = locals->size;
// Index lookup table buffer size is only the number of the locals
@ -4215,102 +4296,173 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
int table_size = (int) locals_size;
if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
table_size++;
}
if (keywords_list && keywords_list->size) {
table_size++;
scope_node->hidden_variable_count = 1;
}
if (requireds_list) {
for (size_t i = 0; i < requireds_list->size; i++) {
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *required = requireds_list->nodes[i];
if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
table_size++;
}
}
}
if (posts_list) {
for (size_t i = 0; i < posts_list->size; i++) {
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *required = posts_list->nodes[i];
if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
table_size++;
}
}
}
// We can create local_table_for_iseq with the correct size
VALUE idtmp = 0;
rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
tbl->size = table_size;
rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
local_table_for_iseq->size = table_size;
for (size_t i = 0; i < locals_size; i++) {
pm_constant_id_t constant_id = locals->ids[i];
//********END OF STEP 1**********
//********STEP 2**********
// Goal: populate iv index table as well as local table, keeping the
// layout of the local table consistent with the layout of the
// stack when calling the method
//
// Do a first pass on all of the parameters, setting their values in
// the local_table_for_iseq, _except_ for Multis who get a hidden
// variable in this step, and will get their names inserted in step 3
// local_index is a cursor that keeps track of the current
// index into local_table_for_iseq. The local table is actually a list,
// and the order of that list must match the order of the items pushed
// on the stack. We need to take in to account things pushed on the
// stack that _might not have a name_ (for example array destructuring).
// This index helps us know which item we're dealing with and also give
// those anonymous items temporary names (as below)
int local_index = 0;
// Here we figure out local table indices and insert them in to the
// index lookup table and local tables.
//
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^^^^^^^^^
if (requireds_list && requireds_list->size) {
for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
ID local;
if (constant_id & TEMP_CONSTANT_IDENTIFIER) {
local = rb_make_temporary_id(i);
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *required = requireds_list->nodes[i];
switch (PM_NODE_TYPE(required)) {
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^^^^^^
case PM_MULTI_TARGET_NODE: {
local = rb_make_temporary_id(local_index);
local_table_for_iseq->ids[local_index] = local;
break;
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^
case PM_REQUIRED_PARAMETER_NODE: {
pm_required_parameter_node_t * param = (pm_required_parameter_node_t *)required;
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
break;
}
default: {
rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
}
else {
local = pm_constant_id_lookup(scope_node, constant_id);
}
tbl->ids[i] = local;
st_insert(index_lookup_table, constant_id, i);
}
// We have keywords and so we need to allocate
// space for another variable
if (table_size > (int) locals_size) {
tbl->ids[locals_size] = rb_make_temporary_id(locals_size);
body->param.lead_num = (int) requireds_list->size;
body->param.flags.has_lead = true;
}
scope_node->index_lookup_table = index_lookup_table;
int arg_size = 0;
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^
if (optionals_list && optionals_list->size) {
body->param.opt_num = (int) optionals_list->size;
arg_size += body->param.opt_num;
LABEL **opt_table = (LABEL **)ALLOC_N(VALUE, optionals_list->size + 1);
LABEL *label;
body->param.flags.has_opt = true;
// TODO: Should we make an api for NEW_LABEL where you can pass
// a pointer to the label it should fill out? We already
// have a list of labels allocated above so it seems wasteful
// to do the copies.
for (size_t i = 0; i < optionals_list->size; i++) {
label = NEW_LABEL(lineno);
opt_table[i] = label;
ADD_LABEL(ret, label);
pm_node_t *optional_node = optionals_list->nodes[i];
PM_COMPILE_NOT_POPPED(optional_node);
}
// Set the last label
label = NEW_LABEL(lineno);
opt_table[optionals_list->size] = label;
ADD_LABEL(ret, label);
body->param.flags.has_opt = TRUE;
body->param.opt_table = (const VALUE *)opt_table;
}
if (requireds_list && requireds_list->size) {
body->param.lead_num = (int) requireds_list->size;
arg_size += body->param.lead_num;
body->param.flags.has_lead = true;
for (size_t i = 0; i < requireds_list->size; i++) {
pm_node_t *required_param = requireds_list->nodes[i];
// TODO: Fix MultiTargetNodes
if (PM_NODE_TYPE_P(required_param, PM_MULTI_TARGET_NODE)) {
PM_COMPILE(required_param);
}
for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
pm_constant_id_t name = ((pm_optional_parameter_node_t *)optionals_list->nodes[i])->name;
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
}
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^
if (parameters_node && parameters_node->rest) {
body->param.rest_start = local_index;
// If there's a trailing comma, we'll have an implicit rest node,
// and we don't want it to impact the rest variables on param
if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
body->param.rest_start = arg_size++;
body->param.flags.has_rest = true;
assert(body->param.rest_start != -1);
pm_constant_id_t name = ((pm_rest_parameter_node_t *)parameters_node->rest)->name;
if (name) {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
}
}
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^^^^^^^^^
if (posts_list && posts_list->size) {
body->param.post_num = (int) posts_list->size;
body->param.post_start = arg_size;
body->param.post_start = local_index;
body->param.flags.has_post = true;
arg_size += body->param.post_num;
for (size_t i = 0; i < posts_list->size; i++, local_index++) {
ID local;
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *post_node = posts_list->nodes[i];
switch (PM_NODE_TYPE(post_node)) {
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^^^^^^
case PM_MULTI_TARGET_NODE: {
local = rb_make_temporary_id(local_index);
local_table_for_iseq->ids[local_index] = local;
break;
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^
case PM_REQUIRED_PARAMETER_NODE: {
pm_required_parameter_node_t * param = (pm_required_parameter_node_t *)post_node;
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
break;
}
default: {
rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
}
}
}
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^^^^^
// Keywords create an internal variable on the parse tree
if (keywords_list && keywords_list->size) {
body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
keyword->num = (int) keywords_list->size;
arg_size += keyword->num;
keyword->bits_start = arg_size++;
body->param.flags.has_kw = true;
const VALUE default_values = rb_ary_hidden_new(1);
@ -4318,11 +4470,13 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
ID *ids = calloc(keywords_list->size, sizeof(ID));
for (size_t i = 0; i < keywords_list->size; i++) {
for (size_t i = 0; i < keywords_list->size; i++, local_index++) {
pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
pm_constant_id_t name;
switch PM_NODE_TYPE(keyword_parameter_node) {
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^^
case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
pm_optional_keyword_parameter_node_t *cast = ((pm_optional_keyword_parameter_node_t *)keyword_parameter_node);
@ -4337,9 +4491,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
int index = pm_lookup_local_index(iseq, scope_node, name);
int kw_bits_idx = table_size - body->param.keyword->bits_start;
int keyword_idx = (int)(i + scope_node->hidden_variable_count);
ADD_INSN2(ret, &dummy_line_node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx - 1));
ADD_INSN2(ret, &dummy_line_node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(i - 1));
ADD_INSNL(ret, &dummy_line_node, branchif, end_label);
PM_COMPILE(value);
ADD_SETLOCAL(ret, &dummy_line_node, index, 0);
@ -4350,6 +4502,8 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
break;
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^
case PM_REQUIRED_KEYWORD_PARAMETER_NODE: {
name = ((pm_required_keyword_parameter_node_t *)keyword_parameter_node)->name;
keyword->required_num++;
@ -4360,9 +4514,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
}
}
ids[i] = pm_constant_id_lookup(scope_node, name);
ID local = pm_constant_id_lookup(scope_node, name);
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
ids[i] = local;
}
keyword->bits_start = local_index;
keyword->table = ids;
VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
@ -4377,66 +4534,183 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
}
keyword->default_values = dvs;
// Hidden local for keyword arguments
ID local = rb_make_temporary_id(local_index);
local_table_for_iseq->ids[local_index] = local;
local_index++;
}
if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1) {
body->param.flags.ambiguous_param0 = true;
}
if (parameters_node) {
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^^
if (parameters_node->keyword_rest) {
if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_NO_KEYWORDS_PARAMETER_NODE)) {
body->param.flags.accepts_no_kwarg = true;
}
else {
if (body->param.flags.has_kw) {
arg_size--;
}
else {
body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
}
keyword->rest_start = arg_size++;
keyword->rest_start = local_index;
body->param.flags.has_kwrest = true;
pm_constant_id_t constant_id = ((pm_keyword_rest_parameter_node_t *)parameters_node->keyword_rest)->name;
if (constant_id) {
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
}
}
}
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^
if (parameters_node->block) {
body->param.block_start = arg_size;
body->param.block_start = local_index;
body->param.flags.has_block = true;
pm_constant_id_t name = ((pm_block_parameter_node_t *)parameters_node->block)->name;
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
}
}
//********END OF STEP 2**********
// The local table is now consistent with expected
// stack layout
// If there's only one required element in the parameters
// CRuby needs to recognize it as an ambiguous parameter
if (body->type == ISEQ_TYPE_BLOCK && arg_size == 1 && requireds_list && requireds_list->size == 1) {
body->param.flags.ambiguous_param0 = true;
//********STEP 3**********
// Goal: fill in the names of the parameters in MultiTargetNodes
//
// Go through requireds again to set the multis
if (requireds_list && requireds_list->size) {
for (size_t i = 0; i < requireds_list->size; i++) {
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *required = requireds_list->nodes[i];
if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
local_index = pm_compile_multi_assign_params((pm_multi_target_node_t *)required, index_lookup_table, local_table_for_iseq, scope_node, local_index);
}
}
}
iseq_calc_param_size(iseq);
body->param.size = arg_size;
// Go through posts again to set the multis
if (posts_list && posts_list->size) {
for (size_t i = 0; i < posts_list->size; i++) {
// For each MultiTargetNode, we're going to have one
// additional anonymous local not represented in the locals table
// We want to account for this in our table size
pm_node_t *post= posts_list->nodes[i];
if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
local_index = pm_compile_multi_assign_params((pm_multi_target_node_t *)post, index_lookup_table, local_table_for_iseq, scope_node, local_index);
}
}
}
// Set any anonymous locals for the for node
if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
ID local = rb_make_temporary_id(local_index);
local_table_for_iseq->ids[local_index] = local;
local_index++;
}
// Fill in any NumberedParameters, if they exist
if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
int maximum = ((pm_numbered_parameters_node_t *)scope_node->parameters)->maximum;
for (int i = 0; i < maximum; i++, local_index++) {
pm_constant_id_t constant_id = locals->ids[i];
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
}
}
//********END OF STEP 3**********
//********STEP 4**********
// Goal: fill in the method body locals
// To be explicit, these are the non-parameter locals
uint32_t locals_body_index = 0;
// Calculating the parameter size above does not account for numbered
// parameters. We can _only_ have numbered parameters if we don't have
// non numbered parameters. We verify this through asserts, and add the
// maximum numbered parameter size accordingly.
pm_node_t *block_parameters = NULL;
switch (PM_NODE_TYPE(scope_node->ast_node)) {
case PM_BLOCK_NODE: {
block_parameters = ((pm_block_node_t *) scope_node->ast_node)->parameters;
locals_body_index = ((pm_block_node_t *)scope_node->ast_node)->locals_body_index;
break;
}
case PM_DEF_NODE: {
locals_body_index = ((pm_def_node_t *)scope_node->ast_node)->locals_body_index;
break;
}
case PM_LAMBDA_NODE: {
block_parameters = ((pm_lambda_node_t *) scope_node->ast_node)->parameters;
locals_body_index = ((pm_lambda_node_t *)scope_node->ast_node)->locals_body_index;
break;
}
default:
RUBY_ASSERT("unreachable");
break;
default: {
}
}
if (block_parameters != NULL && PM_NODE_TYPE_P(block_parameters, PM_NUMBERED_PARAMETERS_NODE)) {
RUBY_ASSERT(body->param.size == 0);
body->param.size += ((pm_numbered_parameters_node_t *) block_parameters)->maximum;
if (scope_node->locals.size) {
for (size_t i = locals_body_index; i < scope_node->locals.size; i++) {
pm_constant_id_t constant_id = locals->ids[i];
if (constant_id) {
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
local_index++;
}
}
}
iseq_set_local_table(iseq, tbl);
// We fill in the block_locals, if they exist
// lambda { |x; y| y }
// ^
if (block_locals && block_locals->size) {
for (size_t i = 0; i < block_locals->size; i++, local_index++) {
pm_constant_id_t constant_id = ((pm_block_local_variable_node_t *)block_locals->nodes[i])->name;
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
}
}
//********END OF STEP 4**********
// We set the index_lookup_table on the scope node so we can
// refer to the parameters correctly
scope_node->index_lookup_table = index_lookup_table;
iseq_calc_param_size(iseq);
iseq_set_local_table(iseq, local_table_for_iseq);
scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
//********STEP 5************
// Goal: compile anything that needed to be compiled
if (optionals_list && optionals_list->size) {
LABEL **opt_table = (LABEL **)ALLOC_N(VALUE, optionals_list->size + 1);
LABEL *label;
// TODO: Should we make an api for NEW_LABEL where you can pass
// a pointer to the label it should fill out? We already
// have a list of labels allocated above so it seems wasteful
// to do the copies.
for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
label = NEW_LABEL(lineno);
opt_table[i] = label;
ADD_LABEL(ret, label);
pm_node_t *optional_node = optionals_list->nodes[i];
PM_COMPILE_NOT_POPPED(optional_node);
}
// Set the last label
label = NEW_LABEL(lineno);
opt_table[optionals_list->size] = label;
ADD_LABEL(ret, label);
body->param.opt_table = (const VALUE *)opt_table;
}
switch (body->type) {
case ISEQ_TYPE_BLOCK: {

View file

@ -6,18 +6,15 @@ typedef struct pm_scope_node {
pm_node_t base;
struct pm_scope_node *previous;
pm_node_t *ast_node;
struct pm_parameters_node *parameters;
pm_node_t *parameters;
pm_node_t *body;
pm_constant_id_list_t locals;
pm_parser_t *parser;
// There are sometimes when we need to track
// hidden variables that we have put on
// the local table for the stack to use, so
// that we properly account for them when giving
// local indexes. We do this with the
// hidden_variable_count
int hidden_variable_count;
// The size of the local table
// on the iseq which includes
// locals and hidden variables
int local_table_for_iseq_size;
ID *constants;
st_table *index_lookup_table;