Rework the engine's internal recursion limit (#2969)

This patch unifies the recursion limit checking for RegExp, function call and JSON as well.
Until now the limit was only a counter which was increased/decreased at certain points.
This counter has been substituted with a numeric limit which allows to restrict the stack usage.

This patch fixes #2963 and resolves the closed #2258 issue.

Co-authored-by: Gabor Loki loki@inf.u-szeged.hu
JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2019-07-23 15:31:37 +02:00
committed by GitHub
parent f53dba1a3a
commit 4a9e185840
20 changed files with 156 additions and 216 deletions
+19
View File
@@ -1542,6 +1542,25 @@ typedef struct
*/
#define ECMA_SYMBOL_HASH_SHIFT 2
#if (JERRY_STACK_LIMIT != 0)
/**
* Check the current stack usage. If the limit is reached a RangeError is raised.
*/
#define ECMA_CHECK_STACK_USAGE() \
do \
{ \
if (ecma_get_current_stack_usage () > CONFIG_MEM_STACK_LIMIT) \
{ \
return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size exceeded.")); \
} \
} while (0)
#else /* JERRY_STACK_LIMIT == 0) */
/**
* If the stack limit is unlimited, this check is an empty macro.
*/
#define ECMA_CHECK_STACK_USAGE()
#endif /* (JERRY_STACK_LIMIT != 0) */
/**
* @}
* @}
+15
View File
@@ -1599,6 +1599,21 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
} /* ecma_bytecode_deref */
#if (JERRY_STACK_LIMIT != 0)
/**
* Check the current stack usage by calculating the difference from the initial stack base.
*
* @return current stack usage in bytes
*/
uintptr_t JERRY_ATTR_NOINLINE
ecma_get_current_stack_usage (void)
{
volatile int __sp;
return (uintptr_t) (JERRY_CONTEXT (stack_base) - (uintptr_t)&__sp);
} /* ecma_get_current_stack_usage */
#endif /* (JERRY_STACK_LIMIT != 0) */
/**
* @}
* @}
+3
View File
@@ -393,6 +393,9 @@ ecma_value_t ecma_clear_error_reference (ecma_value_t value, bool set_abort_flag
void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p);
void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p);
#if (JERRY_STACK_LIMIT != 0)
uintptr_t ecma_get_current_stack_usage (void);
#endif /* (JERRY_STACK_LIMIT != 0) */
/* ecma-helpers-external-pointers.c */
bool ecma_create_native_pointer_property (ecma_object_t *obj_p, void *native_p, void *info_p);
+4 -3
View File
@@ -42,9 +42,10 @@ ecma_init (void)
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_PRESSURE_GC;
#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter) = JERRY_CALL_STACK_LIMIT;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
#if (JERRY_STACK_LIMIT != 0)
volatile int sp;
JERRY_CONTEXT (stack_base) = (uintptr_t)&sp;
#endif /* (JERRY_STACK_LIMIT != 0) */
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
ecma_job_queue_init ();
@@ -705,16 +705,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
|| ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION
|| !ecma_op_function_has_construct_flag (arguments_list_p));
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
if (JERRY_UNLIKELY (JERRY_CONTEXT (function_call_counter) == 0))
{
return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum call stack size is exceeded."));
}
else
{
JERRY_CONTEXT (function_call_counter)--;
}
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
ECMA_CHECK_STACK_USAGE ();
switch (ecma_get_object_type (func_obj_p))
{
@@ -729,10 +720,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
arguments_list_p,
arguments_list_len);
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter)++;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
return ret_value;
}
@@ -823,10 +810,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
ecma_free_value (this_binding);
}
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter)++;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
return ret_value;
}
case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION:
@@ -837,9 +820,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
this_arg_value,
arguments_list_p,
arguments_list_len);
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter)++;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value)))
{
@@ -888,10 +868,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
ecma_deref_object (local_env_p);
}
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter)++;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
return ret_value;
}
#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */
@@ -902,10 +878,6 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
}
}
#if defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0)
JERRY_CONTEXT (function_call_counter)++;
#endif /* defined (JERRY_CALL_STACK_LIMIT) && (JERRY_CALL_STACK_LIMIT != 0) */
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL;
ecma_extended_object_t *ext_function_p;
@@ -22,6 +22,7 @@
#include "ecma-objects.h"
#include "ecma-regexp-object.h"
#include "ecma-try-catch-macro.h"
#include "jcontext.h"
#include "jrt-libc-includes.h"
#include "lit-char-helpers.h"
#include "re-compiler.h"
@@ -63,13 +64,6 @@
*/
#define RE_IS_CAPTURE_GROUP(x) (((x) < RE_OP_NON_CAPTURE_GROUP_START) ? 1 : 0)
/*
* Check RegExp recursion depth limit
*/
#if defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0)
JERRY_STATIC_ASSERT (JERRY_REGEXP_RECURSION_LIMIT > 0, regexp_recursion_limit_must_be_greater_than_zero);
#endif /* defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT) != 0) */
/**
* Parse RegExp flags (global, ignoreCase, multiline)
*
@@ -351,7 +345,7 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
const lit_utf8_byte_t *str_p, /**< input string pointer */
const lit_utf8_byte_t **out_str_p) /**< [out] matching substring iterator */
{
REGEXP_RECURSION_COUNTER_DECREASE_AND_TEST ();
ECMA_CHECK_STACK_USAGE ();
const lit_utf8_byte_t *str_curr_p = str_p;
while (true)
@@ -364,14 +358,12 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
{
JERRY_TRACE_MSG ("Execute RE_OP_MATCH: match\n");
*out_str_p = str_curr_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_TRUE; /* match */
}
case RE_OP_CHAR:
{
if (str_curr_p >= re_ctx_p->input_end_p)
{
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -383,7 +375,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ch1 != ch2)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -395,7 +386,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
{
if (str_curr_p >= re_ctx_p->input_end_p)
{
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -405,7 +395,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (lit_char_is_line_terminator (ch))
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -425,7 +414,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -436,7 +424,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
}
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
case RE_OP_ASSERT_END:
@@ -452,7 +439,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (!(re_ctx_p->flags & RE_FLAG_MULTILINE))
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -463,7 +449,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
}
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
case RE_OP_ASSERT_WORD_BOUNDARY:
@@ -495,7 +480,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (is_wordchar_left == is_wordchar_right)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -507,7 +491,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (is_wordchar_left != is_wordchar_right)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -575,7 +558,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
}
JMEM_FINALIZE_LOCAL_ARRAY (saved_bck_p);
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value;
}
case RE_OP_CHAR_CLASS:
@@ -588,7 +570,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (str_curr_p >= re_ctx_p->input_end_p)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -619,7 +600,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (!is_match)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -629,7 +609,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (is_match)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -660,7 +639,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (str_curr_p >= re_ctx_p->input_end_p)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -670,7 +648,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ch1 != ch2)
{
JERRY_TRACE_MSG ("fail\n");
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -694,7 +671,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -709,7 +685,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
bc_p = old_bc_p;
re_ctx_p->saved_p[RE_GLOBAL_START_IDX] = old_start_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
case RE_OP_SAVE_AND_MATCH:
@@ -717,7 +692,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
JERRY_TRACE_MSG ("End of pattern is reached: match\n");
re_ctx_p->saved_p[RE_GLOBAL_END_IDX] = str_curr_p;
*out_str_p = str_curr_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_TRUE; /* match */
}
case RE_OP_ALTERNATIVE:
@@ -782,7 +756,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -841,7 +814,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -866,7 +838,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -876,7 +847,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
}
re_ctx_p->saved_p[start_idx] = old_start_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
case RE_OP_CAPTURE_NON_GREEDY_GROUP_END:
@@ -922,7 +892,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -971,7 +940,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (re_ctx_p->num_of_iterations_p[iter_idx] >= min
&& str_curr_p== re_ctx_p->saved_p[start_idx])
{
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
@@ -993,7 +961,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1018,7 +985,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1040,7 +1006,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1052,7 +1017,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
/* restore if fails */
re_ctx_p->saved_p[end_idx] = old_end_p;
re_ctx_p->num_of_iterations_p[iter_idx]--;
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
case RE_OP_NON_GREEDY_ITERATOR:
@@ -1077,7 +1041,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1101,7 +1064,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
str_curr_p = sub_str_p;
num_of_iter++;
}
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
default:
@@ -1145,7 +1107,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
if (ecma_is_value_true (match_value))
{
*out_str_p = sub_str_p;
REGEXP_RECURSION_COUNTER_INCREASE ();
return match_value; /* match */
}
else if (ECMA_IS_VALUE_ERROR (match_value))
@@ -1161,7 +1122,6 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */
lit_utf8_read_prev (&str_curr_p);
num_of_iter--;
}
REGEXP_RECURSION_COUNTER_INCREASE ();
return ECMA_VALUE_FALSE; /* fail */
}
}
@@ -1250,7 +1210,6 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */
re_ctx.input_start_p = input_curr_p;
const lit_utf8_byte_t *input_end_p = re_ctx.input_start_p + input_buffer_size;
re_ctx.input_end_p = input_end_p;
REGEXP_RECURSION_COUNTER_INIT ();
/* 1. Read bytecode header and init regexp matcher context. */
re_ctx.flags = bc_p->header.status_flags;
@@ -28,46 +28,6 @@
* @{
*/
#if defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0)
/**
* Decrease the recursion counter and test it.
* If the counter reaches the limit of the recursion depth
* it will return with a range error.
*/
#define REGEXP_RECURSION_COUNTER_DECREASE_AND_TEST() \
do \
{ \
if (--re_ctx_p->recursion_counter == 0) \
{ \
return ecma_raise_range_error (ECMA_ERR_MSG ("RegExp recursion limit is exceeded.")); \
} \
} \
while (0)
/**
* Increase the recursion counter.
*/
#define REGEXP_RECURSION_COUNTER_INCREASE() (++re_ctx_p->recursion_counter)
/**
* Set the recursion counter to the max depth of the recursion.
*/
#define REGEXP_RECURSION_COUNTER_INIT() (re_ctx.recursion_counter = JERRY_REGEXP_RECURSION_LIMIT)
#else /* !(defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0)) */
/**
* Decrease the recursion counter and test it.
* If the counter reaches the limit of the recursion depth
* it will return with a range error.
*/
#define REGEXP_RECURSION_COUNTER_DECREASE_AND_TEST()
/**
* Increase the recursion counter.
*/
#define REGEXP_RECURSION_COUNTER_INCREASE()
/**
* Set the recursion counter to the max depth of the recursion.
*/
#define REGEXP_RECURSION_COUNTER_INIT()
#endif /* defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0) */
/**
* RegExp flags
* Note:
@@ -88,9 +48,6 @@ typedef struct
const lit_utf8_byte_t **saved_p; /**< saved result string pointers, ECMA 262 v5, 15.10.2.1, State */
const lit_utf8_byte_t *input_start_p; /**< start of input pattern string */
const lit_utf8_byte_t *input_end_p; /**< end of input pattern string */
#if defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0)
uint32_t recursion_counter; /**< RegExp recursion counter */
#endif /* defined (JERRY_REGEXP_RECURSION_LIMIT) && (JERRY_REGEXP_RECURSION_LIMIT != 0) */
uint32_t num_of_captures; /**< number of capture groups */
uint32_t num_of_non_captures; /**< number of non-capture groups */
uint32_t *num_of_iterations_p; /**< number of iterations */