diff --git a/include/prism/allocator.h b/include/prism/allocator.h new file mode 100644 index 0000000000..7bb7e23775 --- /dev/null +++ b/include/prism/allocator.h @@ -0,0 +1,46 @@ +# /** + * @file allocator.h + * + * An arena allocator. + */ +#ifndef PRISM_ALLOCATOR_H +#define PRISM_ALLOCATOR_H + +#include "prism/defines.h" +#include +#include +#include + +typedef struct pm_allocator_page { + struct pm_allocator_page *next; + char *start; + char *current; + char *end; +} pm_allocator_page_t; + +typedef struct pm_allocator { + pm_allocator_page_t head; + pm_allocator_page_t *tail; +} pm_allocator_t; + +/** + * Initialize an allocator with the given capacity. + */ +void pm_allocator_init(pm_allocator_t *allocator, size_t capacity); + +/** + * Allocate memory from the given allocator. + */ +void * pm_allocator_arena_alloc(pm_allocator_t *allocator, size_t size, size_t alignment); + +/** + * Allocate memory from the given allocator and zero out the memory. + */ +void * pm_allocator_arena_calloc(pm_allocator_t *allocator, size_t count, size_t size, size_t alignment); + +/** + * Frees the internal memory associated with the allocator. + */ +void pm_allocator_free(pm_allocator_t *allocator); + +#endif diff --git a/include/prism/node.h b/include/prism/node.h index e8686a327c..0f11a7f478 100644 --- a/include/prism/node.h +++ b/include/prism/node.h @@ -7,6 +7,7 @@ #define PRISM_NODE_H #include "prism/defines.h" +#include "prism/allocator.h" #include "prism/parser.h" #include "prism/util/pm_buffer.h" @@ -23,7 +24,7 @@ * @param list The list to append to. * @param node The node to append. */ -void pm_node_list_append(pm_node_list_t *list, pm_node_t *node); +void pm_node_list_append(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_t *node); /** * Prepend a new node onto the beginning of the node list. @@ -31,7 +32,7 @@ void pm_node_list_append(pm_node_list_t *list, pm_node_t *node); * @param list The list to prepend to. * @param node The node to prepend. */ -void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node); +void pm_node_list_prepend(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_t *node); /** * Concatenate the given node list onto the end of the other node list. @@ -39,14 +40,7 @@ void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node); * @param list The list to concatenate onto. * @param other The list to concatenate. */ -void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other); - -/** - * Free the internal memory associated with the given node list. - * - * @param list The list to free. - */ -void pm_node_list_free(pm_node_list_t *list); +void pm_node_list_concat(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_list_t *other); /** * Deallocate a node and all of its children. diff --git a/include/prism/parser.h b/include/prism/parser.h index 048955409b..fc123a5a96 100644 --- a/include/prism/parser.h +++ b/include/prism/parser.h @@ -7,6 +7,7 @@ #define PRISM_PARSER_H #include "prism/defines.h" +#include "prism/allocator.h" #include "prism/ast.h" #include "prism/encoding.h" #include "prism/options.h" @@ -568,6 +569,12 @@ typedef struct pm_scope { /** A pointer to the previous scope in the linked list. */ struct pm_scope *previous; + /** + * This allocator is responsible for allocating the space for the list of + * the locals and the list of implicit parameters in the scope. + */ + pm_allocator_t allocator; + /** The IDs of the locals in the given scope. */ pm_locals_t locals; @@ -625,6 +632,12 @@ typedef uint32_t pm_state_stack_t; * it's considering. */ struct pm_parser { + /** The allocator used to allocate nodes and their fields. */ + pm_allocator_t allocator; + + /** The allocator used to allocate lists of block exits. */ + pm_allocator_t block_exits_allocator; + /** The current state of the lexer. */ pm_lex_state_t lex_state; diff --git a/include/prism/util/pm_constant_pool.h b/include/prism/util/pm_constant_pool.h index 6df23f8f50..4f3d5519ac 100644 --- a/include/prism/util/pm_constant_pool.h +++ b/include/prism/util/pm_constant_pool.h @@ -11,6 +11,7 @@ #define PRISM_CONSTANT_POOL_H #include "prism/defines.h" +#include "prism/allocator.h" #include #include @@ -54,10 +55,11 @@ void pm_constant_id_list_init(pm_constant_id_list_t *list); /** * Initialize a list of constant ids with a given capacity. * + * @param allocator The allocator to use to allocate space for the list. * @param list The list to initialize. * @param capacity The initial capacity of the list. */ -void pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity); +void pm_constant_id_list_init_capacity(pm_allocator_t *allocator, pm_constant_id_list_t *list, size_t capacity); /** * Append a constant id to a list of constant ids. Returns false if any diff --git a/include/prism/util/pm_integer.h b/include/prism/util/pm_integer.h index 91b28ad2f3..06e15177b9 100644 --- a/include/prism/util/pm_integer.h +++ b/include/prism/util/pm_integer.h @@ -7,6 +7,7 @@ #define PRISM_NUMBER_H #include "prism/defines.h" +#include "prism/allocator.h" #include "prism/util/pm_buffer.h" #include @@ -77,12 +78,13 @@ typedef enum { * has already been validated, as internal validation checks are not performed * here. * + * @param allocator The allocator to use to allocate memory for the integer. * @param integer The integer to parse into. * @param base The base of the integer. * @param start The start of the string. * @param end The end of the string. */ -void pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end); +void pm_integer_parse(pm_allocator_t *allocator, pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end); /** * Compare two integers. This function returns -1 if the left integer is less diff --git a/include/prism/util/pm_string.h b/include/prism/util/pm_string.h index d23792c0ba..a1911d33f3 100644 --- a/include/prism/util/pm_string.h +++ b/include/prism/util/pm_string.h @@ -7,6 +7,7 @@ #define PRISM_STRING_H #include "prism/defines.h" +#include "prism/allocator.h" #include #include @@ -126,7 +127,7 @@ PRISM_EXPORTED_FUNCTION bool pm_string_file_init(pm_string_t *string, const char * * @param string The string to ensure is owned. */ -void pm_string_ensure_owned(pm_string_t *string); +void pm_string_ensure_owned(pm_allocator_t *allocator, pm_string_t *string); /** * Compare the underlying lengths and bytes of two strings. Returns 0 if the diff --git a/src/allocator.c b/src/allocator.c new file mode 100644 index 0000000000..97f3d3107b --- /dev/null +++ b/src/allocator.c @@ -0,0 +1,120 @@ +#include "prism/allocator.h" + +#define PM_ALLOCATOR_CAPACITY_DEFAULT 4096 + +/** + * Initialize an allocator with the given capacity. + */ +void +pm_allocator_init(pm_allocator_t *allocator, size_t capacity) { + char *memory = xmalloc(capacity); + if (memory == NULL) { + fprintf(stderr, "[pm_allocator_init] failed to allocate memory\n"); + abort(); + } + + *allocator = (pm_allocator_t) { + .head = { + .next = NULL, + .start = memory, + .current = memory, + .end = memory + capacity + }, + .tail = &allocator->head + }; +} + +/** + * Allocate memory from the given allocator. + */ +void * +pm_allocator_arena_alloc(pm_allocator_t *allocator, size_t size, size_t alignment) { + assert(size > 0); + + char *result = allocator->tail->current; + uintptr_t delta = 0; + uintptr_t current = (uintptr_t) allocator->tail->current; + if (current % alignment != 0) { + delta = alignment - (current % alignment); + } + result += delta; + + char *next = result + size; + + if (next >= allocator->tail->end) { + size_t next_capacity = PM_ALLOCATOR_CAPACITY_DEFAULT; + + // In case the requested size is larger than our default capacity, scale + // up just this node in the linked list to fit the allocation. + if (size > next_capacity) next_capacity = size; + + char *memory = xmalloc(sizeof(pm_allocator_page_t) + next_capacity); + if (memory == NULL) { + fprintf(stderr, "[pm_allocator_arena_alloc] failed to allocate memory\n"); + abort(); + } + + pm_allocator_page_t *next_allocator_page = (pm_allocator_page_t *) memory; + char *start = memory + sizeof(pm_allocator_page_t); + + // Assume the alignment is correct because malloc should give us back + // the most relaxed alignment possible. + assert(((uintptr_t) start) % alignment == 0); + + *next_allocator_page = (pm_allocator_page_t) { + .next = NULL, + .start = start, + .current = NULL, // set at the end + .end = start + next_capacity + }; + + allocator->tail->next = next_allocator_page; + allocator->tail = next_allocator_page; + + result = allocator->tail->start; + next = result + size; + } + + allocator->tail->current = next; + return result; +} + +/** + * Allocate memory from the given allocator and zero out the memory. + */ +void * +pm_allocator_arena_calloc(pm_allocator_t *allocator, size_t count, size_t size, size_t alignment) { + assert(size > 0); + + size_t product = count * size; + assert(product / size == count); + + void *memory = pm_allocator_arena_alloc(allocator, product, alignment); + memset(memory, 0, product); + + return memory; +} + +/** + * Frees the internal memory associated with the allocator. + */ +void pm_allocator_free(pm_allocator_t *allocator) { + bool top_level = true; + + for (pm_allocator_page_t *current = &allocator->head; current != NULL;) { + pm_allocator_page_t *previous = current; + current = current->next; + + // If the memory block is immediately after the allocation of the + // allocator itself, then it came from pm_allocator_arena_alloc, so we + // should free it. Otherwise we assume it was embedded into another + // struct or allocated on the stack. + if (top_level) { + xfree(previous->start); + } else { + xfree(previous); + } + + top_level = false; + } +} diff --git a/src/prism.c b/src/prism.c index 0ad9dcff9a..6949ace3cc 100644 --- a/src/prism.c +++ b/src/prism.c @@ -576,6 +576,7 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) { *scope = (pm_scope_t) { .previous = parser->current_scope, + .allocator = { 0 }, .locals = { 0 }, .parameters = PM_SCOPE_PARAMETERS_NONE, .implicit_parameters = { 0 }, @@ -583,6 +584,7 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) { .closed = closed }; + pm_allocator_init(&scope->allocator, 1024); parser->current_scope = scope; return true; } @@ -742,13 +744,6 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan */ #define PM_LOCALS_HASH_THRESHOLD 9 -static void -pm_locals_free(pm_locals_t *locals) { - if (locals->capacity > 0) { - xfree(locals->locals); - } -} - /** * Use as simple and fast a hash function as we can that still properly mixes * the bits. @@ -766,12 +761,11 @@ pm_locals_hash(pm_constant_id_t name) { * above the threshold for switching to a hash, then we'll switch to a hash. */ static void -pm_locals_resize(pm_locals_t *locals) { +pm_locals_resize(pm_allocator_t *allocator, pm_locals_t *locals) { uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2); assert(next_capacity > locals->capacity); - pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t)); - if (next_locals == NULL) abort(); + pm_local_t *next_locals = pm_allocator_arena_calloc(allocator, next_capacity, sizeof(pm_local_t), sizeof(void *)); if (next_capacity < PM_LOCALS_HASH_THRESHOLD) { if (locals->size > 0) { @@ -796,7 +790,6 @@ pm_locals_resize(pm_locals_t *locals) { } } - pm_locals_free(locals); locals->locals = next_locals; locals->capacity = next_capacity; } @@ -817,9 +810,9 @@ pm_locals_resize(pm_locals_t *locals) { * @return True if the local was added, and false if the local already exists. */ static bool -pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) { +pm_locals_write(pm_allocator_t *allocator, pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) { if (locals->size >= (locals->capacity / 4 * 3)) { - pm_locals_resize(locals); + pm_locals_resize(allocator, locals); } if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { @@ -950,8 +943,15 @@ pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) { * written but not read in certain contexts. */ static void -pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) { - pm_constant_id_list_init_capacity(list, locals->size); +pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) { + // If there were no locals in the scope, then zero out the memory for the + // constant id list and immediately return. + if (locals->size == 0) { + *list = (pm_constant_id_list_t) { 0 }; + return; + } + + pm_constant_id_list_init_capacity(&parser->allocator, list, locals->size); // If we're still below the threshold for switching to a hash, then we only // need to loop over the locals until we hit the size because the locals are @@ -1870,21 +1870,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, static size_t pm_statements_node_body_length(pm_statements_node_t *node); -/** - * This function is here to allow us a place to extend in the future when we - * implement our own arena allocation. - */ -static inline void * -pm_alloc_node(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { - void *memory = xcalloc(1, size); - if (memory == NULL) { - fprintf(stderr, "Failed to allocate %d bytes\n", (int) size); - abort(); - } - return memory; -} - -#define PM_ALLOC_NODE(parser, type) (type *) pm_alloc_node(parser, sizeof(type)) +#define PM_ALLOC_NODE(parser, type) (type *) pm_allocator_arena_calloc(&parser->allocator, 1, sizeof(type), sizeof(void *)) /** * Allocate a new MissingNode node. @@ -2022,13 +2008,13 @@ pm_arguments_node_size(pm_arguments_node_t *node) { * Append an argument to an arguments node. */ static void -pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argument) { +pm_arguments_node_arguments_append(pm_parser_t *parser, pm_arguments_node_t *node, pm_node_t *argument) { if (pm_arguments_node_size(node) == 0) { node->base.location.start = argument->location.start; } node->base.location.end = argument->location.end; - pm_node_list_append(&node->arguments, argument); + pm_node_list_append(&parser->allocator, &node->arguments, argument); } /** @@ -2064,12 +2050,12 @@ pm_array_node_size(pm_array_node_t *node) { * Append an argument to an array node. */ static inline void -pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) { +pm_array_node_elements_append(pm_parser_t *parser, pm_array_node_t *node, pm_node_t *element) { if (!node->elements.size && !node->opening_loc.start) { node->base.location.start = element->location.start; } - pm_node_list_append(&node->elements, element); + pm_node_list_append(&parser->allocator, &node->elements, element); node->base.location.end = element->location.end; // If the element is not a static literal, then the array is not a static @@ -2127,9 +2113,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node node->rest = child; found_rest = true; } else if (found_rest) { - pm_node_list_append(&node->posts, child); + pm_node_list_append(&parser->allocator, &node->posts, child); } else { - pm_node_list_append(&node->requireds, child); + pm_node_list_append(&parser->allocator, &node->requireds, child); } } @@ -2214,8 +2200,8 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin } static inline void -pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t *inner) { - pm_node_list_append(&node->requireds, inner); +pm_array_pattern_node_requireds_append(pm_parser_t *parser, pm_array_pattern_node_t *node, pm_node_t *inner) { + pm_node_list_append(&parser->allocator, &node->requireds, inner); } /** @@ -2515,8 +2501,8 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) * Append a new block-local variable to a BlockParametersNode node. */ static void -pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) { - pm_node_list_append(&node->locals, (pm_node_t *) local); +pm_block_parameters_node_append_local(pm_parser_t *parser, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) { + pm_node_list_append(&parser->allocator, &node->locals, (pm_node_t *) local); if (node->base.location.start == NULL) node->base.location.start = local->base.location.start; node->base.location.end = local->base.location.end; @@ -2638,7 +2624,7 @@ pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator); pm_arguments_node_t *arguments = pm_arguments_node_create(parser); - pm_arguments_node_arguments_append(arguments, argument); + pm_arguments_node_arguments_append(parser, arguments, argument); node->arguments = arguments; node->name = pm_parser_constant_id_token(parser, operator); @@ -2885,12 +2871,6 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const }; pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); - - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -2946,11 +2926,6 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons .value = value }; - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -2983,11 +2958,6 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3020,11 +2990,6 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, .value = value }; - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3057,11 +3022,6 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_write_read_name_init(parser, &node->read_name, &node->write_name); - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3094,11 +3054,6 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const .value = value }; - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3122,11 +3077,6 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { .message_loc = target->message_loc }; - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3154,11 +3104,6 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { .block = target->block }; - // Here we're going to free the target, since it is no longer necessary. - // However, we don't want to call `pm_node_destroy` because we want to keep - // around all of its children since we just reused them. - xfree(target); - return node; } @@ -3214,10 +3159,10 @@ pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node * Append a new condition to a CaseNode node. */ static void -pm_case_node_condition_append(pm_case_node_t *node, pm_node_t *condition) { +pm_case_node_condition_append(pm_parser_t *parser, pm_case_node_t *node, pm_node_t *condition) { assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE)); - pm_node_list_append(&node->conditions, condition); + pm_node_list_append(&parser->allocator, &node->conditions, condition); node->base.location.end = condition->location.end; } @@ -3268,10 +3213,10 @@ pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, p * Append a new condition to a CaseMatchNode node. */ static void -pm_case_match_node_condition_append(pm_case_match_node_t *node, pm_node_t *condition) { +pm_case_match_node_condition_append(pm_parser_t *parser, pm_case_match_node_t *node, pm_node_t *condition) { assert(PM_NODE_TYPE_P(condition, PM_IN_NODE)); - pm_node_list_append(&node->conditions, condition); + pm_node_list_append(&parser->allocator, &node->conditions, condition); node->base.location.end = condition->location.end; } @@ -4080,7 +4025,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { // much more efficient, as we could instead resize the node list to only point // to 1...-1. for (size_t index = 1; index < nodes->size - 1; index++) { - pm_node_list_append(&node->requireds, nodes->nodes[index]); + pm_node_list_append(&parser->allocator, &node->requireds, nodes->nodes[index]); } return node; @@ -4244,11 +4189,11 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { memcpy(digits, start, (unsigned long) (point - start)); memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1)); - pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1); + pm_integer_parse(&parser->allocator, &node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1); digits[0] = '1'; if (end - point > 1) memset(digits + 1, '0', (size_t) (end - point - 1)); - pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + (end - point)); + pm_integer_parse(&parser->allocator, &node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + (end - point)); free(digits); pm_integers_reduce(&node->numerator, &node->denominator); @@ -4433,7 +4378,7 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme pm_node_t *element; PM_NODE_LIST_FOREACH(elements, index, element) { - pm_node_list_append(&node->elements, element); + pm_node_list_append(&parser->allocator, &node->elements, element); } return node; @@ -4642,8 +4587,8 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { * Append a new element to a hash node. */ static inline void -pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) { - pm_node_list_append(&hash->elements, element); +pm_hash_node_elements_append(pm_parser_t *parser, pm_hash_node_t *hash, pm_node_t *element) { + pm_node_list_append(&parser->allocator, &hash->elements, element); bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE); if (static_literal) { @@ -4856,7 +4801,7 @@ pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token default: assert(false && "unreachable"); break; } - pm_integer_parse(&node->value, integer_base, token->start, token->end); + pm_integer_parse(&parser->allocator, &node->value, integer_base, token->start, token->end); return node; } @@ -4913,7 +4858,7 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const default: assert(false && "unreachable"); break; } - pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1); + pm_integer_parse(&parser->allocator, &node->numerator, integer_base, token->start, token->end - 1); return node; } @@ -5101,7 +5046,7 @@ pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable * literals. */ static void -pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { +pm_interpolated_node_append(pm_parser_t *parser, pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { switch (PM_NODE_TYPE(part)) { case PM_STRING_NODE: pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN); @@ -5136,7 +5081,7 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p break; } - pm_node_list_append(parts, part); + pm_node_list_append(&parser->allocator, parts, part); } /** @@ -5164,7 +5109,7 @@ pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_tok } static inline void -pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expression_node_t *node, pm_node_t *part) { +pm_interpolated_regular_expression_node_append(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) { if (node->base.location.start > part->location.start) { node->base.location.start = part->location.start; } @@ -5172,7 +5117,7 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio node->base.location.end = part->location.end; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(parser, (pm_node_t *) node, &node->parts, part); } static inline void @@ -5206,7 +5151,7 @@ pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_inte * which could potentially use a chilled string otherwise. */ static inline void -pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { +pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) { #define CLEAR_FLAGS(node) \ node->base.flags = (pm_node_flags_t) (node->base.flags & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) @@ -5275,7 +5220,7 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ break; } - pm_node_list_append(&node->parts, part); + pm_node_list_append(&parser->allocator, &node->parts, part); #undef CLEAR_FLAGS #undef MUTABLE_FLAGS @@ -5315,7 +5260,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin if (parts != NULL) { pm_node_t *part; PM_NODE_LIST_FOREACH(parts, index, part) { - pm_interpolated_string_node_append(node, part); + pm_interpolated_string_node_append(parser, node, part); } } @@ -5332,12 +5277,12 @@ pm_interpolated_string_node_closing_set(pm_interpolated_string_node_t *node, con } static void -pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_t *part) { +pm_interpolated_symbol_node_append(pm_parser_t *parser, pm_interpolated_symbol_node_t *node, pm_node_t *part) { if (node->parts.size == 0 && node->opening_loc.start == NULL) { node->base.location.start = part->location.start; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(parser, (pm_node_t *) node, &node->parts, part); node->base.location.end = MAX(node->base.location.end, part->location.end); } @@ -5371,7 +5316,7 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin if (parts != NULL) { pm_node_t *part; PM_NODE_LIST_FOREACH(parts, index, part) { - pm_interpolated_symbol_node_append(node, part); + pm_interpolated_symbol_node_append(parser, node, part); } } @@ -5402,8 +5347,8 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi } static inline void -pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) { - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); +pm_interpolated_xstring_node_append(pm_parser_t *parser, pm_interpolated_x_string_node_t *node, pm_node_t *part) { + pm_interpolated_node_append(parser, (pm_node_t *) node, &node->parts, part); node->base.location.end = part->location.end; } @@ -5473,14 +5418,14 @@ pm_keyword_hash_node_create(pm_parser_t *parser) { * Append an element to a KeywordHashNode node. */ static void -pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *element) { +pm_keyword_hash_node_elements_append(pm_parser_t *parser, pm_keyword_hash_node_t *hash, pm_node_t *element) { // If the element being added is not an AssocNode or does not have a symbol // key, then we want to turn the SYMBOL_KEYS flag off. if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) { pm_node_flag_unset((pm_node_t *)hash, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS); } - pm_node_list_append(&hash->elements, element); + pm_node_list_append(&parser->allocator, &hash->elements, element); if (hash->base.location.start == NULL) { hash->base.location.start = element->location.start; } @@ -5911,19 +5856,19 @@ pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t node->rest = target; } else { pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS); - pm_node_list_append(&node->rights, target); + pm_node_list_append(&parser->allocator, &node->rights, target); } } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) { if (node->rest == NULL) { node->rest = target; } else { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST); - pm_node_list_append(&node->rights, target); + pm_node_list_append(&parser->allocator, &node->rights, target); } } else if (node->rest == NULL) { - pm_node_list_append(&node->lefts, target); + pm_node_list_append(&parser->allocator, &node->lefts, target); } else { - pm_node_list_append(&node->rights, target); + pm_node_list_append(&parser->allocator, &node->rights, target); } if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) { @@ -5978,10 +5923,6 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, .value = value }; - // Explicitly do not call pm_node_destroy here because we want to keep - // around all of the information within the MultiWriteNode node. - xfree(target); - return node; } @@ -6227,27 +6168,27 @@ pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) * Append a required parameter to a ParametersNode node. */ static void -pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *param) { +pm_parameters_node_requireds_append(pm_parser_t *parser, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->requireds, param); + pm_node_list_append(&parser->allocator, ¶ms->requireds, param); } /** * Append an optional parameter to a ParametersNode node. */ static void -pm_parameters_node_optionals_append(pm_parameters_node_t *params, pm_optional_parameter_node_t *param) { +pm_parameters_node_optionals_append(pm_parser_t *parser, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) { pm_parameters_node_location_set(params, (pm_node_t *) param); - pm_node_list_append(¶ms->optionals, (pm_node_t *) param); + pm_node_list_append(&parser->allocator, ¶ms->optionals, (pm_node_t *) param); } /** * Append a post optional arguments parameter to a ParametersNode node. */ static void -pm_parameters_node_posts_append(pm_parameters_node_t *params, pm_node_t *param) { +pm_parameters_node_posts_append(pm_parser_t *parser, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->posts, param); + pm_node_list_append(&parser->allocator, ¶ms->posts, param); } /** @@ -6263,9 +6204,9 @@ pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) { * Append a keyword parameter to a ParametersNode node. */ static void -pm_parameters_node_keywords_append(pm_parameters_node_t *params, pm_node_t *param) { +pm_parameters_node_keywords_append(pm_parser_t *parser, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->keywords, param); + pm_node_list_append(&parser->allocator, ¶ms->keywords, param); } /** @@ -6618,8 +6559,8 @@ pm_rescue_node_consequent_set(pm_rescue_node_t *node, pm_rescue_node_t *conseque * Append an exception node to a rescue node, and update the location. */ static void -pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) { - pm_node_list_append(&node->exceptions, exception); +pm_rescue_node_exceptions_append(pm_parser_t *parser, pm_rescue_node_t *node, pm_node_t *exception) { + pm_node_list_append(&parser->allocator, &node->exceptions, exception); node->base.location.end = exception->location.end; } @@ -6901,7 +6842,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, } } - pm_node_list_append(&node->body, statement); + pm_node_list_append(&parser->allocator, &node->body, statement); pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE); } @@ -6909,9 +6850,9 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, * Prepend a new node to the given StatementsNode node's body. */ static void -pm_statements_node_body_prepend(pm_statements_node_t *node, pm_node_t *statement) { +pm_statements_node_body_prepend(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement) { pm_statements_node_body_update(node, statement); - pm_node_list_prepend(&node->body, statement); + pm_node_list_prepend(&parser->allocator, &node->body, statement); pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE); } @@ -6958,14 +6899,35 @@ pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_t return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY); } +/** + * Move contents of parser->current_string into the parser's arena allocator + * for a pm_string_t that will be on a node. + */ +static pm_string_t +pm_flush_current_string(pm_parser_t *parser) +{ + pm_string_t current_string = parser->current_string; + if (current_string.type == PM_STRING_OWNED) { + size_t len = pm_string_length(&parser->current_string); + if (len > 0) { + uint8_t *new_source = pm_allocator_arena_alloc(&parser->allocator, len, 1); + memcpy(new_source, pm_string_source(&parser->current_string), len); + pm_string_owned_init(¤t_string, new_source, len); + } + } + pm_string_free(&parser->current_string); + parser->current_string = PM_STRING_EMPTY; + return current_string; +} + /** * Allocate a new StringNode node and create it using the current string on the * parser. */ static pm_string_node_t * pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) { - pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string); - parser->current_string = PM_STRING_EMPTY; + pm_string_t current_string = pm_flush_current_string(parser); + pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, ¤t_string); return node; } @@ -7249,8 +7211,8 @@ pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_t */ static pm_symbol_node_t * pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) { - pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, value, &parser->current_string, false)); - parser->current_string = PM_STRING_EMPTY; + pm_string_t current_string = pm_flush_current_string(parser); + pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, ¤t_string, parse_symbol_encoding(parser, value, ¤t_string, false)); return node; } @@ -7359,11 +7321,6 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end }; pm_node_flag_set((pm_node_t *) new_node, parse_symbol_encoding(parser, &content, &node->unescaped, true)); - // We are explicitly _not_ using pm_node_destroy here because we don't want - // to trash the unescaped string. We could instead copy the string if we - // know that it is owned, but we're taking the fast path for now. - xfree(node); - return new_node; } @@ -7396,11 +7353,6 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { .unescaped = node->unescaped }; - // We are explicitly _not_ using pm_node_destroy here because we don't want - // to trash the unescaped string. We could instead copy the string if we - // know that it is owned, but we're taking the fast path for now. - xfree(node); - return new_node; } @@ -7461,9 +7413,9 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { * Append a name to an undef node. */ static void -pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { +pm_undef_node_append(pm_parser_t *parser, pm_undef_node_t *node, pm_node_t *name) { node->base.location.end = name->location.end; - pm_node_list_append(&node->names, name); + pm_node_list_append(&parser->allocator, &node->names, name); } /** @@ -7618,9 +7570,9 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { * Append a new condition to a when node. */ static void -pm_when_node_conditions_append(pm_when_node_t *node, pm_node_t *condition) { +pm_when_node_conditions_append(pm_parser_t *parser, pm_when_node_t *node, pm_node_t *condition) { node->base.location.end = condition->location.end; - pm_node_list_append(&node->conditions, condition); + pm_node_list_append(&parser->allocator, &node->conditions, condition); } /** @@ -7823,7 +7775,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) { */ static inline void pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) { - pm_locals_write(&parser->current_scope->locals, constant_id, start, end, reads); + pm_locals_write(&parser->current_scope->allocator, &parser->current_scope->locals, constant_id, start, end, reads); } /** @@ -7898,8 +7850,7 @@ static void pm_parser_scope_pop(pm_parser_t *parser) { pm_scope_t *scope = parser->current_scope; parser->current_scope = scope->previous; - pm_locals_free(&scope->locals); - pm_node_list_free(&scope->implicit_parameters); + pm_allocator_free(&scope->allocator); xfree(scope); } @@ -13419,7 +13370,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_arguments_node_t *arguments = pm_arguments_node_create(parser); call->arguments = arguments; - pm_arguments_node_arguments_append(arguments, value); + pm_arguments_node_arguments_append(parser, arguments, value); call->base.location.end = arguments->base.location.end; parse_write_name(parser, &call->name); @@ -13437,7 +13388,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod call->arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(call->arguments, value); + pm_arguments_node_arguments_append(parser, call->arguments, value); target->location.end = value->location.end; // Replace the name with "[]=". @@ -13787,9 +13738,9 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) { - pm_hash_node_elements_append((pm_hash_node_t *) node, element); + pm_hash_node_elements_append(parser, (pm_hash_node_t *) node, element); } else { - pm_keyword_hash_node_elements_append((pm_keyword_hash_node_t *) node, element); + pm_keyword_hash_node_elements_append(parser, (pm_keyword_hash_node_t *) node, element); } // If there's no comma after the element, then we're done. @@ -13819,7 +13770,7 @@ parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t arguments->arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(arguments->arguments, argument); + pm_arguments_node_arguments_append(parser, arguments->arguments, argument); } /** @@ -13975,7 +13926,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE); argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value); - pm_keyword_hash_node_elements_append(bare_hash, argument); + pm_keyword_hash_node_elements_append(parser, bare_hash, argument); argument = (pm_node_t *) bare_hash; // Then parse more if we have a comma @@ -14193,9 +14144,9 @@ parse_parameters( pm_node_t *param = (pm_node_t *) parse_required_destructured_parameter(parser); if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { - pm_parameters_node_requireds_append(params, param); + pm_parameters_node_requireds_append(parser, params, param); } else { - pm_parameters_node_posts_append(params, param); + pm_parameters_node_posts_append(parser, params, param); } break; } @@ -14225,7 +14176,7 @@ parse_parameters( pm_parameters_node_block_set(params, param); } else { pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_BLOCK_MULTI); - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parameters_node_posts_append(parser, params, (pm_node_t *) param); } break; @@ -14245,7 +14196,7 @@ parse_parameters( // If we already have a keyword rest parameter, then we replace it with the // forwarding parameter and move the keyword rest parameter to the posts list. pm_node_t *keyword_rest = params->keyword_rest; - pm_parameters_node_posts_append(params, keyword_rest); + pm_parameters_node_posts_append(parser, params, keyword_rest); if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); params->keyword_rest = NULL; } @@ -14302,7 +14253,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter((pm_node_t *)param); } - pm_parameters_node_optionals_append(params, param); + pm_parameters_node_optionals_append(parser, params, param); // If the value of the parameter increased the number of // reads of that parameter, then we need to warn that we @@ -14325,13 +14276,13 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter((pm_node_t *)param); } - pm_parameters_node_requireds_append(params, (pm_node_t *) param); + pm_parameters_node_requireds_append(parser, params, (pm_node_t *) param); } else { pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); if (repeated) { pm_node_flag_set_repeated_parameter((pm_node_t *)param); } - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parameters_node_posts_append(parser, params, (pm_node_t *) param); } break; @@ -14362,7 +14313,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(param); } - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser, params, param); break; } case PM_TOKEN_SEMICOLON: @@ -14376,7 +14327,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(param); } - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser, params, param); break; } default: { @@ -14403,7 +14354,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(param); } - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser, params, param); // If parsing the value of the parameter resulted in error recovery, // then we can put a missing node in its place and stop parsing the @@ -14445,7 +14396,7 @@ parse_parameters( pm_parameters_node_rest_set(params, param); } else { pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI); - pm_parameters_node_posts_append(params, param); + pm_parameters_node_posts_append(parser, params, param); } break; @@ -14488,7 +14439,7 @@ parse_parameters( pm_parameters_node_keyword_rest_set(params, param); } else { pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI); - pm_parameters_node_posts_append(params, param); + pm_parameters_node_posts_append(parser, params, param); } break; @@ -14504,7 +14455,7 @@ parse_parameters( pm_parameters_node_rest_set(params, param); } else { pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_SPLAT_MULTI); - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parameters_node_posts_append(parser, params, (pm_node_t *) param); } } else { pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); @@ -14579,7 +14530,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type do { pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION); - pm_rescue_node_exceptions_append(rescue, expression); + pm_rescue_node_exceptions_append(parser, rescue, expression); // If we hit a newline, then this is the end of the rescue expression. We // can continue on to parse the statements. @@ -14788,7 +14739,7 @@ parse_block_parameters( pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous); if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local); - pm_block_parameters_node_append_local(block_parameters, local); + pm_block_parameters_node_append_local(parser, block_parameters, local); } while (accept1(parser, PM_TOKEN_COMMA)); } } @@ -15037,7 +14988,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept if (arguments->arguments == NULL) { arguments->arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(arguments->arguments, arguments->block); + pm_arguments_node_arguments_append(parser, arguments->arguments, arguments->block); } arguments->block = (pm_node_t *) block; } @@ -15099,7 +15050,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) { // block exit to the list of exits for the expression, and // the node parsing will handle validating it instead. assert(parser->current_block_exits != NULL); - pm_node_list_append(parser->current_block_exits, node); + pm_node_list_append(&parser->block_exits_allocator, parser->current_block_exits, node); } else { // Otherwise, if we haven't gone through an expression // context, then this is just invalid and we'll add the @@ -15179,7 +15130,7 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { // However, they could still become valid in a higher level context if // there is another list above this one. In this case we'll push all of // the block exits up to the previous list. - pm_node_list_concat(previous_block_exits, parser->current_block_exits); + pm_node_list_concat(&parser->block_exits_allocator, previous_block_exits, parser->current_block_exits); } else { // If we did not match a trailing while/until and this was the last // chance to do so, then all of the block exits in the list are invalid @@ -15347,8 +15298,6 @@ parse_conditional(pm_parser_t *parser, pm_context_t context) { } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return parent; } @@ -15669,11 +15618,11 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s } pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); - if (part) pm_interpolated_symbol_node_append(symbol, part); + if (part) pm_interpolated_symbol_node_append(parser, symbol, part); while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_interpolated_symbol_node_append(symbol, part); + pm_interpolated_symbol_node_append(parser, symbol, part); } } @@ -15710,10 +15659,10 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_token_t bounds = not_provided(parser); pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped); - pm_interpolated_symbol_node_append(symbol, part); + pm_interpolated_symbol_node_append(parser, symbol, part); part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string); - pm_interpolated_symbol_node_append(symbol, part); + pm_interpolated_symbol_node_append(parser, symbol, part); if (next_state != PM_LEX_STATE_NONE) { lex_state_set(parser, next_state); @@ -15861,12 +15810,12 @@ parse_variable(pm_parser_t *parser) { } pm_node_t *node = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false); - pm_node_list_append(¤t_scope->implicit_parameters, node); + pm_node_list_append(¤t_scope->allocator, ¤t_scope->implicit_parameters, node); return node; } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) { pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); - pm_node_list_append(¤t_scope->implicit_parameters, node); + pm_node_list_append(¤t_scope->allocator, ¤t_scope->implicit_parameters, node); return node; } @@ -15922,10 +15871,10 @@ parse_method_definition_name(pm_parser_t *parser) { } static void -parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) { +parse_heredoc_dedent_string(pm_parser_t *parser, pm_string_t *string, size_t common_whitespace) { // Get a reference to the string struct that is being held by the string // node. This is the value we're going to actually manipulate. - pm_string_ensure_owned(string); + pm_string_ensure_owned(&parser->allocator, string); // Now get the bounds of the existing string. We'll use this as a // destination to move bytes into. We'll also use it for bounds checking @@ -15986,7 +15935,7 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w pm_string_node_t *string_node = ((pm_string_node_t *) node); if (dedent_next) { - parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace); + parse_heredoc_dedent_string(parser, &string_node->unescaped, common_whitespace); } if (string_node->unescaped.length == 0) { @@ -16078,22 +16027,24 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { // In that case we need to switch to an interpolated string to // be able to contain all of the parts. if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + pm_allocator_t parts_allocator; + pm_allocator_init(&parts_allocator, sizeof(pm_node_t *) * 4); pm_node_list_t parts = { 0 }; pm_token_t delimiters = not_provided(parser); pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped); - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); do { part = (pm_node_t *) pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters); - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); parser_lex(parser); } while (match1(parser, PM_TOKEN_STRING_CONTENT)); expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF); node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); - pm_node_list_free(&parts); + pm_allocator_free(&parts_allocator); } else if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) { node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); } else if (match1(parser, PM_TOKEN_EOF)) { @@ -16137,17 +16088,19 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { } else { // If we get here, then we have interpolation so we'll need // to create a string or symbol node with interpolation. + pm_allocator_t parts_allocator; + pm_allocator_init(&parts_allocator, sizeof(pm_node_t *) * 8); pm_node_list_t parts = { 0 }; pm_token_t string_opening = not_provided(parser); pm_token_t string_closing = not_provided(parser); pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped); pm_node_flag_set(part, parse_unescaped_encoding(parser)); - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); } } @@ -16161,18 +16114,20 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); } - pm_node_list_free(&parts); + pm_allocator_free(&parts_allocator); } } else { // If we get here, then the first part of the string is not plain // string content, in which case we need to parse the string as an // interpolated string. + pm_allocator_t parts_allocator; + pm_allocator_init(&parts_allocator, sizeof(pm_node_t *) * 8); pm_node_list_t parts = { 0 }; pm_node_t *part; while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); } } @@ -16186,7 +16141,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); } - pm_node_list_free(&parts); + pm_allocator_free(&parts_allocator); } if (current == NULL) { @@ -16215,11 +16170,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) { pm_token_t bounds = not_provided(parser); pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds); - pm_interpolated_string_node_append(container, current); + pm_interpolated_string_node_append(parser, container, current); current = (pm_node_t *) container; } - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); + pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node); } } @@ -16366,7 +16321,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures // attaching its constant. In this case we'll create an array pattern and // attach our constant to it. pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing); - pm_array_pattern_node_requireds_append(pattern_node, inner); + pm_array_pattern_node_requireds_append(parser, pattern_node, inner); return (pm_node_t *) pattern_node; } @@ -16545,7 +16500,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_token_t operator = not_provided(parser); pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(&parser->allocator, &assocs, assoc); break; } } @@ -16561,7 +16516,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, first_node->location.start, first_node->location.end); pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(&parser->allocator, &assocs, assoc); break; } } @@ -16580,7 +16535,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node rest = assoc; } else { pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(&parser->allocator, &assocs, assoc); } } else { pm_node_t *key; @@ -16614,13 +16569,11 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); } - pm_node_list_append(&assocs, assoc); + pm_node_list_append(&parser->allocator, &assocs, assoc); } } pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); - xfree(assocs.nodes); - pm_static_literals_free(&keys); return node; } @@ -16701,7 +16654,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing); - pm_array_pattern_node_requireds_append(node, inner); + pm_array_pattern_node_requireds_append(parser, node, inner); return (pm_node_t *) node; } case PM_TOKEN_BRACE_LEFT: { @@ -17035,14 +16988,14 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // find pattern. We need to parse all of the patterns, put them into a big // list, and then determine which type of node we have. pm_node_list_t nodes = { 0 }; - pm_node_list_append(&nodes, node); + pm_node_list_append(&parser->allocator, &nodes, node); // Gather up all of the patterns into the list. while (accept1(parser, PM_TOKEN_COMMA)) { // Break early here in case we have a trailing comma. if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) { node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); - pm_node_list_append(&nodes, node); + pm_node_list_append(&parser->allocator, &nodes, node); break; } @@ -17061,7 +17014,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag node = parse_pattern_primitives(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA); } - pm_node_list_append(&nodes, node); + pm_node_list_append(&parser->allocator, &nodes, node); } // If the first pattern and the last pattern are rest patterns, then we will @@ -17073,8 +17026,6 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } else { node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes); } - - xfree(nodes.nodes); } else if (leading_rest) { // Otherwise, if we parsed a single splat pattern, then we know we have an // array pattern, so we can go ahead and create that node. @@ -17460,7 +17411,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE); pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, element, &operator, value); - pm_keyword_hash_node_elements_append(hash, assoc); + pm_keyword_hash_node_elements_append(parser, hash, assoc); element = (pm_node_t *) hash; if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { @@ -17472,7 +17423,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_array_node_elements_append(array, element); + pm_array_node_elements_append(parser, array, element); if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; } @@ -17499,8 +17450,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous); } @@ -17531,7 +17480,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_accepts_block_stack_pop(parser); pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) { // If we have a single statement and are ending on a right @@ -17623,8 +17571,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - pm_void_statements_check(parser, statements); return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); } @@ -17928,7 +17874,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b size_t common_whitespace = lex_mode->as.heredoc.common_whitespace; if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { - parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); + parse_heredoc_dedent_string(parser, &cast->unescaped, common_whitespace); } node = (pm_node_t *) cast; @@ -17937,12 +17883,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we get here, then we have multiple parts in the heredoc, // so we'll need to create an interpolated string node to hold // them all. + pm_allocator_t parts_allocator; + pm_allocator_init(&parts_allocator, sizeof(pm_node_t *) * 8); pm_node_list_t parts = { 0 }; - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_node_list_append(&parts, part); + pm_node_list_append(&parts_allocator, &parts, part); } } @@ -17952,7 +17900,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // interpolated node. if (quote == PM_HEREDOC_QUOTE_BACKTICK) { pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening); - cast->parts = parts; + cast->parts.nodes = pm_allocator_arena_alloc(&parser->allocator, sizeof(pm_node_t *) * parts.size, sizeof(pm_node_t *)); + cast->parts.size = parts.size; + cast->parts.capacity = parts.capacity; + memcpy(cast->parts.nodes, parts.nodes, sizeof(pm_node_t *) * parts.size); + pm_allocator_free(&parts_allocator); expect1_heredoc_term(parser, lex_mode); pm_interpolated_xstring_node_closing_set(cast, &parser->previous); @@ -17961,7 +17913,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b node = (pm_node_t *) cast; } else { pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); - pm_node_list_free(&parts); + pm_allocator_free(&parts_allocator); expect1_heredoc_term(parser, lex_mode); pm_interpolated_string_node_closing_set(cast, &parser->previous); @@ -18087,8 +18039,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (accept1(parser, PM_TOKEN_KEYWORD_END)) { pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous); } @@ -18115,12 +18065,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR); pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression); - pm_when_node_conditions_append(when_node, (pm_node_t *) splat_node); + pm_when_node_conditions_append(parser, when_node, (pm_node_t *) splat_node); if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break; } else { pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_CASE_EXPRESSION_AFTER_WHEN); - pm_when_node_conditions_append(when_node, condition); + pm_when_node_conditions_append(parser, when_node, condition); // If we found a missing node, then this is a syntax // error and we should stop looping. @@ -18154,7 +18104,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_case_node_condition_append(case_node, (pm_node_t *) when_node); + pm_case_node_condition_append(parser, case_node, (pm_node_t *) when_node); } // If we didn't parse any conditions (in or when) then we need @@ -18233,7 +18183,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // Now that we have the full pattern and statements, we can // create the node and attach it to the case node. pm_node_t *condition = (pm_node_t *) pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword); - pm_case_match_node_condition_append(case_node, condition); + pm_case_match_node_condition_append(parser, case_node, condition); } // If we didn't parse any conditions (in or when) then we need @@ -18271,8 +18221,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return node; } case PM_TOKEN_KEYWORD_BEGIN: { @@ -18304,8 +18252,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) begin_node; } case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { @@ -18507,8 +18453,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous); } case PM_TOKEN_KEYWORD_DEF: { @@ -18921,7 +18865,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) { pm_node_destroy(parser, name); } else { - pm_undef_node_append(undef, name); + pm_undef_node_append(parser, undef, name); while (match1(parser, PM_TOKEN_COMMA)) { lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); @@ -18933,7 +18877,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b break; } - pm_undef_node_append(undef, name); + pm_undef_node_append(parser, undef, name); } } @@ -18985,8 +18929,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // the name of the module, then we'll handle that here. if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) { pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing); } @@ -19032,8 +18974,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous); } case PM_TOKEN_KEYWORD_NIL: @@ -19121,7 +19061,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_array_node_elements_append(array, (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); + pm_array_node_elements_append(parser, array, (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); } expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); @@ -19156,7 +19096,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } else { // If we hit a separator after we've hit content, then we need to // append that content to the list and reset the current node. - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser, array, current); current = NULL; } @@ -19180,7 +19120,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); parser_lex(parser); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); + pm_interpolated_symbol_node_append(parser, (pm_interpolated_symbol_node_t *) current, string); } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { // If we hit string content and the current node is a symbol node, // then we need to convert the current node into an interpolated @@ -19194,10 +19134,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - pm_interpolated_symbol_node_append(interpolated, first_string); - pm_interpolated_symbol_node_append(interpolated, second_string); + pm_interpolated_symbol_node_append(parser, interpolated, first_string); + pm_interpolated_symbol_node_append(parser, interpolated, second_string); - xfree(current); current = (pm_node_t *) interpolated; } else { assert(false && "unreachable"); @@ -19223,7 +19162,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); - pm_interpolated_symbol_node_append(interpolated, current); + pm_interpolated_symbol_node_append(parser, interpolated, current); interpolated->base.location.start = current->location.start; start_location_set = true; current = (pm_node_t *) interpolated; @@ -19233,7 +19172,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + pm_interpolated_symbol_node_append(parser, (pm_interpolated_symbol_node_t *) current, part); if (!start_location_set) { current->location.start = part->location.start; } @@ -19258,7 +19197,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); - pm_interpolated_symbol_node_append(interpolated, current); + pm_interpolated_symbol_node_append(parser, interpolated, current); interpolated->base.location.start = current->location.start; start_location_set = true; current = (pm_node_t *) interpolated; @@ -19270,7 +19209,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + pm_interpolated_symbol_node_append(parser, (pm_interpolated_symbol_node_t *) current, part); if (!start_location_set) { current->location.start = part->location.start; } @@ -19285,7 +19224,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we have a current node, then we need to append it to the list. if (current) { - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser, array, current); } pm_token_t closing = parser->current; @@ -19316,7 +19255,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t closing = not_provided(parser); pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); - pm_array_node_elements_append(array, string); + pm_array_node_elements_append(parser, array, string); } expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); @@ -19356,7 +19295,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we hit a separator after we've hit content, // then we need to append that content to the list // and reset the current node. - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser, array, current); current = NULL; } @@ -19381,15 +19320,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we hit string content and the current node is // an interpolated string, then we need to append // the string content to the list of child nodes. - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); + pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string); } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { // If we hit string content and the current node is // a string node, then we need to convert the // current node into an interpolated string and add // the string content to the list of child nodes. pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(interpolated, current); - pm_interpolated_string_node_append(interpolated, string); + pm_interpolated_string_node_append(parser, interpolated, current); + pm_interpolated_string_node_append(parser, interpolated, string); current = (pm_node_t *) interpolated; } else { assert(false && "unreachable"); @@ -19414,7 +19353,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(parser, interpolated, current); current = (pm_node_t *) interpolated; } else { // If we hit an embedded variable and the current @@ -19423,7 +19362,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part); break; } case PM_TOKEN_EMBEXPR_BEGIN: { @@ -19443,7 +19382,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); - pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(parser, interpolated, current); current = (pm_node_t *) interpolated; } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { // If we hit an embedded expression and the current @@ -19454,7 +19393,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser); - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part); break; } default: @@ -19466,7 +19405,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we have a current node, then we need to append it to the list. if (current) { - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser, array, current); } pm_token_t closing = parser->current; @@ -19509,7 +19448,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // expression at least has something in it. We'll need to check if the // following token is the end (in which case we can return a plain // regular expression) or if it's not then it has interpolation. - pm_string_t unescaped = parser->current_string; + pm_string_t unescaped = pm_flush_current_string(parser); pm_token_t content = parser->current; bool ascii_only = parser->current_regular_expression_ascii_only; parser_lex(parser); @@ -19547,7 +19486,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING); } - pm_interpolated_regular_expression_node_append(interpolated, part); + pm_interpolated_regular_expression_node_append(parser, interpolated, part); } else { // If the first part of the body of the regular expression is not a // string content, then we have interpolation and we need to create an @@ -19560,7 +19499,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *part; while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_interpolated_regular_expression_node_append(interpolated, part); + pm_interpolated_regular_expression_node_append(parser, interpolated, part); } } @@ -19605,7 +19544,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // at least has something in it. We'll need to check if the // following token is the end (in which case we can return a // plain string) or if it's not then it has interpolation. - pm_string_t unescaped = parser->current_string; + pm_string_t unescaped = pm_flush_current_string(parser);; pm_token_t content = parser->current; parser_lex(parser); @@ -19626,7 +19565,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); pm_node_flag_set(part, parse_unescaped_encoding(parser)); - pm_interpolated_xstring_node_append(node, part); + pm_interpolated_xstring_node_append(parser, node, part); } else { // If the first part of the body of the string is not a string // content, then we have interpolation and we need to create an @@ -19637,7 +19576,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *part; while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { if ((part = parse_string_part(parser)) != NULL) { - pm_interpolated_xstring_node_append(node, part); + pm_interpolated_xstring_node_append(parser, node, part); } } @@ -19969,13 +19908,13 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding pm_token_t opening = not_provided(parser); pm_array_node_t *array = pm_array_node_create(parser, &opening); - pm_array_node_elements_append(array, value); + pm_array_node_elements_append(parser, array, value); value = (pm_node_t *) array; while (accept1(parser, PM_TOKEN_COMMA)) { pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT); - pm_array_node_elements_append(array, element); + pm_array_node_elements_append(parser, array, element); if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; parse_assignment_value_local(parser, element); @@ -20121,7 +20060,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // Next, create the local variable target and add it to the list of // targets for the match. pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth); - pm_node_list_append(&callback_data->match->targets, target); + pm_node_list_append(&parser->allocator, &callback_data->match->targets, target); } } @@ -20776,6 +20715,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } case PM_TOKEN_QUESTION_MARK: { context_push(parser, PM_CONTEXT_TERNARY); + pm_node_list_t current_block_exits = { 0 }; pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); @@ -20796,8 +20736,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t context_pop(parser); pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); } @@ -20809,8 +20747,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t context_pop(parser); pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); } case PM_TOKEN_COLON_COLON: { @@ -20932,7 +20868,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (arguments.arguments == NULL) { arguments.arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(arguments.arguments, arguments.block); + pm_arguments_node_arguments_append(parser, arguments.arguments, arguments.block); } arguments.block = (pm_node_t *) block; @@ -21129,6 +21065,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser, arguments, (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)) ); @@ -21144,6 +21081,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser, arguments, (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)) ); @@ -21157,25 +21095,26 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { (pm_node_t *) call ); - pm_statements_node_body_prepend(statements, (pm_node_t *) write); + pm_statements_node_body_prepend(parser, statements, (pm_node_t *) write); } pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser, arguments, (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)) ); if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) { pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser); - pm_keyword_hash_node_elements_append(keywords, (pm_node_t *) pm_assoc_node_create( + pm_keyword_hash_node_elements_append(parser, keywords, (pm_node_t *) pm_assoc_node_create( parser, (pm_node_t *) pm_symbol_node_synthesized_create(parser, "chomp"), &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }, (pm_node_t *) pm_true_node_synthesized_create(parser) )); - pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords); + pm_arguments_node_arguments_append(parser, arguments, (pm_node_t *) keywords); } pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser); @@ -21280,6 +21219,8 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm assert(source != NULL); *parser = (pm_parser_t) { + .allocator = { 0 }, + .block_exits_allocator = { 0 }, .lex_state = PM_LEX_STATE_BEG, .enclosure_nesting = 0, .lambda_enclosure_nesting = -1, @@ -21329,6 +21270,10 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .current_regular_expression_ascii_only = false }; + // TODO: find a better starting size + pm_allocator_init(&parser->allocator, 4096); + pm_allocator_init(&parser->block_exits_allocator, 4096); + // Initialize the constant pool. We're going to completely guess as to the // number of constants that we'll need based on the size of the input. The // ratio we chose here is actually less arbitrary than you might think. @@ -21551,6 +21496,9 @@ pm_parser_free(pm_parser_t *parser) { while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) { lex_mode_pop(parser); } + + pm_allocator_free(&parser->block_exits_allocator); + pm_allocator_free(&parser->allocator); } /** diff --git a/src/util/pm_constant_pool.c b/src/util/pm_constant_pool.c index 624002cec9..6b897fe1b6 100644 --- a/src/util/pm_constant_pool.c +++ b/src/util/pm_constant_pool.c @@ -14,10 +14,8 @@ pm_constant_id_list_init(pm_constant_id_list_t *list) { * Initialize a list of constant ids with a given capacity. */ void -pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) { - list->ids = xcalloc(capacity, sizeof(pm_constant_id_t)); - if (list->ids == NULL) abort(); - +pm_constant_id_list_init_capacity(pm_allocator_t *allocator, pm_constant_id_list_t *list, size_t capacity) { + list->ids = pm_allocator_arena_calloc(allocator, capacity, sizeof(pm_constant_id_t), sizeof(pm_constant_id_t)); list->size = 0; list->capacity = capacity; } diff --git a/src/util/pm_integer.c b/src/util/pm_integer.c index 5b0f34465c..4b120aef53 100644 --- a/src/util/pm_integer.c +++ b/src/util/pm_integer.c @@ -321,8 +321,6 @@ pm_integer_normalize(pm_integer_t *integer) { uint32_t value = integer->values[0]; bool negative = integer->negative && value != 0; - - pm_integer_free(integer); *integer = (pm_integer_t) { .value = value, .length = 0, .values = NULL, .negative = negative }; } @@ -331,7 +329,7 @@ pm_integer_normalize(pm_integer_t *integer) { * In practice, it converts 10**9 to 1<<32 or 1<<32 to 10**9. */ static void -pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, uint64_t base_from, uint64_t base_to) { +pm_integer_convert_base(pm_allocator_t *allocator, pm_integer_t *destination, const pm_integer_t *source, uint64_t base_from, uint64_t base_to) { size_t source_length; const uint32_t *source_values; INTEGER_EXTRACT(source, source_length, source_values) @@ -383,6 +381,16 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u destination->negative = source->negative; pm_integer_normalize(destination); + // If we need to, copy the allocated memory out of the heap and into the + // allocator. + if (destination->length != 0) { + size_t size = destination->length * sizeof(uint32_t); + uint32_t *values = pm_allocator_arena_alloc(allocator, size, sizeof(uint32_t)); + memcpy(values, destination->values, size); + xfree(destination->values); + destination->values = values; + } + xfree(bigints); pm_integer_free(&base); } @@ -393,12 +401,12 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u * Convert digits to integer with the given power-of-two base. */ static void -pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *digits, size_t digits_length) { +pm_integer_parse_powof2(pm_allocator_t *allocator, pm_integer_t *integer, uint32_t base, const uint8_t *digits, size_t digits_length) { size_t bit = 1; while (base > (uint32_t) (1 << bit)) bit++; size_t length = (digits_length * bit + 31) / 32; - uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); + uint32_t *values = pm_allocator_arena_calloc(allocator, length, sizeof(uint32_t), sizeof(uint32_t)); for (size_t digit_index = 0; digit_index < digits_length; digit_index++) { size_t bit_position = bit * (digits_length - digit_index - 1); @@ -420,7 +428,7 @@ pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *dig * Convert decimal digits to pm_integer_t. */ static void -pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { +pm_integer_parse_decimal(pm_allocator_t *allocator, pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { const size_t batch = 9; size_t length = (digits_length + batch - 1) / batch; @@ -438,7 +446,7 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di } // Convert base from 10**9 to 1<<32. - pm_integer_convert_base(integer, &((pm_integer_t) { .value = 0, .length = length, .values = values, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); + pm_integer_convert_base(allocator, integer, &((pm_integer_t) { .value = 0, .length = length, .values = values, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); xfree(values); } @@ -446,7 +454,7 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di * Parse a large integer from a string that does not fit into uint32_t. */ static void -pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { +pm_integer_parse_big(pm_allocator_t *allocator, pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { // Allocate an array to store digits. uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start)); size_t digits_length = 0; @@ -458,9 +466,9 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t * // Construct pm_integer_t from the digits. if (multiplier == 10) { - pm_integer_parse_decimal(integer, digits, digits_length); + pm_integer_parse_decimal(allocator, integer, digits, digits_length); } else { - pm_integer_parse_powof2(integer, multiplier, digits, digits_length); + pm_integer_parse_powof2(allocator, integer, multiplier, digits, digits_length); } xfree(digits); @@ -472,7 +480,7 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t * * here. */ void -pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) { +pm_integer_parse(pm_allocator_t *allocator, pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) { // Ignore unary +. Unary - is parsed differently and will not end up here. // Instead, it will modify the parsed integer later. if (*start == '+') start++; @@ -528,7 +536,7 @@ pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *s if (value > UINT32_MAX) { // If the integer is too large to fit into a single uint32_t, then // we'll parse it as a big integer. - pm_integer_parse_big(integer, multiplier, start, end); + pm_integer_parse_big(allocator, integer, multiplier, start, end); return; } } @@ -625,12 +633,15 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { } // Otherwise, first we'll convert the base from 1<<32 to 10**9. + pm_allocator_t allocator; + pm_allocator_init(&allocator, 1024); + pm_integer_t converted = { 0 }; - pm_integer_convert_base(&converted, integer, (uint64_t) 1 << 32, 1000000000); + pm_integer_convert_base(&allocator, &converted, integer, (uint64_t) 1 << 32, 1000000000); if (converted.values == NULL) { pm_buffer_append_format(buffer, "%" PRIu32, converted.value); - pm_integer_free(&converted); + pm_allocator_free(&allocator); return; } @@ -655,7 +666,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { // Finally, append the string to the buffer and free the digits. pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset); xfree(digits); - pm_integer_free(&converted); + pm_allocator_free(&allocator); } /** diff --git a/src/util/pm_string.c b/src/util/pm_string.c index dfc121b6a2..42547bd72f 100644 --- a/src/util/pm_string.c +++ b/src/util/pm_string.c @@ -250,13 +250,13 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { * copy over the previous source. */ void -pm_string_ensure_owned(pm_string_t *string) { +pm_string_ensure_owned(pm_allocator_t *allocator, pm_string_t *string) { if (string->type == PM_STRING_OWNED) return; size_t length = pm_string_length(string); const uint8_t *source = pm_string_source(string); - uint8_t *memory = xmalloc(length); + uint8_t *memory = pm_allocator_arena_alloc(allocator, length, 1); if (!memory) return; pm_string_owned_init(string, memory, length); diff --git a/templates/src/node.c.erb b/templates/src/node.c.erb index da30a96ec4..bb2f3267d4 100644 --- a/templates/src/node.c.erb +++ b/templates/src/node.c.erb @@ -7,83 +7,75 @@ * the list to be twice as large as it was before. If the reallocation fails, * this function returns false, otherwise it returns true. */ -static bool -pm_node_list_grow(pm_node_list_t *list, size_t size) { +static void +pm_node_list_grow(pm_allocator_t *allocator, pm_node_list_t *list, size_t size) { size_t requested_size = list->size + size; - // If the requested size caused overflow, return false. - if (requested_size < list->size) return false; + // If the requested size caused overflow, fail. + assert(requested_size - size == list->size); - // If the requested size is within the existing capacity, return true. - if (requested_size < list->capacity) return true; + // If the requested size is within the existing capacity, return. + if (requested_size < list->capacity) return; // Otherwise, reallocate the list to be twice as large as it was before. - size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + size_t next_capacity; + if (list->capacity == 0) { + next_capacity = 4; + } else { + next_capacity = list->capacity * 2; - // If multiplying by 2 caused overflow, return false. - if (next_capacity < list->capacity) return false; + // If multiplying by 2 caused overflow, fail. + assert(next_capacity / 2 == list->capacity); + } // If we didn't get enough by doubling, keep doubling until we do. while (requested_size > next_capacity) { size_t double_capacity = next_capacity * 2; // Ensure we didn't overflow by multiplying by 2. - if (double_capacity < next_capacity) return false; + assert(double_capacity / 2 == next_capacity); next_capacity = double_capacity; } - pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); - if (nodes == NULL) return false; + pm_node_t **nodes = pm_allocator_arena_alloc(allocator, sizeof(pm_node_t *) * next_capacity, sizeof(pm_node_t *)); + memcpy(nodes, list->nodes, list->size * sizeof(pm_node_t *)); list->nodes = nodes; list->capacity = next_capacity; - return true; } /** * Append a new node onto the end of the node list. */ void -pm_node_list_append(pm_node_list_t *list, pm_node_t *node) { - if (pm_node_list_grow(list, 1)) { - list->nodes[list->size++] = node; - } +pm_node_list_append(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_t *node) { + pm_node_list_grow(allocator, list, 1); + list->nodes[list->size++] = node; } /** * Prepend a new node onto the beginning of the node list. */ void -pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) { - if (pm_node_list_grow(list, 1)) { - memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *)); - list->nodes[0] = node; - list->size++; - } +pm_node_list_prepend(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_t *node) { + pm_node_list_grow(allocator, list, 1); + memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *)); + list->nodes[0] = node; + list->size++; } /** * Concatenate the given node list onto the end of the other node list. */ void -pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) { - if (other->size > 0 && pm_node_list_grow(list, other->size)) { +pm_node_list_concat(pm_allocator_t *allocator, pm_node_list_t *list, pm_node_list_t *other) { + if (other->size > 0) { + pm_node_list_grow(allocator, list, other->size); memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *)); list->size += other->size; } } -/** - * Free the internal memory associated with the given node list. - */ -void -pm_node_list_free(pm_node_list_t *list) { - if (list->capacity > 0) { - xfree(list->nodes); - *list = (pm_node_list_t) { 0 }; - } -} - PRISM_EXPORTED_FUNCTION void pm_node_destroy(pm_parser_t *parser, pm_node_t *node); @@ -94,7 +86,6 @@ static void pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) { pm_node_t *node; PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node); - pm_node_list_free(list); } /** @@ -108,12 +99,12 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { <%- nodes.each do |node| -%> #line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" case <%= node.type %>: { - <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%> + <%- if node.fields.any? { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::StringField, Prism::Template::NodeListField].include?(field.class) } -%> pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node; <%- end -%> <%- node.fields.each do |field| -%> <%- case field -%> - <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%> + <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField, Prism::Template::ConstantListField, Prism::Template::IntegerField -%> <%- when Prism::Template::NodeField -%> pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>); <%- when Prism::Template::OptionalNodeField -%> @@ -121,13 +112,9 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>); } <%- when Prism::Template::StringField -%> - pm_string_free(&cast-><%= field.name %>); + (void)cast; <%- when Prism::Template::NodeListField -%> pm_node_list_destroy(parser, &cast-><%= field.name %>); - <%- when Prism::Template::ConstantListField -%> - pm_constant_id_list_free(&cast-><%= field.name %>); - <%- when Prism::Template::IntegerField -%> - pm_integer_free(&cast-><%= field.name %>); <%- else -%> <%- raise -%> <%- end -%> @@ -140,7 +127,6 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { assert(false && "unreachable"); break; } - xfree(node); } /**