This commit replaces scopeOp, the only remaining place that mutates *Ns in
place, with nsOp, which performs copy-on-write for *Ns.
As a result, the "eval" command no longer mutates the passed namespace. The
default namespace it uses is also changed to match the default of "-source" (an
amalgamated namespace from the local and upvalue scopes), making "-source $file"
equivalent to "eval (slurp <$file)", and formally deprecated.
Another result is that (*Evaler).Eval can now guard the mutation of the global
namespace with the mutex, making it concurrency-safe to execute multiple sources
that touch the global namespace.
This fixes#1137.
Also change the variable name used to keep the Exception returned from "err" to
"exc".
This uncovers several error scenarios that were not returning Exception, and
would result in the absense of stack traces when such errors occur.
This change is a preparation step for refining all *Op types to return Exception
as the error.
Keeping Exception as a struct type will make such a change error-prone, since
a (*Exception)(nil) != error(nil), so if an *Op returns a nil *Exception, it
is not nil if the return value is stored in an error-typed variable.
Doing something like the following is likely to result in too many open
files (assuming `ulimit -n` == 256) resulting in a panic:
> fn fact [n]{ if (== $n 0) { put 1 } else { put (* $n (fact (- $n 1))) } }
> fact 60
panic: interface conversion: error is *os.SyscallError, not
*eval.Exception
goroutine 24161 [running]:
github.com/elves/elvish/pkg/eval.(*pipelineOp).exec.func1(0x152a5a0,
0xc000dca210, 0xc000a44e70, 0xc00057b540, 0xc001030590, 0x203000)
github.com/elves/elvish/pkg/eval/compile_effect.go:153 +0x152
created by github.com/elves/elvish/pkg/eval.(*pipelineOp).exec
github.com/elves/elvish/pkg/eval/compile_effect.go:149 +0x225
Exception: elvish exited with 2
Fixes#1208
- Move NewEnvListVar to pkg/eval/vars.
- De-export GlobPattern, GlobFlag and ExternalCmd.
- Merge editor.go and chdir.go into eval.go, value_helper.go into compile_value.go.
- Remove eval_internal_test.go and replace it with a new test for $pid.
- Make Evaler mostly thread-safe. The only remaining thread-unsafe part is the
modules field, which is more tricky than other fields.
- Remove the state and evalerScopes type, and move their fields into Evaler.
- Expose valuePrefix via a get method, and change PortsFromFiles to take the
prefix instead of a *Evaler. Also expose a PortsFromStdFiles.
- Make Evaler a normal field of Frame, instead of an embedded field. This makes
access to global states more explicit.
- Remove reliance on scopeOp, concentrating all the scope for creating the local
scope in (*closure).call.
- Check options at the very beginning, and include all unsupported options in the
error.
Commit 734eb95 changed most uses of `fm.ports[x].` to a public method
that makes the intent clearer. This changes a couple of uses that were
overlooked by that prior change.
I stumbled across a comment that began with "XXX". It was clearly meant as
a "TODO" comment. This changes all such occurrences. However, a few "XXX"
comments are ambiguous and a better prefix might be "WARNING". The "TODO"
prefix at least ensures someone, eventually, looks into the situation
and either rewords the comment or fixes the problem. This change means
everyone can assume searching for "// TODO" will find all such comments
rather than requiring they also know to search for "// XXX".
Commit 8c71635ca3 moved the creation to the start
of pipelines; the approach works for simplistic cases like "x = 1 | nop", but
fails in more complex cases, such as "nop (x = 1) | nop".
The correct place to hoist variable creations is the lexical scope, and this
commit implements this approach.
This fixes#623.
This avoids race conditions of accessing the local scope. The test case
"x = 1", "put $x | y = (all)" used to contain a race condition but no longer
does.
This addresses #73.