Ruby 4.1.0dev (2026-10-27 revision db563a357875d4efbadec58b0fd8695ac9a82f42)
options.c
1#include "prism/internal/options.h"
2
4
5#include "prism/internal/allocator.h"
6#include "prism/internal/char.h"
7#include "prism/internal/stringy.h"
8
9#include <stdlib.h>
10#include <string.h>
11
17pm_options_new(void) {
18 pm_options_t *options = xcalloc(1, sizeof(pm_options_t));
19 if (options == NULL) abort();
20
21 options->line = 1;
22 return options;
23}
24
28void
29pm_options_cleanup(pm_options_t *options) {
30 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
31 pm_options_scope_t *scope = &options->scopes[scope_index];
32 xfree_sized(scope->locals, scope->locals_count * sizeof(pm_string_t));
33 }
34
35 xfree_sized(options->scopes, options->scopes_count * sizeof(pm_options_scope_t));
36}
37
43void
44pm_options_free(pm_options_t *options) {
45 pm_options_cleanup(options);
46 xfree_sized(options, sizeof(pm_options_t));
47}
48
52void
53pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data) {
54 options->shebang_callback = shebang_callback;
55 options->shebang_callback_data = shebang_callback_data;
56}
57
61const pm_string_t *
62pm_options_filepath(const pm_options_t *options) {
63 return &options->filepath;
64}
65
69void
70pm_options_filepath_set(pm_options_t *options, const char *filepath) {
71 pm_string_init(&options->filepath, (const uint8_t *) filepath, strlen(filepath));
72}
73
77void
78pm_options_encoding_set(pm_options_t *options, const char *encoding) {
79 pm_string_init(&options->encoding, (const uint8_t *) encoding, strlen(encoding));
80}
81
85void
86pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) {
87 options->encoding_locked = encoding_locked;
88}
89
93void
94pm_options_line_set(pm_options_t *options, int32_t line) {
95 options->line = line;
96}
97
101void
102pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) {
103 options->frozen_string_literal = frozen_string_literal ? PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED : PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
104}
105
109void
110pm_options_command_line_set(pm_options_t *options, uint8_t command_line) {
111 options->command_line = command_line;
112}
113
117static PRISM_INLINE bool
118is_number(const char *string, size_t length) {
119 return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length;
120}
121
127bool
128pm_options_version_set(pm_options_t *options, const char *version, size_t length) {
129 if (version == NULL) {
130 options->version = PM_OPTIONS_VERSION_LATEST;
131 return true;
132 }
133
134 if (length == 3) {
135 if (strncmp(version, "3.3", 3) == 0) {
136 options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
137 return true;
138 }
139
140 if (strncmp(version, "3.4", 3) == 0) {
141 options->version = PM_OPTIONS_VERSION_CRUBY_3_4;
142 return true;
143 }
144
145 if (strncmp(version, "3.5", 3) == 0 || strncmp(version, "4.0", 3) == 0) {
146 options->version = PM_OPTIONS_VERSION_CRUBY_4_0;
147 return true;
148 }
149
150 if (strncmp(version, "4.1", 3) == 0) {
151 options->version = PM_OPTIONS_VERSION_CRUBY_4_1;
152 return true;
153 }
154
155 return false;
156 }
157
158 if (length >= 4 && is_number(version + 4, length - 4)) {
159 if (strncmp(version, "3.3.", 4) == 0) {
160 options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
161 return true;
162 }
163
164 if (strncmp(version, "3.4.", 4) == 0) {
165 options->version = PM_OPTIONS_VERSION_CRUBY_3_4;
166 return true;
167 }
168
169 if (strncmp(version, "3.5.", 4) == 0 || strncmp(version, "4.0.", 4) == 0) {
170 options->version = PM_OPTIONS_VERSION_CRUBY_4_0;
171 return true;
172 }
173
174 if (strncmp(version, "4.1.", 4) == 0) {
175 options->version = PM_OPTIONS_VERSION_CRUBY_4_1;
176 return true;
177 }
178 }
179
180 if (length == 6) {
181 if (strncmp(version, "latest", 6) == 0) {
182 options->version = PM_OPTIONS_VERSION_LATEST;
183 return true;
184 }
185 }
186
187 return false;
188}
189
194void
195pm_options_version_set_lowest(pm_options_t *options) {
196 options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
197}
198
203void
204pm_options_version_set_highest(pm_options_t *options) {
205 options->version = PM_OPTIONS_VERSION_LATEST;
206}
207
211void
212pm_options_main_script_set(pm_options_t *options, bool main_script) {
213 options->main_script = main_script;
214}
215
219void
220pm_options_partial_script_set(pm_options_t *options, bool partial_script) {
221 options->partial_script = partial_script;
222}
223
227bool
228pm_options_freeze(const pm_options_t *options) {
229 return options->freeze;
230}
231
235void
236pm_options_freeze_set(pm_options_t *options, bool freeze) {
237 options->freeze = freeze;
238}
239
243bool
244pm_options_attach_comments(const pm_options_t *options) {
245 return options->attach_comments;
246}
247
251void
252pm_options_attach_comments_set(pm_options_t *options, bool attach_comments) {
253 options->attach_comments = attach_comments;
254}
255
259uint8_t pm_options_raise_error(const pm_options_t *options) {
260 return options->raise_error;
261}
262
266void pm_options_raise_error_set(pm_options_t *options, uint8_t raise_error) {
267 options->raise_error = raise_error;
268}
269
270// For some reason, GCC analyzer thinks we're leaking allocated scopes and
271// locals here, even though we definitely aren't. This is a false positive.
272// Ideally we wouldn't need to suppress this.
273#if defined(__GNUC__) && (__GNUC__ >= 10)
274#pragma GCC diagnostic push
275#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
276#endif
277
281bool
282pm_options_scopes_init(pm_options_t *options, size_t scopes_count) {
283 options->scopes_count = scopes_count;
284 options->scopes = xcalloc(scopes_count, sizeof(pm_options_scope_t));
285 return options->scopes != NULL;
286}
287
292const pm_options_scope_t *
293pm_options_scope(const pm_options_t *options, size_t index) {
294 return &options->scopes[index];
295}
296
302pm_options_scope_mut(pm_options_t *options, size_t index) {
303 return &options->scopes[index];
304}
305
310void
311pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) {
312 scope->locals_count = locals_count;
313 scope->locals = xcalloc(locals_count, sizeof(pm_string_t));
314 scope->forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
315 if (scope->locals == NULL) abort();
316}
317
322const pm_string_t *
323pm_options_scope_local(const pm_options_scope_t *scope, size_t index) {
324 return &scope->locals[index];
325}
326
332pm_options_scope_local_mut(pm_options_scope_t *scope, size_t index) {
333 return &scope->locals[index];
334}
335
339void
340pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) {
341 scope->forwarding = forwarding;
342}
343
349static uint32_t
350pm_options_read_u32(const char *data) {
351 if (((uintptr_t) data) % sizeof(uint32_t) == 0) {
352 return *((uint32_t *) data);
353 } else {
354 uint32_t value;
355 memcpy(&value, data, sizeof(uint32_t));
356 return value;
357 }
358}
359
365static int32_t
366pm_options_read_s32(const char *data) {
367 if (((uintptr_t) data) % sizeof(int32_t) == 0) {
368 return *((int32_t *) data);
369 } else {
370 int32_t value;
371 memcpy(&value, data, sizeof(int32_t));
372 return value;
373 }
374}
375
383void
384pm_options_read(pm_options_t *options, const char *data) {
385 options->line = 1; // default
386 if (data == NULL) return;
387
388 uint32_t filepath_length = pm_options_read_u32(data);
389 data += 4;
390
391 if (filepath_length > 0) {
392 pm_string_init(&options->filepath, (const uint8_t *) data, filepath_length);
393 data += filepath_length;
394 }
395
396 options->line = pm_options_read_s32(data);
397 data += 4;
398
399 uint32_t encoding_length = pm_options_read_u32(data);
400 data += 4;
401
402 if (encoding_length > 0) {
403 pm_string_init(&options->encoding, (const uint8_t *) data, encoding_length);
404 data += encoding_length;
405 }
406
407 options->frozen_string_literal = (int8_t) *data++;
408 options->command_line = (uint8_t) *data++;
409 options->version = (pm_options_version_t) *data++;
410 options->encoding_locked = ((uint8_t) *data++) > 0;
411 options->main_script = ((uint8_t) *data++) > 0;
412 options->partial_script = ((uint8_t) *data++) > 0;
413 options->freeze = ((uint8_t) *data++) > 0;
414 options->attach_comments = ((uint8_t) *data++) > 0;
415
416 uint32_t scopes_count = pm_options_read_u32(data);
417 data += 4;
418
419 if (scopes_count > 0) {
420 if (!pm_options_scopes_init(options, scopes_count)) return;
421
422 for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
423 uint32_t locals_count = pm_options_read_u32(data);
424 data += 4;
425
426 pm_options_scope_t *scope = &options->scopes[scope_index];
427 pm_options_scope_init(scope, locals_count);
428
429 uint8_t forwarding = (uint8_t) *data++;
430 pm_options_scope_forwarding_set(&options->scopes[scope_index], forwarding);
431
432 for (size_t local_index = 0; local_index < locals_count; local_index++) {
433 uint32_t local_length = pm_options_read_u32(data);
434 data += 4;
435
436 pm_string_init(&scope->locals[local_index], (const uint8_t *) data, local_length);
437 data += local_length;
438 }
439 }
440 }
441}
442
443#if defined(__GNUC__) && (__GNUC__ >= 10)
444#pragma GCC diagnostic pop
445#endif
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition inline.h:12
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE
The default value for parameters.
Definition options.h:49
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should not be frozen.
Definition options.h:35
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made frozen.
Definition options.h:46
void(* pm_options_shebang_callback_t)(pm_options_t *options, const uint8_t *source, size_t length, void *shebang_callback_data)
The callback called when additional switches are found in a shebang comment that need to be processed...
Definition options.h:75
A view of a sequence of bytes.
Definition stringy.h:23