Instrumented Code
While our intention is that you should not have to look at or understand instrumented code, scribble
is still beta software and bugs will happen. In such cases it may be useful for you to understand how Scribble generates instrumented code. For this we will look at two examples.
Function Annotations
To understand function instrumentation lets look at the following (slightly contrived) example:
The instrumented code generated would look like this:
There are several important things to note:
Scribble interposes on functions by renaming them, making them private, and adding a new stub with the same signature as the original function. For example, note that the original code from the
add
function now lives in the new private function_original_Foo_add
(lines 20-23).In the generated wrapper
add()
we invoke the renamed original function (line 13), and check theif_succeeds
annotation after the call (lines 14-17).The values of
old
expressions are evaluated before the call (line 12) and require a temporary variable. However, instead of just adding multiple temporary variables, we group all of the temporary variables needed inside of a single struct (vars0
) defined on lines 4-6. This is done to reduce the extra stack space used by instrumented code.Which custom property failed is signaled by emitting the
AssertionFailed
event (line 15).
Property Maps
There are two ways to get the instrumentation metadata with Scribble:
Using
--output-mode json
(or-m json
) command line option as part of JSON output ininstrumentationMetadata
key.Using
--instrumentation-metadata-file path/to/meta-file.json
command line option to get it in a standalone JSON file at specified path.
Contract Annotations
For contract invariants there are several additional changes. Let's consider this slightly different example:
Now the emitted code is quite a bit more involved:
Again there are several changes:
A new helper contract -
__scribble_ReentrancyUtils
(lines 2-4) was added. This contract was also added as a base contract ofFoo
. (line 6).__scribble_ReentrancyUtils
contains a single boolean property that is used for tracking whether the current stack frame is the root frame of an external call. This is useful as public functions can be called both internally and externally, but we only want to check contract invariants in them when they are called externally.A new function
__scribble_Foo_check_state_invariants_internal
was added (lines 36-41) which actually checks whether the invariant is true. It is only called indirectly, by__scribble_check_state_invariants
which is itself called by the wrapped functions.The public
add
function (lines 22-33) was wrapped similarly to functions in the previous section. The wrapper (lines 22-29) invokes the__scribble_check_state_invariants
function at the end of execution (line 27) to check whether the invariant is preserved by the call toadd
.The check on line 27 is done conditionally. This is a twist to make sure we are not checking contract invariants inside of public functions when the public functions are called internally. If
add
was declaredexternal
then the call on line 27 would be unconditional.Note that the contract invariants are also checked at the end of the constructor (line 18).
Last updated
Was this helpful?