diff --git a/prism_compile.c b/prism_compile.c index 779ebd2003..c04040394e 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -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 = ¶meters_node->optionals; requireds_list = ¶meters_node->requireds; keywords_list = ¶meters_node->keywords; posts_list = ¶meters_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; } - 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; - - for (size_t i = 0; i < locals_size; i++) { - pm_constant_id_t constant_id = locals->ids[i]; - ID local; - if (constant_id & TEMP_CONSTANT_IDENTIFIER) { - local = rb_make_temporary_id(i); - } - 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); - } - - scope_node->index_lookup_table = index_lookup_table; - - int arg_size = 0; - - 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; - - // 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; - + if (requireds_list) { 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 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 (parameters_node && parameters_node->rest) { - // 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); + 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++; + } } } - if (posts_list && posts_list->size) { - body->param.post_num = (int) posts_list->size; - body->param.post_start = arg_size; - body->param.flags.has_post = true; - arg_size += body->param.post_num; + // We can create local_table_for_iseq with the correct size + VALUE idtmp = 0; + 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; + + //********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; + // 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))); + } + } + } + + body->param.lead_num = (int) requireds_list->size; + body->param.flags.has_lead = true; } + // 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; + body->param.flags.has_opt = true; + + 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.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 = local_index; + body->param.flags.has_post = true; + + 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; - break; + 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; - break; + locals_body_index = ((pm_lambda_node_t *)scope_node->ast_node)->locals_body_index; + break; + } + default: { } - default: - RUBY_ASSERT("unreachable"); - break; } - 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: { diff --git a/prism_compile.h b/prism_compile.h index 7f45ddd1d2..34fac178d1 100644 --- a/prism_compile.h +++ b/prism_compile.h @@ -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;