In Java you have instrumentation agents that can do arbitrary code rewrite at runtime, including for code that has already been JITted (causing a deoptimization of it). It's first-class supported, unlike in Go.
It's used extensively to (for example) add debugging or tracing to functions in libraries or to sanitize certain code path. At work, we were able to kill off jndi from Log4J to prevent Log4Shell within hours of the announcement this way while waiting for updates of thousands of dependencies.
This is all possible and quite neat to dive into the specifics, but if you really want to be able swap a std lib call, just turn it into a variable and change it.
// code.go
var now = time.Now
// code_test.go
func TestCode(t *testing.T) {
nowSwap := now
t.Cleanup (func() {
now = nowSwap
}
now = func() time.Time {
return time.Date(...)
}
}
I've used a different approach to this: there's no real need to modify the compiled binary code because Go compiles everything from source, so you can patch the functions at the source level instead: https://github.com/YuriyNasretdinov/golang-soft-mocks
The way it works is that at the start of every function it adds an if statement that atomically checks whether or not the function has been intercepted, and if it did, then executes the replacement function instead. This also addresses the inlining issue.
My tool no longer works since it was rewriting GOPATH, and Go since effectively switched to Go Modules, but if you're persistent enough you can make it work with Go modules too — all you need to do is rewrite the Go module cache instead of GOPATH and you're good to go.
This is cool, just don't let Rob Pike see this, he will have a conniption. Glad you called out that it shouldn't be used because this is about the most magical thing I have ever seen in Go
9 comments
[ 355 ms ] story [ 51.5 ms ] threadIt's used extensively to (for example) add debugging or tracing to functions in libraries or to sanitize certain code path. At work, we were able to kill off jndi from Log4J to prevent Log4Shell within hours of the announcement this way while waiting for updates of thousands of dependencies.
The way it works is that at the start of every function it adds an if statement that atomically checks whether or not the function has been intercepted, and if it did, then executes the replacement function instead. This also addresses the inlining issue.
My tool no longer works since it was rewriting GOPATH, and Go since effectively switched to Go Modules, but if you're persistent enough you can make it work with Go modules too — all you need to do is rewrite the Go module cache instead of GOPATH and you're good to go.
That reminded me of the Go Playground, where it is always 2009
https://go.dev/play/p/VrWYHGbtc6m
https://pkg.go.dev/testing/synctest#hdr-Time