From: Arran Cudbard-Bell Date: Sat, 11 Nov 2017 01:10:16 +0000 (+0000) Subject: Restructure the unlang interpreter to get rid of the majority of the gotos X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a7bc554277dd575b8027edf89484ef301ad286f7;p=thirdparty%2Ffreeradius-server.git Restructure the unlang interpreter to get rid of the majority of the gotos Two loops, one handles going back up the stack, the other handles executing instructions, and pushing new frames onto the stack. --- diff --git a/src/include/interpreter.h b/src/include/interpreter.h index 14ade3a8579..e2081f9967c 100644 --- a/src/include/interpreter.h +++ b/src/include/interpreter.h @@ -90,6 +90,12 @@ typedef enum { UNLANG_ACTION_STOP_PROCESSING //!< Break out of processing the current request (unwind). } unlang_action_t; +typedef enum { + UNLANG_FRAME_ACTION_POP = 1, + UNLANG_FRAME_ACTION_CONTINUE, + UNLANG_FRAME_ACTION_YIELD +} unlang_frame_action_t; + typedef enum { UNLANG_GROUP_TYPE_SIMPLE = 0, //!< Execute each of the children sequentially, until we execute //!< all of the children, or one returns #UNLANG_ACTION_BREAK. @@ -278,7 +284,7 @@ typedef struct { unlang_type_t unwind; //!< Unwind to this one if it exists. ///< This is used for break and return. - bool resume : 1; //!< resume the current section after calling a sub-section + bool repeat : 1; //!< resume the current section after calling a sub-section bool top_frame : 1; //!< are we the top frame of the stack? union { diff --git a/src/main/unlang_interpret.c b/src/main/unlang_interpret.c index 63074325e9e..75b3d0100fd 100644 --- a/src/main/unlang_interpret.c +++ b/src/main/unlang_interpret.c @@ -129,7 +129,7 @@ static void unlang_dump_frame(REQUEST *request, unlang_stack_frame_t *frame) RDEBUG("result %s", fr_int2str(mod_rcode_table, frame->result, "")); RDEBUG("priority %d", frame->priority); RDEBUG("unwind %d", frame->unwind); - RDEBUG("resume %s", frame->resume ? "yes" : "no"); + RDEBUG("repeat %s", frame->repeat ? "yes" : "no"); REXDENT(); } @@ -203,7 +203,7 @@ static inline void unlang_push(unlang_stack_t *stack, unlang_t *program, frame->result = result; frame->priority = -1; frame->unwind = UNLANG_TYPE_NULL; - frame->resume = false; + frame->repeat = false; frame->state = NULL; } @@ -297,7 +297,7 @@ static unlang_action_t unlang_load_balance(REQUEST *request, * No frame? This is the first time we've been called. * Go find one. */ - if (!frame->resume) { + if (!frame->repeat) { RDEBUG4("%s setting up", frame->instruction->debug_name); if (g->vpt) { @@ -488,7 +488,7 @@ static unlang_action_t unlang_load_balance(REQUEST *request, * Push the child, and yield for a later return. */ unlang_push(stack, frame->redundant.child, frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME); - frame->resume = true; + frame->repeat = true; return UNLANG_ACTION_PUSHED_CHILD; } @@ -1411,7 +1411,7 @@ static unlang_action_t unlang_foreach(REQUEST *request, g = unlang_generic_to_group(instruction); - if (!frame->resume) { + if (!frame->repeat) { int i, foreach_depth = -1; VALUE_PAIR *vps; @@ -1522,7 +1522,7 @@ static unlang_action_t unlang_foreach(REQUEST *request, * Push the child, and yield for a later return. */ unlang_push(stack, g->children, frame->result, UNLANG_NEXT_CONTINUE, UNLANG_SUB_FRAME); - frame->resume = true; + frame->repeat = true; return UNLANG_ACTION_PUSHED_CHILD; } @@ -2053,55 +2053,131 @@ unlang_op_t unlang_ops[] = { [UNLANG_TYPE_MAX] = { NULL, NULL, false } }; -/* - * Interpret the various types of blocks. +/** Update the current result after each instruction, and after popping each stack frame + * + * @param[in] request The current request. + * @param[in] frame The curren stack frame. + * @param[in,out] result The current section result. + * @param[in,out] priority The current section priority. + * @return + * - UNLANG_FRAME_ACTION_CONTINUE evaluate more instructions. + * - UNLANG_FRAME_ACTION_POP the final result has been calculated for this frame. */ -static rlm_rcode_t unlang_run(REQUEST *request) +static inline unlang_frame_action_t unlang_calculate_result(REQUEST *request, unlang_stack_frame_t *frame, + rlm_rcode_t *result, int *priority) { - unlang_t *instruction; - int priority; - rlm_rcode_t result; - unlang_stack_frame_t *frame; - unlang_action_t action = UNLANG_ACTION_BREAK; - unlang_stack_t *stack = request->stack; + unlang_t *instruction = frame->instruction; + unlang_stack_t *stack = request->stack; -#ifndef NDEBUG - if (DEBUG_ENABLED5) DEBUG("###### unlang_run is starting"); - DUMP_STACK; -#endif + RDEBUG4("** [%i] %s - have (%s %d) module returned (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, frame->result, ""), + frame->priority, + fr_int2str(mod_rcode_table, *result, ""), + *priority); /* - * If we're called from a module, re-set this so that the - * indentation works correctly... - * - * @todo - save / restore this across frames? + * Don't set action or priority if we don't have one. */ - request->module = NULL; - rad_assert(request->runnable_id < 0); + if (*result == RLM_MODULE_UNKNOWN) return UNLANG_FRAME_ACTION_CONTINUE; - RDEBUG4("** [%i] %s - entered", stack->depth, __FUNCTION__); + /* + * The child's action says return. Do so. + */ + if (instruction->actions[*result] == MOD_ACTION_RETURN) { + if (*priority < 0) *priority = 0; + + RDEBUG4("** [%i] %s - action says to return with (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, *result, ""), + *priority); + frame->result = *result; + frame->priority = *priority; + return UNLANG_FRAME_ACTION_POP; + } /* - * We don't have a return code yet. + * If "reject", break out of the loop and return + * reject. */ - result = RLM_MODULE_UNKNOWN; + if (instruction->actions[*result] == MOD_ACTION_REJECT) { + if (*priority < 0) *priority = 0; + + RDEBUG4("** [%i] %s - action says to return with (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, RLM_MODULE_REJECT, ""), + *priority); + frame->result = RLM_MODULE_REJECT; + frame->priority = *priority; + return UNLANG_FRAME_ACTION_POP; + } -start_subsection: - priority = -1; + /* + * The array holds a default priority for this return + * code. Grab it in preference to any unset priority. + */ + if (*priority < 0) { + *priority = instruction->actions[*result]; - rad_assert(stack->depth > 0); - rad_assert(stack->depth < UNLANG_STACK_MAX); + RDEBUG4("** [%i] %s - setting priority to (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, *result, ""), + *priority); + } - frame = &stack->frame[stack->depth]; + /* + * We're higher than any previous priority, remember this + * return code and priority. + */ + if (*priority > frame->priority) { + frame->result = *result; + frame->priority = *priority; + + RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, *result, ""), + *priority); + } /* - * Loop over all modules in this list. + * If we've been told to stop processing + * it, do so. */ - while (frame->instruction != NULL) { - REQUEST *parent; + if (frame->unwind != 0) { + RDEBUG4("** [%i] %s - unwinding current frame with (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, frame->result, ""), + frame->priority); + return UNLANG_FRAME_ACTION_POP; + } + + return frame->next ? UNLANG_FRAME_ACTION_CONTINUE : UNLANG_FRAME_ACTION_POP; +} + +/** Evaluates all the unlang nodes in a section + * + * @param[in] request The current request. + * @param[in] frame The curren stack frame. + * @param[in,out] result The current section result. + * @param[in,out] priority The current section priority. + * @return + * - UNLANG_FRAME_ACTION_CONTINUE evaluate more instructions in the current stack frame + * which may not be the same frame as when this function + * was called. + * - UNLANG_FRAME_ACTION_POP the final result has been calculated for this frame. + */ +static inline unlang_frame_action_t unlang_frame_eval(REQUEST *request, unlang_stack_frame_t *frame, + rlm_rcode_t *result, int *priority) +{ + unlang_stack_t *stack = request->stack; -resume_subsection: - instruction = frame->instruction; + /* + * Loop over all the instructions in this list. + */ + while (frame->instruction) { + REQUEST *parent; + unlang_t *instruction = frame->instruction; + unlang_action_t action = UNLANG_ACTION_BREAK; DUMP_STACK; @@ -2128,7 +2204,7 @@ resume_subsection: break; } - if ((unlang_ops[instruction->type].debug_braces) && !frame->resume) { + if (!frame->repeat && (unlang_ops[instruction->type].debug_braces)) { RDEBUG2("%s {", instruction->debug_name); RINDENT(); } @@ -2139,13 +2215,13 @@ resume_subsection: RDEBUG4("** [%i] %s >> %s", stack->depth, __FUNCTION__, unlang_ops[instruction->type].name); - action = unlang_ops[instruction->type].func(request, &result, &priority); + action = unlang_ops[instruction->type].func(request, result, priority); RDEBUG4("** [%i] %s << %s (%d)", stack->depth, __FUNCTION__, - fr_int2str(unlang_action_table, action, ""), priority); + fr_int2str(unlang_action_table, action, ""), *priority); - rad_assert(priority >= -1); - rad_assert(priority <= MOD_PRIORITY_MAX); + rad_assert(*priority >= -1); + rad_assert(*priority <= MOD_PRIORITY_MAX); switch (action) { case UNLANG_ACTION_STOP_PROCESSING: @@ -2153,18 +2229,18 @@ resume_subsection: case UNLANG_ACTION_PUSHED_CHILD: rad_assert(&stack->frame[stack->depth] > frame); - result = frame->result; - goto start_subsection; + *result = frame->result; + return UNLANG_FRAME_ACTION_CONTINUE; case UNLANG_ACTION_BREAK: - if (priority < 0) priority = 0; - frame->result = result; - frame->priority = priority; + if (*priority < 0) *priority = 0; + frame->result = *result; + frame->priority = *priority; frame->next = NULL; - goto done_subsection; + return UNLANG_FRAME_ACTION_POP; case UNLANG_ACTION_CALCULATE_RESULT: - if (result == RLM_MODULE_YIELD) { + if (*result == RLM_MODULE_YIELD) { /* * Detach is magic. The parent * "create" function takes care @@ -2172,125 +2248,43 @@ resume_subsection: * pointer... */ if (frame->instruction->type == UNLANG_TYPE_DETACH) { - RDEBUG4("** [%i] %s - detaching child with current (%s %d)", stack->depth, __FUNCTION__, + RDEBUG4("** [%i] %s - detaching child with current (%s %d)", + stack->depth, __FUNCTION__, fr_int2str(mod_rcode_table, frame->result, ""), frame->priority); DUMP_STACK; - return RLM_MODULE_YIELD; + *result = RLM_MODULE_YIELD; } rad_assert(frame->instruction->type == UNLANG_TYPE_RESUME); - frame->resume = true; + frame->repeat = true; RDEBUG4("** [%i] %s - yielding with current (%s %d)", stack->depth, __FUNCTION__, fr_int2str(mod_rcode_table, frame->result, ""), frame->priority); DUMP_STACK; - return RLM_MODULE_YIELD; + *result = RLM_MODULE_YIELD; + return UNLANG_FRAME_ACTION_YIELD; } - frame->resume = false; + frame->repeat = false; - calculate_result: if (unlang_ops[instruction->type].debug_braces) { REXDENT(); RDEBUG2("} # %s (%s)", instruction->debug_name, - fr_int2str(mod_rcode_table, result, "")); - } - action = UNLANG_ACTION_CALCULATE_RESULT; - - RDEBUG4("** [%i] %s - have (%s %d) module returned (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, frame->result, ""), - frame->priority, - fr_int2str(mod_rcode_table, result, ""), - priority); - - /* - * Don't set action or priority if we don't have one. - */ - if (result == RLM_MODULE_UNKNOWN) goto keep_going; - - /* - * The child's action says return. Do so. - */ - if (instruction->actions[result] == MOD_ACTION_RETURN) { - if (priority < 0) priority = 0; - - RDEBUG4("** [%i] %s - action says to return with (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, result, ""), - priority); - frame->result = result; - frame->priority = priority; - /* @todo - REXDENT? */ - goto done_subsection; - } - - /* - * If "reject", break out of the loop and return - * reject. - */ - if (instruction->actions[result] == MOD_ACTION_REJECT) { - if (priority < 0) priority = 0; - - RDEBUG4("** [%i] %s - action says to return with (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, RLM_MODULE_REJECT, ""), - priority); - frame->result = RLM_MODULE_REJECT; - frame->priority = priority; - /* @todo - REXDENT? */ - goto done_subsection; - } - - /* - * The array holds a default priority for this return - * code. Grab it in preference to any unset priority. - */ - if (priority < 0) { - priority = instruction->actions[result]; - - RDEBUG4("** [%i] %s - setting priority to (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, result, ""), - priority); + fr_int2str(mod_rcode_table, *result, "")); } - /* - * We're higher than any previous priority, remember this - * return code and priority. - */ - if (priority > frame->priority) { - frame->result = result; - frame->priority = priority; - - RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, result, ""), - priority); + if (unlang_calculate_result(request, frame, result, priority) == UNLANG_FRAME_ACTION_POP) { + return UNLANG_FRAME_ACTION_POP; } - - /* - * If we've been told to stop processing - * it, do so. - */ - if (frame->unwind != 0) { - RDEBUG4("** [%i] %s - unwinding current frame with (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, frame->result, ""), - frame->priority); - /* @todo - REXDENT? */ - goto done_subsection; - } - /* FALL-THROUGH */ case UNLANG_ACTION_CONTINUE: - keep_going: if ((action == UNLANG_ACTION_CONTINUE) && unlang_ops[instruction->type].debug_braces) { REXDENT(); RDEBUG2("}"); } + break; } /* switch over return code from the interpreter function */ frame->instruction = frame->next; @@ -2302,92 +2296,155 @@ resume_subsection: fr_int2str(mod_rcode_table, frame->result, ""), frame->priority); -done_subsection: - - /* - * We're at the top frame, return the result from the - * stack, and get rid of the top frame. - */ - if (frame->top_frame) { - top_frame: - RDEBUG4("** [%i] %s - returning %s", stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, frame->result, "")); - result = frame->result; - stack->depth--; - DUMP_STACK; - return result; - } + return UNLANG_FRAME_ACTION_POP; +} - /* - * The result / priority is returned from - * the sub-section, and made into our - * current result / priority, as if we - * had performed a module call. - */ - result = frame->result; - priority = frame->priority; +/* + * Interpret the various types of blocks. + */ +static rlm_rcode_t unlang_run(REQUEST *request) +{ + int priority; + unlang_frame_action_t fa = UNLANG_FRAME_ACTION_CONTINUE; /* - * We're done everything: return. + * We don't have a return code yet. */ - if (stack->depth == 0) { - return result; - } - - unlang_pop(stack); + rlm_rcode_t result = RLM_MODULE_UNKNOWN; + unlang_stack_frame_t *frame; - RDEBUG4("** [%i] %s - continuing after subsection with (%s %d)", - stack->depth, __FUNCTION__, - fr_int2str(mod_rcode_table, result, ""), - priority); + unlang_stack_t *stack = request->stack; +#ifndef NDEBUG + if (DEBUG_ENABLED5) DEBUG("###### unlang_run is starting"); DUMP_STACK; +#endif /* - * Reset the local variables, and check - * for a (local) top frame. + * If we're called from a module, re-set this so that the + * indentation works correctly... + * + * @todo - save / restore this across frames? */ - frame = &stack->frame[stack->depth]; + request->module = NULL; + rad_assert(request->runnable_id < 0); - /* - * Resume a "foreach" loop, or a "load-balance" section. - */ - if (frame->resume) goto resume_subsection; + RDEBUG4("** [%i] %s - interpreter entered", stack->depth, __FUNCTION__); - /* - * If we're done, merge the last result / priority in. - */ - if (frame->top_frame) { - /* - * Nothing in this section, use the top frame result. - */ - if ((priority < 0) || (result == RLM_MODULE_UNKNOWN)) { + do { + switch (fa) { + case UNLANG_FRAME_ACTION_CONTINUE: /* Evaluate the current frame */ + priority = -1; + + rad_assert(stack->depth > 0); + rad_assert(stack->depth < UNLANG_STACK_MAX); + + frame = &stack->frame[stack->depth]; + fa = unlang_frame_eval(request, frame, &result, &priority); + continue; + + case UNLANG_FRAME_ACTION_POP: /* Pop this frame and check the one beneath it */ + /* + * The result / priority is returned from + * the sub-section, and made into our + * current result / priority, as if we + * had performed a module call. + */ result = frame->result; priority = frame->priority; - } - - if (priority > frame->priority) { - frame->result = result; - frame->priority = priority; - RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)", + /* + * Head on back up the stack + */ + unlang_pop(stack); + frame = &stack->frame[stack->depth]; + RDEBUG4("** [%i] %s - continuing after subsection with (%s %d)", stack->depth, __FUNCTION__, fr_int2str(mod_rcode_table, result, ""), priority); + DUMP_STACK; + + /* + * Resume a "foreach" loop, or a "load-balance" section + * or anything else that needs to be checked on the way + * back on up the stack. + */ + if (frame->repeat) { + fa = UNLANG_FRAME_ACTION_CONTINUE; + continue; + } + + /* + * If we're done, merge the last result / priority in. + */ + if (frame->top_frame) break; /* return */ + + /* + * Close out the section we entered earlier + */ + if (unlang_ops[frame->instruction->type].debug_braces) { + REXDENT(); + RDEBUG2("} # %s (%s)", frame->instruction->debug_name, + fr_int2str(mod_rcode_table, result, "")); + } + + fa = unlang_calculate_result(request, frame, &result, &priority); + /* + * If we're continuing after popping a frame + * then we advance the instruction else we + * end up executing the same code over and over... + */ + if (fa == UNLANG_FRAME_ACTION_CONTINUE) { + frame->instruction = frame->next; + if (frame->instruction) frame->next = frame->instruction->next; + /* + * Else if we're really done with this frame + * print some helpful debug... + */ + } else { + RDEBUG4("** [%i] %s - done current subsection with (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, frame->result, ""), + frame->priority); + } + continue; + + case UNLANG_FRAME_ACTION_YIELD: + rad_assert(frame->result == RLM_MODULE_YIELD); + return frame->result; } - goto top_frame; + break; + } while (!frame->top_frame); + + /* + * Nothing in this section, use the top frame result. + */ + if ((priority < 0) || (result == RLM_MODULE_UNKNOWN)) { + result = frame->result; + priority = frame->priority; } - instruction = frame->instruction; - if (!instruction) { - RERROR("Empty instruction. Hard-coding to reject"); - DUMP_STACK; - frame->result = result = RLM_MODULE_REJECT; - frame->priority = 0; - goto done_subsection; + if (priority > frame->priority) { + frame->result = result; + frame->priority = priority; + + RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)", + stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, result, ""), + priority); } - goto calculate_result; + /* + * We're at the top frame, return the result from the + * stack, and get rid of the top frame. + */ + RDEBUG4("** [%i] %s - interpreter exiting, returning %s", stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, frame->result, "")); + result = frame->result; + stack->depth--; + DUMP_STACK; + + return result; } static unlang_group_t empty_group = {