profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/hyangah/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.

hyangah/mgodoc 10

Godoc on Mobile Device (android/ios)

hyangah/bugs 0

core dump for various bug debugging (nothing interesting)

hyangah/catapult 0

Catapult

hyangah/cobra 0

A Commander for modern Go CLI interactions

hyangah/cosmos-sdk 0

:chains: A Framework for Building High Value Public Blockchains :sparkles:

hyangah/dd-trace-go 0

A Go tracing package for Datadog APM

hyangah/delve 0

Delve is a debugger for the Go programming language.

hyangah/demangle 0

C++ symbol name demangler written in Go

issue commentgolang/go

proposal: time: context-aware time Ticker and Timer

Reset() should not allow the Timer to be reused, because the context is still expired. Reset() would only work as long as the context has not yet expired.

alercah

comment created time in 19 minutes

issue openedgolang/go

image decode panic: runtime error: makeslice: len out of range

<!-- Please answer these questions before submitting your issue. Thanks! For questions please use one of our forums: https://github.com/golang/go/wiki/Questions -->

What version of Go are you using (go version)?

<pre> $ go version go version go1.16.3 linux/amd64 </pre>

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

<details><summary><code>go env</code> Output</summary><br><pre> $ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/root/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/root/go" GOPRIVATE="" GOPROXY="https://goproxy.io" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.16.3" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3967062922=/tmp/go-build -gno-record-gcc-switches" </pre></details>

What did you do?

<!-- If possible, provide a recipe for reproducing the error. A complete runnable program is good. A link on play.golang.org is best. --> $ go run main.go

package main

import (
	"image"
	_ "image/png"
	_ "image/jpeg"
	"bytes"
	"encoding/hex"
)

func main() {
	data, err := hex.DecodeString("89504e470d0a1a0a0000000d4948445200efbfbd0d0000200804000000ea1b40ad30303030494441544889")
	//data, err := hex.DecodeString("89504e470d0a1a0a0000000d49484452002056616c75653e0403000000c6a32a2b0000002d504c54452200ff00ffff8800ff22ff000099ffff6600dd00ff77ff00ff000000ff99ddff00ff00bbffbb000044ff00ff44d2b049bd30303030494441542891")
	if err != nil {
		panic(err)
	}
	image.Decode(bytes.NewReader(data))
}

play.golang.org

What did you expect to see?

not panic

What did you see instead?

panic: runtime error: makeslice: len out of range

goroutine 1 [running]: image.NewNRGBA(0x0, 0x0, 0xefbfbd, 0xd000020, 0x4c83c0) /usr/local/go/src/image/image.go:393 +0x8f image/png.(*decoder).readImagePass(0xc00003ec00, 0x7f4024488058, 0xc000064050, 0x0, 0xc000064000, 0x503d00, 0xc000064050, 0x0, 0x0) /usr/local/go/src/image/png/reader.go:450 +0x242e image/png.(*decoder).decode(0xc00003ec00, 0x0, 0x0, 0x0, 0x0) /usr/local/go/src/image/png/reader.go:372 +0x638 image/png.(*decoder).parseIDAT(0xc00003ec00, 0x30303030, 0x4e14b8, 0x4) /usr/local/go/src/image/png/reader.go:849 +0x36 image/png.(*decoder).parseChunk(0xc00003ec00, 0x0, 0x0) /usr/local/go/src/image/png/reader.go:908 +0x3a7 image/png.Decode(0x503908, 0xc00005a1e0, 0xc00005a1e0, 0x503908, 0xc00005a1e0, 0x8) /usr/local/go/src/image/png/reader.go:967 +0x14f image.Decode(0x503928, 0xc000070180, 0x60, 0xc000066060, 0x56, 0x60, 0x2b, 0x0) /usr/local/go/src/image/format.go:93 +0x102 main.main() /root/d/main.go:17 +0xfa exit status 2

created time in 43 minutes

issue openedgolang/go

cmd/compile: internal compiler error: bad live variable at entry: [6]string value

$ gotip version
go version devel go1.17-14a18b7d25 Thu Apr 22 04:07:38 2021 +0000 windows/amd64
package p

func f() {
        var s string
        s = s + "" + s + "" + s + ""
        for true {
        }
}
$ gotip tool compile crash.go
crash.go:5:26: internal compiler error: bad live variable at entry of f: [6]string value

goroutine 20 [running]:
runtime/debug.Stack()
        D:/users/f65362c/alberto/other/gotip/src/runtime/debug/stack.go:24 +0x65
cmd/compile/internal/base.FatalfAt(0xc0000a4a00, 0x95d039, 0x1, 0xc0003d38d0, 0x0, 0xc0000a9354)
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/base/print.go:227 +0x157
cmd/compile/internal/liveness.(*liveness).epilogue(0xc0000f62c0)
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/liveness/plive.go:838 +0xc39
cmd/compile/internal/liveness.Compute(0xc0000f62c0, 0xc0003591e0, 0x972918, 0x1)
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/liveness/plive.go:1353 +0x8f
cmd/compile/internal/ssagen.genssa(0xc0000f62c0, 0xc0003c4f50)
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/ssagen/ssa.go:6564 +0x9e
cmd/compile/internal/ssagen.Compile(0xc0000f62c0, 0x0)
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/ssagen/pgen.go:175 +0x266
cmd/compile/internal/gc.compileFunctions.func2.1()
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/gc/compile.go:130 +0x65
created by cmd/compile/internal/gc.compileFunctions.func2
        D:/users/f65362c/alberto/other/gotip/src/cmd/compile/internal/gc/compile.go:128 +0xcf

