terminal: adds embedded scripting language (#1466)

If the argument of 'source' ends in '.star' it will be interpreted as a
starlark script.
If the argument of 'source' is '-' an interactive starlark repl will be
started.

For documentation on how the starlark execution environment works see
Documentation/cli/starlark.md.

The starlark API is autogenerated from the JSON-RPC API by
script/gen-starlark-bindings.go.
In general for each JSON-RPC API a single global starlark function is
created.
When one of those functions is called (through a starlark script) the
arguments are converted to go structs using reflection. See
unmarshalStarlarkValue in pkg/terminal/starbind/conv.go.
If there are no type conversion errors the JSON-RPC call is executed.
The return value of the JSON-RPC call is converted back into a starlark
value by interfaceToStarlarkValue (same file):

* primitive types (such as integers, floats or strings) are converted
  by creating the corresponding starlark value.
* compound types (such as structs and slices) are converted by wrapping
  their reflect.Value object into a type that implements the relevant
  starlark interfaces.
* api.Variables are treated specially so that their Value field can be
  of the proper type instead of always being a string.

Implements #1415, #1443
This commit is contained in:
Alessandro Arzilli
2019-07-02 19:55:27 +02:00
committed by Derek Parker
parent 116b9631dc
commit ed35dce7a3
43 changed files with 15947 additions and 6 deletions

View File

@ -17,6 +17,7 @@ import (
"github.com/go-delve/delve/pkg/config"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc/test"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
@ -29,12 +30,15 @@ var testBackend, buildMode string
func TestMain(m *testing.M) {
flag.StringVar(&testBackend, "backend", "", "selects backend")
flag.StringVar(&buildMode, "test-buildmode", "", "selects build mode")
var logConf string
flag.StringVar(&logConf, "log", "", "configures logging")
flag.Parse()
test.DefaultTestBackend(&testBackend)
if buildMode != "" && buildMode != "pie" {
fmt.Fprintf(os.Stderr, "unknown build mode %q", buildMode)
os.Exit(1)
}
logflags.Setup(logConf != "", logConf, "")
os.Exit(test.RunTestsWithFixtures(m))
}
@ -70,14 +74,49 @@ func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
return
}
func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
outfh, err := ioutil.TempFile("", "cmdtestout")
if err != nil {
ft.t.Fatalf("could not create temporary file: %v", err)
}
stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
defer func() {
os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
outfh.Close()
outbs, err1 := ioutil.ReadFile(outfh.Name())
if err1 != nil {
ft.t.Fatalf("could not read temporary output file: %v", err)
}
outstr = string(outbs)
if logCommandOutput {
ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
}
os.Remove(outfh.Name())
}()
_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
return
}
func (ft *FakeTerminal) MustExec(cmdstr string) string {
outstr, err := ft.Exec(cmdstr)
if err != nil {
ft.t.Errorf("output of %q: %q", cmdstr, outstr)
ft.t.Fatalf("Error executing <%s>: %v", cmdstr, err)
}
return outstr
}
func (ft *FakeTerminal) MustExecStarlark(starlarkProgram string) string {
outstr, err := ft.ExecStarlark(starlarkProgram)
if err != nil {
ft.t.Errorf("output of %q: %q", starlarkProgram, outstr)
ft.t.Fatalf("Error executing <%s>: %v", starlarkProgram, err)
}
return outstr
}
func (ft *FakeTerminal) AssertExec(cmdstr, tgt string) {
out := ft.MustExec(cmdstr)
if out != tgt {
@ -870,3 +909,7 @@ func TestIssue1493(t *testing.T) {
}
})
}
func findStarFile(name string) string {
return filepath.Join(test.FindFixturesDir(), name+".star")
}