mirror of
https://github.com/ruby/ruby.git
synced 2025-09-15 08:33:58 +02:00
[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:
parent
e1d995a96c
commit
85ad5be9ee
2 changed files with 406 additions and 135 deletions
528
prism_compile.c
528
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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue