Rust: add Drop impl for p_context_t
This commit is contained in:
parent
5afb3599f9
commit
450c2f1cff
@ -297,32 +297,36 @@ pub fn <%= @grammar.prefix %>context_new(input: &[u8]) -> <%= @grammar.prefix %>
|
||||
context
|
||||
}
|
||||
|
||||
<% free_token_node_used = @grammar.tree && @grammar.free_token_node != "" %>
|
||||
<% if free_token_node_used %>
|
||||
impl Drop for <%= @grammar.prefix %>context_t {
|
||||
/* Run the free_token_node user code block for every token node in the tree. */
|
||||
fn drop(&mut self) {
|
||||
/* Named so that ${context.<field>} expansions resolve here. */
|
||||
let context = self;
|
||||
for i in 0..context.<%= @grammar.prefix %>tree_nodes.len() {
|
||||
if context.<%= @grammar.prefix %>tree_nodes[i].is_token {
|
||||
let token_node_id = i;
|
||||
<%= expand_code(@grammar.free_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id]") %>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<% end %>
|
||||
/**
|
||||
* Deinitialize and deallocate lexer/parser context structure.
|
||||
*
|
||||
* The memory owned by the context is released when the context is dropped, so
|
||||
* this function only has to consume it. It is provided for symmetry with
|
||||
* <%= @grammar.prefix %>context_new() and with the other target languages;
|
||||
* letting the context go out of scope has the same effect.
|
||||
*
|
||||
* @param context
|
||||
* Lexer/parser context structure.
|
||||
*/
|
||||
<% free_token_node_used = @grammar.tree && @grammar.free_token_node != "" %>
|
||||
<% if free_token_node_used %>
|
||||
/* The context is taken as mutable for the benefit of the free_token_node user
|
||||
* code block below, which is permitted but not required to modify the node it
|
||||
* is freeing. */
|
||||
#[allow(unused_mut)]
|
||||
<% else %>
|
||||
/* Without a free_token_node code block this function has nothing to do. The
|
||||
* context is consumed and the memory it owns is released when it is dropped. */
|
||||
#[allow(unused_variables)]
|
||||
<% end %>
|
||||
pub fn <%= @grammar.prefix %>context_delete(<%= free_token_node_used ? "mut " : "" %>context: <%= @grammar.prefix %>context_t) {
|
||||
<% if free_token_node_used %>
|
||||
for i in 0..context.<%= @grammar.prefix %>tree_nodes.len() {
|
||||
if context.<%= @grammar.prefix %>tree_nodes[i].is_token {
|
||||
let token_node_id = i;
|
||||
<%= expand_code(@grammar.free_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id]") %>
|
||||
}
|
||||
}
|
||||
<% end %>
|
||||
pub fn <%= @grammar.prefix %>context_delete(context: <%= @grammar.prefix %>context_t) {
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
||||
@ -530,15 +530,16 @@ Start -> a:a b:b;
|
||||
The `free_token_node` statement user code block is not emitted for D language
|
||||
since D has a garbage collector.
|
||||
|
||||
The code block is emitted for the Rust target, where it runs from
|
||||
`p_context_delete()`.
|
||||
The code block is emitted for the Rust target, where it is run from a `Drop`
|
||||
implementation generated for `p_context_t`.
|
||||
It therefore runs exactly once however the context is disposed of, whether that
|
||||
is by calling `p_context_delete()` or by simply letting the context go out of
|
||||
scope.
|
||||
A `ptype` or token user field which owns its memory (a `String`, a `Vec`, a
|
||||
`Box`, and so on) is released when the context is dropped and does not need a
|
||||
`free_token_node` code block.
|
||||
The statement is only needed for memory which Rust does not track, such as a
|
||||
raw pointer obtained from `Box::into_raw()`.
|
||||
Note that the generated `p_context_t` does not implement `Drop`, so a
|
||||
`free_token_node` code block only runs if `p_context_delete()` is called.
|
||||
|
||||
##> `lex_fn` statement - specifying a custom lexer function
|
||||
|
||||
@ -1691,9 +1692,9 @@ provide a code block which frees that memory; if specified, the
|
||||
|
||||
For Rust targets, `p_context_delete()` takes the context by value and consumes
|
||||
it.
|
||||
The memory owned by the context is released when the context is dropped, so the
|
||||
call is only strictly required when the grammar supplies a `free_token_node`
|
||||
code block, which runs from `p_context_delete()`.
|
||||
Everything the context owns is released when it is dropped, including running
|
||||
any `free_token_node` code block, so calling this function is optional for a
|
||||
Rust target; letting the context go out of scope has the same effect.
|
||||
|
||||
Rust example:
|
||||
|
||||
|
||||
@ -10,8 +10,18 @@ fn main() {
|
||||
assert!(start.b().valid());
|
||||
assert_eq!(2, unsafe { *start.b().pvalue() });
|
||||
|
||||
/* The free_token_node code block runs from p_context_delete(), not before. */
|
||||
/* The free_token_node code block runs when the context is disposed of, not
|
||||
* before, and frees each of the two token nodes exactly once. */
|
||||
assert_eq!(0, FREED.load(Ordering::SeqCst));
|
||||
p_context_delete(context);
|
||||
assert_eq!(2, FREED.load(Ordering::SeqCst));
|
||||
|
||||
/* Letting the context go out of scope runs the code block too, so a caller
|
||||
* which never calls p_context_delete() does not leak. */
|
||||
{
|
||||
let mut context = p_context_new(b"ab");
|
||||
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||
assert_eq!(2, FREED.load(Ordering::SeqCst));
|
||||
}
|
||||
assert_eq!(4, FREED.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user