mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-30 10:17:03 +08:00 
			
		
		
		
	Implements set command for pointers and numerical values
set <variable name> <value>
This commit is contained in:
		
							
								
								
									
										14
									
								
								_fixtures/testvariables3.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								_fixtures/testvariables3.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"runtime" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	i1 := 1 | ||||||
|  | 	i2 := 2 | ||||||
|  | 	p1 := &i1 | ||||||
|  | 	runtime.Breakpoint() | ||||||
|  | 	fmt.Printf("%d %d %v\n", i1, i2, p1) | ||||||
|  | } | ||||||
| @ -6,6 +6,9 @@ import ( | |||||||
| 	"debug/gosym" | 	"debug/gosym" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"go/ast" | ||||||
|  | 	"go/parser" | ||||||
|  | 	"go/token" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| @ -337,6 +340,15 @@ func (scope *EvalScope) EvalVariable(name string) (*Variable, error) { | |||||||
| 	return v, err | 	return v, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Sets the value of the named variable | ||||||
|  | func (scope *EvalScope) SetVariable(name, value string) error { | ||||||
|  | 	addr, err := scope.ExtractVariableInfo(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return addr.setValue(value) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) { | func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) { | ||||||
| 	rdr := scope.DwarfReader() | 	rdr := scope.DwarfReader() | ||||||
| 	v, err := scope.extractVarInfoFromEntry(entry, rdr) | 	v, err := scope.extractVarInfoFromEntry(entry, rdr) | ||||||
| @ -627,6 +639,27 @@ func (v *Variable) loadValueInternal(printStructName bool, recurseLevel int) (st | |||||||
| 	return "", fmt.Errorf("could not find value for type %s", v.dwarfType) | 	return "", fmt.Errorf("could not find value for type %s", v.dwarfType) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (v *Variable) setValue(value string) error { | ||||||
|  | 	v = v.resolveTypedefs() | ||||||
|  |  | ||||||
|  | 	switch t := v.dwarfType.(type) { | ||||||
|  | 	case *dwarf.PtrType: | ||||||
|  | 		return v.writeUint(false, value, int64(v.thread.dbp.arch.PtrSize())) | ||||||
|  | 	case *dwarf.ComplexType: | ||||||
|  | 		return v.writeComplex(value, t.ByteSize) | ||||||
|  | 	case *dwarf.IntType: | ||||||
|  | 		return v.writeUint(true, value, t.ByteSize) | ||||||
|  | 	case *dwarf.UintType: | ||||||
|  | 		return v.writeUint(false, value, t.ByteSize) | ||||||
|  | 	case *dwarf.FloatType: | ||||||
|  | 		return v.writeFloat(value, t.ByteSize) | ||||||
|  | 	case *dwarf.BoolType: | ||||||
|  | 		return v.writeBool(value) | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("Can not set value of variables of type: %T\n", t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (thread *Thread) readString(addr uintptr) (string, error) { | func (thread *Thread) readString(addr uintptr) (string, error) { | ||||||
| 	// string data structure is always two ptrs in size. Addr, followed by len | 	// string data structure is always two ptrs in size. Addr, followed by len | ||||||
| 	// http://research.swtch.com/godata | 	// http://research.swtch.com/godata | ||||||
| @ -775,6 +808,81 @@ func (v *Variable) readComplex(size int64) (string, error) { | |||||||
| 	return fmt.Sprintf("(%s + %si)", r, i), nil | 	return fmt.Sprintf("(%s + %si)", r, i), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (v *Variable) writeComplex(value string, size int64) error { | ||||||
|  | 	var real, imag float64 | ||||||
|  |  | ||||||
|  | 	expr, err := parser.ParseExpr(value) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var lits []*ast.BasicLit | ||||||
|  |  | ||||||
|  | 	if e, ok := expr.(*ast.ParenExpr); ok { | ||||||
|  | 		expr = e.X | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch e := expr.(type) { | ||||||
|  | 	case *ast.BinaryExpr: // "<float> + <float>i" or "<float>i + <float>" | ||||||
|  | 		x, xok := e.X.(*ast.BasicLit) | ||||||
|  | 		y, yok := e.Y.(*ast.BasicLit) | ||||||
|  | 		if e.Op != token.ADD || !xok || !yok { | ||||||
|  | 			return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 		} | ||||||
|  | 		lits = []*ast.BasicLit{x, y} | ||||||
|  | 	case *ast.CallExpr: // "complex(<float>, <float>)" | ||||||
|  | 		tname, ok := e.Fun.(*ast.Ident) | ||||||
|  | 		if !ok { | ||||||
|  | 			return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 		} | ||||||
|  | 		if (tname.Name != "complex64") && (tname.Name != "complex128") { | ||||||
|  | 			return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 		} | ||||||
|  | 		if len(e.Args) != 2 { | ||||||
|  | 			return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 		} | ||||||
|  | 		for i := range e.Args { | ||||||
|  | 			lit, ok := e.Args[i].(*ast.BasicLit) | ||||||
|  | 			if !ok { | ||||||
|  | 				return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 			} | ||||||
|  | 			lits = append(lits, lit) | ||||||
|  | 		} | ||||||
|  | 		lits[1].Kind = token.IMAG | ||||||
|  | 		lits[1].Value = lits[1].Value + "i" | ||||||
|  | 	case *ast.BasicLit: // "<float>" or "<float>i" | ||||||
|  | 		lits = []*ast.BasicLit{e} | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, lit := range lits { | ||||||
|  | 		var err error | ||||||
|  | 		var v float64 | ||||||
|  | 		switch lit.Kind { | ||||||
|  | 		case token.FLOAT, token.INT: | ||||||
|  | 			v, err = strconv.ParseFloat(lit.Value, int(size/2)) | ||||||
|  | 			real += v | ||||||
|  | 		case token.IMAG: | ||||||
|  | 			v, err = strconv.ParseFloat(lit.Value[:len(lit.Value)-1], int(size/2)) | ||||||
|  | 			imag += v | ||||||
|  | 		default: | ||||||
|  | 			return fmt.Errorf("Not a complex constant: %s", value) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = v.writeFloatRaw(real, int64(size/2)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	imagaddr := *v | ||||||
|  | 	imagaddr.Addr += uintptr(size / 2) | ||||||
|  | 	return imagaddr.writeFloatRaw(imag, int64(size/2)) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (v *Variable) readInt(size int64) (string, error) { | func (v *Variable) readInt(size int64) (string, error) { | ||||||
| 	n, err := v.thread.readIntRaw(v.Addr, size) | 	n, err := v.thread.readIntRaw(v.Addr, size) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -813,6 +921,37 @@ func (v *Variable) readUint(size int64) (string, error) { | |||||||
| 	return strconv.FormatUint(n, 10), nil | 	return strconv.FormatUint(n, 10), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (v *Variable) writeUint(signed bool, value string, size int64) error { | ||||||
|  | 	var n uint64 | ||||||
|  | 	var err error | ||||||
|  | 	if signed { | ||||||
|  | 		var m int64 | ||||||
|  | 		m, err = strconv.ParseInt(value, 0, int(size*8)) | ||||||
|  | 		n = uint64(m) | ||||||
|  | 	} else { | ||||||
|  | 		n, err = strconv.ParseUint(value, 0, int(size*8)) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	val := make([]byte, size) | ||||||
|  |  | ||||||
|  | 	switch size { | ||||||
|  | 	case 1: | ||||||
|  | 		val[0] = byte(n) | ||||||
|  | 	case 2: | ||||||
|  | 		binary.LittleEndian.PutUint16(val, uint16(n)) | ||||||
|  | 	case 4: | ||||||
|  | 		binary.LittleEndian.PutUint32(val, uint32(n)) | ||||||
|  | 	case 8: | ||||||
|  | 		binary.LittleEndian.PutUint64(val, uint64(n)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = v.thread.writeMemory(v.Addr, val) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) { | func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) { | ||||||
| 	var n uint64 | 	var n uint64 | ||||||
|  |  | ||||||
| @ -856,6 +995,30 @@ func (v *Variable) readFloat(size int64) (string, error) { | |||||||
| 	return "", fmt.Errorf("could not read float") | 	return "", fmt.Errorf("could not read float") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (v *Variable) writeFloat(value string, size int64) error { | ||||||
|  | 	f, err := strconv.ParseFloat(value, int(size*8)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return v.writeFloatRaw(f, size) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (v *Variable) writeFloatRaw(f float64, size int64) error { | ||||||
|  | 	buf := bytes.NewBuffer(make([]byte, 0, size)) | ||||||
|  |  | ||||||
|  | 	switch size { | ||||||
|  | 	case 4: | ||||||
|  | 		n := float32(f) | ||||||
|  | 		binary.Write(buf, binary.LittleEndian, n) | ||||||
|  | 	case 8: | ||||||
|  | 		n := float64(f) | ||||||
|  | 		binary.Write(buf, binary.LittleEndian, n) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := v.thread.writeMemory(v.Addr, buf.Bytes()) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| func (v *Variable) readBool() (string, error) { | func (v *Variable) readBool() (string, error) { | ||||||
| 	val, err := v.thread.readMemory(v.Addr, 1) | 	val, err := v.thread.readMemory(v.Addr, 1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -869,6 +1032,19 @@ func (v *Variable) readBool() (string, error) { | |||||||
| 	return "true", nil | 	return "true", nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (v *Variable) writeBool(value string) error { | ||||||
|  | 	b, err := strconv.ParseBool(value) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	val := []byte{0} | ||||||
|  | 	if b { | ||||||
|  | 		val[0] = *(*byte)(unsafe.Pointer(&b)) | ||||||
|  | 	} | ||||||
|  | 	_, err = v.thread.writeMemory(v.Addr, val) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| func (v *Variable) readFunctionPtr() (string, error) { | func (v *Variable) readFunctionPtr() (string, error) { | ||||||
| 	val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize()) | 	val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ import ( | |||||||
| type varTest struct { | type varTest struct { | ||||||
| 	name    string | 	name    string | ||||||
| 	value   string | 	value   string | ||||||
|  | 	setTo   string | ||||||
| 	varType string | 	varType string | ||||||
| 	err     error | 	err     error | ||||||
| } | } | ||||||
| @ -38,48 +39,67 @@ func evalVariable(p *Process, symbol string) (*Variable, error) { | |||||||
| 	return scope.EvalVariable(symbol) | 	return scope.EvalVariable(symbol) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (tc *varTest) settable() bool { | ||||||
|  | 	return tc.setTo != "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (tc *varTest) afterSet() varTest { | ||||||
|  | 	r := *tc | ||||||
|  | 	r.value = r.setTo | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func setVariable(p *Process, symbol, value string) error { | ||||||
|  | 	scope, err := p.CurrentThread.Scope() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return scope.SetVariable(symbol, value) | ||||||
|  | } | ||||||
|  |  | ||||||
| const varTestBreakpointLineNumber = 59 | const varTestBreakpointLineNumber = 59 | ||||||
|  |  | ||||||
| func TestVariableEvaluation(t *testing.T) { | func TestVariableEvaluation(t *testing.T) { | ||||||
| 	testcases := []varTest{ | 	testcases := []varTest{ | ||||||
| 		{"a1", "foofoofoofoofoofoo", "struct string", nil}, | 		{"a1", "foofoofoofoofoofoo", "", "struct string", nil}, | ||||||
| 		{"a10", "ofo", "struct string", nil}, | 		{"a10", "ofo", "", "struct string", nil}, | ||||||
| 		{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil}, | 		{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "", "[3]main.FooBar", nil}, | ||||||
| 		{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil}, | 		{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "", "struct []main.FooBar", nil}, | ||||||
| 		{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil}, | 		{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "", "struct []*main.FooBar", nil}, | ||||||
| 		{"a2", "6", "int", nil}, | 		{"a2", "6", "10", "int", nil}, | ||||||
| 		{"a3", "7.23", "float64", nil}, | 		{"a3", "7.23", "3.1", "float64", nil}, | ||||||
| 		{"a4", "[2]int [1,2]", "[2]int", nil}, | 		{"a4", "[2]int [1,2]", "", "[2]int", nil}, | ||||||
| 		{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil}, | 		{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil}, | ||||||
| 		{"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil}, | 		{"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil}, | ||||||
| 		{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, | 		{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil}, | ||||||
| 		{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, | 		{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil}, | ||||||
| 		{"a9", "*main.FooBar nil", "*main.FooBar", nil}, | 		{"a9", "*main.FooBar nil", "", "*main.FooBar", nil}, | ||||||
| 		{"baz", "bazburzum", "struct string", nil}, | 		{"baz", "bazburzum", "", "struct string", nil}, | ||||||
| 		{"neg", "-1", "int", nil}, | 		{"neg", "-1", "-20", "int", nil}, | ||||||
| 		{"f32", "1.2", "float32", nil}, | 		{"f32", "1.2", "1.1", "float32", nil}, | ||||||
| 		{"c64", "(1 + 2i)", "complex64", nil}, | 		{"c64", "(1 + 2i)", "(4 + 5i)", "complex64", nil}, | ||||||
| 		{"c128", "(2 + 3i)", "complex128", nil}, | 		{"c128", "(2 + 3i)", "(6.3 + 7i)", "complex128", nil}, | ||||||
| 		{"a6.Baz", "8", "int", nil}, | 		{"a6.Baz", "8", "20", "int", nil}, | ||||||
| 		{"a7.Baz", "5", "int", nil}, | 		{"a7.Baz", "5", "25", "int", nil}, | ||||||
| 		{"a8.Baz", "feh", "struct string", nil}, | 		{"a8.Baz", "feh", "", "struct string", nil}, | ||||||
| 		{"a9.Baz", "nil", "int", fmt.Errorf("a9 is nil")}, | 		{"a9.Baz", "nil", "", "int", fmt.Errorf("a9 is nil")}, | ||||||
| 		{"a9.NonExistent", "nil", "int", fmt.Errorf("a9 has no member NonExistent")}, | 		{"a9.NonExistent", "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")}, | ||||||
| 		{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, // reread variable after member | 		{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil}, // reread variable after member | ||||||
| 		{"i32", "[2]int32 [1,2]", "[2]int32", nil}, | 		{"i32", "[2]int32 [1,2]", "", "[2]int32", nil}, | ||||||
| 		{"b1", "true", "bool", nil}, | 		{"b1", "true", "false", "bool", nil}, | ||||||
| 		{"b2", "false", "bool", nil}, {"i8", "1", "int8", nil}, | 		{"b2", "false", "true", "bool", nil}, | ||||||
| 		{"u16", "65535", "uint16", nil}, | 		{"i8", "1", "2", "int8", nil}, | ||||||
| 		{"u32", "4294967295", "uint32", nil}, | 		{"u16", "65535", "0", "uint16", nil}, | ||||||
| 		{"u64", "18446744073709551615", "uint64", nil}, | 		{"u32", "4294967295", "1", "uint32", nil}, | ||||||
| 		{"u8", "255", "uint8", nil}, | 		{"u64", "18446744073709551615", "2", "uint64", nil}, | ||||||
| 		{"up", "5", "uintptr", nil}, | 		{"u8", "255", "3", "uint8", nil}, | ||||||
| 		{"f", "main.barfoo", "func()", nil}, | 		{"up", "5", "4", "uintptr", nil}, | ||||||
| 		{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil}, | 		{"f", "main.barfoo", "", "func()", nil}, | ||||||
| 		{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil}, | 		{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil}, | ||||||
| 		{"main.p1", "10", "int", nil}, | 		{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil}, | ||||||
| 		{"p1", "10", "int", nil}, | 		{"main.p1", "10", "12", "int", nil}, | ||||||
| 		{"NonExistent", "", "", fmt.Errorf("could not find symbol value for NonExistent")}, | 		{"p1", "10", "13", "int", nil}, | ||||||
|  | 		{"NonExistent", "", "", "", fmt.Errorf("could not find symbol value for NonExistent")}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | ||||||
| @ -101,6 +121,18 @@ func TestVariableEvaluation(t *testing.T) { | |||||||
| 					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error()) | 					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error()) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			if tc.settable() { | ||||||
|  | 				assertNoError(setVariable(p, tc.name, tc.setTo), t, "SetVariable()") | ||||||
|  | 				variable, err = evalVariable(p, tc.name) | ||||||
|  | 				assertNoError(err, t, "EvalVariable()") | ||||||
|  | 				assertVariable(t, variable, tc.afterSet()) | ||||||
|  |  | ||||||
|  | 				assertNoError(setVariable(p, tc.name, tc.value), t, "SetVariable()") | ||||||
|  | 				variable, err := evalVariable(p, tc.name) | ||||||
|  | 				assertNoError(err, t, "EvalVariable()") | ||||||
|  | 				assertVariable(t, variable, tc) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @ -165,39 +197,39 @@ func TestLocalVariables(t *testing.T) { | |||||||
| 	}{ | 	}{ | ||||||
| 		{(*EvalScope).LocalVariables, | 		{(*EvalScope).LocalVariables, | ||||||
| 			[]varTest{ | 			[]varTest{ | ||||||
| 				{"a1", "foofoofoofoofoofoo", "struct string", nil}, | 				{"a1", "foofoofoofoofoofoo", "", "struct string", nil}, | ||||||
| 				{"a10", "ofo", "struct string", nil}, | 				{"a10", "ofo", "", "struct string", nil}, | ||||||
| 				{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil}, | 				{"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "", "[3]main.FooBar", nil}, | ||||||
| 				{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil}, | 				{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "", "struct []main.FooBar", nil}, | ||||||
| 				{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil}, | 				{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "", "struct []*main.FooBar", nil}, | ||||||
| 				{"a2", "6", "int", nil}, | 				{"a2", "6", "", "int", nil}, | ||||||
| 				{"a3", "7.23", "float64", nil}, | 				{"a3", "7.23", "", "float64", nil}, | ||||||
| 				{"a4", "[2]int [1,2]", "[2]int", nil}, | 				{"a4", "[2]int [1,2]", "", "[2]int", nil}, | ||||||
| 				{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil}, | 				{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil}, | ||||||
| 				{"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil}, | 				{"a6", "main.FooBar {Baz: 8, Bur: word}", "", "main.FooBar", nil}, | ||||||
| 				{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, | 				{"a7", "*main.FooBar {Baz: 5, Bur: strum}", "", "*main.FooBar", nil}, | ||||||
| 				{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, | 				{"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "", "main.FooBar2", nil}, | ||||||
| 				{"a9", "*main.FooBar nil", "*main.FooBar", nil}, | 				{"a9", "*main.FooBar nil", "", "*main.FooBar", nil}, | ||||||
| 				{"b1", "true", "bool", nil}, | 				{"b1", "true", "", "bool", nil}, | ||||||
| 				{"b2", "false", "bool", nil}, | 				{"b2", "false", "", "bool", nil}, | ||||||
| 				{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil}, | 				{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil}, | ||||||
| 				{"c128", "(2 + 3i)", "complex128", nil}, | 				{"c128", "(2 + 3i)", "", "complex128", nil}, | ||||||
| 				{"c64", "(1 + 2i)", "complex64", nil}, | 				{"c64", "(1 + 2i)", "", "complex64", nil}, | ||||||
| 				{"f", "main.barfoo", "func()", nil}, | 				{"f", "main.barfoo", "", "func()", nil}, | ||||||
| 				{"f32", "1.2", "float32", nil}, | 				{"f32", "1.2", "", "float32", nil}, | ||||||
| 				{"i32", "[2]int32 [1,2]", "[2]int32", nil}, | 				{"i32", "[2]int32 [1,2]", "", "[2]int32", nil}, | ||||||
| 				{"i8", "1", "int8", nil}, | 				{"i8", "1", "", "int8", nil}, | ||||||
| 				{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil}, | 				{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "", "main.Nest", nil}, | ||||||
| 				{"neg", "-1", "int", nil}, | 				{"neg", "-1", "", "int", nil}, | ||||||
| 				{"u16", "65535", "uint16", nil}, | 				{"u16", "65535", "", "uint16", nil}, | ||||||
| 				{"u32", "4294967295", "uint32", nil}, | 				{"u32", "4294967295", "", "uint32", nil}, | ||||||
| 				{"u64", "18446744073709551615", "uint64", nil}, | 				{"u64", "18446744073709551615", "", "uint64", nil}, | ||||||
| 				{"u8", "255", "uint8", nil}, | 				{"u8", "255", "", "uint8", nil}, | ||||||
| 				{"up", "5", "uintptr", nil}}}, | 				{"up", "5", "", "uintptr", nil}}}, | ||||||
| 		{(*EvalScope).FunctionArguments, | 		{(*EvalScope).FunctionArguments, | ||||||
| 			[]varTest{ | 			[]varTest{ | ||||||
| 				{"bar", "main.FooBar {Baz: 10, Bur: lorem}", "main.FooBar", nil}, | 				{"bar", "main.FooBar {Baz: 10, Bur: lorem}", "", "main.FooBar", nil}, | ||||||
| 				{"baz", "bazburzum", "struct string", nil}}}, | 				{"baz", "bazburzum", "", "struct string", nil}}}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | ||||||
| @ -302,3 +334,57 @@ func TestFrameEvaluation(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestComplexSetting(t *testing.T) { | ||||||
|  | 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | ||||||
|  | 		pc, _, _ := p.goSymTable.LineToPC(fixture.Source, varTestBreakpointLineNumber) | ||||||
|  |  | ||||||
|  | 		_, err := p.SetBreakpoint(pc) | ||||||
|  | 		assertNoError(err, t, "SetBreakpoint() returned an error") | ||||||
|  |  | ||||||
|  | 		err = p.Continue() | ||||||
|  | 		assertNoError(err, t, "Continue() returned an error") | ||||||
|  |  | ||||||
|  | 		h := func(setExpr, value string) { | ||||||
|  | 			assertNoError(setVariable(p, "c128", setExpr), t, "SetVariable()") | ||||||
|  | 			variable, err := evalVariable(p, "c128") | ||||||
|  | 			assertNoError(err, t, "EvalVariable()") | ||||||
|  | 			if variable.Value != value { | ||||||
|  | 				t.Fatalf("Wrong value of c128: \"%s\", expected \"%s\" after setting it to \"%s\"", variable.Value, value, setExpr) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		h("3.2i", "(0 + 3.2i)") | ||||||
|  | 		h("1.1", "(1.1 + 0i)") | ||||||
|  | 		h("1 + 3.3i", "(1 + 3.3i)") | ||||||
|  | 		h("complex128(1.2, 3.4)", "(1.2 + 3.4i)") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestPointerSetting(t *testing.T) { | ||||||
|  | 	withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) { | ||||||
|  | 		assertNoError(p.Continue(), t, "Continue() returned an error") | ||||||
|  |  | ||||||
|  | 		pval := func(value string) { | ||||||
|  | 			variable, err := evalVariable(p, "p1") | ||||||
|  | 			assertNoError(err, t, "EvalVariable()") | ||||||
|  | 			if variable.Value != value { | ||||||
|  | 				t.Fatalf("Wrong value of p1, \"%s\" expected \"%s\"", variable.Value, value) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pval("*1") | ||||||
|  |  | ||||||
|  | 		// change p1 to point to i2 | ||||||
|  | 		scope, err := p.CurrentThread.Scope() | ||||||
|  | 		assertNoError(err, t, "Scope()") | ||||||
|  | 		i2addr, err := scope.ExtractVariableInfo("i2") | ||||||
|  | 		assertNoError(err, t, "EvalVariableAddr()") | ||||||
|  | 		assertNoError(setVariable(p, "p1", strconv.Itoa(int(i2addr.Addr))), t, "SetVariable()") | ||||||
|  | 		pval("*2") | ||||||
|  |  | ||||||
|  | 		// change the value of i2 check that p1 also changes | ||||||
|  | 		assertNoError(setVariable(p, "i2", "5"), t, "SetVariable()") | ||||||
|  | 		pval("*5") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | |||||||
| @ -53,6 +53,9 @@ type Client interface { | |||||||
| 	// ListPackageVariablesFor lists all package variables in the context of a thread. | 	// ListPackageVariablesFor lists all package variables in the context of a thread. | ||||||
| 	ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) | 	ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) | ||||||
|  |  | ||||||
|  | 	// SetVariable sets the value of a variable | ||||||
|  | 	SetVariable(scope api.EvalScope, symbol, value string) error | ||||||
|  |  | ||||||
| 	// ListSources lists all source files in the process matching filter. | 	// ListSources lists all source files in the process matching filter. | ||||||
| 	ListSources(filter string) ([]string, error) | 	ListSources(filter string) ([]string, error) | ||||||
| 	// ListFunctions lists all functions in the process matching filter. | 	// ListFunctions lists all functions in the process matching filter. | ||||||
|  | |||||||
| @ -442,6 +442,14 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api | |||||||
| 	return &converted, err | 	return &converted, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error { | ||||||
|  | 	s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return s.SetVariable(symbol, value) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (d *Debugger) Goroutines() ([]*api.Goroutine, error) { | func (d *Debugger) Goroutines() ([]*api.Goroutine, error) { | ||||||
| 	goroutines := []*api.Goroutine{} | 	goroutines := []*api.Goroutine{} | ||||||
| 	gs, err := d.process.GoroutinesInfo() | 	gs, err := d.process.GoroutinesInfo() | ||||||
|  | |||||||
| @ -155,6 +155,11 @@ func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Varia | |||||||
| 	return v, err | 	return v, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { | ||||||
|  | 	var unused int | ||||||
|  | 	return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *RPCClient) ListSources(filter string) ([]string, error) { | func (c *RPCClient) ListSources(filter string) ([]string, error) { | ||||||
| 	var sources []string | 	var sources []string | ||||||
| 	err := c.call("ListSources", filter, &sources) | 	err := c.call("ListSources", filter, &sources) | ||||||
|  | |||||||
| @ -249,6 +249,17 @@ func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) erro | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type SetSymbolArgs struct { | ||||||
|  | 	Scope  api.EvalScope | ||||||
|  | 	Symbol string | ||||||
|  | 	Value  string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error { | ||||||
|  | 	*unused = 0 | ||||||
|  | 	return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *RPCServer) ListSources(filter string, sources *[]string) error { | func (s *RPCServer) ListSources(filter string, sources *[]string) error { | ||||||
| 	ss, err := s.debugger.Sources(filter) | 	ss, err := s.debugger.Sources(filter) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
| @ -629,7 +629,31 @@ func TestClientServer_EvalVariable(t *testing.T) { | |||||||
| 		t.Logf("var1: <%s>", var1.Value) | 		t.Logf("var1: <%s>", var1.Value) | ||||||
|  |  | ||||||
| 		if var1.Value != "foofoofoofoofoofoo" { | 		if var1.Value != "foofoofoofoofoofoo" { | ||||||
| 			t.Fatalf("Wrong variable value (EvalVariable)", var1.Value) | 			t.Fatalf("Wrong variable value: %v", var1.Value) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestClientServer_SetVariable(t *testing.T) { | ||||||
|  | 	withTestClient("testvariables", t, func(c service.Client) { | ||||||
|  | 		fp := testProgPath(t, "testvariables") | ||||||
|  | 		_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 59}) | ||||||
|  | 		assertNoError(err, t, "CreateBreakpoint()") | ||||||
|  |  | ||||||
|  | 		state := <-c.Continue() | ||||||
|  |  | ||||||
|  | 		if state.Err != nil { | ||||||
|  | 			t.Fatalf("Continue(): %v\n", state.Err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		assertNoError(c.SetVariable(api.EvalScope{ -1, 0 }, "a2", "8"), t, "SetVariable()") | ||||||
|  |  | ||||||
|  | 		a2, err := c.EvalVariable(api.EvalScope{ -1, 0 }, "a2") | ||||||
|  |  | ||||||
|  | 		t.Logf("a2: <%s>", a2.Value) | ||||||
|  |  | ||||||
|  | 		if a2.Value != "8" { | ||||||
|  | 			t.Fatalf("Wrong variable value: %v", a2.Value) | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | |||||||
| @ -67,6 +67,7 @@ func DebugCommands(client service.Client) *Commands { | |||||||
| 		{aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."}, | 		{aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."}, | ||||||
| 		{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."}, | 		{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."}, | ||||||
| 		{aliases: []string{"print", "p"}, cmdFn: g0f0(printVar), helpMsg: "Evaluate a variable."}, | 		{aliases: []string{"print", "p"}, cmdFn: g0f0(printVar), helpMsg: "Evaluate a variable."}, | ||||||
|  | 		{aliases: []string{"set"}, cmdFn: g0f0(setVar), helpMsg: "Changes the value of a variable."}, | ||||||
| 		{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."}, | 		{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."}, | ||||||
| 		{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."}, | 		{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."}, | ||||||
| 		{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."}, | 		{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."}, | ||||||
| @ -557,6 +558,14 @@ func printVar(client service.Client, scope api.EvalScope, args ...string) error | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func setVar(client service.Client, scope api.EvalScope, args ...string) error { | ||||||
|  | 	if len(args) != 2 { | ||||||
|  | 		return fmt.Errorf("wrong number of arguments") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return client.SetVariable(scope, args[0], args[1]) | ||||||
|  | } | ||||||
|  |  | ||||||
| func filterVariables(vars []api.Variable, filter string) []string { | func filterVariables(vars []api.Variable, filter string) []string { | ||||||
| 	reg, err := regexp.Compile(filter) | 	reg, err := regexp.Compile(filter) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli