Skip to content

Commit 698f838

Browse files
authored
terminal/starbind: allow modification of structs returned by API (#3872)
Structs returned to starlark scripts by API calls were immutable, this made amend_breakpoint nearly impossible to use since its argument must be a api.Breakpoint struct which the caller has received from get_breakpoint and modified.
1 parent d97b471 commit 698f838

File tree

5 files changed

+127
-54
lines changed

5 files changed

+127
-54
lines changed

_fixtures/amend_breakpoint.star

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bp = get_breakpoint(0, "afuncbreak").Breakpoint
2+
bp.Stacktrace = 2
3+
bp.HitCond = "== 2"
4+
amend_breakpoint(bp)
5+
bp2 = get_breakpoint(0, "afuncbreak").Breakpoint
6+
print(bp)

_scripts/gen-starlark-bindings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func genMapping(bindings []binding) []byte {
226226

227227
fmt.Fprintf(buf, "err := env.ctx.Client().CallAPI(%q, &rpcArgs, &rpcRet)\n", binding.fn.Name())
228228
fmt.Fprintf(buf, "if err != nil { return starlark.None, err }\n")
229-
fmt.Fprintf(buf, "return env.interfaceToStarlarkValue(rpcRet), nil\n")
229+
fmt.Fprintf(buf, "return env.interfaceToStarlarkValue(&rpcRet), nil\n")
230230

231231
fmt.Fprintf(buf, "})\n")
232232

pkg/terminal/starbind/conv.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ func (env *Env) interfaceToStarlarkValue(v interface{}) starlark.Value {
5757
return starlark.None
5858
case error:
5959
return starlark.String(v.Error())
60+
case reflect.Value:
61+
if v.Type().Kind() == reflect.Struct {
62+
return structAsStarlarkValue{v, env}
63+
}
64+
return env.interfaceToStarlarkValue(v.Interface())
6065
default:
6166
vval := reflect.ValueOf(v)
6267
switch vval.Type().Kind() {
@@ -185,9 +190,48 @@ func (v structAsStarlarkValue) Attr(name string) (starlark.Value, error) {
185190
}
186191
r := v.v.FieldByName(name)
187192
if !r.IsValid() {
188-
return starlark.None, fmt.Errorf("no field named %q in %T", name, v.v.Interface())
193+
return starlark.None, starlark.NoSuchAttrError(fmt.Sprintf("no field named %q in %T", name, v.v.Interface()))
189194
}
190-
return v.env.interfaceToStarlarkValue(r.Interface()), nil
195+
return v.env.interfaceToStarlarkValue(r), nil
196+
}
197+
198+
func (v structAsStarlarkValue) SetField(name string, value starlark.Value) (err error) {
199+
defer func() {
200+
// reflect.Value.SetInt, SetFloat, etc panic
201+
ierr := recover()
202+
if ierr == nil {
203+
return
204+
}
205+
err, _ = ierr.(error)
206+
if err == nil {
207+
panic(ierr)
208+
}
209+
err = fmt.Errorf("can not assign to %T.%q: %v", v.v.Interface(), name, err)
210+
}()
211+
if r, err := v.valueAttr(name); err != nil || r != nil {
212+
return starlark.NoSuchAttrError(fmt.Sprintf("no field named %s in %T", name, v.v.Interface()))
213+
}
214+
r := v.v.FieldByName(name)
215+
if !r.IsValid() {
216+
return starlark.NoSuchAttrError(fmt.Sprintf("no field named %q in %T", name, v.v.Interface()))
217+
}
218+
switch value := value.(type) {
219+
case starlark.Int:
220+
n, ok := value.Int64()
221+
if !ok {
222+
return fmt.Errorf("can not assign big integer to %T.%q", v.v.Interface(), name)
223+
}
224+
r.SetInt(n)
225+
case starlark.Float:
226+
r.SetFloat(float64(value))
227+
case starlark.String:
228+
r.SetString(value.GoString())
229+
case starlark.Bool:
230+
r.SetBool(bool(value))
231+
default:
232+
return fmt.Errorf("can not assign value of type %T to %T.%q", value, v.v.Interface(), name)
233+
}
234+
return nil
191235
}
192236

193237
func (v structAsStarlarkValue) valueAttr(name string) (starlark.Value, error) {

0 commit comments

Comments
 (0)