eval: Remove default ports from Evaler.

The ports are now made anew each time EvalWithStdPorts is called. This
fixes #571.
This commit is contained in:
Qi Xiao 2018-02-28 21:35:32 -05:00
parent 802f5ab31e
commit 5a714929e8
5 changed files with 35 additions and 34 deletions

View File

@ -35,15 +35,15 @@ const (
)
const (
defaultValueOutIndicator = "▶ "
initIndent = vals.NoPretty
defaultValuePrefix = "▶ "
initIndent = vals.NoPretty
)
// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
evalerScopes
evalerPorts
valuePrefix string
DaemonClient *daemon.Client
modules map[string]Ns
// bundled modules
@ -60,18 +60,14 @@ type evalerScopes struct {
// NewEvaler creates a new Evaler.
func NewEvaler() *Evaler {
valueOutIndicator := defaultValueOutIndicator
builtin := builtinNs.Clone()
builtin["value-out-indicator"] = vars.NewFromPtr(&valueOutIndicator)
ev := &Evaler{
valuePrefix: defaultValuePrefix,
evalerScopes: evalerScopes{
Global: make(Ns),
Builtin: builtinNs,
},
evalerPorts: newEvalerPorts(
os.Stdin, os.Stdout, os.Stderr, &valueOutIndicator),
modules: map[string]Ns{
"builtin": builtin,
},
@ -80,12 +76,15 @@ func NewEvaler() *Evaler {
intCh: nil,
}
builtin["value-out-indicator"] = vars.NewFromPtr(&ev.valuePrefix)
return ev
}
// Close releases resources allocated when creating this Evaler.
func (ev *Evaler) Close() {
ev.evalerPorts.close()
// Close releases resources allocated when creating this Evaler. Currently this
// does nothing and always returns a nil error.
func (ev *Evaler) Close() error {
return nil
}
// InstallDaemonClient installs a daemon client to the Evaler.
@ -142,15 +141,17 @@ func (ev *Evaler) eval(op Op, ports []*Port, src *Source) error {
return ec.Eval(op)
}
// Eval sets up the Evaler with standard ports and evaluates an Op. The supplied
// name and text are used in diagnostic messages.
func (ev *Evaler) Eval(op Op, src *Source) error {
return ev.EvalWithPorts(ev.ports[:], op, src)
// EvalWithStdPorts sets up the Evaler with standard ports and evaluates an Op.
// The supplied name and text are used in diagnostic messages.
func (ev *Evaler) EvalWithStdPorts(op Op, src *Source) error {
stdPorts := newStdPorts(os.Stdin, os.Stdout, os.Stderr, ev.valuePrefix)
defer stdPorts.close()
return ev.Eval(op, stdPorts.ports[:], src)
}
// EvalWithPorts sets up the Evaler with the given ports and evaluates an Op.
// Eval sets up the Evaler with the given ports and evaluates an Op.
// The supplied name and text are used in diagnostic messages.
func (ev *Evaler) EvalWithPorts(ports []*Port, op Op, src *Source) error {
func (ev *Evaler) Eval(op Op, ports []*Port, src *Source) error {
// Ignore TTOU.
//
// When a subprocess in its own process group puts itself in the foreground,
@ -223,7 +224,7 @@ func (ev *Evaler) SourceText(src *Source) error {
if err != nil {
return err
}
return ev.Eval(op, src)
return ev.EvalWithStdPorts(op, src)
}
func readFileUTF8(fname string) (string, error) {

View File

@ -12,41 +12,41 @@ const (
stderrChanSize = 32
)
type evalerPorts struct {
ports [3]*Port
relayeWait *sync.WaitGroup
type stdPorts struct {
ports [3]*Port
relayerWait *sync.WaitGroup
}
func newEvalerPorts(stdin, stdout, stderr *os.File, prefix *string) evalerPorts {
func newStdPorts(stdin, stdout, stderr *os.File, prefix string) stdPorts {
stdoutChan := make(chan interface{}, stdoutChanSize)
stderrChan := make(chan interface{}, stderrChanSize)
var relayerWait sync.WaitGroup
relayerWait := new(sync.WaitGroup)
relayerWait.Add(2)
go relayChanToFile(stdoutChan, stdout, prefix, &relayerWait)
go relayChanToFile(stderrChan, stderr, prefix, &relayerWait)
go relayChanToFile(stdoutChan, stdout, prefix, relayerWait)
go relayChanToFile(stderrChan, stderr, prefix, relayerWait)
return evalerPorts{
return stdPorts{
[3]*Port{
{File: stdin, Chan: ClosedChan},
{File: stdout, Chan: stdoutChan, CloseChan: true},
{File: stderr, Chan: stderrChan, CloseChan: true},
},
&relayerWait,
relayerWait,
}
}
func relayChanToFile(ch <-chan interface{}, file *os.File, prefix *string, w *sync.WaitGroup) {
func relayChanToFile(ch <-chan interface{}, file *os.File, prefix string, w *sync.WaitGroup) {
for v := range ch {
file.WriteString(*prefix)
file.WriteString(prefix)
file.WriteString(vals.Repr(v, initIndent))
file.WriteString("\n")
}
w.Done()
}
func (ep *evalerPorts) close() {
func (ep *stdPorts) close() {
ep.ports[1].Close()
ep.ports[2].Close()
ep.relayeWait.Wait()
ep.relayerWait.Wait()
}

View File

@ -15,7 +15,7 @@ func TestEvalerPorts(t *testing.T) {
defer stderrReader.Close()
prefix := "> "
ep := newEvalerPorts(DevNull, stdout, stderr, &prefix)
ep := newStdPorts(DevNull, stdout, stderr, prefix)
ep.ports[1].Chan <- "x"
ep.ports[1].Chan <- "y"
ep.ports[2].Chan <- "bad"

View File

@ -49,7 +49,7 @@ func script(ev *eval.Evaler, args []string, cmd, compileOnly bool) error {
return nil
}
return ev.Eval(op, src)
return ev.EvalWithStdPorts(op, src)
}
var errSourceNotUTF8 = errors.New("source is not UTF-8")

View File

@ -117,7 +117,7 @@ func evalAndCollect(ev *eval.Evaler, code string) (
{File: outFile, Chan: outChan},
{File: errFile, Chan: eval.BlackholeChan},
}
err = ev.EvalWithPorts(ports, op, src)
err = ev.Eval(op, ports, src)
outFile.Close()
close(outChan)