Tentatively putting as relblocker in the 1.17 milestone since it's a tip regression, but feel free to re-label.

cc @randall77

created time in an hour

issue commentgolang/go

cmd/compile: fi missing from LHS of fi, err := fs.FileInfo(~R0), ~R1

Change https://golang.org/cl/312630 mentions this issue: cmd/compile: copy definition node for inline vars

nimelehin

comment created time in an hour

issue commentgolang/go

proposal: spec: generics: use type sets to remove type keyword in constraints

Here's another shower-thought: Using operation sets would also give us a clear and easily described algorithm to reject some empty type sets. Namely, by computing the operation set and look for conflicting operations.

As an example, we already reject this interface definition:

type X interface {
   M(int)
   M()
}

This has "duplicate methods M". In terms of operation sets, the interface element M(int) allows the "operation" (I know that @ianlancetaylor doesn't include method calls in "operation", but I will, for the sake of discussing operation sets) "call a method M with parameter int". So the operation set of X is {call method M with int, call method M without parameter}, which contains conflicts, so no type can ever provide all these operations, so no type can ever be in the type set. So the existing check of unimplementable interfaces would be a special case of this check.

Similarly, we could theoretically build the interface

type Y interface {
   [42]int
   [23]int
}

The operation set here would be {len returns an untyped integer with value 42, len returns an untyped integer with value 23}, which have conflicts and can thus be rejected.

These examples could, of course, be detected more easily. But the point is that we could have a single rule, that covers these cases and more.

Admittedly, I'm not sure how many practically relevant cases can be caught, though. ~T for a type which does not have itself as an underlying type isn't caught, for example. Neither is the example from the proposal

type Unsatisfiable interface {
	int | float32
	String() string
}

We'd also still need to provide a list of "conflicts" at least, which we might not want to do.

I don't think on its own this is a compelling case, but it might be something to keep in mind if we where to move to a form of operation sets.

ianlancetaylor

comment created time in an hour

GollumEvent

issue openedgolang/go

attempt to support "go build -buildmode=plugin -linkshared",it build success but run failed now.

<!-- Please answer these questions before submitting your issue. Thanks! For questions please use one of our forums: https://github.com/golang/go/wiki/Questions -->

What version of Go are you using (go version)?

<pre> $ go version go version go1.16.3 linux/amd64 </pre>

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

<details><summary><code>go env</code> Output</summary><br><pre> $ go env GO111MODULE="off" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/root/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/root/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/mnt/d/wsl/01.Tools/golang/1.16.3/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/mnt/d/wsl/01.Tools/golang/1.16.3/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.16.3" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build4112717964=/tmp/go-build -gno-record-gcc-switches"

</pre></details>

What did you do?

first, use go install -buildmode=shared -linkshared std to build and install libstd.so second, use go build -buildmode=plugin -linkshared plugin.go to build plugin.so plugin.go:

package main
import "fmt"
func Print(){
	fmt.Println("plugin")
}

then, use go build -linkshared main.go to build main.go whit linkshared main.go

package main

import (
	"log"
	"plugin"
)

func main() {
	p, err := plugin.Open("./plugin.so")
	if err != nil {
		log.Println(err)
	}
	f, err := p.Lookup("Print")
	if err != nil {
		log.Println(err)
	}
	f.(func())()
}

then I run main. <!-- If possible, provide a recipe for reproducing the error. A complete runnable program is good. A link on play.golang.org is best. -->

What did you expect to see?

main process run and print "plugin"

What did you see instead?

./main
SIGTRAP: trace trap
PC=0x7fb9410a81c1 m=0 sigcode=128

goroutine 0 [idle]:
runtime: unknown pc 0x7fb9410a81c1
stack: frame={sp:0x7ffe9713c240, fp:0x0} stack=[0x7ffe9693d8a0,0x7ffe9713c8e0)
00007ffe9713c140:  00007fb9410a64e0  00007ffe9713c230 
00007ffe9713c150:  0000000000000001  00007fb94383c548 
00007ffe9713c160:  0000000000000006  00000000000047a8 
00007ffe9713c170:  0000000000000ab0  0000000000000000 
00007ffe9713c180:  0000000000001000  0000000064cd305f 
00007ffe9713c190:  000056530156bc08  00007ffe9713c358 
00007ffe9713c1a0:  00007fb9410a68cc  00007ffe9713c230 
00007ffe9713c1b0:  00007ffe9713c240  00007fb9438491e9 
00007ffe9713c1c0:  0000000000000001  0000000000000000 
00007ffe9713c1d0:  0000000000000009  0000000000000000 
00007ffe9713c1e0:  0000000000000001  000056530156b8a0 
00007ffe9713c1f0:  0000000000000004  0000000000000000 
00007ffe9713c200:  000056530156b8a0  000056530156bc08 
00007ffe9713c210:  0000000000000000  0000000000000000 
00007ffe9713c220:  0000000000000000  0000000000000001 
00007ffe9713c230:  00000000ffffffff  00000000000037a8 
00007ffe9713c240: <00007fb9410a81ec  00007fb94384eb8a 
00007ffe9713c250:  0000000000000000  0000000000000001 
00007ffe9713c260:  00007ffe9713c9f8  00007ffe9713ca08 
00007ffe9713c270:  000056530156b8a0  00007fb94384ec91 
00007ffe9713c280:  78f25a33371354c3  00007ffe9713c5e0 
00007ffe9713c290:  00007ffe9713c480  fffffffffffffff0 
00007ffe9713c2a0:  0000000000000001  0000000000000006 
00007ffe9713c2b0:  000056530156b8a0  00007fb941013915 
00007ffe9713c2c0:  0000000000000000  00007ffe9713c4c0 
00007ffe9713c2d0:  00007fb9438522c0  00007ffe9713c430 
00007ffe9713c2e0:  4c576d5352587769  0000000000000000 
00007ffe9713c2f0:  0000000000000002  0000003200000008 
00007ffe9713c300:  0000000000000000  0000000000000000 
00007ffe9713c310:  0000000000000000  000000770000007c 
00007ffe9713c320:  0000005b0000006e  0000000000000000 
00007ffe9713c330:  00007fb9410a6000  0000000000000006 
runtime: unknown pc 0x7fb9410a81c1
stack: frame={sp:0x7ffe9713c240, fp:0x0} stack=[0x7ffe9693d8a0,0x7ffe9713c8e0)
00007ffe9713c140:  00007fb9410a64e0  00007ffe9713c230 
00007ffe9713c150:  0000000000000001  00007fb94383c548 
00007ffe9713c160:  0000000000000006  00000000000047a8 
00007ffe9713c170:  0000000000000ab0  0000000000000000 
00007ffe9713c180:  0000000000001000  0000000064cd305f 
00007ffe9713c190:  000056530156bc08  00007ffe9713c358 
00007ffe9713c1a0:  00007fb9410a68cc  00007ffe9713c230 
00007ffe9713c1b0:  00007ffe9713c240  00007fb9438491e9 
00007ffe9713c1c0:  0000000000000001  0000000000000000 
00007ffe9713c1d0:  0000000000000009  0000000000000000 
00007ffe9713c1e0:  0000000000000001  000056530156b8a0 
00007ffe9713c1f0:  0000000000000004  0000000000000000 
00007ffe9713c200:  000056530156b8a0  000056530156bc08 
00007ffe9713c210:  0000000000000000  0000000000000000 
00007ffe9713c220:  0000000000000000  0000000000000001 
00007ffe9713c230:  00000000ffffffff  00000000000037a8 
00007ffe9713c240: <00007fb9410a81ec  00007fb94384eb8a 
00007ffe9713c250:  0000000000000000  0000000000000001 
00007ffe9713c260:  00007ffe9713c9f8  00007ffe9713ca08 
00007ffe9713c270:  000056530156b8a0  00007fb94384ec91 
00007ffe9713c280:  78f25a33371354c3  00007ffe9713c5e0 
00007ffe9713c290:  00007ffe9713c480  fffffffffffffff0 
00007ffe9713c2a0:  0000000000000001  0000000000000006 
00007ffe9713c2b0:  000056530156b8a0  00007fb941013915 
00007ffe9713c2c0:  0000000000000000  00007ffe9713c4c0 
00007ffe9713c2d0:  00007fb9438522c0  00007ffe9713c430 
00007ffe9713c2e0:  4c576d5352587769  0000000000000000 
00007ffe9713c2f0:  0000000000000002  0000003200000008 
00007ffe9713c300:  0000000000000000  0000000000000000 
00007ffe9713c310:  0000000000000000  000000770000007c 
00007ffe9713c320:  0000005b0000006e  0000000000000000 
00007ffe9713c330:  00007fb9410a6000  0000000000000006 

goroutine 1 [syscall]:
runtime.cgocall(0x7fb9423d65f0, 0xc000362c40, 0xc000432018)
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/runtime/cgocall.go:154 +0x65 fp=0xc000362c10 sp=0xc000362bd8 pc=0x7fb941c292c5
plugin._Cfunc_pluginOpen(0xc000438000, 0xc000432018, 0x0)
	_cgo_gotypes.go:76 +0x4e fp=0xc000362c40 sp=0xc000362c10 pc=0x7fb9423b386e
plugin.open.func1(0xc000438000, 0x1001, 0x1001, 0xc000432018, 0x7fb943832ae0)
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/plugin/plugin_dlopen.go:64 +0x7d fp=0xc000362c70 sp=0xc000362c40 pc=0x7fb9423b4c3d
plugin.open(0x5653005ca015, 0xb, 0xc000000300, 0x300000002, 0xc000000300)
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/plugin/plugin_dlopen.go:64 +0x34e fp=0xc000362ee8 sp=0xc000362c70 pc=0x7fb9423b3cee
plugin.Open(...)
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/plugin/plugin.go:32
()
	/mnt/d/wsl/02.Code/01.GOPATH/src/plugindemo/main/main.go:9 +0x4a fp=0xc000362f88 sp=0xc000362ee8 pc=0x5653005c922a
runtime.main()
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/runtime/proc.go:225 +0x2aa fp=0xc000362fe0 sp=0xc000362f88 pc=0x7fb941c72aca
runtime.goexit()
	/mnt/d/wsl/01.Tools/golang/1.16.3/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc000362fe8 sp=0xc000362fe0 pc=0x7fb941cb3061

rax    0x0
rbx    0x7fb9410aa7b0
rcx    0x7ffe9713ca08
rdx    0x7ffe9713ca08
rdi    0x7fb9410ab040
rsi    0x7ffe9713c9f8
rbp    0x1
rsp    0x7ffe9713c240
r8     0x0
r9     0x1
r10    0x0
r11    0x7fb94109bbe0
r12    0x7ffe9713c9f8
r13    0x7ffe9713ca08
r14    0x7fb9410aa7b8
r15    0x0
rip    0x7fb9410a81c1
rflags 0x202
cs     0x33
fs     0x0
gs     0x0

then I use objdump -D plugin.so , I found that the function runtime.addmoduledata is not in plt of plugin.so, when go.link.addmoduledata is called in dllopen, it jumped to a wrong address. image

If I can add runtime.addmoduledata to plt in plugin.so, maybe I can use plugin.Open to open the plugin.so success.so,I modified the linker in toolchain. then this mistake is fixed.

image image

Then, when I run the main. it returns an error: "plugin was built with a different version of package". I found the pkghash of the pkgs in libstd.so is blank in plugin. I modifier the linker:

image

It will not generate pkghash of the pkg in libstd.so to plugin.so in linktime.

After these modification,the main process runs success,and I tried some more complex plugin,it works well. But I can't judge are there any other risk after these modification. I want to use it in production environment. Can anyone help me?

--

created time in an hour

issue commentgolang/go

"TestKVWithEmptyValue" of etcd-io/etcd fails with Master golang

@laboger Can you please give some pointers?

Rajalakshmi-Girish

comment created time in an hour

issue commentgolang/go

all: stop using direct syscalls on OpenBSD

@ianlancetaylor @cherrymui is there any way that we are able to get some traction on this before we reach the end of the current development cycle?

The changes for openbsd/386 were sent out during the previous development cycle and are still pending review. I also have openbsd/arm changes that I was holding until the openbsd/386 changes landed (in order to try to reduce the amount of tangle).

4a6f656c

comment created time in 2 hours

pull request commentgo-delve/delve

service/dap: cancel next before sending stopped event

I think I should elaborate on my last message: I would prefer that there was a UI to notify the user of this event but there is no way to do it through DAP and vscode has been aware of this for a while now and nothing has been done about it, so the preferable solution is not on the table.

What I am worried about is that without a prominent indication that the current goroutine changed users that are mindlessly clicking the step over function will be confused that control flow will seem to jump around randomly.

Delve's command line client has implemented the autocontinue solution for many years and we haven't received many complaints about it (although still greater than zero). So doing the same thing in dap is somewhat safe and at least consistent with other parts of delve.

suzmue

comment created time in 2 hours

pull request commentgo-delve/delve

dap: handle SetVariable requests

That shows how rarely this feature is used.

Or it could be an indication that people don't even bother filing issues and just decide quietly to not not rely on the broken feature. Hopefully not.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.

You need a test for field and parent having the same name. You used to have a bug for that case before in the code. Let's make sure it got fixed.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Baz", "42")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 1, "bar", "bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: set global integer 'p1'.+					client.VariablesRequest(1002)+					globals := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals, -1, "p1", "main.p1", "10", noChildren)+					client.SetVariableRequest(1002, "p1", "-10")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "-10"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1002)+					globals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals2, -1, "p1", "main.p1", "-10", noChildren)+				},+				disconnect: true,+			}})+	})+}++// TestSetVariableWithCall tests SetVariable functionality that may require function calls support.+func TestSetVariableWithCall(t *testing.T) {+	protest.MustSupportFunctionCalls(t, testBackend)+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			// Stop at the breakpoints set within the program, and after returning from barfoo (67)+			fixture.Source, []int{67},+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: set string 'baz'+					expectVarExact(t, args, 0, "baz", "baz", `"bazburzum"`, noChildren)+					client.SetVariableRequest(1000, "baz", `"BazBurZum"`)++					// After setting a variable, StoppedEvent must be sent+					// so the client can request new stack trace, and scopes info.+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), `"BazBurZum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// Check 'baz' was changed.+					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 0, "baz", "baz", `"BazBurZum"`, noChildren)++					// Test: try to set string type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Bur", `"ipsum"`)+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), `"ipsum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args3 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args3, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// TODO(hyangah): test with an integer field of a global variable (struct type).+				},+			}, {+				// Stop at second breakpoint.+				execute: func() {+					handleStop(t, client, 1, "main.barfoo", -1)+					// Test: set string 'a1' in main.barfoo.+					// This shouldn't affect 'a1' in main.foobar -+					// we will check that in the next breakpoint.

You don't need to go to next breakpoint to check this. You can just load parent scope with a scopes request. That's what happens when users click on it in the UI to view all the vars. Then once you do that, you can try to set it again. That should impact the topmost scope because that's your default. If you switch back to it, you should see it.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) { 	s.send(response) } +func (s *Server) doCall(goid, frame int, expr string) ([]*proc.Variable, error) {+	// This call might be evaluated in the context of the frame that is not topmost+	// if the editor is set to view the variables for one of the parent frames.+	// If the call expression refers to any of these variables, unlike regular+	// expressions, it will evaluate them in the context of the topmost frame,+	// and the user will get an unexpected result or an unexpected symbol error.+	// We prevent this but disallowing any frames other than topmost.+	if frame > 0 {+		return nil, fmt.Errorf("call is only supported with topmost stack frame")+	}+	stateBeforeCall, err := s.debugger.State( /*nowait*/ true)+	if err != nil {+		return nil, err+	}+	state, err := s.debugger.Command(&api.DebuggerCommand{+		Name:                 api.Call,+		ReturnInfoLoadConfig: api.LoadConfigFromProc(&DefaultLoadConfig),+		Expr:                 expr,+		UnsafeCall:           false,+		GoroutineID:          goid,+	}, nil)+	if _, isexited := err.(proc.ErrProcessExited); isexited || err == nil && state.Exited {+		return nil, errTerminated+	}+	if err != nil {+		return nil, err+	}++	// Call commands used for variable assignments do not return a value.+	// In go '=' is not an operator. Check if go/parser complains.+	// If the above Call command passed but the expression is not a valid+	// go expression, we just handled an assignment, and there is no+	// return value for assignment.+	if _, err := parser.ParseExpr(expr); err != nil {

It's hard for an experimental feature to get adoption both from the users and from the client authors if it doesn't have documentation. It's hard for adoption to stick if the experience is not good.

I am not sure what you mean by "what's preloaded through alias".

For what it's worth, I think the current PR does this backwards. I think in case of assignment of an in-scope variable, DAP/Vscode assumption that only the scope variables, not threads and frames, need refreshing is fine. I think sending a stopped event here is too extreme and will negatively impact user experience due to the reset frame error and collapsed UI, preventing the user to see the variable value change in front of their eyes. But in case of running an actual function, which resumes execution in a way that gives other goroutines time to start, run, and exit, it would be more accurate to refresh everything. That change would be out of scope for this PR, of course.

hyangah

comment created time in 4 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Baz", "42")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 1, "bar", "bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: set global integer 'p1'.+					client.VariablesRequest(1002)+					globals := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals, -1, "p1", "main.p1", "10", noChildren)+					client.SetVariableRequest(1002, "p1", "-10")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "-10"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1002)+					globals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals2, -1, "p1", "main.p1", "-10", noChildren)+				},+				disconnect: true,+			}})+	})+}++// TestSetVariableWithCall tests SetVariable functionality that may require function calls support.+func TestSetVariableWithCall(t *testing.T) {+	protest.MustSupportFunctionCalls(t, testBackend)+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			// Stop at the breakpoints set within the program, and after returning from barfoo (67)+			fixture.Source, []int{67},+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: set string 'baz'+					expectVarExact(t, args, 0, "baz", "baz", `"bazburzum"`, noChildren)+					client.SetVariableRequest(1000, "baz", `"BazBurZum"`)++					// After setting a variable, StoppedEvent must be sent+					// so the client can request new stack trace, and scopes info.+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), `"BazBurZum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// Check 'baz' was changed.+					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 0, "baz", "baz", `"BazBurZum"`, noChildren)++					// Test: try to set string type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Bur", `"ipsum"`)+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), `"ipsum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args3 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args3, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// TODO(hyangah): test with an integer field of a global variable (struct type).+				},+			}, {+				// Stop at second breakpoint.+				execute: func() {+					handleStop(t, client, 1, "main.barfoo", -1)+					// Test: set string 'a1' in main.barfoo.+					// This shouldn't affect 'a1' in main.foobar -

You should load the scope for the other one and set again. That will impact the one in the topmost, won't it?

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Baz", "42")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 1, "bar", "bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: set global integer 'p1'.+					client.VariablesRequest(1002)+					globals := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals, -1, "p1", "main.p1", "10", noChildren)+					client.SetVariableRequest(1002, "p1", "-10")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "-10"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1002)+					globals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals2, -1, "p1", "main.p1", "-10", noChildren)+				},+				disconnect: true,+			}})+	})+}++// TestSetVariableWithCall tests SetVariable functionality that may require function calls support.+func TestSetVariableWithCall(t *testing.T) {+	protest.MustSupportFunctionCalls(t, testBackend)+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			// Stop at the breakpoints set within the program, and after returning from barfoo (67)+			fixture.Source, []int{67},+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: set string 'baz'+					expectVarExact(t, args, 0, "baz", "baz", `"bazburzum"`, noChildren)+					client.SetVariableRequest(1000, "baz", `"BazBurZum"`)++					// After setting a variable, StoppedEvent must be sent+					// so the client can request new stack trace, and scopes info.+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), `"BazBurZum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// Check 'baz' was changed.+					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 0, "baz", "baz", `"BazBurZum"`, noChildren)++					// Test: try to set string type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Bur", `"ipsum"`)+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), `"ipsum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args3 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args3, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren)++					// Test: set integer 'a2'

