summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <[email protected]>2025-01-14 13:10:46 -0500
committergit <[email protected]>2025-01-14 20:31:38 +0000
commit51d3d6ac8c2e3b6b6dacd80a9ddf11adc46fde08 ()
tree811c3723b8e7069eb12aa10536fff6088631e0aa
parentf5fa1ee5f6e0e29e15063e8b62eb0ce7042bb29b (diff)
[ruby/prism] Support forwarding flags on scopes
When parent scopes around an eval are forwarding parameters (like *, **, &, or ...) we need to know that information when we are in the parser. As such, we need to support passing that information into the scopes option. In order to do this, unfortunately we need a bunch of changes. The scopes option was previously an array of array of strings. These corresponded to the names of the locals in the parent scopes. We still support this, but now additionally support passing in a Prism::Scope instance at each index in the array. This Prism::Scope class holds both the names of the locals as well as an array of forwarding parameter names (symbols corresponding to the forwarding parameters). There is convenience function on the Prism module that creates a Prism::Scope object using Prism.scope. In JavaScript, we now additionally support an object much the same as the Ruby side. In Java, we now have a ParsingOptions.Scope class that holds that information. In the dump APIs, these objects in all 3 languages will add an additional byte for the forwarding flags in the middle of the scopes serialization. All of this is in service of properly parsing the following code: ```ruby def foo(*) = eval("bar(*)") ``` https://.com/ruby/prism/commit/21abb6b7c4
-rw-r--r--lib/prism/ffi.rb29
-rw-r--r--lib/prism/parse_result.rb28
-rw-r--r--prism/extension.c64
-rw-r--r--prism/options.c12
-rw-r--r--prism/options.h27
-rw-r--r--prism/prism.c2
-rw-r--r--test/prism/api/parse_test.rb18
7 files changed, 171 insertions, 9 deletions
@@ -478,10 +478,35 @@ module Prism
values << scopes.length
scopes.each do |scope|
template << "L"
- values << scope.length
- scope.each do |local|
name = local.name
template << "L"
values << name.bytesize
@@ -879,4 +879,32 @@ module Prism
freeze
end
end
end
@@ -24,6 +24,7 @@ VALUE rb_cPrismParseResult;
VALUE rb_cPrismLexResult;
VALUE rb_cPrismParseLexResult;
VALUE rb_cPrismStringQuery;
VALUE rb_cPrismDebugEncoding;
@@ -38,6 +39,10 @@ ID rb_id_option_partial_script;
ID rb_id_option_scopes;
ID rb_id_option_version;
ID rb_id_source_for;
/******************************************************************************/
/* IO of Ruby code */
@@ -95,14 +100,53 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
VALUE scope = rb_ary_entry(scopes, scope_index);
- // Check that the scope is an array. If it's not, then raise a type
- // error.
- if (!RB_TYPE_P(scope, T_ARRAY)) {
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scope));
}
// Initialize the scope array.
- size_t locals_count = RARRAY_LEN(scope);
pm_options_scope_t *options_scope = &options->scopes[scope_index];
if (!pm_options_scope_init(options_scope, locals_count)) {
rb_raise(rb_eNoMemError, "failed to allocate memory");
@@ -110,7 +154,7 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
// Iterate over the locals and add them to the scope.
for (size_t local_index = 0; local_index < locals_count; local_index++) {
- VALUE local = rb_ary_entry(scope, local_index);
// Check that the local is a symbol. If it's not, then raise a
// type error.
@@ -123,6 +167,9 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
const char *name = rb_id2name(SYM2ID(local));
pm_string_constant_init(scope_local, name, strlen(name));
}
}
}
@@ -1302,6 +1349,7 @@ Init_prism(void) {
rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
// Intern all of the IDs eagerly that we support so that we don't have to do
// it every time we parse.
@@ -1316,6 +1364,10 @@ Init_prism(void) {
rb_id_option_scopes = rb_intern_const("scopes");
rb_id_option_version = rb_intern_const("version");
rb_id_source_for = rb_intern("for");
/**
* The version of the prism library.
@@ -181,6 +181,7 @@ PRISM_EXPORTED_FUNCTION bool
pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) {
scope->locals_count = locals_count;
scope->locals = xcalloc(locals_count, sizeof(pm_string_t));
return scope->locals != NULL;
}
@@ -193,6 +194,14 @@ pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) {
}
/**
* Free the internal memory associated with the options.
*/
PRISM_EXPORTED_FUNCTION void
@@ -300,6 +309,9 @@ pm_options_read(pm_options_t *options, const char *data) {
return;
}
for (size_t local_index = 0; local_index < locals_count; local_index++) {
uint32_t local_length = pm_options_read_u32(data);
data += 4;
@@ -39,8 +39,26 @@ typedef struct pm_options_scope {
/** The names of the locals in the scope. */
pm_string_t *locals;
} pm_options_scope_t;
// Forward declaration needed by the callback typedef.
struct pm_options;
@@ -338,6 +356,14 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, si
PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index);
/**
* Free the internal memory associated with the options.
*
* @param options The options struct whose internal memory should be freed.
@@ -386,6 +412,7 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
* | # bytes | field |
* | ------- | -------------------------- |
* | `4` | the number of locals |
* | ... | the locals |
*
* Each local is laid out as follows:
@@ -22492,7 +22492,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
// Scopes given from the outside are not allowed to have numbered
// parameters.
- parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
const pm_string_t *local = pm_options_scope_local_get(scope, local_index);
@@ -140,6 +140,24 @@ module Prism
end
end
private
def find_source_file_node(program)