Construct a new pipeline executor.
The required additional input is specified by the IPipelineStep#requiredInput|required input configuration of each step in the pipeline
.
The Pipeline to execute, probably created with createPipeline.
External configuration and input required to execute the given pipeline.
Optional
canSwitchStage: trueRetrieve the current stage the pipeline executor is in.
Optional
intermediate: falseExecute the next IPipelineStep|step and return the name of the IPipelineStep|step that was executed, so you can guard if the IPipelineStep|step differs from what you are interested in. Furthermore, it returns the IPipelineStep|step's result.
Optional
expectedStepName: PassedNameA safeguard if you want to retrieve the result. If given, it causes the execution to fail if the next step is not the one you expect.
Without expectedStepName
, please refrain from accessing the result, as you have no safeguards if the pipeline changes.
Switch to the next stage of the pipeline executor.
This will fail if either a step change is currently not valid (as not all steps have been executed), or if there is no next stage (i.e., the pipeline is already completed or in the last stage).
This only makes sense if you have already run a request and want to re-use the per-file results for a new one. (or if for whatever reason, you did not pass information for the pipeline with the constructor).
Data for the new request
The pipeline executor allows to execute arbitrary pipelines in a step-by-step fashion. If you are not yet in the possession of a pipeline, you can use the createPipeline function to create one for yourself, based on the steps that you want to execute.
Those steps are split into two phases or "stages" (which is the name that we will use in the following), represented by the PipelineStepStage type. These allow us to separate things that have to be done once per-file, e.g., actually parsing the AST, from those that we need to repeat 'once per request' (whatever this request may be). In other words, what can be cached between operations and what cannot.
Furthermore, this executor follows an iterable fashion to be as flexible as possible (e.g., to be instrumented with measurements). So, you can use the pipeline executor like this:
Of course, you might think, that this is rather overkill if you simply want to receive the result. And this is true. Therefore, if you do not want to perform some kind of magic in-between steps, you can use the allRemainingSteps function like this:
As the name suggests, you can combine this name with previous calls to nextStep to only execute the remaining steps in case, for whatever reason you only want to instrument some steps.
By default, the PipelineExecutor does not offer an automatic way to repeat requests (mostly to prevent accidental errors). However, you can use the updateRequest function to reset the request steps and re-execute them for a new request. This allows something like the following:
Example - Slicing With the Pipeline Executor:
Suppose, you want to... you know slice a file (which was, at one point the origin of flowR), then you can either create a pipeline yourself with the respective steps, or you can use the DEFAULT_SLICING_PIPELINE (and friends). With it, slicing essentially becomes 'easy-as-pie':
But now, we want to slice for
x
in the first line as well! We can do that by adding:Note
Even though using the pipeline executor introduces a small performance overhead, we consider it to be the baseline for performance benchmarking. It may very well be possible to squeeze out a little bit more by directly constructing the steps in the right order. However, we consider this to be negligible when compared with the time required for, for example, the dataflow analysis of larger files.
See