That doesn't use call and you already cover this above.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Baz", "42")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 1, "bar", "bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: set global integer 'p1'.+					client.VariablesRequest(1002)+					globals := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals, -1, "p1", "main.p1", "10", noChildren)+					client.SetVariableRequest(1002, "p1", "-10")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "-10"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1002)+					globals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals2, -1, "p1", "main.p1", "-10", noChildren)+				},+				disconnect: true,+			}})+	})+}++// TestSetVariableWithCall tests SetVariable functionality that may require function calls support.+func TestSetVariableWithCall(t *testing.T) {+	protest.MustSupportFunctionCalls(t, testBackend)+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			// Stop at the breakpoints set within the program, and after returning from barfoo (67)+			fixture.Source, []int{67},

I thought you removed this? Maybe that was the other test. Also it might be easier to just set 66 explicitly, then to rely on runtime breakpoint and then special case the line number. We did that in some test to test that we stop at correct lines, but you shouldn't need this here.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Baz", "42")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 1, "bar", "bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: set global integer 'p1'.+					client.VariablesRequest(1002)+					globals := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals, -1, "p1", "main.p1", "10", noChildren)+					client.SetVariableRequest(1002, "p1", "-10")+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), "-10"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1002)+					globals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, globals2, -1, "p1", "main.p1", "-10", noChildren)+				},+				disconnect: true,+			}})+	})+}++// TestSetVariableWithCall tests SetVariable functionality that may require function calls support.+func TestSetVariableWithCall(t *testing.T) {+	protest.MustSupportFunctionCalls(t, testBackend)+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			// Stop at the breakpoints set within the program, and after returning from barfoo (67)+			fixture.Source, []int{67},+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: set string 'baz'+					expectVarExact(t, args, 0, "baz", "baz", `"bazburzum"`, noChildren)+					client.SetVariableRequest(1000, "baz", `"BazBurZum"`)++					// After setting a variable, StoppedEvent must be sent+					// so the client can request new stack trace, and scopes info.+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), `"BazBurZum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// Check 'baz' was changed.+					client.VariablesRequest(1000)+					args2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args2, 0, "baz", "baz", `"BazBurZum"`, noChildren)++					// Test: try to set string type of composite type var 'bar'. That is supported.+					barRef := expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(barRef, "Bur", `"ipsum"`)+					client.ExpectStoppedEvent(t)+					if got, want := client.ExpectSetVariableResponse(t), `"ipsum"`; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q", got, want)+					}+					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1000)+					args3 := client.ExpectVariablesResponse(t)+					expectVarExact(t, args3, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren)++					// Test: set integer 'a2'+					client.VariablesRequest(1001)+					locals := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)+					client.SetVariableRequest(1001, "a2", "42")+					client.ExpectStoppedEvent(t)++					if got, want := client.ExpectSetVariableResponse(t), "42"; got.Success != true || got.Body.Value != want {+						t.Errorf("got %#v, want {Success=true, Body.Value=%q}", got, want)+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					client.VariablesRequest(1001)+					locals2 := client.ExpectVariablesResponse(t)+					expectVarExact(t, locals2, -1, "a2", "a2", "42", noChildren)++					// Test: set integer 'a2' with an invalid value.+					client.SetVariableRequest(1001, "a2", "false")+					if got, want := client.ExpectErrorResponse(t), "can not convert false constant to int"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// TODO(hyangah): test with an integer field of a global variable (struct type).

