eth/tracers: do the JSON serialization via .js to capture C faults #22857
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Supersedes #22725.
Credits for the repro test case and the fix idea go to @holiman, I just found it easier to implement the thing in a new PR than to keep hacking one that already did a non-ideal fix.
Duktape's JSON serializer has some hard coded depth limits, currently set at 1000. Any object deeper than that fails to serialize, resulting in a C++ exception. Unfortunately Go can't catch that exception, so the tracer (along with Geth) panics.
Duktape itself has a
duk_safe_call
method, the purpose of which is exactly to allow catching these exceptions in client code that would otherwise be unable to do, however the wrapper in go-duktape did a dumb C wrapping which makes this method unusable from Go (i.e.fn
can't be meaningfully filled since we don't have access to theduk_xxx
methods):Our last option is to use the JavaScript
JSON.stringify
method to do the flattening. This still uses the C code underneath, so it is equally as fast, but it also does the C++ exception handling, so it's also safe. Duktape is also weird, so this PR also gets weird.Duktape can only call functions and methods on objects that are already in its heap/stack. Getting the
JSON.stringify
method on there turned out to be strangely funky. Currently the best idea I had was to push a(JSON.stringify)
evaluation onto the .js stack, which when evaluated will push the actual method we want to call onto the stack (and not the string of it). Then I can call this method to do the real work. This whole dance needs to be special cased to be only done when callingresult
, since whereasJsonEncode
was always fast, this extra .js object conversion is very slow, so we can't afford to do it once per step.