7 Running functions
Our goal is to run a R instrumented function, under a given evaluation mode.
To do so, some prerequisites have to be met, prior to use some wyz.code.offensiveProgramming utilities to proceed to the function execution with context capture and human-readable feedback generation.
7.1 Prerequisites
To run a R function, it has to be instrumented. Two requirements have to be met
- the function must comply with semantic parameter naming
- the function return type must be specified
These prerequesites might be matched in two ways depending of transient or persistent invocations.
Persistent invocation means that function return type information is stored into an object according to a convention, and therefore could be retrieved from it to be used and reused whenever and wherever needed.
7.2 Transient invocations
Transient invocation means that function return type information is is explicitely set/given at invocation time. Invocation will be achieved dynamically and not persisted anywhere. In particular invocation context remains volatile, as long as you do not save it into a piece of code.
Transient invocation is a convenient way to discover and to play with an offensive programming instrumented package.
7.2.1 Nominal case
Here is a typical to invoque an offensive programming instrumented function in a transient way. Use function runTransientFunction to achieve desired result.
To get the definition for ‘emo’ variable, please refer to 6.
h <- function(x_s) x_s
runTransientFunction(h, list('neonira'), emo$type, 'x_s')
#> $status
#> [1] TRUE
#>
#> $value
#> [1] "neonira"
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s neonira TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_s neonira TRUE good type in values
Semantically, function h takes a vector of strings as argument and returns a vector of strings.
As provided parameter ‘neonira’ is a vector of type character, parameter_type_checks succeed.
As returned value is a vector of type character, function_return_type_check also succeeds.
Therefore global status is TRUE. Generally, when used to use this function, you just jump onto the status and go deeper only when negative.
7.2.1.1 Wrong parameter type
Let’s change the provided argument from ‘neonira’ to pi value.
h <- function(x_s) x_s
runTransientFunction(h, list(pi), emo$type, 'x_s')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
The status is FALSE. Looking at details, we see that root causes are parameter_type_checks failure and function_return_type_check failure.
We faced a case where function specification is unchanged but provided argument is not complying to specification, thus generating a double error.
If you have a great experience of R, you know that discovering such root causes are challenging due to weakly typed and script nature of the R language.
7.2.1.2 Change expected return type
Let’s change the expected function return type, to be double x_d.
h <- function(x_s) x_s
runTransientFunction(h, list(pi), emo$type, 'x_d')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 3.141593 TRUE good type in values
The status is still FALSE but there is now one single root cause that is parameter_type_checks failure.
We now face a case where function specification is unchanged but expected return type is expected to be a double. Provided argument is not complying to specification. To fix the issue, just rename the parameter to be x_d or any other defined and recorded factory type that matches a double.
7.2.2 Prerequisite mismatch
What if function is not fulfilling prerequisites
g <- function(x) x # No semantic name compliance
runTransientFunction(g, list(pi), EvaluationMode(defineEvaluationModes()[3]), 'x_d')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x 3.141593 FALSE unknown suffix, [NA]
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 3.141593 TRUE good type in values
Impact is parameter_type_checks failure. This reminds you to verify name compliance cf. 5.2 before testing transient invocations.
7.2.3 Object function call
What if the function you desire to call is an object function?
In such a case, be sure to pass the object as first parameter of the list of parameters, as shown by example below, based on a S3 object. A priori, all kind of R objects are supported: S3, S4, RC, R6 and environment objects.
7.2.3.1 Right way
library(data.table)
source(file.path(system.file(package = 'wyz.code.offensiveProgramming'),
'code-samples', 'both-defs/good/partial',
'Addition_TCFI_Partial_S3.R'), encoding = 'UTF-8')
a <- Addition_TCFI_Partial_S3()
runTransientFunction(addInteger.Addition_TCFI_Partial_S3, list(a, 3L, 4L),
emo$type, 'x_i')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity
#> 1: object_o_1 <Addition_TCFI_Partial_S3> FALSE
#> 2: x_i 3 TRUE
#> 3: y_i 4 TRUE
#> message
#> 1: wrong length, was expecting [1] , got [3]
#> 2: good type in values
#> 3: good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7 TRUE good type in values
7.2.3.2 Omitting object
What if I omit object as first parameter?
runTransientFunction(addInteger.Addition_TCFI_Partial_S3, list(NULL, 3L, 4L),
emo$type, 'x_i')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity
#> 1: object_o_1 FALSE
#> 2: x_i 3 TRUE
#> 3: y_i 4 TRUE
#> message
#> 1: wrong length, was expecting [1] , got [0]
#> 2: good type in values
#> 3: good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7 TRUE good type in values
7.3 Persistent invocations
Transient invocations are convenient but limited. Especially, when you create classes, they do not appear to be as friendly and useful as necessary.
When dealing with your own class code, you may opt for an easier and more industrial approach that is class instrumentation. Prerequisite remains the same, but you may fulfill them much more easily by defining a variable named function_return_type in your class.
7.3.1 Verifying recorded information
To verify function return types definition of a R object, you may use the low level function verifyFunctionReturnTypesDefinition or the higher level one named retrieveFunctionReturnTypes.
7.3.2 Nominal persistent case
Here is a typical to invoque an offensive programming instrumented function in a peristent way. Use function runFunction to achieve desired result.
source(system.file('code-samples/frt-defs/good/partial/AdditionFIPartial.R',
package = 'wyz.code.offensiveProgramming'))
runFunction(AdditionFIPartial(), 'addInteger', list(1:3, 6:8), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: y_i 6,7,8 TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7, 9,11 TRUE good type in values
The status is TRUE expressing parameter_type_checks and function_return_type_check compliance.
7.3.3 Subtile change
Just change the function name. Now expectations brought by the function definition are not the same. In particular semantic names for function parameters and function return types are no more correct.
runFunction(AdditionFIPartial(), 'addDouble', list(1:3, 6:8), emo$type)
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] FALSE
#>
#> $parameter_check
#> [1] FALSE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_d 1,2,3 FALSE wrong type in values
#> 2: y_d 6,7,8 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 7, 9,11 FALSE wrong type in values
The status is FALSE expressing the two incompliances.
7.3.4 Call case with named and positional parameters
runFunction(AdditionFIPartial(), 'addInteger', list(y_i = 1:3, 6:8), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: y_i 1,2,3 TRUE good type in values
#> 2: x_i 6,7,8 TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7, 9,11 TRUE good type in values
Look at parameter_type_checks to ensure arguments are well associated.
7.3.5 Call case with ellipsis
runFunction(AdditionFIPartial(), 'addMultiDouble', list(1:3, 1:7), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: ... 1,2,3 TRUE ellipsis matches all
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 34 TRUE good type in values
As ellipsis matches all inputs, this case behaves correctly.
7.3.6 Second call case with ellipsis
runFunction(AdditionFIPartial(), 'addMultiInteger',
list(1:3, 1:7, 0, floor(pi)), emo$type)
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 32 33 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] FALSE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#> 3: ... 0 TRUE ellipsis matches all
#> 4: ... 3 TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 32,33,34 FALSE wrong type in values
Here, ellipsis still matches all, but parameter list is type heterogeneous. This brings two issues. Value 0 is double, not integer and floor function returns also a double. To get a correct results, enforce inout parameter coercision to expected type.
runFunction(AdditionFIPartial(), 'addMultiInteger',
list(1:3, 1:7, 0L, as.integer(floor(pi))), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 32 33 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#> 3: ... 0 TRUE ellipsis matches all
#> 4: ... 3 TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 32,33,34 TRUE good type in values