Skip to content

Commit 2dd555b

Browse files
authored
Merge pull request #1313 from Workiva/godebugUpdate
[go1.20] internal/godebug env watcher update
2 parents e518760 + c849f60 commit 2dd555b

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

compiler/natives/src/internal/godebug/godebug.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package godebug
55

66
import (
77
"sync"
8+
_ "unsafe" // go:linkname
89
)
910

1011
type Setting struct {
@@ -37,6 +38,9 @@ func (s *Setting) Value() string {
3738
return *s.value.Load()
3839
}
3940

41+
//go:linkname setUpdate runtime.godebug_setUpdate
42+
func setUpdate(update func(def, env string))
43+
4044
func update(def, env string) {
4145
updateMu.Lock()
4246
defer updateMu.Unlock()

compiler/natives/src/runtime/runtime.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,45 @@ func nanotime() int64 {
492492
const millisecond = 1_000_000
493493
return js.Global.Get("Date").New().Call("getTime").Int64() * millisecond
494494
}
495+
496+
const godebugEnvKey = `GODEBUG`
497+
498+
var godebugUpdate func(def, env string)
499+
500+
// godebug_setUpdate implements the setUpdate in src/internal/godebug/godebug.go
501+
func godebug_setUpdate(update func(def, env string)) {
502+
godebugUpdate = update
503+
godebugEnv := getEnvString(godebugEnvKey)
504+
godebug_notify(godebugEnvKey, godebugEnv)
505+
}
506+
507+
func getEnvString(key string) string {
508+
process := js.Global.Get(`process`)
509+
if process == js.Undefined {
510+
return ``
511+
}
512+
513+
env := process.Get(`env`)
514+
if env == js.Undefined {
515+
return ``
516+
}
517+
518+
value := env.Get(key)
519+
if value == js.Undefined {
520+
return ``
521+
}
522+
523+
return value.String()
524+
}
525+
526+
// godebug_notify is the function is called by syscall anytime an environment
527+
// variable is set or unset. It emit the GODEBUG setting if it was changed.
528+
func godebug_notify(key, value string) {
529+
update := godebugUpdate
530+
if update == nil || key != godebugEnvKey {
531+
return
532+
}
533+
534+
godebugDefault := ``
535+
update(godebugDefault, value)
536+
}

compiler/natives/src/syscall/syscall_js_wasm.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package syscall
22

33
import (
44
"syscall/js"
5+
_ "unsafe" // go:linkname
56
)
67

78
func runtime_envs() []string {
@@ -36,6 +37,7 @@ func setenv_c(k, v string) {
3637
return
3738
}
3839
process.Get("env").Set(k, v)
40+
godebug_notify(k, v)
3941
}
4042

4143
func unsetenv_c(k string) {
@@ -44,8 +46,12 @@ func unsetenv_c(k string) {
4446
return
4547
}
4648
process.Get("env").Delete(k)
49+
godebug_notify(k, ``)
4750
}
4851

52+
//go:linkname godebug_notify runtime.godebug_notify
53+
func godebug_notify(key, value string)
54+
4955
func setStat(st *Stat_t, jsSt js.Value) {
5056
// This method is an almost-exact copy of upstream, except for 4 places where
5157
// time stamps are obtained as floats in lieu of int64. Upstream wasm emulates

tests/runtime_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ package tests
55
import (
66
"fmt"
77
"runtime"
8+
"strconv"
9+
"strings"
810
"testing"
11+
_ "unsafe"
912

1013
"github.com/google/go-cmp/cmp"
1114
"github.com/gopherjs/gopherjs/js"
@@ -160,3 +163,42 @@ func TestCallers(t *testing.T) {
160163
panic("panic")
161164
})
162165
}
166+
167+
// Need this to tunnel into `internal/godebug` and run a test
168+
// without causing a dependency cycle with the `testing` package.
169+
//
170+
//go:linkname godebug_setUpdate runtime.godebug_setUpdate
171+
func godebug_setUpdate(update func(string, string))
172+
173+
func Test_GoDebugInjection(t *testing.T) {
174+
buf := []string{}
175+
update := func(def, env string) {
176+
if def != `` {
177+
t.Errorf(`Expected the default value to be empty but got %q`, def)
178+
}
179+
buf = append(buf, strconv.Quote(env))
180+
}
181+
check := func(want string) {
182+
if got := strings.Join(buf, `, `); got != want {
183+
t.Errorf(`Unexpected result: got: %q, want: %q`, got, want)
184+
}
185+
buf = buf[:0]
186+
}
187+
188+
// Call it multiple times to ensure that the watcher is only injected once.
189+
// Each one of these calls should emit an update first, then when GODEBUG is set.
190+
godebug_setUpdate(update)
191+
godebug_setUpdate(update)
192+
check(`"", ""`) // two empty strings for initial update calls.
193+
194+
t.Setenv(`GODEBUG`, `gopherJSTest=ben`)
195+
check(`"gopherJSTest=ben"`) // must only be once for update for new value.
196+
197+
godebug_setUpdate(update)
198+
check(`"gopherJSTest=ben"`) // must only be once for initial update with already set value.
199+
200+
t.Setenv(`GODEBUG`, `gopherJSTest=tom`)
201+
t.Setenv(`GODEBUG`, `gopherJSTest=sam`)
202+
t.Setenv(`NOT_GODEBUG`, `gopherJSTest=bob`)
203+
check(`"gopherJSTest=tom", "gopherJSTest=sam"`)
204+
}

0 commit comments

Comments
 (0)