void FnCallWrite(Writer *writer, const FnCall *call) { WriterWrite(writer, call->name); WriterWriteChar(writer, '('); for (const Rlist *rp = call->args; rp != NULL; rp = rp->next) { switch (rp->val.type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, RlistScalarValue(rp), true); break; case RVAL_TYPE_FNCALL: FnCallWrite(writer, RlistFnCallValue(rp)); break; default: WriterWrite(writer, "(** Unknown argument **)\n"); break; } if (rp->next != NULL) { WriterWriteChar(writer, ','); } } WriterWriteChar(writer, ')'); }
void RvalWriteParts(Writer *writer, const void* item, RvalType type) { if (item == NULL) { return; } switch (type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, item); break; case RVAL_TYPE_LIST: RlistWrite(writer, item); break; case RVAL_TYPE_FNCALL: FnCallWrite(writer, item); break; case RVAL_TYPE_NOPROMISEE: WriterWrite(writer, "(no-one)"); break; case RVAL_TYPE_CONTAINER: JsonWrite(writer, item, 0); break; } }
void RvalWrite(Writer *writer, Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, RvalScalarValue(rval)); break; case RVAL_TYPE_LIST: RlistWrite(writer, RvalRlistValue(rval)); break; case RVAL_TYPE_FNCALL: FnCallWrite(writer, RvalFnCallValue(rval)); break; case RVAL_TYPE_NOPROMISEE: WriterWrite(writer, "(no-one)"); break; case RVAL_TYPE_CONTAINER: JsonWrite(writer, RvalContainerValue(rval), 0); break; } }
void RvalWrite(Writer *writer, Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, RvalScalarValue(rval)); break; case RVAL_TYPE_LIST: RlistWrite(writer, RvalRlistValue(rval)); break; case RVAL_TYPE_FNCALL: FnCallWrite(writer, RvalFnCallValue(rval)); break; case RVAL_TYPE_NOPROMISEE: WriterWrite(writer, "(no-one)"); break; default: ProgrammingError("Unknown rval type %c", rval.type); } }
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller) { assert(ctx); assert(policy); assert(fp); fp->caller = caller; if (!EvalContextGetEvalOption(ctx, EVAL_OPTION_EVAL_FUNCTIONS)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because evaluation was turned off in the evaluator", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } const FnCallType *fp_type = FnCallTypeGet(fp->name); if (!fp_type) { if (caller) { Log(LOG_LEVEL_ERR, "No such FnCall '%s' in promise '%s' near line %zd", fp->name, PromiseGetBundle(caller)->source_path, caller->offset.line); } else { Log(LOG_LEVEL_ERR, "No such FnCall '%s', context info unavailable", fp->name); } return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rlist *expargs = NewExpArgs(ctx, policy, fp, fp_type); Writer *fncall_writer = NULL; const char *fncall_string = ""; if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { fncall_writer = StringWriter(); FnCallWrite(fncall_writer, fp); fncall_string = StringWriterData(fncall_writer); } // Check if arguments are resolved, except for delayed evaluation functions if ( ! (fp_type->options & FNCALL_OPTION_DELAYED_EVALUATION) && RlistIsUnresolved(expargs)) { // Special case: ifelse(isvariable("x"), $(x), "default") // (the first argument will come down expanded as "!any") if (strcmp(fp->name, "ifelse") == 0 && RlistLen(expargs) == 3 && strcmp("!any", RlistScalarValueSafe(expargs)) == 0 && !RlistIsUnresolved(expargs->next->next)) { Log(LOG_LEVEL_DEBUG, "Allowing ifelse() function evaluation even" " though its arguments contain unresolved variables: %s", fncall_string); } else { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Skipping function evaluation for now," " arguments contain unresolved variables: %s", fncall_string); WriterClose(fncall_writer); } RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } } Rval cached_rval; if ((fp_type->options & FNCALL_OPTION_CACHED) && EvalContextFunctionCacheGet(ctx, fp, expargs, &cached_rval)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Using previously cached result for function: %s", fncall_string); WriterClose(fncall_writer); } Writer *w = StringWriter(); FnCallWrite(w, fp); WriterClose(w); RlistDestroy(expargs); return (FnCallResult) { FNCALL_SUCCESS, RvalCopy(cached_rval) }; } if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Evaluating function: %s", fncall_string); WriterClose(fncall_writer); } FnCallResult result = CallFunction(ctx, policy, fp, expargs); if (result.status == FNCALL_FAILURE) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } if (fp_type->options & FNCALL_OPTION_CACHED) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_VERBOSE, "Caching result for function '%s'", StringWriterData(w)); WriterClose(w); EvalContextFunctionCachePut(ctx, fp, expargs, &result.rval); } RlistDestroy(expargs); return result; }
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller) { assert(ctx); assert(policy); assert(fp); fp->caller = caller; if (!EvalContextGetEvalOption(ctx, EVAL_OPTION_EVAL_FUNCTIONS)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because evaluation was turned off in the evaluator", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } const FnCallType *fp_type = FnCallTypeGet(fp->name); if (!fp_type) { if (caller) { Log(LOG_LEVEL_ERR, "No such FnCall '%s' in promise '%s' near line %zd", fp->name, PromiseGetBundle(caller)->source_path, caller->offset.line); } else { Log(LOG_LEVEL_ERR, "No such FnCall '%s', context info unavailable", fp->name); } return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rlist *expargs = NewExpArgs(ctx, policy, fp); Writer *fncall_writer; const char *fncall_string; if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { fncall_writer = StringWriter(); FnCallWrite(fncall_writer, fp); fncall_string = StringWriterData(fncall_writer); } if (RlistIsUnresolved(expargs)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Skipping function evaluation for now," " arguments contain unresolved variables: %s", fncall_string); WriterClose(fncall_writer); } RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rval cached_rval; if ((fp_type->options & FNCALL_OPTION_CACHED) && EvalContextFunctionCacheGet(ctx, fp, expargs, &cached_rval)) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Using previously cached result for function: %s", fncall_string); WriterClose(fncall_writer); } Writer *w = StringWriter(); FnCallWrite(w, fp); WriterClose(w); RlistDestroy(expargs); return (FnCallResult) { FNCALL_SUCCESS, RvalCopy(cached_rval) }; } if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { Log(LOG_LEVEL_DEBUG, "Evaluating function: %s", fncall_string); WriterClose(fncall_writer); } FnCallResult result = CallFunction(ctx, policy, fp, expargs); if (result.status == FNCALL_FAILURE) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } else if (result.rval.type == RVAL_TYPE_LIST && !result.rval.item) { Rlist *seq = NULL; // don't pass NULL items to evaluator RlistPrepend(&seq, CF_NULL_VALUE, RVAL_TYPE_SCALAR); result.rval.item = seq; } if (fp_type->options & FNCALL_OPTION_CACHED) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_VERBOSE, "Caching result for function '%s'", StringWriterData(w)); WriterClose(w); EvalContextFunctionCachePut(ctx, fp, expargs, &result.rval); } RlistDestroy(expargs); return result; }
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller) { assert(ctx); assert(policy); assert(fp); fp->caller = caller; if (!EvalContextGetEvalOption(ctx, EVAL_OPTION_EVAL_FUNCTIONS)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because evaluation was turned off in the evaluator", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } else if (caller && !EvalContextPromiseIsActive(ctx, caller)) { Log(LOG_LEVEL_VERBOSE, "Skipping function '%s', because it was excluded by classes", fp->name); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } const FnCallType *fp_type = FnCallTypeGet(fp->name); if (!fp_type) { if (caller) { Log(LOG_LEVEL_ERR, "No such FnCall '%s' in promise '%s' near line %llu", fp->name, PromiseGetBundle(caller)->source_path, (unsigned long long)caller->offset.line); } else { Log(LOG_LEVEL_ERR, "No such FnCall '%s', context info unavailable", fp->name); } return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rlist *expargs = NewExpArgs(ctx, policy, fp); if (RlistIsUnresolved(expargs)) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } Rval cached_rval; if ((fp_type->options & FNCALL_OPTION_CACHED) && EvalContextFunctionCacheGet(ctx, fp, expargs, &cached_rval)) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_DEBUG, "Using previously cached result for function '%s'", StringWriterData(w)); WriterClose(w); RlistDestroy(expargs); return (FnCallResult) { FNCALL_SUCCESS, RvalCopy(cached_rval) }; } FnCallResult result = CallFunction(ctx, policy, fp, expargs); if (result.status == FNCALL_FAILURE) { RlistDestroy(expargs); return (FnCallResult) { FNCALL_FAILURE, { FnCallCopy(fp), RVAL_TYPE_FNCALL } }; } if (fp_type->options & FNCALL_OPTION_CACHED) { Writer *w = StringWriter(); FnCallWrite(w, fp); Log(LOG_LEVEL_VERBOSE, "Caching result for function '%s'", StringWriterData(w)); WriterClose(w); EvalContextFunctionCachePut(ctx, fp, expargs, &result.rval); } RlistDestroy(expargs); return result; }