Open Jorropo opened 1 year ago
This came up in arguably dubious real world code of mine however I think this could be a realistical usecase in some complex applications. Fixing this wouldn't be very hard (maintain a map of all pointers converted and reuse items of the map instead of recursing until infinity).
Imo the best fix is to go with #60335 and remove support for any recursion based conversion in ValueOf
but this is breaking the Go1 compatibility.
package main
import "syscall/js"
func main() {
a := []any{nil}
b := []any{a, a}
js.Global().Call("foo", js.ValueOf(b))
}
Also provides an incorrect result here it makes two copies of a
in JS land instead of one copy with two aliased pointers, available at git clone -b other-manifestation https://github.com/Jorropo/go-bug-report-syscall-js-reference-cycle
.
I'll submit a fix for this later over the week.
Fixing this wouldn't be very hard (maintain a map of all pointers converted and reuse items of the map instead of recursing until infinity).
This is way harder than it looks, the issue is slices, consider thoses case:
a := []int{1,2,3,4,5}
b := a[2:]
return js.ValueOf([]any{a[:4:4], b})
return js.ValueOf([]any{b, a})
A simple recursive descent which memoize pointers into a map would first create a partial copy of the slice, before later then finding they need to be aliased. So either you maintain a list of potentially aliased slices and go update everytime theses kinds of clashes happen. Or you do a runtime pointer analysis first and then do a second scan this time producing the slices correctly.
This could also be solved by intrinsifying js.ValueOf
and trying to solve the aliasing problem at compile time, my guess is that this almost never comes up, and a conservative compiler that solves the easy cases of pointer aliasing would be able to solve this. This is clearly a nuclear option here.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes (go1.20.4)
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
What did you expect to see?
What did you see instead?