diff --git a/prism/constant_pool.h b/prism/constant_pool.h index 8244c0d747d..61221ca9b30 100644 --- a/prism/constant_pool.h +++ b/prism/constant_pool.h @@ -11,6 +11,7 @@ #define PRISM_CONSTANT_POOL_H #include "prism/compiler/exported.h" +#include "prism/compiler/inline.h" #include "prism/compiler/nodiscard.h" #include "prism/compiler/nonnull.h" @@ -28,6 +29,22 @@ extern "C" { */ typedef uint32_t pm_constant_id_t; +/** + * Returns the index of the constant with the given id among the constants of + * the parser that produced it. The indices run from 0 to one less than + * pm_parser_constants_size, so they can index a cache with one entry per + * constant that was filled using pm_parser_constant_at. + * + * @param constant_id the id of the constant. Optional constant fields on nodes + * hold 0 when they are absent, which is not a valid id, so check for it + * before calling this function. + * @returns the index of the constant + */ +static PRISM_INLINE size_t +pm_constant_id_index(pm_constant_id_t constant_id) { + return (size_t) constant_id - 1; +} + /** * A list of constant IDs. Usually used to represent a set of locals. */ diff --git a/prism/parser.c b/prism/parser.c index b9103d7c259..1b3cce1df8c 100644 --- a/prism/parser.c +++ b/prism/parser.c @@ -7,6 +7,7 @@ #include "prism/internal/magic_comments.h" #include "prism/internal/xxh3.h" +#include #include #include @@ -338,15 +339,13 @@ pm_parser_constants_size(const pm_parser_t *parser) { } /** - * Iterates over the constants in the constant pool associated with the given - * parser and calls the given callback for each constant. + * Returns the constant at the given index in the constant pool associated with + * the given parser. */ -void -pm_parser_constants_each(const pm_parser_t *parser, pm_constant_callback_t callback, void *data) { - for (uint32_t index = 0; index < parser->constant_pool.size; index++) { - const pm_constant_t *constant = &parser->constant_pool.constants[index]; - callback(constant, data); - } +const pm_constant_t * +pm_parser_constant_at(const pm_parser_t *parser, size_t index) { + assert(index < parser->constant_pool.size); + return &parser->constant_pool.constants[index]; } /** diff --git a/prism/parser.h b/prism/parser.h index 0a057769831..4644204d843 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -364,30 +364,38 @@ PRISM_EXPORTED_FUNCTION const pm_diagnostic_t * pm_parser_warnings_first(const p PRISM_EXPORTED_FUNCTION size_t pm_parser_constants_size(const pm_parser_t *parser) PRISM_NONNULL(1); /** - * A callback function that can be used to process constants found while - * parsing. - */ -typedef void (*pm_constant_callback_t)(const pm_constant_t *constant, void *data); - -/** - * Iterates over the constants in the constant pool associated with the given - * parser and calls the given callback for each constant. + * Returns the constant at the given index in the constant pool associated with + * the given parser. Together with pm_constant_id_index, this lets a caller + * build a cache with one entry per constant and then find the entry for any + * constant id, as in: * - * @param parser the parser whose constants we want to iterate over - * @param callback the callback function to call for each constant. This function - * will be passed a pointer to the constant and the data parameter passed to - * this function. - * @param data the data to pass to the callback function for each constant. This - * can be NULL if no data needs to be passed to the callback function. + * ```c + * size_t size = pm_parser_constants_size(parser); + * for (size_t index = 0; index < size; index++) { + * cache[index] = convert(pm_parser_constant_at(parser, index)); + * } + * + * value = cache[pm_constant_id_index(constant_id)]; + * ``` + * + * For any constant id, pm_parser_constant_at(parser, + * pm_constant_id_index(constant_id)) is pm_parser_constant(parser, + * constant_id). + * + * @param parser the parser whose constant pool we want to look up from + * @param index the index of the constant, which must be less than + * pm_parser_constants_size(parser) + * @returns a pointer to the constant at the given index */ -PRISM_EXPORTED_FUNCTION void pm_parser_constants_each(const pm_parser_t *parser, pm_constant_callback_t callback, void *data) PRISM_NONNULL(1); +PRISM_EXPORTED_FUNCTION const pm_constant_t * pm_parser_constant_at(const pm_parser_t *parser, size_t index) PRISM_NONNULL(1); /** * Returns a pointer to the constant at the given id in the constant pool * associated with the given parser. * * @param parser the parser whose constant pool we want to look up from - * @param constant_id the id of the constant to look up (1-based) + * @param constant_id the id of the constant to look up. Optional constant + * fields on nodes hold 0 when they are absent, which is not a valid id. * @returns a pointer to the constant at the given id */ PRISM_EXPORTED_FUNCTION const pm_constant_t * pm_parser_constant(const pm_parser_t *parser, pm_constant_id_t constant_id) PRISM_NONNULL(1); diff --git a/prism/templates/ext/prism/api_node.c.erb b/prism/templates/ext/prism/api_node.c.erb index ec509426743..4419caace15 100644 --- a/prism/templates/ext/prism/api_node.c.erb +++ b/prism/templates/ext/prism/api_node.c.erb @@ -127,17 +127,14 @@ pm_node_stack_pop(pm_node_stack_node_t **stack) { return visit; } -typedef struct { - VALUE constants; - rb_encoding *encoding; -} pm_ast_constants_each_data_t; - -static void -pm_ast_constants_each(const pm_constant_t *constant, void *data) { - pm_ast_constants_each_data_t *constants_data = (pm_ast_constants_each_data_t *) data; +/** + * Build the Symbol for the given constant. + */ +static VALUE +pm_ast_constant(const pm_constant_t *constant, rb_encoding *encoding) { int state = 0; - VALUE string = rb_enc_str_new((const char *) pm_constant_start(constant), pm_constant_length(constant), constants_data->encoding); + VALUE string = rb_enc_str_new((const char *) pm_constant_start(constant), pm_constant_length(constant), encoding); VALUE value = rb_protect(rb_str_intern, string, &state); if (state != 0) { @@ -145,14 +142,17 @@ pm_ast_constants_each(const pm_constant_t *constant, void *data) { rb_set_errinfo(Qnil); } - rb_ary_push(constants_data->constants, value); + return value; } VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source, bool freeze) { - VALUE constants = rb_ary_new_capa(pm_parser_constants_size(parser)); - pm_ast_constants_each_data_t constants_data = { .constants = constants, .encoding = encoding }; - pm_parser_constants_each(parser, pm_ast_constants_each, &constants_data); + size_t constants_size = pm_parser_constants_size(parser); + VALUE constants = rb_ary_new_capa((long) constants_size); + + for (size_t index = 0; index < constants_size; index++) { + rb_ary_push(constants, pm_ast_constant(pm_parser_constant_at(parser, index), encoding)); + } pm_arena_t *node_arena = pm_arena_new(); pm_node_stack_node_t *node_stack = NULL; @@ -237,15 +237,15 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- when Ruby::Prism::Template::ConstantField -%> #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" assert(cast-><%= field.name %> != 0); - argv[<%= index %>] = RARRAY_AREF(constants, cast-><%= field.name %> - 1); + argv[<%= index %>] = RARRAY_AREF(constants, pm_constant_id_index(cast-><%= field.name %>)); <%- when Ruby::Prism::Template::OptionalConstantField -%> - argv[<%= index %>] = cast-><%= field.name %> == 0 ? Qnil : RARRAY_AREF(constants, cast-><%= field.name %> - 1); + argv[<%= index %>] = cast-><%= field.name %> == 0 ? Qnil : RARRAY_AREF(constants, pm_constant_id_index(cast-><%= field.name %>)); <%- when Ruby::Prism::Template::ConstantListField -%> #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" argv[<%= index %>] = rb_ary_new_capa(cast-><%= field.name %>.size); for (size_t index = 0; index < cast-><%= field.name %>.size; index++) { assert(cast-><%= field.name %>.ids[index] != 0); - rb_ary_push(argv[<%= index %>], RARRAY_AREF(constants, cast-><%= field.name %>.ids[index] - 1)); + rb_ary_push(argv[<%= index %>], RARRAY_AREF(constants, pm_constant_id_index(cast-><%= field.name %>.ids[index]))); } if (freeze) rb_obj_freeze(argv[<%= index %>]); <%- when Ruby::Prism::Template::LocationField -%>