That would be a regular set as well, not a call, won't it?

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onReverseContinueRequest(request *dap.ReverseContinueRequest) { 	s.sendNotYetImplementedErrorResponse(request.Request) } -// onSetVariableRequest sends a not-yet-implemented error response.-// Capability 'supportsSetVariable' is not set 'initialize' response.-func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) { // TODO V0-	s.sendNotYetImplementedErrorResponse(request.Request)+func variableByName(v *proc.Variable, name string) (*proc.Variable, bool) {+	if v == nil {+		return nil, false+	}+	if v.Name == name {+		return v, true+	}++	for _, child := range v.Children {+		if child.Name == name {+			return &child, true+		}+		// No need to look into the child's children - DAP clients are expected+		// to provide the variable reference number, so the name is the name of+		// the variable's direct child.+	}+	return nil, false+}++// nameToEvaluateName finds the variable of the specified name+// from the given variable and its children, and returns its+// evaluateName.+func (s *Server) nameToEvaluateName(v *fullyQualifiedVariable, name string) (string, error) {+	children, err := s.childrenOfVariable(v)+	if err != nil {+		return "", err+	}+	evaluateName := name+	for _, c := range children {+		if c.Name == name {+			if c.EvaluateName != "" {+				evaluateName = c.EvaluateName+			}+			break+		}+	}+	return evaluateName, nil+}++// onSetVariableRequest handles 'setVariable' requests.+func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) {+	arg := request.Arguments++	v, ok := s.variableHandles.get(arg.VariablesReference)+	if !ok {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference))+		return+	}+	// We need to translate the arg.Name to its evaluateName if the name+	// refers to a field or element of a variable.+	// https://github.com/microsoft/vscode/issues/120774+	// Look up the evaluateName.+	evaluateName, err := s.nameToEvaluateName(v, arg.Name)+	if err != nil {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+		return+	}+	named, ok := variableByName(v.Variable, arg.Name)+	if !ok {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference))+		return+	}+	// Assume the top-most frame & the current goroutine.+	// It is not clear if DAP allows SetVariable requests+	// for variables or memory location not visible from the scope.+	// TODO: keep track of the frame for a variable and allow alternate+	// frame, or send explicit error messages for unsupported frames.+	goid, frame := -1, 0++	switch named.Kind {+	case reflect.String:+		if _, err := s.doCall(goid, frame, fmt.Sprintf("%v=%v", evaluateName, arg.Value)); err != nil {+			s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+			return+		}+	default:+		if err := s.debugger.SetVariableInScope(goid, frame, 0, evaluateName, arg.Value); err != nil {+			s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+			return+		}+		// Send StoppedEvent so the client retrieve threads/scopes again.

