mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
[PRISM] Use a flat lookup table for locals instead of an st_table
This commit is contained in:
1 parent
e6cf66a8e7
commit
e10f2521a2
4 files changed
+221
-157
Notes:
git
2026-03-21 00:37:53 +00:00
No files matched your search
@@ -1096,41 +1096,15 @@ rb_iseq_new_with_opt(VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
|
||||
return iseq_translate(iseq);
|
||||
}
|
||||
|
||||
struct pm_iseq_new_with_opt_data {
|
||||
rb_iseq_t *iseq;
|
||||
pm_scope_node_t *node;
|
||||
};
|
||||
|
||||
VALUE
|
||||
pm_iseq_new_with_opt_try(VALUE d)
|
||||
{
|
||||
struct pm_iseq_new_with_opt_data *data = (struct pm_iseq_new_with_opt_data *)d;
|
||||
|
||||
// This can compile child iseqs, which can raise syntax errors
|
||||
pm_iseq_compile_node(data->iseq, data->node);
|
||||
|
||||
// This raises an exception if there is a syntax error
|
||||
finish_iseq_build(data->iseq);
|
||||
|
||||
return Qundef;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a step in the prism compiler that is called once all of the various
|
||||
* options have been established. It is called from one of the pm_iseq_new_*
|
||||
* functions or from the RubyVM::InstructionSequence APIs. It is responsible for
|
||||
* allocating the instruction sequence, calling into the compiler, and returning
|
||||
* the built instruction sequence.
|
||||
*
|
||||
* Importantly, this is also the function where the compiler is re-entered to
|
||||
* compile child instruction sequences. A child instruction sequence is always
|
||||
* compiled using a scope node, which is why we cast it explicitly to that here
|
||||
* in the parameters (as opposed to accepting a generic pm_node_t *).
|
||||
* Core implementation for building a prism iseq. This does not use rb_protect,
|
||||
* so any exceptions (e.g. from finish_iseq_build) propagate normally up the
|
||||
* call stack — matching the parse.y compiler's behavior.
|
||||
*/
|
||||
rb_iseq_t *
|
||||
pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
|
||||
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
|
||||
enum rb_iseq_type type, const rb_compile_option_t *option, int *error_state)
|
||||
pm_iseq_build(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
|
||||
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
|
||||
enum rb_iseq_type type, const rb_compile_option_t *option)
|
||||
{
|
||||
rb_iseq_t *iseq = iseq_alloc();
|
||||
ISEQ_BODY(iseq)->prism = true;
|
||||
@@ -1157,15 +1131,59 @@ pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpa
|
||||
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &code_location, node->ast_node->node_id,
|
||||
parent, isolated_depth, type, node->script_lines == NULL ? Qnil : *node->script_lines, option);
|
||||
|
||||
pm_iseq_compile_node(iseq, node);
|
||||
finish_iseq_build(iseq);
|
||||
|
||||
return iseq_translate(iseq);
|
||||
}
|
||||
|
||||
struct pm_iseq_new_with_opt_data {
|
||||
rb_iseq_t *iseq;
|
||||
pm_scope_node_t *node;
|
||||
VALUE name, path, realpath;
|
||||
int first_lineno, isolated_depth;
|
||||
const rb_iseq_t *parent;
|
||||
enum rb_iseq_type type;
|
||||
const rb_compile_option_t *option;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
pm_iseq_new_with_opt_try(VALUE d)
|
||||
{
|
||||
struct pm_iseq_new_with_opt_data *data = (struct pm_iseq_new_with_opt_data *)d;
|
||||
data->iseq = pm_iseq_build(data->node, data->name, data->path, data->realpath,
|
||||
data->first_lineno, data->parent, data->isolated_depth,
|
||||
data->type, data->option);
|
||||
return Qundef;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a step in the prism compiler that is called once all of the various
|
||||
* options have been established. It is called from one of the pm_iseq_new_*
|
||||
* functions or from the RubyVM::InstructionSequence APIs.
|
||||
*
|
||||
* This function uses rb_protect to catch exceptions, storing the error state
|
||||
* in the provided out parameter. This is only needed at top-level entry points
|
||||
* where the caller wants to handle errors gracefully. Child iseqs compiled
|
||||
* during the compilation process do NOT go through this function — they use
|
||||
* pm_iseq_build directly, letting exceptions propagate naturally (matching
|
||||
* the parse.y compiler's behavior).
|
||||
*/
|
||||
rb_iseq_t *
|
||||
pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
|
||||
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
|
||||
enum rb_iseq_type type, const rb_compile_option_t *option, int *error_state)
|
||||
{
|
||||
struct pm_iseq_new_with_opt_data data = {
|
||||
.iseq = iseq,
|
||||
.node = node
|
||||
.node = node, .name = name, .path = path, .realpath = realpath,
|
||||
.first_lineno = first_lineno, .parent = parent,
|
||||
.isolated_depth = isolated_depth, .type = type, .option = option
|
||||
};
|
||||
rb_protect(pm_iseq_new_with_opt_try, (VALUE)&data, error_state);
|
||||
|
||||
if (*error_state) return NULL;
|
||||
|
||||
return iseq_translate(iseq);
|
||||
return data.iseq;
|
||||
}
|
||||
|
||||
rb_iseq_t *
|
||||
|
||||
+81
-112
@@ -142,6 +142,19 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node
|
||||
#define PM_COMPILE_NOT_POPPED(node) \
|
||||
pm_compile_node(iseq, (node), ret, false, scope_node)
|
||||
|
||||
// Direct-indexed lookup table. -1 means "not present".
|
||||
#define PM_INDEX_LOOKUP_TABLE_INIT { .values = NULL, .capacity = 0, .owned = false }
|
||||
|
||||
static inline void
|
||||
pm_index_lookup_table_init(pm_index_lookup_table_t *table, int constants_size, rb_iseq_t *iseq)
|
||||
{
|
||||
int capacity = constants_size + PM_INDEX_LOOKUP_SPECIALS;
|
||||
table->values = compile_data_alloc2_type(iseq, int, capacity);
|
||||
memset(table->values, -1, capacity * sizeof(int));
|
||||
table->capacity = capacity;
|
||||
table->owned = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached line lookup that avoids repeated binary searches. Since the compiler
|
||||
* walks the AST roughly in source order, consecutive lookups tend to be for
|
||||
@@ -1311,14 +1324,15 @@ static pm_local_index_t
|
||||
pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
|
||||
{
|
||||
pm_local_index_t lindex = { 0 };
|
||||
st_data_t local_index;
|
||||
int local_index;
|
||||
|
||||
int level;
|
||||
for (level = 0; level < start_depth; level++) {
|
||||
scope_node = scope_node->previous;
|
||||
}
|
||||
|
||||
while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
|
||||
while (!pm_index_lookup_table_lookup(&scope_node->index_lookup_table, constant_id, &local_index))
|
||||
{
|
||||
level++;
|
||||
|
||||
if (scope_node->previous) {
|
||||
@@ -1342,12 +1356,10 @@ pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_con
|
||||
// We add a constants mapping on the scope_node which is a mapping from
|
||||
// these constant_id indexes to the CRuby IDs that they represent.
|
||||
// This helper method allows easy access to those IDs
|
||||
static ID
|
||||
static inline ID
|
||||
pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
|
||||
{
|
||||
if (constant_id < 1 || constant_id > pm_parser_constants_size(scope_node->parser)) {
|
||||
rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
|
||||
}
|
||||
RUBY_ASSERT(constant_id >= 1 && constant_id <= pm_parser_constants_size(scope_node->parser));
|
||||
return scope_node->constants[constant_id - 1];
|
||||
}
|
||||
|
||||
@@ -1356,18 +1368,11 @@ pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_i
|
||||
{
|
||||
debugs("[new_child_iseq]> ---------------------------------------\n");
|
||||
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
|
||||
int error_state;
|
||||
rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
|
||||
rb_iseq_t *ret_iseq = pm_iseq_build(node, name,
|
||||
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
|
||||
line_no, parent,
|
||||
isolated_depth ? isolated_depth + 1 : 0,
|
||||
type, ISEQ_COMPILE_DATA(iseq)->option, &error_state);
|
||||
|
||||
if (error_state) {
|
||||
pm_scope_node_destroy(node);
|
||||
RUBY_ASSERT(ret_iseq == NULL);
|
||||
rb_jump_tag(error_state);
|
||||
}
|
||||
type, ISEQ_COMPILE_DATA(iseq)->option);
|
||||
debugs("[new_child_iseq]< ---------------------------------------\n");
|
||||
return ret_iseq;
|
||||
}
|
||||
@@ -3307,30 +3312,27 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
|
||||
void
|
||||
pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
|
||||
{
|
||||
// This is very important, otherwise the scope node could be seen as having
|
||||
// certain flags set that _should not_ be set.
|
||||
memset(scope, 0, sizeof(pm_scope_node_t));
|
||||
if (previous) {
|
||||
// Copy inherited fields from the parent scope in one shot, then
|
||||
// zero out the fields that are scope-specific.
|
||||
*scope = *previous;
|
||||
scope->locals = (pm_constant_id_list_t) { 0 };
|
||||
scope->parameters = NULL;
|
||||
scope->body = NULL;
|
||||
scope->local_table_for_iseq_size = 0;
|
||||
scope->index_lookup_table = (pm_index_lookup_table_t) PM_INDEX_LOOKUP_TABLE_INIT;
|
||||
scope->pre_execution_anchor = NULL;
|
||||
}
|
||||
else {
|
||||
memset(scope, 0, sizeof(pm_scope_node_t));
|
||||
}
|
||||
|
||||
scope->base.type = PM_SCOPE_NODE;
|
||||
scope->base.location.start = node->location.start;
|
||||
scope->base.location.length = node->location.length;
|
||||
|
||||
scope->previous = previous;
|
||||
scope->ast_node = (pm_node_t *) node;
|
||||
|
||||
if (previous) {
|
||||
scope->parser = previous->parser;
|
||||
scope->options = previous->options;
|
||||
scope->line_offsets = previous->line_offsets;
|
||||
scope->start_line = previous->start_line;
|
||||
scope->encoding = previous->encoding;
|
||||
scope->filepath_encoding = previous->filepath_encoding;
|
||||
scope->constants = previous->constants;
|
||||
scope->coverage_enabled = previous->coverage_enabled;
|
||||
scope->script_lines = previous->script_lines;
|
||||
scope->last_line = previous->last_line;
|
||||
}
|
||||
|
||||
switch (PM_NODE_TYPE(node)) {
|
||||
case PM_BLOCK_NODE: {
|
||||
const pm_block_node_t *cast = (const pm_block_node_t *) node;
|
||||
@@ -3427,8 +3429,8 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
|
||||
void
|
||||
pm_scope_node_destroy(pm_scope_node_t *scope_node)
|
||||
{
|
||||
if (scope_node->index_lookup_table) {
|
||||
st_free_table(scope_node->index_lookup_table);
|
||||
if (scope_node->index_lookup_table.owned) {
|
||||
xfree(scope_node->index_lookup_table.values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3618,8 +3620,7 @@ pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope
|
||||
pm_scope_node_t next_scope_node;
|
||||
pm_scope_node_init(&def.base, &next_scope_node, scope_node);
|
||||
|
||||
int error_state;
|
||||
const rb_iseq_t *mandatory_only_iseq = pm_iseq_new_with_opt(
|
||||
const rb_iseq_t *mandatory_only_iseq = pm_iseq_build(
|
||||
&next_scope_node,
|
||||
rb_iseq_base_label(iseq),
|
||||
rb_iseq_path(iseq),
|
||||
@@ -3628,16 +3629,10 @@ pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope
|
||||
NULL,
|
||||
0,
|
||||
ISEQ_TYPE_METHOD,
|
||||
ISEQ_COMPILE_DATA(iseq)->option,
|
||||
&error_state
|
||||
ISEQ_COMPILE_DATA(iseq)->option
|
||||
);
|
||||
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
|
||||
|
||||
if (error_state) {
|
||||
RUBY_ASSERT(ISEQ_BODY(iseq)->mandatory_only_iseq == NULL);
|
||||
rb_jump_tag(error_state);
|
||||
}
|
||||
|
||||
pm_scope_node_destroy(&next_scope_node);
|
||||
return COMPILE_OK;
|
||||
}
|
||||
@@ -4755,33 +4750,7 @@ pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_sc
|
||||
PUSH_SEQ(ret, ensure);
|
||||
}
|
||||
|
||||
struct pm_local_table_insert_ctx {
|
||||
pm_scope_node_t *scope_node;
|
||||
rb_ast_id_table_t *local_table_for_iseq;
|
||||
int local_index;
|
||||
};
|
||||
|
||||
static int
|
||||
pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
|
||||
{
|
||||
if (!existing) {
|
||||
pm_constant_id_t constant_id = (pm_constant_id_t) *key;
|
||||
struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
|
||||
|
||||
pm_scope_node_t *scope_node = ctx->scope_node;
|
||||
rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
|
||||
int local_index = ctx->local_index;
|
||||
|
||||
ID local = pm_constant_id_lookup(scope_node, constant_id);
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
|
||||
*value = (st_data_t)local_index;
|
||||
|
||||
ctx->local_index++;
|
||||
}
|
||||
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a local into the local table for the iseq. This is used to create the
|
||||
@@ -4789,24 +4758,23 @@ pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int
|
||||
* inserted are regular named locals, as opposed to special forwarding locals.
|
||||
*/
|
||||
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)
|
||||
pm_insert_local_index(pm_constant_id_t constant_id, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
|
||||
{
|
||||
RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
|
||||
|
||||
ID local = pm_constant_id_lookup(scope_node, constant_id);
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
|
||||
pm_index_lookup_table_insert(index_lookup_table, constant_id, local_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a local into the local table for the iseq that is a special forwarding
|
||||
* local variable.
|
||||
* Insert a special forwarding local (*, **, &, ...) into the local table.
|
||||
*/
|
||||
static void
|
||||
pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
|
||||
pm_insert_local_special(pm_constant_id_t special_id, ID local_name, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
|
||||
{
|
||||
local_table_for_iseq->ids[local_index] = local_name;
|
||||
st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
|
||||
pm_index_lookup_table_insert(index_lookup_table, special_id, local_index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4816,7 +4784,7 @@ pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_t
|
||||
* local and index lookup tables and increments the local index as necessary.
|
||||
*/
|
||||
static int
|
||||
pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
|
||||
pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
|
||||
{
|
||||
for (size_t index = 0; index < node->lefts.size; index++) {
|
||||
const pm_node_t *left = node->lefts.nodes[index];
|
||||
@@ -6312,8 +6280,9 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
// hidden variables and multi target nodes
|
||||
size_t locals_size = locals->size;
|
||||
|
||||
// Index lookup table buffer size is only the number of the locals
|
||||
st_table *index_lookup_table = st_init_numtable();
|
||||
// Index lookup table buffer size is only the number of the locals.
|
||||
// We'll initialize it after computing table_size below.
|
||||
pm_index_lookup_table_t index_lookup_table = PM_INDEX_LOOKUP_TABLE_INIT;
|
||||
|
||||
int table_size = (int) locals_size;
|
||||
|
||||
@@ -6437,6 +6406,10 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
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;
|
||||
|
||||
// Init the direct-indexed lookup table. The capacity is based on the
|
||||
// parser's constant pool size (for regular locals) plus special slots.
|
||||
pm_index_lookup_table_init(&index_lookup_table, (int) pm_parser_constants_size(scope_node->parser), iseq);
|
||||
|
||||
//********END OF STEP 1**********
|
||||
|
||||
//********STEP 2**********
|
||||
@@ -6489,7 +6462,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -6522,7 +6495,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6548,14 +6521,14 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
|
||||
// ^
|
||||
body->param.flags.anon_rest = true;
|
||||
pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index, &index_lookup_table, local_table_for_iseq);
|
||||
}
|
||||
|
||||
local_index++;
|
||||
@@ -6595,7 +6568,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -6630,7 +6603,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
local_index++;
|
||||
}
|
||||
@@ -6660,7 +6633,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
local_index++;
|
||||
}
|
||||
@@ -6722,12 +6695,12 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
body->param.flags.anon_kwrest = true;
|
||||
pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index, &index_lookup_table, local_table_for_iseq);
|
||||
}
|
||||
|
||||
local_index++;
|
||||
@@ -6741,7 +6714,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
body->param.rest_start = local_index;
|
||||
body->param.flags.has_rest = true;
|
||||
body->param.flags.anon_rest = true;
|
||||
pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index++, &index_lookup_table, local_table_for_iseq);
|
||||
|
||||
// Add the anonymous **
|
||||
RUBY_ASSERT(!body->param.flags.has_kw);
|
||||
@@ -6750,16 +6723,16 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
body->param.flags.anon_kwrest = true;
|
||||
body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
|
||||
keyword->rest_start = local_index;
|
||||
pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index++, &index_lookup_table, local_table_for_iseq);
|
||||
|
||||
// Add the anonymous &
|
||||
body->param.block_start = local_index;
|
||||
body->param.flags.has_block = true;
|
||||
pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index++, &index_lookup_table, local_table_for_iseq);
|
||||
}
|
||||
|
||||
// Add the ...
|
||||
pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_DOT3, idDot3, local_index++, &index_lookup_table, local_table_for_iseq);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -6785,11 +6758,11 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
}
|
||||
else {
|
||||
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
|
||||
pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index, &index_lookup_table, local_table_for_iseq);
|
||||
}
|
||||
|
||||
local_index++;
|
||||
@@ -6825,7 +6798,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
const pm_node_t *required = requireds_list->nodes[i];
|
||||
|
||||
if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
|
||||
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, index_lookup_table, local_table_for_iseq, scope_node, local_index);
|
||||
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6839,7 +6812,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
const pm_node_t *post = posts_list->nodes[i];
|
||||
|
||||
if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
|
||||
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, index_lookup_table, local_table_for_iseq, scope_node, local_index);
|
||||
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6867,7 +6840,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
const uint8_t param_name[] = { '_', '1' + i };
|
||||
pm_constant_id_t constant_id = pm_parser_constant_find(scope_node->parser, param_name, 2);
|
||||
RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
|
||||
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
|
||||
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
body->param.lead_num = maximum;
|
||||
body->param.flags.has_lead = true;
|
||||
@@ -6890,7 +6863,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
if (block_locals && block_locals->size) {
|
||||
for (size_t i = 0; i < block_locals->size; i++, local_index++) {
|
||||
pm_constant_id_t constant_id = ((const 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);
|
||||
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6899,14 +6872,13 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
for (size_t i = 0; i < scope_node->locals.size; i++) {
|
||||
pm_constant_id_t constant_id = locals->ids[i];
|
||||
if (constant_id) {
|
||||
struct pm_local_table_insert_ctx ctx;
|
||||
ctx.scope_node = scope_node;
|
||||
ctx.local_table_for_iseq = local_table_for_iseq;
|
||||
ctx.local_index = local_index;
|
||||
|
||||
st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
|
||||
|
||||
local_index = ctx.local_index;
|
||||
int existing;
|
||||
if (!pm_index_lookup_table_lookup(&index_lookup_table, constant_id, &existing)) {
|
||||
ID local = pm_constant_id_lookup(scope_node, constant_id);
|
||||
local_table_for_iseq->ids[local_index] = local;
|
||||
pm_index_lookup_table_insert(&index_lookup_table, constant_id, local_index);
|
||||
local_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6914,10 +6886,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
|
||||
//********END OF STEP 4**********
|
||||
|
||||
// We set the index_lookup_table on the scope node so we can
|
||||
// refer to the parameters correctly
|
||||
if (scope_node->index_lookup_table) {
|
||||
st_free_table(scope_node->index_lookup_table);
|
||||
}
|
||||
// refer to the parameters correctly.
|
||||
scope_node->index_lookup_table = index_lookup_table;
|
||||
iseq_calc_param_size(iseq);
|
||||
|
||||
@@ -11389,15 +11358,15 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines
|
||||
scope_node->line_offsets = pm_parser_line_offsets(parser);
|
||||
scope_node->start_line = pm_parser_start_line(parser);
|
||||
size_t constants_size = pm_parser_constants_size(parser);
|
||||
scope_node->constants = constants_size ? xcalloc(constants_size, sizeof(ID)) : NULL;
|
||||
scope_node->constants = constants_size ? xmalloc(constants_size * sizeof(ID)) : NULL;
|
||||
|
||||
pm_intern_constants_ctx_t intern_ctx = { .constants = scope_node->constants, .encoding = scope_node->encoding, .index = 0 };
|
||||
pm_parser_constants_each(parser, pm_intern_constants_callback, &intern_ctx);
|
||||
|
||||
scope_node->index_lookup_table = st_init_numtable();
|
||||
pm_constant_id_list_t *locals = &scope_node->locals;
|
||||
pm_index_lookup_table_init_heap(&scope_node->index_lookup_table, (int) constants_size);
|
||||
for (size_t index = 0; index < locals->size; index++) {
|
||||
st_insert(scope_node->index_lookup_table, locals->ids[index], index);
|
||||
pm_index_lookup_table_insert(&scope_node->index_lookup_table, locals->ids[index], (int) index);
|
||||
}
|
||||
|
||||
// If we got here, this is a success and we can return Qnil to indicate that
|
||||
|
||||
+85
-7
@@ -16,6 +16,82 @@ typedef struct pm_local_index_struct {
|
||||
// A declaration for the struct that lives in compile.c.
|
||||
struct iseq_link_anchor;
|
||||
|
||||
/**
|
||||
* A direct-indexed lookup table mapping constant IDs to local variable indices.
|
||||
* Regular constant IDs (1..constants_size) index directly. Special forwarding
|
||||
* parameter IDs (idMULT|FLAG, etc.) are mapped to 4 extra slots at the end.
|
||||
*
|
||||
* All lookups are O(1) — a single array dereference.
|
||||
* The table is arena-allocated for child scopes (no explicit free needed).
|
||||
*/
|
||||
typedef struct {
|
||||
/** Array of local indices, indexed by constant_id. -1 means not present. */
|
||||
int *values;
|
||||
|
||||
/** Total number of slots (constants_size + PM_INDEX_LOOKUP_SPECIALS). */
|
||||
int capacity;
|
||||
|
||||
/** Whether the values array is heap-allocated and needs explicit free. */
|
||||
bool owned;
|
||||
} pm_index_lookup_table_t;
|
||||
|
||||
/** Number of extra slots for special forwarding parameter IDs. */
|
||||
#define PM_INDEX_LOOKUP_SPECIALS 4
|
||||
|
||||
/** Slot offsets for special forwarding parameters (relative to constants_size). */
|
||||
#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t) (1 << 31))
|
||||
#define PM_INDEX_LOOKUP_SPECIAL_MULT 0
|
||||
#define PM_INDEX_LOOKUP_SPECIAL_POW 1
|
||||
#define PM_INDEX_LOOKUP_SPECIAL_AND 2
|
||||
#define PM_INDEX_LOOKUP_SPECIAL_DOT3 3
|
||||
|
||||
/**
|
||||
* Special constant IDs for forwarding parameters. These use bit 31 to
|
||||
* distinguish them from regular prism constant pool IDs. The lower bits
|
||||
* encode which special slot (0-3) they map to in the lookup table.
|
||||
*/
|
||||
#define PM_CONSTANT_MULT ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_MULT))
|
||||
#define PM_CONSTANT_POW ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_POW))
|
||||
#define PM_CONSTANT_AND ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_AND))
|
||||
#define PM_CONSTANT_DOT3 ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_DOT3))
|
||||
|
||||
static inline int
|
||||
pm_index_lookup_table_index(const pm_index_lookup_table_t *table, pm_constant_id_t key)
|
||||
{
|
||||
if (LIKELY(!(key & PM_SPECIAL_CONSTANT_FLAG))) {
|
||||
return (int) key - 1;
|
||||
}
|
||||
return table->capacity - PM_INDEX_LOOKUP_SPECIALS + (int)(key & ~PM_SPECIAL_CONSTANT_FLAG);
|
||||
}
|
||||
|
||||
static inline void
|
||||
pm_index_lookup_table_insert(pm_index_lookup_table_t *table, pm_constant_id_t key, int value)
|
||||
{
|
||||
int idx = pm_index_lookup_table_index(table, key);
|
||||
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
|
||||
table->values[idx] = value;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pm_index_lookup_table_lookup(const pm_index_lookup_table_t *table, pm_constant_id_t key, int *value)
|
||||
{
|
||||
int idx = pm_index_lookup_table_index(table, key);
|
||||
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
|
||||
if (table->values[idx] == -1) return 0;
|
||||
*value = table->values[idx];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pm_index_lookup_table_init_heap(pm_index_lookup_table_t *table, int constants_size)
|
||||
{
|
||||
int cap = constants_size + PM_INDEX_LOOKUP_SPECIALS;
|
||||
table->values = (int *) ruby_xmalloc(cap * sizeof(int));
|
||||
memset(table->values, -1, cap * sizeof(int));
|
||||
table->capacity = cap;
|
||||
table->owned = true;
|
||||
}
|
||||
|
||||
// ScopeNodes are helper nodes, and will never be part of the AST. We manually
|
||||
// declare them here to avoid generating them.
|
||||
typedef struct pm_scope_node {
|
||||
@@ -54,7 +130,14 @@ typedef struct pm_scope_node {
|
||||
int local_table_for_iseq_size;
|
||||
|
||||
ID *constants;
|
||||
st_table *index_lookup_table;
|
||||
|
||||
/**
|
||||
* A flat lookup table mapping constant IDs (or special IDs) to local
|
||||
* variable indices. When allocated from the compile data arena (child
|
||||
* scopes), no explicit free is needed. When heap-allocated (top-level
|
||||
* scope in pm_parse_process), owned is set to true so destroy can free it.
|
||||
*/
|
||||
pm_index_lookup_table_t index_lookup_table;
|
||||
|
||||
// The current coverage setting, passed down through the various scopes.
|
||||
int coverage_enabled;
|
||||
@@ -96,12 +179,6 @@ typedef struct {
|
||||
bool parsed;
|
||||
} pm_parse_result_t;
|
||||
|
||||
#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31))
|
||||
#define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG))
|
||||
#define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG))
|
||||
#define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG))
|
||||
#define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG))
|
||||
|
||||
void pm_parse_result_init(pm_parse_result_t *result);
|
||||
VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error);
|
||||
VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines);
|
||||
@@ -116,5 +193,6 @@ rb_iseq_t *pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE
|
||||
rb_iseq_t *pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt, int *error_state);
|
||||
rb_iseq_t *pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, int *error_state);
|
||||
rb_iseq_t *pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option, int *error_state);
|
||||
rb_iseq_t *pm_iseq_build(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option);
|
||||
|
||||
VALUE pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node);
|
||||
@@ -1840,9 +1840,8 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
|
||||
const pm_options_scope_t *options_scope = pm_options_scope(result.options, scopes_count - scopes_index - 1);
|
||||
parent_scope->coverage_enabled = coverage_enabled;
|
||||
parent_scope->parser = result.parser;
|
||||
parent_scope->index_lookup_table = st_init_numtable();
|
||||
|
||||
int locals_count = ISEQ_BODY(iseq)->local_table_size;
|
||||
pm_index_lookup_table_init_heap(&parent_scope->index_lookup_table, (int) pm_parser_constants_size(result.parser));
|
||||
parent_scope->local_table_for_iseq_size = locals_count;
|
||||
pm_constant_id_list_init(&parent_scope->locals);
|
||||
|
||||
@@ -1877,7 +1876,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
|
||||
constant_id = pm_parser_constant_find(result.parser, source, length);
|
||||
}
|
||||
|
||||
st_insert(parent_scope->index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
|
||||
pm_index_lookup_table_insert(&parent_scope->index_lookup_table, constant_id, local_index);
|
||||
}
|
||||
|
||||
pm_constant_id_list_append(result.arena, &parent_scope->locals, constant_id);
|
||||
|
||||
Reference in new issue
Block a user