From 7dd8ba37f11d38fa6a45737922e6af1dbcc9b468 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Sat, 5 Feb 2022 12:15:32 +0100 Subject: [PATCH] Allow changing recursion depth limit at runtime --- include/asm/fstack.h | 1 + include/asm/lexer.h | 1 + src/asm/fstack.c | 12 ++++++++++-- src/asm/lexer.c | 20 ++++++++++++-------- src/asm/opt.c | 32 ++++++++++++++++++++++++++++++++ src/asm/rgbasm.5 | 7 +++++-- test/asm/opt-r.asm | 13 +++++++++++++ test/asm/opt-r.err | 6 ++++++ test/asm/opt-r.out | 0 9 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 test/asm/opt-r.asm create mode 100644 test/asm/opt-r.err create mode 100644 test/asm/opt-r.out diff --git a/include/asm/fstack.h b/include/asm/fstack.h index 986964f7..39dbc51b 100644 --- a/include/asm/fstack.h +++ b/include/asm/fstack.h @@ -77,6 +77,7 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step, void fstk_StopRept(void); bool fstk_Break(void); +void fstk_NewRecursionDepth(size_t newDepth); void fstk_Init(char const *mainPath, size_t maxDepth); #endif /* RGBDS_ASM_FSTACK_H */ diff --git a/include/asm/lexer.h b/include/asm/lexer.h index 0ad6de48..ce2544e7 100644 --- a/include/asm/lexer.h +++ b/include/asm/lexer.h @@ -83,6 +83,7 @@ struct CaptureBody { size_t size; }; +void lexer_CheckRecursionDepth(void); char const *lexer_GetFileName(void); uint32_t lexer_GetLineNo(void); uint32_t lexer_GetColNo(void); diff --git a/src/asm/fstack.c b/src/asm/fstack.c index b61112eb..ceb81190 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -284,8 +284,9 @@ bool yywrap(void) */ static void newContext(struct FileStackNode *fileInfo) { - if (++contextDepth >= maxRecursionDepth) - fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); + ++contextDepth; + fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded + struct Context *context = malloc(sizeof(*context)); if (!context) @@ -507,6 +508,13 @@ bool fstk_Break(void) return true; } +void fstk_NewRecursionDepth(size_t newDepth) +{ + if (contextDepth >= newDepth) + fatalerror("Recursion limit (%zu) exceeded\n", newDepth); + maxRecursionDepth = newDepth; +} + void fstk_Init(char const *mainPath, size_t maxDepth) { struct LexerState *state = lexer_OpenFile(mainPath); diff --git a/src/asm/lexer.c b/src/asm/lexer.c index a06dd4c0..19c98edd 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -668,14 +668,8 @@ static void beginExpansion(char const *str, bool owned, char const *name) if (!size) return; - if (name) { - size_t depth = 0; - - for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) { - if (depth++ >= maxRecursionDepth) - fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); - } - } + if (name) + lexer_CheckRecursionDepth(); struct Expansion *exp = malloc(sizeof(*exp)); @@ -692,6 +686,16 @@ static void beginExpansion(char const *str, bool owned, char const *name) lexerState->expansions = exp; } +void lexer_CheckRecursionDepth(void) +{ + size_t depth = 0; + + for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) { + if (depth++ >= maxRecursionDepth) + fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); + } +} + static void freeExpansion(struct Expansion *expansion) { free(expansion->name); diff --git a/src/asm/opt.c b/src/asm/opt.c index 7b5aec8c..608289ad 100644 --- a/src/asm/opt.c +++ b/src/asm/opt.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,6 +6,7 @@ #include #include +#include "asm/fstack.h" #include "asm/lexer.h" #include "asm/main.h" #include "asm/section.h" @@ -17,6 +19,7 @@ struct OptStackEntry { bool haltnop; bool optimizeLoads; bool warningsAreErrors; + size_t maxRecursionDepth; // Don't be confused: we use the size of the **global variable** `warningStates`! enum WarningState warningStates[sizeof(warningStates)]; struct OptStackEntry *next; @@ -39,6 +42,12 @@ void opt_P(uint8_t fill) fillByte = fill; } +void opt_R(size_t newDepth) +{ + fstk_NewRecursionDepth(newDepth); + lexer_CheckRecursionDepth(); +} + void opt_h(bool halt) { haltnop = halt; @@ -86,6 +95,29 @@ void opt_Parse(char *s) } break; + case 'r': { + ++s; // Skip 'r' + while (isblank(*s)) + ++s; // Skip leading whitespace + + if (s[0] == '\0') { + error("Missing argument to option 'r'\n"); + break; + } + + char *endptr; + unsigned long newDepth = strtoul(s, &endptr, 10); + + if (*endptr != '\0') { + error("Invalid argument to option 'r' (\"%s\")\n", s); + } else if (errno == ERANGE) { + error("Argument to 'r' is out of range (\"%s\")\n", s); + } else { + opt_R(newDepth); + } + break; + } + case 'h': if (s[1] == '\0') opt_h(false); diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index ab10d704..ce5b3ced 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -1993,6 +1993,7 @@ block, all of them but the first one are ignored. .Ss Changing options while assembling .Ic OPT can be used to change some of the options during assembling from within the source, instead of defining them on the command-line. +.Pq See Xr rgbasm 1 . .Pp .Ic OPT takes a comma-separated list of options as its argument: @@ -2008,8 +2009,10 @@ POPO LD [$FF88], A ; optimized to use LDH by default .Ed .Pp -The options that OPT can modify are currently: -.Cm b , g , p , h , L , +The options that +.Ic OPT +can modify are currently: +.Cm b , g , p , r , h , L , and .Cm W . The Boolean flag options diff --git a/test/asm/opt-r.asm b/test/asm/opt-r.asm new file mode 100644 index 00000000..1889f913 --- /dev/null +++ b/test/asm/opt-r.asm @@ -0,0 +1,13 @@ +OPT r34 ; :3 +OPT r 360 + +; Invalid +OPT r ; Missing arg +OPT r 2a ; Bad decimal + +; Check that it has an effect +OPT r 1 +MACRO m + m +ENDM + m diff --git a/test/asm/opt-r.err b/test/asm/opt-r.err new file mode 100644 index 00000000..9fbd09ff --- /dev/null +++ b/test/asm/opt-r.err @@ -0,0 +1,6 @@ +error: opt-r.asm(5): + Missing argument to option 'r' +error: opt-r.asm(6): + Invalid argument to option 'r' ("2a") +FATAL: opt-r.asm(13): + Recursion limit (1) exceeded diff --git a/test/asm/opt-r.out b/test/asm/opt-r.out new file mode 100644 index 00000000..e69de29b