Skip to content

Commit d8c7e6b

Browse files
committed
IndexTargetNode should always have ATTRIBUTE_WRITE
Because this is a user-facing change, we also need to deal with the fact that CRuby 3.3.0 was just released. In order to support workflows that want to parse exactly as CRuby parses in a specific version, this PR introduces a new option to the options struct that is "version". This allows you to specify that you want "3.3.0" parsing. I'm not sure if this is the correct solution. Another solution is to just fork and keep around the old branch for security patches. Or we could keep around a copy of the source files within this repository as another directory and only update when necessary. There are a lot of potential solutions here. Because this change is so small and the check for it is so minimal, I've decided to go with this enum. If this ends up entirely cluttering the codebase with version checks, we'll come up with another solution. But for now this works, so we're going to go in this direction for a bit until we determine it's no longer working.
1 parent a590c03 commit d8c7e6b

File tree

9 files changed

+106
-13
lines changed

9 files changed

+106
-13
lines changed

ext/prism/extension.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ ID rb_option_id_encoding;
2222
ID rb_option_id_line;
2323
ID rb_option_id_frozen_string_literal;
2424
ID rb_option_id_verbose;
25+
ID rb_option_id_version;
2526
ID rb_option_id_scopes;
2627

2728
/******************************************************************************/
@@ -131,6 +132,14 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
131132
if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, value == Qtrue);
132133
} else if (key_id == rb_option_id_verbose) {
133134
pm_options_suppress_warnings_set(options, value != Qtrue);
135+
} else if (key_id == rb_option_id_version) {
136+
if (!NIL_P(value)) {
137+
const char *version = check_string(value);
138+
139+
if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
140+
rb_raise(rb_eArgError, "invalid version: %"PRIsVALUE, value);
141+
}
142+
}
134143
} else if (key_id == rb_option_id_scopes) {
135144
if (!NIL_P(value)) build_options_scopes(options, value);
136145
} else {
@@ -1013,6 +1022,7 @@ Init_prism(void) {
10131022
rb_option_id_line = rb_intern_const("line");
10141023
rb_option_id_frozen_string_literal = rb_intern_const("frozen_string_literal");
10151024
rb_option_id_verbose = rb_intern_const("verbose");
1025+
rb_option_id_version = rb_intern_const("version");
10161026
rb_option_id_scopes = rb_intern_const("scopes");
10171027

10181028
/**

include/prism/options.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ typedef struct pm_options_scope {
2424
pm_string_t *locals;
2525
} pm_options_scope_t;
2626

27+
/**
28+
* The version of prism that we should be parsing with. This is used to allow
29+
* consumers to specify which behavior they want in case they need to parse
30+
* exactly as a specific version of CRuby.
31+
*/
32+
typedef enum {
33+
/** The current version of prism. */
34+
PM_OPTIONS_VERSION_LATEST = 0,
35+
36+
/** The vendored version of prism in CRuby 3.3.0. */
37+
PM_OPTIONS_VERSION_CRUBY_3_3_0 = 1
38+
} pm_options_version_t;
39+
2740
/**
2841
* The options that can be passed to the parser.
2942
*/
@@ -55,6 +68,13 @@ typedef struct {
5568
*/
5669
pm_options_scope_t *scopes;
5770

71+
/**
72+
* The version of prism that we should be parsing with. This is used to
73+
* allow consumers to specify which behavior they want in case they need to
74+
* parse exactly as a specific version of CRuby.
75+
*/
76+
pm_options_version_t version;
77+
5878
/** Whether or not the frozen string literal option has been set. */
5979
bool frozen_string_literal;
6080

@@ -106,6 +126,18 @@ PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *
106126
*/
107127
PRISM_EXPORTED_FUNCTION void pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings);
108128

129+
/**
130+
* Set the version option on the given options struct by parsing the given
131+
* string. If the string contains an invalid option, this returns false.
132+
* Otherwise, it returns true.
133+
*
134+
* @param options The options struct to set the version on.
135+
* @param version The version to set.
136+
* @param length The length of the version string.
137+
* @return Whether or not the version was parsed successfully.
138+
*/
139+
PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
140+
109141
/**
110142
* Allocate and zero out the scopes array on the given options struct.
111143
*
@@ -167,9 +199,17 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
167199
* | ... | the encoding bytes |
168200
* | `1` | frozen string literal |
169201
* | `1` | suppress warnings |
202+
* | `1` | the version |
170203
* | `4` | the number of scopes |
171204
* | ... | the scopes |
172205
*
206+
* The version field is an enum, so it should be one of the following values:
207+
*
208+
* | value | version |
209+
* | ----- | ------------------------- |
210+
* | `0` | use the latest version of prism |
211+
* | `1` | use the version of prism that is vendored in CRuby 3.3.0 |
212+
*
173213
* Each scope is layed out as follows:
174214
*
175215
* | # bytes | field |

include/prism/parser.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "prism/ast.h"
1010
#include "prism/defines.h"
1111
#include "prism/encoding.h"
12+
#include "prism/options.h"
1213
#include "prism/util/pm_constant_pool.h"
1314
#include "prism/util/pm_list.h"
1415
#include "prism/util/pm_newline_list.h"
@@ -662,6 +663,12 @@ struct pm_parser {
662663
*/
663664
const pm_encoding_t *explicit_encoding;
664665

666+
/** The current parameter name id on parsing its default value. */
667+
pm_constant_id_t current_param_name;
668+
669+
/** The version of prism that we should use to parse. */
670+
pm_options_version_t version;
671+
665672
/** Whether or not we're at the beginning of a command. */
666673
bool command_start;
667674

@@ -684,9 +691,6 @@ struct pm_parser {
684691
/** This flag indicates that we are currently parsing a keyword argument. */
685692
bool in_keyword_arg;
686693

687-
/** The current parameter name id on parsing its default value. */
688-
pm_constant_id_t current_param_name;
689-
690694
/**
691695
* Whether or not the parser has seen a token that has semantic meaning
692696
* (i.e., a token that is not a comment or whitespace).

lib/prism/ffi.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ def dump_options(options)
314314
template << "C"
315315
values << (options.fetch(:verbose, true) ? 0 : 1)
316316

317+
template << "C"
318+
values << { nil => 0, "3.3.0" => 1, "latest" => 0 }.fetch(options[:version])
319+
317320
template << "L"
318321
if (scopes = options[:scopes])
319322
values << scopes.length

src/options.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,33 @@ pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings)
4040
options->suppress_warnings = suppress_warnings;
4141
}
4242

43+
/**
44+
* Set the version option on the given options struct by parsing the given
45+
* string. If the string contains an invalid option, this returns false.
46+
* Otherwise, it returns true.
47+
*/
48+
PRISM_EXPORTED_FUNCTION bool
49+
pm_options_version_set(pm_options_t *options, const char *version, size_t length) {
50+
if (version == NULL && length == 0) {
51+
options->version = PM_OPTIONS_VERSION_LATEST;
52+
return true;
53+
}
54+
55+
if (length == 5) {
56+
if (strncmp(version, "3.3.0", 5) == 0) {
57+
options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0;
58+
return true;
59+
}
60+
61+
if (strncmp(version, "latest", 6) == 0) {
62+
options->version = PM_OPTIONS_VERSION_LATEST;
63+
return true;
64+
}
65+
}
66+
67+
return false;
68+
}
69+
4370
/**
4471
* Allocate and zero out the scopes array on the given options struct.
4572
*/
@@ -163,6 +190,7 @@ pm_options_read(pm_options_t *options, const char *data) {
163190

164191
options->frozen_string_literal = *data++;
165192
options->suppress_warnings = *data++;
193+
options->version = (pm_options_version_t) *data++;
166194

167195
uint32_t scopes_count = pm_options_read_u32(data);
168196
data += 4;

src/prism.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2172,11 +2172,16 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
21722172
static pm_index_target_node_t *
21732173
pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
21742174
pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t);
2175+
pm_node_flags_t flags = target->base.flags;
2176+
2177+
if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) {
2178+
flags |= PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE;
2179+
}
21752180

21762181
*node = (pm_index_target_node_t) {
21772182
{
21782183
.type = PM_INDEX_TARGET_NODE,
2179-
.flags = target->base.flags,
2184+
.flags = flags,
21802185
.location = target->base.location
21812186
},
21822187
.receiver = target->receiver,
@@ -17320,6 +17325,9 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
1732017325
parser->suppress_warnings = true;
1732117326
}
1732217327

17328+
// version option
17329+
parser->version = options->version;
17330+
1732317331
// scopes option
1732417332
for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
1732517333
const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index);

test/prism/snapshots/arrays.txt

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/prism/snapshots/unparser/corpus/literal/assignment.txt

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/prism/snapshots/whitequark/masgn_attr.txt

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)