I can swear I wrote a comment about this, but I don't see it anymore.

So in a similar fashion as with call assignments, I think resetting all threads and frames and sending a stopped event here might be an overkill. You might end up with a different stop reason because you pick it from the debugger and not based on doCommand code. You will have to wait to load all threads. Etc. Unfortunately here vscode doesn't help us and doesn't send a scopes request to refresh the variables. And ideally we do want to refresh all variables in the selected scope, both in the UI and in the handles map. And the reason why I say variables and not just this one variable is because ideally we want three things updated: (1) the value of the thing you just set (2) the value of any children that got reset because you reset the parent (3) the inlined value within the value of the parent variable I tried this with javascript. They update (1) and (2), but not (3). The best way to update all three would be to file an issue against vscode and tell them they need to send a scopes request like they do after evaluate requests.

Settling for (1) and (2) is good enough for the time being. And I think the way to do that is to return new variables reference in the setVariable response. That's how you update the cached value. You just let the old one be (we can optimize this eventually and clear, but for now we just stop using its key), and you give the editor a new reference for the refreshed variable.

You should be able to test this with two pointers p1 and p2 of he same type and then set variable p1 to p2. What should we see? The expanded dereferenced value should not disappear and should change to whatever *p2 is. Does that work?

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.

I think you mean try to set a nested field of a composite var.

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) { 	} 	// Assume the top-most frame & the current goroutine. 	// It is not clear if DAP allows SetVariable requests-	// for variables or memory location not visible from the scode.+	// for variables or memory location not visible from the scope.

