From 3dde0a6f395e88196dc6617aa5224f6bfdc445e0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 19 Jan 2016 18:07:24 -0800 Subject: [PATCH] Handle allocation failures during parsing --- include/tree_sitter/runtime.h | 2 +- spec/runtime/parser_spec.cc | 30 ++++- spec/runtime/stack_spec.cc | 33 ++--- src/runtime/document.c | 10 +- src/runtime/lexer.c | 3 + src/runtime/parser.c | 236 +++++++++++++++++++++++----------- src/runtime/stack.c | 50 +++++-- src/runtime/stack.h | 13 +- src/runtime/tree.c | 12 ++ src/runtime/vector.h | 21 ++- 10 files changed, 288 insertions(+), 122 deletions(-) diff --git a/include/tree_sitter/runtime.h b/include/tree_sitter/runtime.h index bbf9dff7..18f55ed4 100644 --- a/include/tree_sitter/runtime.h +++ b/include/tree_sitter/runtime.h @@ -84,7 +84,7 @@ void ts_document_set_input_string(TSDocument *, const char *); TSDebugger ts_document_debugger(const TSDocument *); void ts_document_set_debugger(TSDocument *, TSDebugger); void ts_document_edit(TSDocument *, TSInputEdit); -void ts_document_parse(TSDocument *); +int ts_document_parse(TSDocument *); void ts_document_invalidate(TSDocument *); TSNode ts_document_root_node(const TSDocument *); size_t ts_document_parse_count(const TSDocument *); diff --git a/spec/runtime/parser_spec.cc b/spec/runtime/parser_spec.cc index a4b942fa..4c875bea 100644 --- a/spec/runtime/parser_spec.cc +++ b/spec/runtime/parser_spec.cc @@ -30,7 +30,7 @@ describe("Parser", [&]() { auto set_text = [&](const char *text) { input = new SpyInput(text, chunk_size); ts_document_set_input(doc, input->input()); - ts_document_parse(doc); + AssertThat(ts_document_parse(doc), Equals(0)); root = ts_document_root_node(doc); AssertThat(ts_node_end_byte(root), Equals(strlen(text))); @@ -450,6 +450,34 @@ describe("Parser", [&]() { AssertThat(record_alloc::outstanding_allocation_indices(), IsEmpty()); } }); + + it("handles allocation failures during parsing", [&]() { + ts_document_set_language(doc, get_test_language("cpp")); + + set_text("int main() { return vector().size(); }"); + + size_t allocation_count = record_alloc::allocation_count(); + AssertThat(allocation_count, IsGreaterThan(1)); + + char *string = ts_node_string(root, doc); + AssertThat(string, Equals("(translation_unit (function_definition " + "(identifier) " + "(function_declarator (identifier)) " + "(compound_statement " + "(return_statement (call_expression (field_expression " + "(call_expression (template_call " + "(identifier) " + "(type_name (identifier) (abstract_pointer_declarator)))) " + "(identifier)))))))")); + free(string); + + for (size_t i = 0; i < allocation_count; i++) { + record_alloc::start(); + record_alloc::fail_at_allocation_index(i); + ts_document_invalidate(doc); + AssertThat(ts_document_parse(doc), Equals(-1)); + } + }); }); }); diff --git a/spec/runtime/stack_spec.cc b/spec/runtime/stack_spec.cc index 7d96facd..3ed40bc6 100644 --- a/spec/runtime/stack_spec.cc +++ b/spec/runtime/stack_spec.cc @@ -214,10 +214,8 @@ describe("Stack", [&]() { * A0__B1__C2__D3__G6. * \__E4__F5__/ */ - bool merged = ts_stack_push(stack, 0, stateG, trees[6]); - AssertThat(merged, IsFalse()); - merged = ts_stack_push(stack, 1, stateG, trees[6]); - AssertThat(merged, IsTrue()); + AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 1, stateG, trees[6]), Equals(StackPushResultMerged)); AssertThat(ts_stack_head_count(stack), Equals(1)); const StackEntry *entry1 = ts_stack_head(stack, 0); @@ -239,10 +237,8 @@ describe("Stack", [&]() { * A0__B1__C2__D3__G(6|7) * \__E4__F5____/ */ - bool merged = ts_stack_push(stack, 0, stateG, trees[6]); - AssertThat(merged, IsFalse()); - merged = ts_stack_push(stack, 1, stateG, trees[7]); - AssertThat(merged, IsTrue()); + AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 1, stateG, trees[7]), Equals(StackPushResultMerged)); AssertThat(ts_stack_head_count(stack), Equals(1)); AssertThat(tree_selection_spy.call_count, Equals(1)); @@ -258,19 +254,15 @@ describe("Stack", [&]() { * A0__B1__C2__D3__G6__H7. * \__E4__F5__G6. */ - bool merged = ts_stack_push(stack, 0, stateG, trees[6]); - AssertThat(merged, IsFalse()); - merged = ts_stack_push(stack, 0, stateH, trees[7]); - AssertThat(merged, IsFalse()); - merged = ts_stack_push(stack, 1, stateG, trees[6]); - AssertThat(merged, IsFalse()); + AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 0, stateH, trees[7]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 1, stateG, trees[6]), Equals(StackPushResultContinued)); /* * A0__B1__C2__D3__G6__H7. * \__E4__F5_/ */ - merged = ts_stack_push(stack, 1, stateH, trees[7]); - AssertThat(merged, IsTrue()); + AssertThat(ts_stack_push(stack, 1, stateH, trees[7]), Equals(StackPushResultMerged)); AssertThat(ts_stack_head_count(stack), Equals(1)); StackEntry *head = ts_stack_head(stack, 0); @@ -298,10 +290,8 @@ describe("Stack", [&]() { tree_selection_spy.tree_to_return = parent; tree_selection_spy.call_count = 0; - bool merged = ts_stack_push(stack, 1, stateB, trees[2]); - AssertThat(merged, IsFalse()); - merged = ts_stack_push(stack, 1, stateC, trees[3]); - AssertThat(merged, IsTrue()); + AssertThat(ts_stack_push(stack, 1, stateB, trees[2]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 1, stateC, trees[3]), Equals(StackPushResultMerged)); AssertThat(tree_selection_spy.call_count, Equals(1)); AssertThat(ts_stack_head_count(stack), Equals(1)); @@ -368,8 +358,7 @@ describe("Stack", [&]() { * A0__B1__C2__D3__G6__H7. * \__E4__F5__/ */ - bool merged = ts_stack_push(stack, 0, stateH, trees[7]); - AssertThat(merged, IsFalse()); + AssertThat(ts_stack_push(stack, 0, stateH, trees[7]), Equals(StackPushResultContinued)); AssertThat(ts_stack_head_count(stack), Equals(1)); /* diff --git a/src/runtime/document.c b/src/runtime/document.c index 0aaf6762..b5416e56 100644 --- a/src/runtime/document.c +++ b/src/runtime/document.c @@ -70,21 +70,25 @@ void ts_document_edit(TSDocument *self, TSInputEdit edit) { ts_tree_edit(self->tree, edit); } -void ts_document_parse(TSDocument *self) { +int ts_document_parse(TSDocument *self) { if (!self->input.read_fn || !self->parser.language) - return; + return 0; TSTree *reusable_tree = self->valid ? self->tree : NULL; if (reusable_tree && !reusable_tree->has_changes) - return; + return 0; TSTree *tree = ts_parser_parse(&self->parser, self->input, reusable_tree); + if (!tree) + return -1; + ts_tree_retain(tree); if (self->tree) ts_tree_release(self->tree); self->tree = tree; self->parse_count++; self->valid = true; + return 0; } void ts_document_invalidate(TSDocument *self) { diff --git a/src/runtime/lexer.c b/src/runtime/lexer.c index 1e8d6f3e..6dacfe0b 100644 --- a/src/runtime/lexer.c +++ b/src/runtime/lexer.c @@ -108,6 +108,9 @@ static TSTree *ts_lexer__accept(TSLexer *self, TSSymbol symbol, result = ts_tree_make_leaf(symbol, padding, size, metadata); } + if (!result) + return NULL; + if (fragile) result->lex_state = self->starting_state; diff --git a/src/runtime/parser.c b/src/runtime/parser.c index 4a5686a5..2d2bad93 100644 --- a/src/runtime/parser.c +++ b/src/runtime/parser.c @@ -32,15 +32,23 @@ typedef struct { bool is_verifying; } LookaheadState; +typedef enum { + UpdatedStackHead, + RemovedStackHead, + FailedToUpdateStackHead, +} ParseActionResult; + /* * Private */ -static void ts_parser__breakdown_top_of_stack(TSParser *self, int head) { +static ParseActionResult ts_parser__breakdown_top_of_stack(TSParser *self, int head) { TSTree *last_child = NULL; do { Vector pop_results = ts_stack_pop(self->stack, head, 1, false); + if (!pop_results.size) + return FailedToUpdateStackHead; /* * Since only one entry (not counting extra trees) is being popped from the @@ -70,19 +78,36 @@ static void ts_parser__breakdown_top_of_stack(TSParser *self, int head) { LOG("breakdown_push sym:%s, size:%lu", SYM_NAME(last_child->symbol), ts_tree_total_size(last_child).chars); - merged = - ts_stack_push(self->stack, pop_result->head_index, state, last_child); + + switch (ts_stack_push(self->stack, head_index, state, last_child)) { + case StackPushResultFailed: + return FailedToUpdateStackHead; + case StackPushResultMerged: + merged = true; + case StackPushResultContinued: + merged = false; + break; + } } - for (size_t j = 1, count = pop_result->tree_count; j < count; j++) - merged = ts_stack_push(self->stack, pop_result->head_index, state, - pop_result->trees[j]); - - assert((i == 0) ^ merged); + for (size_t j = 1, count = pop_result->tree_count; j < count; j++) { + TSTree *tree = pop_result->trees[j]; + switch (ts_stack_push(self->stack, head_index, state, tree)) { + case StackPushResultFailed: + return FailedToUpdateStackHead; + case StackPushResultMerged: + merged = true; + case StackPushResultContinued: + merged = false; + break; + } + } } ts_free(removed_trees); } while (last_child && last_child->child_count > 0); + + return UpdatedStackHead; } static void ts_parser__pop_reusable_subtree(LookaheadState *state); @@ -244,33 +269,43 @@ static TSTree *ts_parser__select_tree(void *data, TSTree *left, TSTree *right) { * Parse Actions */ -static bool ts_parser__shift(TSParser *self, int head, TSStateId parse_state, - TSTree *lookahead) { - if (ts_stack_push(self->stack, head, parse_state, lookahead)) { - LOG("merge head:%d", head); - vector_erase(&self->lookahead_states, head); - return false; - } else { - return true; +static ParseActionResult ts_parser__shift(TSParser *self, int head, + TSStateId parse_state, + TSTree *lookahead) { + switch (ts_stack_push(self->stack, head, parse_state, lookahead)) { + case StackPushResultFailed: + return FailedToUpdateStackHead; + case StackPushResultMerged: + LOG("merge head:%d", head); + vector_erase(&self->lookahead_states, head); + return RemovedStackHead; + case StackPushResultContinued: + return UpdatedStackHead; } } -static bool ts_parser__shift_extra(TSParser *self, int head, TSStateId state, +static ParseActionResult ts_parser__shift_extra(TSParser *self, int head, TSStateId state, TSTree *lookahead) { TSSymbolMetadata metadata = self->language->symbol_metadata[lookahead->symbol]; - if (metadata.structural && ts_stack_head_count(self->stack) > 1) + if (metadata.structural && ts_stack_head_count(self->stack) > 1) { lookahead = ts_tree_make_copy(lookahead); + if (!lookahead) + return FailedToUpdateStackHead; + } + lookahead->extra = true; return ts_parser__shift(self, head, state, lookahead); } -static bool ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, +static ParseActionResult ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, int child_count, bool extra, bool fragile, bool count_extra) { vector_clear(&self->reduce_parents); const TSSymbolMetadata *all_metadata = self->language->symbol_metadata; TSSymbolMetadata metadata = all_metadata[symbol]; Vector pop_results = ts_stack_pop(self->stack, head, child_count, count_extra); + if (!pop_results.element_size) + return FailedToUpdateStackHead; int last_head_index = -1; size_t removed_heads = 0; @@ -306,11 +341,13 @@ static bool ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, break; } - parent = - ts_tree_make_node(symbol, pop_result->tree_count - trailing_extra_count, - pop_result->trees, metadata); + size_t child_count = pop_result->tree_count - trailing_extra_count; + parent = ts_tree_make_node(symbol, child_count, pop_result->trees, metadata); + if (!parent) + return FailedToUpdateStackHead; } - vector_push(&self->reduce_parents, &parent); + if (!vector_push(&self->reduce_parents, &parent)) + return FailedToUpdateStackHead; /* * If another path led to the same stack head, add this new parent tree @@ -336,8 +373,10 @@ static bool ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, } LOG("split_during_reduce new_head:%d", new_head); - LookaheadState lookahead_state = *(LookaheadState *)vector_get(&self->lookahead_states, head); - vector_push(&self->lookahead_states, &lookahead_state); + LookaheadState lookahead_state = + *(LookaheadState *)vector_get(&self->lookahead_states, head); + if (!vector_push(&self->lookahead_states, &lookahead_state)) + return FailedToUpdateStackHead; } /* @@ -369,21 +408,31 @@ static bool ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, * If the given state already existed at a different head of the stack, * then remove the lookahead state for the head. */ - if (ts_stack_push(self->stack, new_head, state, parent)) { - LOG("merge_during_reduce head:%d", new_head); - vector_erase(&self->lookahead_states, new_head); - removed_heads++; - continue; + switch (ts_stack_push(self->stack, new_head, state, parent)) { + case StackPushResultFailed: + return FailedToUpdateStackHead; + case StackPushResultMerged: + LOG("merge_during_reduce head:%d", new_head); + vector_erase(&self->lookahead_states, new_head); + removed_heads++; + continue; + case StackPushResultContinued: + break; } if (trailing_extra_count > 0) { for (size_t j = 0; j < trailing_extra_count; j++) { size_t index = pop_result->tree_count - trailing_extra_count + j; - if (ts_stack_push(self->stack, new_head, state, - pop_result->trees[index])) { - vector_erase(&self->lookahead_states, new_head); - removed_heads++; - continue; + TSTree *tree = pop_result->trees[index]; + switch (ts_stack_push(self->stack, new_head, state, tree)) { + case StackPushResultFailed: + return FailedToUpdateStackHead; + case StackPushResultMerged: + vector_erase(&self->lookahead_states, new_head); + removed_heads++; + continue; + case StackPushResultContinued: + break; } } } @@ -405,25 +454,34 @@ static bool ts_parser__reduce(TSParser *self, int head, TSSymbol symbol, } } - return removed_heads < revealed_heads; + if (removed_heads < revealed_heads) + return UpdatedStackHead; + else + return RemovedStackHead; } -static void ts_parser__reduce_error(TSParser *self, int head, +static ParseActionResult ts_parser__reduce_error(TSParser *self, int head, size_t child_count, TSTree *lookahead) { - bool result = ts_parser__reduce(self, head, ts_builtin_sym_error, child_count, - false, false, true); - if (result) { - TSTree **parent = vector_back(&self->reduce_parents); - StackEntry *stack_entry = ts_stack_head(self->stack, head); - stack_entry->position = - ts_length_add(stack_entry->position, lookahead->padding); - (*parent)->size = ts_length_add((*parent)->size, lookahead->padding); - (*parent)->fragile_left = (*parent)->fragile_right = true; - lookahead->padding = ts_length_zero(); + switch(ts_parser__reduce(self, head, ts_builtin_sym_error, child_count, + false, false, true)) { + case FailedToUpdateStackHead: + return FailedToUpdateStackHead; + case RemovedStackHead: + return RemovedStackHead; + case UpdatedStackHead: { + TSTree **parent = vector_back(&self->reduce_parents); + StackEntry *stack_entry = ts_stack_head(self->stack, head); + stack_entry->position = + ts_length_add(stack_entry->position, lookahead->padding); + (*parent)->size = ts_length_add((*parent)->size, lookahead->padding); + (*parent)->fragile_left = (*parent)->fragile_right = true; + lookahead->padding = ts_length_zero(); + return UpdatedStackHead; + } } } -static bool ts_parser__handle_error(TSParser *self, int head, TSTree *lookahead) { +static ParseActionResult ts_parser__handle_error(TSParser *self, int head, TSTree *lookahead) { size_t error_token_count = 1; StackEntry *entry_before_error = ts_stack_head(self->stack, head); @@ -449,7 +507,7 @@ static bool ts_parser__handle_error(TSParser *self, int head, TSTree *lookahead) LOG("recover state:%u, count:%lu", state_after_error, error_token_count + i); ts_parser__reduce_error(self, head, error_token_count + i, lookahead); - return true; + return UpdatedStackHead; } } @@ -462,9 +520,12 @@ static bool ts_parser__handle_error(TSParser *self, int head, TSTree *lookahead) * current lookahead token, advance to the next token. */ LOG("skip token:%s", SYM_NAME(lookahead->symbol)); - ts_parser__shift(self, head, ts_stack_top_state(self->stack, head), - lookahead); + TSStateId state = ts_stack_top_state(self->stack, head); + if (ts_parser__shift(self, head, state, lookahead) == FailedToUpdateStackHead) + return FailedToUpdateStackHead; lookahead = self->language->lex_fn(&self->lexer, 0, true); + if (!lookahead) + return FailedToUpdateStackHead; error_token_count++; /* @@ -473,12 +534,12 @@ static bool ts_parser__handle_error(TSParser *self, int head, TSTree *lookahead) if (lookahead->symbol == ts_builtin_sym_end) { LOG("fail_to_recover"); ts_parser__reduce_error(self, head, -1, lookahead); - return false; + return RemovedStackHead; } } } -static void ts_parser__start(TSParser *self, TSInput input, +static ParseActionResult ts_parser__start(TSParser *self, TSInput input, TSTree *previous_tree) { if (previous_tree) { LOG("parse_after_edit"); @@ -499,10 +560,13 @@ static void ts_parser__start(TSParser *self, TSInput input, vector_clear(&self->lookahead_states); vector_push(&self->lookahead_states, &lookahead_state); self->finished_tree = NULL; + return UpdatedStackHead; } -static void ts_parser__accept(TSParser *self, int head) { +static ParseActionResult ts_parser__accept(TSParser *self, int head) { Vector pop_results = ts_stack_pop(self->stack, head, -1, true); + if (!pop_results.size) + return FailedToUpdateStackHead; for (size_t j = 0; j < pop_results.size; j++) { StackPopResult *pop_result = vector_get(&pop_results, j); @@ -515,6 +579,9 @@ static void ts_parser__accept(TSParser *self, int head) { TSTree **new_children = ts_calloc( root->child_count + leading_extra_count + trailing_extra_count, sizeof(TSTree *)); + if (!new_children) + return FailedToUpdateStackHead; + memcpy(new_children, pop_result->trees, leading_extra_count * sizeof(TSTree *)); memcpy(new_children + leading_extra_count, root->children, @@ -533,13 +600,16 @@ static void ts_parser__accept(TSParser *self, int head) { } } } + + return RemovedStackHead; } /* * Continue performing parse actions for the given head until the current * lookahead symbol is consumed. */ -static bool ts_parser__consume_lookahead(TSParser *self, int head, + +static ParseActionResult ts_parser__consume_lookahead(TSParser *self, int head, TSTree *lookahead) { for (;;) { TSStateId state = ts_stack_top_state(self->stack, head); @@ -578,20 +648,22 @@ static bool ts_parser__consume_lookahead(TSParser *self, int head, if (lookahead_state->is_verifying) { ts_parser__breakdown_top_of_stack(self, current_head); lookahead_state->is_verifying = false; - return false; + return RemovedStackHead; } if (ts_stack_head_count(self->stack) == 1) { - if (ts_parser__handle_error(self, current_head, lookahead)) { - return true; - } else { - ts_parser__accept(self, current_head); - return false; + switch (ts_parser__handle_error(self, current_head, lookahead)) { + case FailedToUpdateStackHead: + return FailedToUpdateStackHead; + case UpdatedStackHead: + return UpdatedStackHead; + case RemovedStackHead: + return ts_parser__accept(self, current_head); } } else { LOG("bail current_head:%d", current_head); ts_parser__remove_head(self, current_head); - return false; + return RemovedStackHead; } case TSParseActionTypeShift: @@ -601,8 +673,8 @@ static bool ts_parser__consume_lookahead(TSParser *self, int head, } else { LOG("shift state:%u", action.data.to_state); lookahead_state->is_verifying = (lookahead->child_count > 0); - return ts_parser__shift(self, current_head, action.data.to_state, - lookahead); + TSStateId state = action.data.to_state; + return ts_parser__shift(self, current_head, state, lookahead); } case TSParseActionTypeReduce: @@ -616,18 +688,24 @@ static bool ts_parser__consume_lookahead(TSParser *self, int head, LOG("reduce sym:%s, child_count:%u, fragile:%s", SYM_NAME(action.data.symbol), action.data.child_count, BOOL_STRING(action.fragile)); - if (!ts_parser__reduce(self, current_head, action.data.symbol, + switch (ts_parser__reduce(self, current_head, action.data.symbol, action.data.child_count, false, - action.fragile, false)) - if (current_head == head) - return false; + action.fragile, false)) { + case FailedToUpdateStackHead: + return FailedToUpdateStackHead; + case RemovedStackHead: + if (current_head == head) + return RemovedStackHead; + break; + case UpdatedStackHead: + break; + } } break; case TSParseActionTypeAccept: LOG("accept"); - ts_parser__accept(self, current_head); - return false; + return ts_parser__accept(self, current_head); } } } @@ -687,7 +765,7 @@ TSTree *ts_parser_parse(TSParser *self, TSInput input, TSTree *previous_tree) { self->is_split = ts_stack_head_count(self->stack) > 1; for (int head = 0; head < ts_stack_head_count(self->stack);) { - for (;;) { + for (bool removed = false; !removed;) { last_position = position; position = ts_stack_top_position(self->stack, head); @@ -707,14 +785,24 @@ TSTree *ts_parser_parse(TSParser *self, TSInput input, TSTree *previous_tree) { ts_stack_top_state(self->stack, head), position.chars); if (position.chars != last_position.chars || - !ts_parser__can_reuse(self, head, lookahead)) + !ts_parser__can_reuse(self, head, lookahead)) { lookahead = ts_parser__get_next_lookahead(self, head); + if (!lookahead) + return NULL; + } LOG("lookahead sym:%s, size:%lu", SYM_NAME(lookahead->symbol), ts_tree_total_chars(lookahead)); - if (!ts_parser__consume_lookahead(self, head, lookahead)) - break; + switch (ts_parser__consume_lookahead(self, head, lookahead)) { + case FailedToUpdateStackHead: + return NULL; + case RemovedStackHead: + removed = true; + break; + case UpdatedStackHead: + break; + } } } diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 687cca2a..a1b28244 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -54,11 +54,11 @@ Stack *ts_stack_new() { goto error; self->pop_results = vector_new(sizeof(StackPopResult), 4); - if (!self->pop_results.contents) + if (!vector_valid(&self->pop_results)) goto error; self->pop_paths = vector_new(sizeof(PopPath), 4); - if (!self->pop_paths.contents) + if (!vector_valid(&self->pop_paths)) goto error; self->tree_selection_payload = NULL; @@ -150,8 +150,11 @@ static bool stack_node_release(StackNode *self) { } static StackNode *stack_node_new(StackNode *next, TSStateId state, TSTree *tree) { - StackNode *self = ts_malloc(sizeof(StackNode)); assert(tree->ref_count > 0); + StackNode *self = ts_malloc(sizeof(StackNode)); + if (!self) + return NULL; + ts_tree_retain(tree); stack_node_retain(next); TSLength position = ts_tree_total_size(tree); @@ -248,15 +251,24 @@ static bool ts_stack__merge_head(Stack *self, int head_index, TSStateId state, * Section: Mutating the stack (Public) */ -bool ts_stack_push(Stack *self, int head_index, TSStateId state, TSTree *tree) { +StackPushResult ts_stack_push(Stack *self, int head_index, TSStateId state, + TSTree *tree) { assert(head_index < self->head_count); + assert(tree); + TSLength position = ts_tree_total_size(tree); if (self->heads[head_index]) position = ts_length_add(self->heads[head_index]->entry.position, position); + if (ts_stack__merge_head(self, head_index, state, tree, position)) - return true; - self->heads[head_index] = stack_node_new(self->heads[head_index], state, tree); - return false; + return StackPushResultMerged; + + StackNode *new_head = stack_node_new(self->heads[head_index], state, tree); + if (!new_head) + return StackPushResultFailed; + + self->heads[head_index] = new_head; + return StackPushResultContinued; } void ts_stack_add_alternative(Stack *self, int head_index, TSTree *tree) { @@ -273,6 +285,9 @@ int ts_stack_split(Stack *self, int head_index) { Vector ts_stack_pop(Stack *self, int head_index, int child_count, bool count_extra) { + vector_clear(&self->pop_results); + vector_clear(&self->pop_paths); + StackNode *previous_head = self->heads[head_index]; int capacity = (child_count == -1) ? STARTING_TREE_CAPACITY : child_count; PopPath initial_path = { @@ -282,9 +297,11 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count, .is_shared = false, }; - vector_clear(&self->pop_results); - vector_clear(&self->pop_paths); - vector_push(&self->pop_paths, &initial_path); + if (!vector_valid(&initial_path.trees)) + goto error; + + if (!vector_push(&self->pop_paths, &initial_path)) + goto error; /* * Reduce along every possible path in parallel. Stop when the given number @@ -319,12 +336,15 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count, } ts_tree_retain(node->entry.tree); - vector_push(&path->trees, &node->entry.tree); + if (!vector_push(&path->trees, &node->entry.tree)) + goto error; path->node = path->node->successors[0]; PopPath path_copy = *path; for (int j = 1; j < node->successor_count; j++) { - vector_push(&self->pop_paths, &path_copy); + if (!vector_push(&self->pop_paths, &path_copy)) + goto error; + PopPath *next_path = vector_back(&self->pop_paths); next_path->node = node->successors[j]; next_path->is_shared = true; @@ -354,11 +374,15 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count, result.head_index = ts_stack__add_head(self, path->node); } - vector_push(&self->pop_results, &result); + if (!vector_push(&self->pop_results, &result)) + goto error; } stack_node_release(previous_head); return self->pop_results; + +error: + return vector_new(0, 0); } void ts_stack_shrink(Stack *self, int head_index, int count) { diff --git a/src/runtime/stack.h b/src/runtime/stack.h index 753916db..bffaacb6 100644 --- a/src/runtime/stack.h +++ b/src/runtime/stack.h @@ -22,6 +22,12 @@ typedef struct { int head_index; } StackPopResult; +typedef enum { + StackPushResultFailed, + StackPushResultMerged, + StackPushResultContinued, +} StackPushResult; + typedef TSTree *(*TreeSelectionFunction)(void *, TSTree *, TSTree *); /* @@ -73,11 +79,10 @@ int ts_stack_entry_next_count(const StackEntry *); StackEntry *ts_stack_entry_next(const StackEntry *, int); /* - * Push a (tree, state) pair onto the given head of the stack. Returns - * a boolean indicating whether the stack head was merged with an - * existing head. + * Push a (tree, state) pair onto the given head of the stack. This could cause + * the head to merge with an existing head. */ -bool ts_stack_push(Stack *, int head, TSStateId, TSTree *); +StackPushResult ts_stack_push(Stack *, int head, TSStateId, TSTree *); /* * Add an alternative tree for the given head of the stack. diff --git a/src/runtime/tree.c b/src/runtime/tree.c index 0e04267a..52c022ed 100644 --- a/src/runtime/tree.c +++ b/src/runtime/tree.c @@ -14,6 +14,9 @@ TSStateId TS_TREE_STATE_ERROR = USHRT_MAX - 1; TSTree *ts_tree_make_leaf(TSSymbol sym, TSLength padding, TSLength size, TSSymbolMetadata metadata) { TSTree *result = ts_malloc(sizeof(TSTree)); + if (!result) + return NULL; + *result = (TSTree){ .ref_count = 1, .symbol = sym, @@ -42,12 +45,18 @@ TSTree *ts_tree_make_error(TSLength size, TSLength padding, char lookahead_char) (TSSymbolMetadata){ .visible = true, .named = true, }); + if (!result) + return NULL; + result->lookahead_char = lookahead_char; return result; } TSTree *ts_tree_make_copy(TSTree *self) { TSTree *result = ts_malloc(sizeof(TSTree)); + if (!result) + return NULL; + *result = *self; return result; } @@ -110,6 +119,9 @@ TSTree *ts_tree_make_node(TSSymbol symbol, size_t child_count, TSTree **children, TSSymbolMetadata metadata) { TSTree *result = ts_tree_make_leaf(symbol, ts_length_zero(), ts_length_zero(), metadata); + if (!result) + return NULL; + ts_tree_set_children(result, child_count, children); return result; } diff --git a/src/runtime/vector.h b/src/runtime/vector.h index c5b1f059..df187479 100644 --- a/src/runtime/vector.h +++ b/src/runtime/vector.h @@ -19,13 +19,23 @@ typedef struct { static inline Vector vector_new(size_t element_size, size_t capacity) { Vector result; - result.contents = ts_calloc(capacity, element_size); result.size = 0; result.capacity = capacity; result.element_size = element_size; + + if (capacity > 0) { + result.contents = ts_calloc(capacity, element_size); + if (!result.contents) + result.element_size = 0; + } + return result; } +static inline bool vector_valid(Vector *self) { + return self->element_size > 0; +} + static inline void vector_delete(Vector *self) { ts_free(self->contents); } @@ -53,17 +63,20 @@ static inline void vector_erase(Vector *self, size_t index) { self->size--; } -static inline void vector_push(Vector *self, void *entry) { +static inline bool vector_push(Vector *self, void *entry) { if (self->size == self->capacity) { self->capacity += 4; - self->contents = - ts_realloc(self->contents, self->capacity * self->element_size); + void *new_contents = ts_realloc(self->contents, self->capacity * self->element_size); + if (!new_contents) + return false; + self->contents = new_contents; } char *contents = (char *)self->contents; memcpy(contents + (self->size * self->element_size), (char *)entry, self->element_size); self->size++; + return true; } static inline void vector_reverse(Vector *self) {