зеркало из https://github.com/github/ruby.git
249 строки
7.5 KiB
C
249 строки
7.5 KiB
C
#include "prism/options.h"
|
|
|
|
/**
|
|
* Set the filepath option on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_filepath_set(pm_options_t *options, const char *filepath) {
|
|
pm_string_constant_init(&options->filepath, filepath, strlen(filepath));
|
|
}
|
|
|
|
/**
|
|
* Set the encoding option on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_encoding_set(pm_options_t *options, const char *encoding) {
|
|
pm_string_constant_init(&options->encoding, encoding, strlen(encoding));
|
|
}
|
|
|
|
/**
|
|
* Set the line option on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_line_set(pm_options_t *options, int32_t line) {
|
|
options->line = line;
|
|
}
|
|
|
|
/**
|
|
* Set the frozen string literal option on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) {
|
|
options->frozen_string_literal = frozen_string_literal ? PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED : PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
|
|
}
|
|
|
|
/**
|
|
* Sets the command line option on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_command_line_set(pm_options_t *options, uint8_t command_line) {
|
|
options->command_line = command_line;
|
|
}
|
|
|
|
/**
|
|
* Set the version option on the given options struct by parsing the given
|
|
* string. If the string contains an invalid option, this returns false.
|
|
* Otherwise, it returns true.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION bool
|
|
pm_options_version_set(pm_options_t *options, const char *version, size_t length) {
|
|
switch (length) {
|
|
case 0:
|
|
if (version == NULL) {
|
|
options->version = PM_OPTIONS_VERSION_LATEST;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
case 5:
|
|
assert(version != NULL);
|
|
|
|
if ((strncmp(version, "3.3.0", length) == 0) || (strncmp(version, "3.3.1", length) == 0)) {
|
|
options->version = PM_OPTIONS_VERSION_CRUBY_3_3;
|
|
return true;
|
|
}
|
|
|
|
if (strncmp(version, "3.4.0", length) == 0) {
|
|
options->version = PM_OPTIONS_VERSION_LATEST;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
case 6:
|
|
assert(version != NULL);
|
|
|
|
if (strncmp(version, "latest", length) == 0) {
|
|
options->version = PM_OPTIONS_VERSION_LATEST;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// For some reason, GCC analyzer thinks we're leaking allocated scopes and
|
|
// locals here, even though we definitely aren't. This is a false positive.
|
|
// Ideally we wouldn't need to suppress this.
|
|
#if defined(__GNUC__) && (__GNUC__ >= 10)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
|
|
#endif
|
|
|
|
/**
|
|
* Allocate and zero out the scopes array on the given options struct.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION bool
|
|
pm_options_scopes_init(pm_options_t *options, size_t scopes_count) {
|
|
options->scopes_count = scopes_count;
|
|
options->scopes = xcalloc(scopes_count, sizeof(pm_options_scope_t));
|
|
return options->scopes != NULL;
|
|
}
|
|
|
|
/**
|
|
* Return a pointer to the scope at the given index within the given options.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION const pm_options_scope_t *
|
|
pm_options_scope_get(const pm_options_t *options, size_t index) {
|
|
return &options->scopes[index];
|
|
}
|
|
|
|
/**
|
|
* Create a new options scope struct. This will hold a set of locals that are in
|
|
* scope surrounding the code that is being parsed.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Return a pointer to the local at the given index within the given scope.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION const pm_string_t *
|
|
pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) {
|
|
return &scope->locals[index];
|
|
}
|
|
|
|
/**
|
|
* Free the internal memory associated with the options.
|
|
*/
|
|
PRISM_EXPORTED_FUNCTION void
|
|
pm_options_free(pm_options_t *options) {
|
|
pm_string_free(&options->filepath);
|
|
pm_string_free(&options->encoding);
|
|
|
|
for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
|
|
pm_options_scope_t *scope = &options->scopes[scope_index];
|
|
|
|
for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
|
|
pm_string_free(&scope->locals[local_index]);
|
|
}
|
|
|
|
xfree(scope->locals);
|
|
}
|
|
|
|
xfree(options->scopes);
|
|
}
|
|
|
|
/**
|
|
* Read a 32-bit unsigned integer from a pointer. This function is used to read
|
|
* the options that are passed into the parser from the Ruby implementation. It
|
|
* handles aligned and unaligned reads.
|
|
*/
|
|
static uint32_t
|
|
pm_options_read_u32(const char *data) {
|
|
if (((uintptr_t) data) % sizeof(uint32_t) == 0) {
|
|
return *((uint32_t *) data);
|
|
} else {
|
|
uint32_t value;
|
|
memcpy(&value, data, sizeof(uint32_t));
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a 32-bit signed integer from a pointer. This function is used to read
|
|
* the options that are passed into the parser from the Ruby implementation. It
|
|
* handles aligned and unaligned reads.
|
|
*/
|
|
static int32_t
|
|
pm_options_read_s32(const char *data) {
|
|
if (((uintptr_t) data) % sizeof(int32_t) == 0) {
|
|
return *((int32_t *) data);
|
|
} else {
|
|
int32_t value;
|
|
memcpy(&value, data, sizeof(int32_t));
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deserialize an options struct from the given binary string. This is used to
|
|
* pass options to the parser from an FFI call so that consumers of the library
|
|
* from an FFI perspective don't have to worry about the structure of our
|
|
* options structs. Since the source of these calls will be from Ruby
|
|
* implementation internals we assume it is from a trusted source.
|
|
*/
|
|
void
|
|
pm_options_read(pm_options_t *options, const char *data) {
|
|
options->line = 1; // default
|
|
if (data == NULL) return;
|
|
|
|
uint32_t filepath_length = pm_options_read_u32(data);
|
|
data += 4;
|
|
|
|
if (filepath_length > 0) {
|
|
pm_string_constant_init(&options->filepath, data, filepath_length);
|
|
data += filepath_length;
|
|
}
|
|
|
|
options->line = pm_options_read_s32(data);
|
|
data += 4;
|
|
|
|
uint32_t encoding_length = pm_options_read_u32(data);
|
|
data += 4;
|
|
|
|
if (encoding_length > 0) {
|
|
pm_string_constant_init(&options->encoding, data, encoding_length);
|
|
data += encoding_length;
|
|
}
|
|
|
|
options->frozen_string_literal = (int8_t) *data++;
|
|
options->command_line = (uint8_t) *data++;
|
|
options->version = (pm_options_version_t) *data++;
|
|
|
|
uint32_t scopes_count = pm_options_read_u32(data);
|
|
data += 4;
|
|
|
|
if (scopes_count > 0) {
|
|
if (!pm_options_scopes_init(options, scopes_count)) return;
|
|
|
|
for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
|
|
uint32_t locals_count = pm_options_read_u32(data);
|
|
data += 4;
|
|
|
|
pm_options_scope_t *scope = &options->scopes[scope_index];
|
|
if (!pm_options_scope_init(scope, locals_count)) {
|
|
pm_options_free(options);
|
|
return;
|
|
}
|
|
|
|
for (size_t local_index = 0; local_index < locals_count; local_index++) {
|
|
uint32_t local_length = pm_options_read_u32(data);
|
|
data += 4;
|
|
|
|
pm_string_constant_init(&scope->locals[local_index], data, local_length);
|
|
data += local_length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(__GNUC__) && (__GNUC__ >= 10)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|