I don't think this comment is accurate. Variable references are always relative to the selected scope. If a variable is outside of the scope, no reference for it would be available. However, you could be interacting with a scope that is not top-most or not on a current goroutine. And that's something we might not want to or be able to support.

While the more explicit error TODO is in the works, it would be helpful to log that you defaulted to the one and only topmost scope, potentially ignoring the goroutine and the frame of the user-selected variable, so when they get an unexpected symbol or type error, they will at least have more info in the logs to explain what the name was evaluated against and why it generated this error.

hyangah

comment created time in 3 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) { 		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference)) 		return 	}-	// We may need to translate the name to the evaluateName if the name+	// We need to translate the arg.Name to its evaluateName if the name 	// refers to a field or element of a variable. 	// https://github.com/microsoft/vscode/issues/120774-	// Compute the evaluateName by running the same code the onVariablesRequest-	// handler uses.+	// Look up the evaluateName.

Sorry for the false alarm, but after studying your code more closely I think your earlier comment was right. You were rerunning the same code as you would for a variables request instead of just looking up what the previous variables request did. I just did not realize it at the time.

hyangah

comment created time in 3 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func TestUnupportedCommandResponses(t *testing.T) { 	}) } +// TestSetVariable tests SetVariable functionality that doesn't involve function calls.+func TestSetVariable(t *testing.T) {+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {+		runDebugSessionWithBPs(t, client, "launch",+			func() {+				client.LaunchRequestWithArgs(map[string]interface{}{+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,+				})+			},+			fixture.Source, nil,+			[]onBreakpoint{{+				execute: func() {+					startLineno := 66 // after runtime.Breakpoint+					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {+						// Go1.15 on windows inserts a NOP after the call to+						// runtime.Breakpoint and marks it same line as the+						// runtime.Breakpoint call, making this flaky, so skip the line check.+						startLineno = -1+					}++					handleStop(t, client, 1, "main.foobar", startLineno)++					// We need 'args' to perform+					//   VariableRequest -> SetVariableRequest -> VariableRequest.++					client.VariablesRequest(1000)+					args := client.ExpectVariablesResponse(t)++					// Test: try to set composite type 'bar'. That is not implemented.+					expectVarExact(t, args, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, hasChildren)+					client.SetVariableRequest(1000, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`)+					if got, want := client.ExpectErrorResponse(t), "*ast.CompositeLit not implemented"; !strings.Contains(got.Body.Error.Format, want) {+						t.Errorf("got %#v, want error string containing %q", got, want)+					}++					// Test: try to set integer type of composite type var 'bar'. That is supported.

You should also be testing other composite types:

  • pointers that have children with no names - do we handle that?
  • arrays that have children with index names
  • maps that have different child structure depending on whether keys and values are scalars (3 subcases actually) and I think set variable might not make sense in some of them
  • interfaces: can a user set .(data)?
hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onReverseContinueRequest(request *dap.ReverseContinueRequest) { 	s.sendNotYetImplementedErrorResponse(request.Request) } -// onSetVariableRequest sends a not-yet-implemented error response.-// Capability 'supportsSetVariable' is not set 'initialize' response.-func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) { // TODO V0-	s.sendNotYetImplementedErrorResponse(request.Request)+func variableByName(v *proc.Variable, name string) (*proc.Variable, bool) {+	if v == nil {+		return nil, false+	}+	if v.Name == name {+		return v, true+	}++	for _, child := range v.Children {+		if child.Name == name {+			return &child, true+		}+		// No need to look into the child's children - DAP clients are expected+		// to provide the variable reference number, so the name is the name of+		// the variable's direct child.+	}+	return nil, false+}++// nameToEvaluateName finds the variable of the specified name+// from the given variable and its children, and returns its+// evaluateName.+func (s *Server) nameToEvaluateName(v *fullyQualifiedVariable, name string) (string, error) {+	children, err := s.childrenOfVariable(v)+	if err != nil {+		return "", err+	}+	evaluateName := name+	for _, c := range children {+		if c.Name == name {+			if c.EvaluateName != "" {+				evaluateName = c.EvaluateName+			}+			break+		}+	}+	return evaluateName, nil+}++// onSetVariableRequest handles 'setVariable' requests.+func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) {+	arg := request.Arguments++	v, ok := s.variableHandles.get(arg.VariablesReference)+	if !ok {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference))+		return+	}+	// We need to translate the arg.Name to its evaluateName if the name+	// refers to a field or element of a variable.+	// https://github.com/microsoft/vscode/issues/120774+	// Look up the evaluateName.+	evaluateName, err := s.nameToEvaluateName(v, arg.Name)+	if err != nil {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+		return+	}+	named, ok := variableByName(v.Variable, arg.Name)+	if !ok {+		s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference))+		return+	}+	// Assume the top-most frame & the current goroutine.+	// It is not clear if DAP allows SetVariable requests+	// for variables or memory location not visible from the scope.+	// TODO: keep track of the frame for a variable and allow alternate+	// frame, or send explicit error messages for unsupported frames.+	goid, frame := -1, 0++	switch named.Kind {+	case reflect.String:+		if _, err := s.doCall(goid, frame, fmt.Sprintf("%v=%v", evaluateName, arg.Value)); err != nil {+			s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+			return+		}+	default:+		if err := s.debugger.SetVariableInScope(goid, frame, 0, evaluateName, arg.Value); err != nil {+			s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error())+			return+		}+		// Send StoppedEvent so the client retrieve threads/scopes again.+		// This is the easiest/safest way to update the cached variable+		// in variablesHandle map but this can be expensive+		// if there are many goroutines and variables in the scope.+		// For the string case that was handled by s.doCall, s.doCall+		// triggers StoppedEvent.+		state, err := s.debugger.State(true)+		if err != nil {+			s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to reset state after setting variable", err.Error())+		}+		s.resetHandlesForStoppedEvent()+		s.sendStoppedEvent(state)+	}+	// update the variable stored in variableHandles

This comment doesn't match the code

hyangah

comment created time in 2 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) { 		expr := strings.Replace(request.Arguments.Expression, "call ", "", 1) 		retVars, err := s.doCall(goid, frame, expr) 		if errors.Is(err, errTerminated) {

I don't think you need to single out this error. There is nothing wrong with either sending an error response here or even a regular response with no return values as the call did complete. It is quite common to send responses even after the terminate event.

hyangah

comment created time in 5 hours

Pull request review commentgo-delve/delve

dap: handle SetVariable requests

 func (s *Server) doCall(goid, frame int, expr string) ([]*proc.Variable, error) 		GoroutineID:          goid, 	}, nil) 	if _, isexited := err.(proc.ErrProcessExited); isexited || err == nil && state.Exited {

@aarzilli It appears that we cannot use StopReason of StopExited in place of this check or StopCallReturned in place of retVars==nil check because there could be multiple reasons for stopping and these particular reasons that are of interest to us are not guaranteed to win (e.g. we might end up with StopUnknown at the end of a call). Is my understanding accurate?

hyangah

comment created time in 3 hours

issue openedgolang/go

x/website:incorrect-example-snippet-under-mod-private-module-repo-auth

<!-- Please answer these questions before submitting your issue. Thanks! For questions please use one of our forums: https://github.com/golang/go/wiki/Questions -->

What version of Go are you using (go version)?

Not applicable.

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Not applicable.

What did you do?

Read through documentation and saw that following example snippet shown under this paragraph is incorrect:

[url "git@github.com:']
    insteadOf = https://github.com/

What did you expect to see?

Either double quotes or single quotes.

What did you see instead?

Both.

created time in 2 hours

issue commentgolang/go

[dev.fuzz] proposal: default timeout should be disabled

To be honest users will not care for tests on the seed corpus and timeouts on it. For me it was always stuff in a directory.

As a user, I respectfully disagree. I used unit tests for filling seed corpus even before dev.fuzz made it much easier. I do care about testing seed corpus and timeouts on it.

dsnet

comment created time in 2 hours

issue commentgolang/go

[dev.fuzz] proposal: default timeout should be disabled

I think there needs to be one other timeout: timeout for one fuzz function invocation. This will allow one to find not only crashers, but hangs due to endless loops, large memory consumption, blocking on reading, etc. https://github.com/dvyukov/go-fuzz has this flag:

  -timeout int
    	test timeout, in seconds (default 10)

I think we can redefine what -timeout means when -fuzz is passed.

dsnet

comment created time in 2 hours

issue commentgolang/go

proposal: unicode: add emoji properties

So, properties can't be added to properties list.
You can't match properties using character classes in regexp and can't add custom character classes either.

What is the recommended way to go then?

gudvinr

comment created time in 2 hours