refactor(tvix/eval): Builderize Evaluation
Make constructing of a new Evaluation use the builder pattern rather than setting public mutable fields. This is currently a pure refactor (no functionality has changed) but has a few advantages: - We've encapsulated the internals of the fields in Evaluation, meaning we can change them without too much breakage of clients - We have type safety that prevents us from ever changing the fields of an Evaluation after it's built (which matters more in a world where we reuse Evaluations). More importantly, this paves the road for doing different things with the construction of an Evaluation - notably, sharing certain things like the GlobalsMap across subsequent evaluations in eg the REPL. Fixes: b/262 Change-Id: I4a27116faac14cdd144fc7c992d14ae095a1aca4 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11956 Tested-by: BuildkiteCI Autosubmit: aspen <root@gws.fyi> Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
parent
d5964c1d54
commit
dfe137786c
15 changed files with 325 additions and 154 deletions
|
@ -167,26 +167,25 @@ fn evaluate(
|
||||||
span.pb_set_style(&tvix_tracing::PB_SPINNER_STYLE);
|
span.pb_set_style(&tvix_tracing::PB_SPINNER_STYLE);
|
||||||
span.pb_set_message("Setting up evaluator…");
|
span.pb_set_message("Setting up evaluator…");
|
||||||
|
|
||||||
let mut eval = tvix_eval::Evaluation::new(
|
let mut eval_builder = tvix_eval::Evaluation::builder(Box::new(TvixIO::new(
|
||||||
Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
|
tvix_store_io.clone() as Rc<dyn EvalIO>,
|
||||||
true,
|
)) as Box<dyn EvalIO>)
|
||||||
);
|
.enable_import()
|
||||||
eval.strict = args.strict;
|
.with_strict(args.strict)
|
||||||
eval.builtins.extend(impure_builtins());
|
.add_builtins(impure_builtins())
|
||||||
if let Some(env) = env {
|
.env(env);
|
||||||
eval.env = Some(env);
|
|
||||||
}
|
|
||||||
add_derivation_builtins(&mut eval, Rc::clone(&tvix_store_io));
|
|
||||||
add_fetcher_builtins(&mut eval, Rc::clone(&tvix_store_io));
|
|
||||||
add_import_builtins(&mut eval, tvix_store_io);
|
|
||||||
configure_nix_path(&mut eval, &args.nix_search_path);
|
|
||||||
|
|
||||||
let source_map = eval.source_map();
|
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
|
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
|
eval_builder = add_import_builtins(eval_builder, tvix_store_io);
|
||||||
|
eval_builder = configure_nix_path(eval_builder, &args.nix_search_path);
|
||||||
|
|
||||||
|
let source_map = eval_builder.source_map().clone();
|
||||||
let result = {
|
let result = {
|
||||||
let mut compiler_observer =
|
let mut compiler_observer =
|
||||||
DisassemblingObserver::new(source_map.clone(), std::io::stderr());
|
DisassemblingObserver::new(source_map.clone(), std::io::stderr());
|
||||||
if args.dump_bytecode {
|
if args.dump_bytecode {
|
||||||
eval.compiler_observer = Some(&mut compiler_observer);
|
eval_builder.set_compiler_observer(Some(&mut compiler_observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut runtime_observer = TracingObserver::new(std::io::stderr());
|
let mut runtime_observer = TracingObserver::new(std::io::stderr());
|
||||||
|
@ -194,10 +193,12 @@ fn evaluate(
|
||||||
if args.trace_runtime_timing {
|
if args.trace_runtime_timing {
|
||||||
runtime_observer.enable_timing()
|
runtime_observer.enable_timing()
|
||||||
}
|
}
|
||||||
eval.runtime_observer = Some(&mut runtime_observer);
|
eval_builder.set_runtime_observer(Some(&mut runtime_observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
span.pb_set_message("Evaluating…");
|
span.pb_set_message("Evaluating…");
|
||||||
|
|
||||||
|
let eval = eval_builder.build();
|
||||||
eval.evaluate(code, path)
|
eval.evaluate(code, path)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -262,21 +263,21 @@ fn interpret(
|
||||||
/// Interpret the given code snippet, but only run the Tvix compiler
|
/// Interpret the given code snippet, but only run the Tvix compiler
|
||||||
/// on it and return errors and warnings.
|
/// on it and return errors and warnings.
|
||||||
fn lint(code: &str, path: Option<PathBuf>, args: &Args) -> bool {
|
fn lint(code: &str, path: Option<PathBuf>, args: &Args) -> bool {
|
||||||
let mut eval = tvix_eval::Evaluation::new_impure();
|
let mut eval_builder = tvix_eval::Evaluation::builder_impure().with_strict(args.strict);
|
||||||
eval.strict = args.strict;
|
|
||||||
|
|
||||||
let source_map = eval.source_map();
|
let source_map = eval_builder.source_map().clone();
|
||||||
|
|
||||||
let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), std::io::stderr());
|
let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), std::io::stderr());
|
||||||
|
|
||||||
if args.dump_bytecode {
|
if args.dump_bytecode {
|
||||||
eval.compiler_observer = Some(&mut compiler_observer);
|
eval_builder.set_compiler_observer(Some(&mut compiler_observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.trace_runtime {
|
if args.trace_runtime {
|
||||||
eprintln!("warning: --trace-runtime has no effect with --compile-only!");
|
eprintln!("warning: --trace-runtime has no effect with --compile-only!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let eval = eval_builder.build();
|
||||||
let result = eval.compile_only(code, path);
|
let result = eval.compile_only(code, path);
|
||||||
|
|
||||||
if args.display_ast {
|
if args.display_ast {
|
||||||
|
|
|
@ -8,7 +8,9 @@ use tikv_jemallocator::Jemalloc;
|
||||||
static GLOBAL: Jemalloc = Jemalloc;
|
static GLOBAL: Jemalloc = Jemalloc;
|
||||||
|
|
||||||
fn interpret(code: &str) {
|
fn interpret(code: &str) {
|
||||||
tvix_eval::Evaluation::new_pure().evaluate(code, None);
|
tvix_eval::Evaluation::builder_pure()
|
||||||
|
.build()
|
||||||
|
.evaluate(code, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_literals(c: &mut Criterion) {
|
fn eval_literals(c: &mut Criterion) {
|
||||||
|
|
|
@ -64,6 +64,192 @@ pub use crate::value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Valu
|
||||||
#[cfg(feature = "impure")]
|
#[cfg(feature = "impure")]
|
||||||
pub use crate::io::StdIO;
|
pub use crate::io::StdIO;
|
||||||
|
|
||||||
|
/// Builder for building an [`Evaluation`].
|
||||||
|
///
|
||||||
|
/// Construct an [`EvaluationBuilder`] by calling one of:
|
||||||
|
///
|
||||||
|
/// - [`Evaluation::builder`] / [`EvaluationBuilder::new`]
|
||||||
|
/// - [`Evaluation::builder_impure`] [`EvaluationBuilder::new_impure`]
|
||||||
|
/// - [`Evaluation::builder_pure`] [`EvaluationBuilder::new_pure`]
|
||||||
|
///
|
||||||
|
/// Then configure the fields by calling the various methods on [`EvaluationBuilder`], and finally
|
||||||
|
/// call [`build`](Self::build) to construct an [`Evaluation`]
|
||||||
|
pub struct EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
|
source_map: SourceCode,
|
||||||
|
builtins: Vec<(&'static str, Value)>,
|
||||||
|
src_builtins: Vec<(&'static str, &'static str)>,
|
||||||
|
env: Option<&'env HashMap<SmolStr, Value>>,
|
||||||
|
io_handle: IO,
|
||||||
|
enable_import: bool,
|
||||||
|
strict: bool,
|
||||||
|
nix_path: Option<String>,
|
||||||
|
compiler_observer: Option<&'co mut dyn CompilerObserver>,
|
||||||
|
runtime_observer: Option<&'ro mut dyn RuntimeObserver>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(aspen): The methods here are intentionally incomplete; feel free to add new ones (ideally
|
||||||
|
// with similar naming conventions to the ones already present) but don't expose fields publically!
|
||||||
|
impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
|
pub fn new(io_handle: IO) -> Self {
|
||||||
|
let mut builtins = builtins::pure_builtins();
|
||||||
|
builtins.extend(builtins::placeholders()); // these are temporary
|
||||||
|
|
||||||
|
Self {
|
||||||
|
source_map: SourceCode::default(),
|
||||||
|
enable_import: false,
|
||||||
|
io_handle,
|
||||||
|
builtins,
|
||||||
|
src_builtins: vec![],
|
||||||
|
env: None,
|
||||||
|
strict: false,
|
||||||
|
nix_path: None,
|
||||||
|
compiler_observer: None,
|
||||||
|
runtime_observer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Evaluation<'co, 'ro, 'env, IO> {
|
||||||
|
Evaluation {
|
||||||
|
source_map: self.source_map,
|
||||||
|
builtins: self.builtins,
|
||||||
|
src_builtins: self.src_builtins,
|
||||||
|
env: self.env,
|
||||||
|
io_handle: self.io_handle,
|
||||||
|
enable_import: self.enable_import,
|
||||||
|
strict: self.strict,
|
||||||
|
nix_path: self.nix_path,
|
||||||
|
compiler_observer: self.compiler_observer,
|
||||||
|
runtime_observer: self.runtime_observer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn io_handle<IO2>(self, io_handle: IO2) -> EvaluationBuilder<'co, 'ro, 'env, IO2> {
|
||||||
|
EvaluationBuilder {
|
||||||
|
io_handle,
|
||||||
|
source_map: self.source_map,
|
||||||
|
builtins: self.builtins,
|
||||||
|
src_builtins: self.src_builtins,
|
||||||
|
env: self.env,
|
||||||
|
enable_import: self.enable_import,
|
||||||
|
strict: self.strict,
|
||||||
|
nix_path: self.nix_path,
|
||||||
|
compiler_observer: self.compiler_observer,
|
||||||
|
runtime_observer: self.runtime_observer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_enable_import(self, enable_import: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
enable_import,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_import(self) -> Self {
|
||||||
|
self.with_enable_import(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_import(self) -> Self {
|
||||||
|
self.with_enable_import(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_builtins<I>(mut self, builtins: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (&'static str, Value)>,
|
||||||
|
{
|
||||||
|
self.builtins.extend(builtins);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_strict(self, strict: bool) -> Self {
|
||||||
|
Self { strict, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn strict(self) -> Self {
|
||||||
|
self.with_strict(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_src_builtin(mut self, name: &'static str, src: &'static str) -> Self {
|
||||||
|
self.src_builtins.push((name, src));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nix_path(self, nix_path: Option<String>) -> Self {
|
||||||
|
Self { nix_path, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env(self, env: Option<&'env HashMap<SmolStr, Value>>) -> Self {
|
||||||
|
Self { env, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compiler_observer(
|
||||||
|
self,
|
||||||
|
compiler_observer: Option<&'co mut dyn CompilerObserver>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
compiler_observer,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_compiler_observer(
|
||||||
|
&mut self,
|
||||||
|
compiler_observer: Option<&'co mut dyn CompilerObserver>,
|
||||||
|
) {
|
||||||
|
self.compiler_observer = compiler_observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_observer(self, runtime_observer: Option<&'ro mut dyn RuntimeObserver>) -> Self {
|
||||||
|
Self {
|
||||||
|
runtime_observer,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_runtime_observer(&mut self, runtime_observer: Option<&'ro mut dyn RuntimeObserver>) {
|
||||||
|
self.runtime_observer = runtime_observer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
|
pub fn source_map(&self) -> &SourceCode {
|
||||||
|
&self.source_map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'co, 'ro, 'env> EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>> {
|
||||||
|
/// Initialize an `Evaluation`, without the import statement available, and
|
||||||
|
/// all IO operations stubbed out.
|
||||||
|
pub fn new_pure() -> Self {
|
||||||
|
Self::new(Box::new(DummyIO) as Box<dyn EvalIO>).with_enable_import(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "impure")]
|
||||||
|
/// Configure an `Evaluation` to have impure features available
|
||||||
|
/// with the given I/O implementation.
|
||||||
|
///
|
||||||
|
/// If no I/O implementation is supplied, [`StdIO`] is used by
|
||||||
|
/// default.
|
||||||
|
pub fn enable_impure(mut self, io: Option<Box<dyn EvalIO>>) -> Self {
|
||||||
|
self.io_handle = io.unwrap_or_else(|| Box::new(StdIO) as Box<dyn EvalIO>);
|
||||||
|
self.enable_import = true;
|
||||||
|
self.builtins.extend(builtins::impure_builtins());
|
||||||
|
|
||||||
|
// Make `NIX_PATH` resolutions work by default, unless the
|
||||||
|
// user already overrode this with something else.
|
||||||
|
if self.nix_path.is_none() {
|
||||||
|
self.nix_path = std::env::var("NIX_PATH").ok();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "impure")]
|
||||||
|
/// Initialise an `Evaluation`, with all impure features turned on by default.
|
||||||
|
pub fn new_impure() -> Self {
|
||||||
|
Self::new_pure().enable_impure(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An `Evaluation` represents how a piece of Nix code is evaluated. It can be
|
/// An `Evaluation` represents how a piece of Nix code is evaluated. It can be
|
||||||
/// instantiated and configured directly, or it can be accessed through the
|
/// instantiated and configured directly, or it can be accessed through the
|
||||||
/// various simplified helper methods available below.
|
/// various simplified helper methods available below.
|
||||||
|
@ -79,42 +265,42 @@ pub struct Evaluation<'co, 'ro, 'env, IO> {
|
||||||
///
|
///
|
||||||
/// This defaults to all pure builtins. Users might want to add
|
/// This defaults to all pure builtins. Users might want to add
|
||||||
/// the set of impure builtins, or other custom builtins.
|
/// the set of impure builtins, or other custom builtins.
|
||||||
pub builtins: Vec<(&'static str, Value)>,
|
builtins: Vec<(&'static str, Value)>,
|
||||||
|
|
||||||
/// Set of builtins that are implemented in Nix itself and should
|
/// Set of builtins that are implemented in Nix itself and should
|
||||||
/// be compiled and inserted in the builtins set.
|
/// be compiled and inserted in the builtins set.
|
||||||
pub src_builtins: Vec<(&'static str, &'static str)>,
|
src_builtins: Vec<(&'static str, &'static str)>,
|
||||||
|
|
||||||
/// Top-level variables to define in the evaluation
|
/// Top-level variables to define in the evaluation
|
||||||
pub env: Option<&'env HashMap<SmolStr, Value>>,
|
env: Option<&'env HashMap<SmolStr, Value>>,
|
||||||
|
|
||||||
/// Implementation of file-IO to use during evaluation, e.g. for
|
/// Implementation of file-IO to use during evaluation, e.g. for
|
||||||
/// impure builtins.
|
/// impure builtins.
|
||||||
///
|
///
|
||||||
/// Defaults to [`DummyIO`] if not set explicitly.
|
/// Defaults to [`DummyIO`] if not set explicitly.
|
||||||
pub io_handle: IO,
|
io_handle: IO,
|
||||||
|
|
||||||
/// Determines whether the `import` builtin should be made
|
/// Determines whether the `import` builtin should be made
|
||||||
/// available. Note that this depends on the `io_handle` being
|
/// available. Note that this depends on the `io_handle` being
|
||||||
/// able to read the files specified as arguments to `import`.
|
/// able to read the files specified as arguments to `import`.
|
||||||
pub enable_import: bool,
|
enable_import: bool,
|
||||||
|
|
||||||
/// Determines whether the returned value should be strictly
|
/// Determines whether the returned value should be strictly
|
||||||
/// evaluated, that is whether its list and attribute set elements
|
/// evaluated, that is whether its list and attribute set elements
|
||||||
/// should be forced recursively.
|
/// should be forced recursively.
|
||||||
pub strict: bool,
|
strict: bool,
|
||||||
|
|
||||||
/// (optional) Nix search path, e.g. the value of `NIX_PATH` used
|
/// (optional) Nix search path, e.g. the value of `NIX_PATH` used
|
||||||
/// for resolving items on the search path (such as `<nixpkgs>`).
|
/// for resolving items on the search path (such as `<nixpkgs>`).
|
||||||
pub nix_path: Option<String>,
|
nix_path: Option<String>,
|
||||||
|
|
||||||
/// (optional) compiler observer for reporting on compilation
|
/// (optional) compiler observer for reporting on compilation
|
||||||
/// details, like the emitted bytecode.
|
/// details, like the emitted bytecode.
|
||||||
pub compiler_observer: Option<&'co mut dyn CompilerObserver>,
|
compiler_observer: Option<&'co mut dyn CompilerObserver>,
|
||||||
|
|
||||||
/// (optional) runtime observer, for reporting on execution steps
|
/// (optional) runtime observer, for reporting on execution steps
|
||||||
/// of Nix code.
|
/// of Nix code.
|
||||||
pub runtime_observer: Option<&'ro mut dyn RuntimeObserver>,
|
runtime_observer: Option<&'ro mut dyn RuntimeObserver>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of evaluating a piece of Nix code. If evaluation succeeded, a value
|
/// Result of evaluating a piece of Nix code. If evaluation succeeded, a value
|
||||||
|
@ -136,61 +322,20 @@ pub struct EvaluationResult {
|
||||||
pub expr: Option<rnix::ast::Expr>,
|
pub expr: Option<rnix::ast::Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'co, 'ro, 'env, IO> Evaluation<'co, 'ro, 'env, IO>
|
impl<'co, 'ro, 'env, IO> Evaluation<'co, 'ro, 'env, IO> {
|
||||||
where
|
pub fn builder(io_handle: IO) -> EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
IO: AsRef<dyn EvalIO> + 'static,
|
EvaluationBuilder::new(io_handle)
|
||||||
{
|
|
||||||
/// Initialize an `Evaluation`.
|
|
||||||
pub fn new(io_handle: IO, enable_import: bool) -> Self {
|
|
||||||
let mut builtins = builtins::pure_builtins();
|
|
||||||
builtins.extend(builtins::placeholders()); // these are temporary
|
|
||||||
|
|
||||||
Self {
|
|
||||||
source_map: SourceCode::default(),
|
|
||||||
enable_import,
|
|
||||||
io_handle,
|
|
||||||
builtins,
|
|
||||||
src_builtins: vec![],
|
|
||||||
env: None,
|
|
||||||
strict: false,
|
|
||||||
nix_path: None,
|
|
||||||
compiler_observer: None,
|
|
||||||
runtime_observer: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'co, 'ro, 'env> Evaluation<'co, 'ro, 'env, Box<dyn EvalIO>> {
|
impl<'co, 'ro, 'env> Evaluation<'co, 'ro, 'env, Box<dyn EvalIO>> {
|
||||||
/// Initialize an `Evaluation`, without the import statement available, and
|
#[cfg(feature = "impure")]
|
||||||
/// all IO operations stubbed out.
|
pub fn builder_impure() -> EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>> {
|
||||||
pub fn new_pure() -> Self {
|
EvaluationBuilder::new_impure()
|
||||||
Self::new(Box::new(DummyIO) as Box<dyn EvalIO>, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "impure")]
|
pub fn builder_pure() -> EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>> {
|
||||||
/// Configure an `Evaluation` to have impure features available
|
EvaluationBuilder::new_pure()
|
||||||
/// with the given I/O implementation.
|
|
||||||
///
|
|
||||||
/// If no I/O implementation is supplied, [`StdIO`] is used by
|
|
||||||
/// default.
|
|
||||||
pub fn enable_impure(&mut self, io: Option<Box<dyn EvalIO>>) {
|
|
||||||
self.io_handle = io.unwrap_or_else(|| Box::new(StdIO) as Box<dyn EvalIO>);
|
|
||||||
self.enable_import = true;
|
|
||||||
self.builtins.extend(builtins::impure_builtins());
|
|
||||||
|
|
||||||
// Make `NIX_PATH` resolutions work by default, unless the
|
|
||||||
// user already overrode this with something else.
|
|
||||||
if self.nix_path.is_none() {
|
|
||||||
self.nix_path = std::env::var("NIX_PATH").ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "impure")]
|
|
||||||
/// Initialise an `Evaluation`, with all impure features turned on by default.
|
|
||||||
pub fn new_impure() -> Self {
|
|
||||||
let mut eval = Self::new_pure();
|
|
||||||
eval.enable_impure(None);
|
|
||||||
eval
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,10 @@ fn eval_test(code_path: PathBuf, expect_success: bool) {
|
||||||
|
|
||||||
let code = std::fs::read_to_string(&code_path).expect("should be able to read test code");
|
let code = std::fs::read_to_string(&code_path).expect("should be able to read test code");
|
||||||
|
|
||||||
let mut eval = crate::Evaluation::new_impure();
|
let eval = crate::Evaluation::builder_impure()
|
||||||
eval.strict = true;
|
.strict()
|
||||||
eval.builtins.extend(mock_builtins::builtins());
|
.add_builtins(mock_builtins::builtins())
|
||||||
|
.build();
|
||||||
|
|
||||||
let result = eval.evaluate(code, Some(code_path.clone()));
|
let result = eval.evaluate(code, Some(code_path.clone()));
|
||||||
let failed = match result.value {
|
let failed = match result.value {
|
||||||
|
@ -128,8 +129,10 @@ fn identity(#[files("src/tests/tvix_tests/identity-*.nix")] code_path: PathBuf)
|
||||||
|
|
||||||
let code = std::fs::read_to_string(code_path).expect("should be able to read test code");
|
let code = std::fs::read_to_string(code_path).expect("should be able to read test code");
|
||||||
|
|
||||||
let mut eval = crate::Evaluation::new(Box::new(crate::StdIO) as Box<dyn EvalIO>, false);
|
let eval = crate::Evaluation::builder(Box::new(crate::StdIO) as Box<dyn EvalIO>)
|
||||||
eval.strict = true;
|
.disable_import()
|
||||||
|
.strict()
|
||||||
|
.build();
|
||||||
|
|
||||||
let result = eval.evaluate(&code, None);
|
let result = eval.evaluate(&code, None);
|
||||||
assert!(
|
assert!(
|
||||||
|
|
|
@ -5,8 +5,9 @@ fn test_source_builtin() {
|
||||||
// Test an evaluation with a source-only builtin. The test ensures
|
// Test an evaluation with a source-only builtin. The test ensures
|
||||||
// that the artificially constructed thunking is correct.
|
// that the artificially constructed thunking is correct.
|
||||||
|
|
||||||
let mut eval = Evaluation::new_pure();
|
let eval = Evaluation::builder_pure()
|
||||||
eval.src_builtins.push(("testSourceBuiltin", "42"));
|
.add_src_builtin("testSourceBuiltin", "42")
|
||||||
|
.build();
|
||||||
|
|
||||||
let result = eval.evaluate("builtins.testSourceBuiltin", None);
|
let result = eval.evaluate("builtins.testSourceBuiltin", None);
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -25,7 +26,9 @@ fn test_source_builtin() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skip_broken_bytecode() {
|
fn skip_broken_bytecode() {
|
||||||
let result = Evaluation::new_pure().evaluate(/* code = */ "x", None);
|
let result = Evaluation::builder_pure()
|
||||||
|
.build()
|
||||||
|
.evaluate(/* code = */ "x", None);
|
||||||
|
|
||||||
assert_eq!(result.errors.len(), 1);
|
assert_eq!(result.errors.len(), 1);
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,16 @@ fn nix_eval(expr: &str, strictness: Strictness) -> String {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[cfg(feature = "impure")]
|
#[cfg(feature = "impure")]
|
||||||
fn compare_eval(expr: &str, strictness: Strictness) {
|
fn compare_eval(expr: &str, strictness: Strictness) {
|
||||||
|
use tvix_eval::EvalIO;
|
||||||
|
|
||||||
let nix_result = nix_eval(expr, strictness);
|
let nix_result = nix_eval(expr, strictness);
|
||||||
let mut eval = tvix_eval::Evaluation::new_pure();
|
let mut eval_builder = tvix_eval::Evaluation::builder_pure();
|
||||||
eval.strict = matches!(strictness, Strictness::Strict);
|
if matches!(strictness, Strictness::Strict) {
|
||||||
eval.io_handle = Box::new(tvix_eval::StdIO);
|
eval_builder = eval_builder.strict();
|
||||||
|
}
|
||||||
|
let eval = eval_builder
|
||||||
|
.io_handle(Box::new(tvix_eval::StdIO) as Box<dyn EvalIO>)
|
||||||
|
.build();
|
||||||
|
|
||||||
let tvix_result = eval
|
let tvix_result = eval
|
||||||
.evaluate(expr, None)
|
.evaluate(expr, None)
|
||||||
|
|
|
@ -40,22 +40,23 @@ fn interpret(code: &str) {
|
||||||
TOKIO_RUNTIME.handle().clone(),
|
TOKIO_RUNTIME.handle().clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut eval = tvix_eval::Evaluation::new(
|
let mut eval_builder = tvix_eval::Evaluation::builder(Box::new(TvixIO::new(
|
||||||
Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
|
tvix_store_io.clone() as Rc<dyn EvalIO>,
|
||||||
true,
|
)) as Box<dyn EvalIO>)
|
||||||
);
|
.enable_import()
|
||||||
|
.add_builtins(impure_builtins());
|
||||||
|
|
||||||
eval.builtins.extend(impure_builtins());
|
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
add_derivation_builtins(&mut eval, Rc::clone(&tvix_store_io));
|
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
add_fetcher_builtins(&mut eval, Rc::clone(&tvix_store_io));
|
eval_builder = add_import_builtins(eval_builder, tvix_store_io);
|
||||||
add_import_builtins(&mut eval, tvix_store_io);
|
eval_builder = configure_nix_path(
|
||||||
configure_nix_path(
|
eval_builder,
|
||||||
&mut eval,
|
|
||||||
// The benchmark requires TVIX_BENCH_NIX_PATH to be set, so barf out
|
// The benchmark requires TVIX_BENCH_NIX_PATH to be set, so barf out
|
||||||
// early, rather than benchmarking tvix returning an error.
|
// early, rather than benchmarking tvix returning an error.
|
||||||
&Some(env::var("TVIX_BENCH_NIX_PATH").expect("TVIX_BENCH_NIX_PATH must be set")),
|
&Some(env::var("TVIX_BENCH_NIX_PATH").expect("TVIX_BENCH_NIX_PATH must be set")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let eval = eval_builder.build();
|
||||||
let result = eval.evaluate(code, None);
|
let result = eval.evaluate(code, None);
|
||||||
|
|
||||||
assert!(result.errors.is_empty());
|
assert!(result.errors.is_empty());
|
||||||
|
|
|
@ -18,13 +18,14 @@ pub use errors::{DerivationError, FetcherError, ImportError};
|
||||||
///
|
///
|
||||||
/// As they need to interact with `known_paths`, we also need to pass in
|
/// As they need to interact with `known_paths`, we also need to pass in
|
||||||
/// `known_paths`.
|
/// `known_paths`.
|
||||||
pub fn add_derivation_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
|
pub fn add_derivation_builtins<'co, 'ro, 'env, IO>(
|
||||||
eval.builtins
|
eval_builder: tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO>,
|
||||||
.extend(derivation::derivation_builtins::builtins(Rc::clone(&io)));
|
io: Rc<TvixStoreIO>,
|
||||||
|
) -> tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
// Add the actual `builtins.derivation` from compiled Nix code
|
eval_builder
|
||||||
eval.src_builtins
|
.add_builtins(derivation::derivation_builtins::builtins(Rc::clone(&io)))
|
||||||
.push(("derivation", include_str!("derivation.nix")));
|
// Add the actual `builtins.derivation` from compiled Nix code
|
||||||
|
.add_src_builtin("derivation", include_str!("derivation.nix"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds fetcher builtins to the passed [tvix_eval::Evaluation]:
|
/// Adds fetcher builtins to the passed [tvix_eval::Evaluation]:
|
||||||
|
@ -32,9 +33,11 @@ pub fn add_derivation_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<
|
||||||
/// * `fetchurl`
|
/// * `fetchurl`
|
||||||
/// * `fetchTarball`
|
/// * `fetchTarball`
|
||||||
/// * `fetchGit`
|
/// * `fetchGit`
|
||||||
pub fn add_fetcher_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
|
pub fn add_fetcher_builtins<'co, 'ro, 'env, IO>(
|
||||||
eval.builtins
|
eval_builder: tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO>,
|
||||||
.extend(fetchers::fetcher_builtins::builtins(Rc::clone(&io)));
|
io: Rc<TvixStoreIO>,
|
||||||
|
) -> tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
|
eval_builder.add_builtins(fetchers::fetcher_builtins::builtins(Rc::clone(&io)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds import-related builtins to the passed [tvix_eval::Evaluation].
|
/// Adds import-related builtins to the passed [tvix_eval::Evaluation].
|
||||||
|
@ -42,10 +45,12 @@ pub fn add_fetcher_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<Tvi
|
||||||
/// These are `filterSource` and `path`
|
/// These are `filterSource` and `path`
|
||||||
///
|
///
|
||||||
/// As they need to interact with the store implementation, we pass [`TvixStoreIO`].
|
/// As they need to interact with the store implementation, we pass [`TvixStoreIO`].
|
||||||
pub fn add_import_builtins<IO>(eval: &mut tvix_eval::Evaluation<IO>, io: Rc<TvixStoreIO>) {
|
pub fn add_import_builtins<'co, 'ro, 'env, IO>(
|
||||||
eval.builtins.extend(import::import_builtins(io));
|
eval_builder: tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO>,
|
||||||
|
io: Rc<TvixStoreIO>,
|
||||||
|
) -> tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
// TODO(raitobezarius): evaluate expressing filterSource as Nix code using path (b/372)
|
// TODO(raitobezarius): evaluate expressing filterSource as Nix code using path (b/372)
|
||||||
|
eval_builder.add_builtins(import::import_builtins(io))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -81,11 +86,11 @@ mod tests {
|
||||||
runtime.handle().clone(),
|
runtime.handle().clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut eval = tvix_eval::Evaluation::new(io.clone() as Rc<dyn EvalIO>, false);
|
let mut eval_builder = tvix_eval::Evaluation::builder(io.clone() as Rc<dyn EvalIO>);
|
||||||
|
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&io));
|
||||||
add_derivation_builtins(&mut eval, Rc::clone(&io));
|
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&io));
|
||||||
add_fetcher_builtins(&mut eval, Rc::clone(&io));
|
eval_builder = add_import_builtins(eval_builder, io);
|
||||||
add_import_builtins(&mut eval, io);
|
let eval = eval_builder.build();
|
||||||
|
|
||||||
// run the evaluation itself.
|
// run the evaluation itself.
|
||||||
eval.evaluate(str, None)
|
eval.evaluate(str, None)
|
||||||
|
|
|
@ -14,12 +14,14 @@ mod tests;
|
||||||
/// Tell the Evaluator to resolve `<nix>` to the path `/__corepkgs__`,
|
/// Tell the Evaluator to resolve `<nix>` to the path `/__corepkgs__`,
|
||||||
/// which has special handling in [tvix_io::TvixIO].
|
/// which has special handling in [tvix_io::TvixIO].
|
||||||
/// This is used in nixpkgs to import `fetchurl.nix` from `<nix>`.
|
/// This is used in nixpkgs to import `fetchurl.nix` from `<nix>`.
|
||||||
pub fn configure_nix_path<IO>(
|
pub fn configure_nix_path<'co, 'ro, 'env, IO>(
|
||||||
eval: &mut tvix_eval::Evaluation<IO>,
|
eval_builder: tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO>,
|
||||||
nix_search_path: &Option<String>,
|
nix_search_path: &Option<String>,
|
||||||
) {
|
) -> tvix_eval::EvaluationBuilder<'co, 'ro, 'env, IO> {
|
||||||
eval.nix_path = nix_search_path
|
eval_builder.nix_path(
|
||||||
.as_ref()
|
nix_search_path
|
||||||
.map(|p| format!("nix=/__corepkgs__:{}", p))
|
.as_ref()
|
||||||
.or_else(|| Some("nix=/__corepkgs__".to_string()));
|
.map(|p| format!("nix=/__corepkgs__:{}", p))
|
||||||
|
.or_else(|| Some("nix=/__corepkgs__".to_string())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,16 +47,18 @@ fn eval_test(code_path: PathBuf, expect_success: bool) {
|
||||||
tokio_runtime.handle().clone(),
|
tokio_runtime.handle().clone(),
|
||||||
));
|
));
|
||||||
// Wrap with TvixIO, so <nix/fetchurl.nix can be imported.
|
// Wrap with TvixIO, so <nix/fetchurl.nix can be imported.
|
||||||
let mut eval = tvix_eval::Evaluation::new(
|
let mut eval_builder = tvix_eval::Evaluation::builder(Box::new(TvixIO::new(
|
||||||
Box::new(TvixIO::new(tvix_store_io.clone() as Rc<dyn EvalIO>)) as Box<dyn EvalIO>,
|
tvix_store_io.clone() as Rc<dyn EvalIO>,
|
||||||
true,
|
)) as Box<dyn EvalIO>)
|
||||||
);
|
.enable_import()
|
||||||
|
.strict();
|
||||||
|
|
||||||
eval.strict = true;
|
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
add_derivation_builtins(&mut eval, tvix_store_io.clone());
|
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io));
|
||||||
add_fetcher_builtins(&mut eval, tvix_store_io.clone());
|
eval_builder = add_import_builtins(eval_builder, tvix_store_io);
|
||||||
add_import_builtins(&mut eval, tvix_store_io.clone());
|
eval_builder = configure_nix_path(eval_builder, &None);
|
||||||
configure_nix_path(&mut eval, &None);
|
|
||||||
|
let eval = eval_builder.build();
|
||||||
|
|
||||||
let result = eval.evaluate(code, Some(code_path.clone()));
|
let result = eval.evaluate(code, Some(code_path.clone()));
|
||||||
let failed = match result.value {
|
let failed = match result.value {
|
||||||
|
|
|
@ -653,11 +653,13 @@ mod tests {
|
||||||
Arc::<DummyBuildService>::default(),
|
Arc::<DummyBuildService>::default(),
|
||||||
tokio_runtime.handle().clone(),
|
tokio_runtime.handle().clone(),
|
||||||
));
|
));
|
||||||
let mut eval = tvix_eval::Evaluation::new(io.clone() as Rc<dyn EvalIO>, true);
|
|
||||||
|
|
||||||
add_derivation_builtins(&mut eval, io.clone());
|
let mut eval_builder =
|
||||||
add_fetcher_builtins(&mut eval, io.clone());
|
tvix_eval::Evaluation::builder(io.clone() as Rc<dyn EvalIO>).enable_import();
|
||||||
add_import_builtins(&mut eval, io);
|
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&io));
|
||||||
|
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&io));
|
||||||
|
eval_builder = add_import_builtins(eval_builder, io);
|
||||||
|
let eval = eval_builder.build();
|
||||||
|
|
||||||
// run the evaluation itself.
|
// run the evaluation itself.
|
||||||
eval.evaluate(str, None)
|
eval.evaluate(str, None)
|
||||||
|
|
|
@ -23,8 +23,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = tvix_serde::from_str_with_config::<Config, _>(code, |eval| {
|
let result = tvix_serde::from_str_with_config::<Config, _>(code, |eval_builder| {
|
||||||
eval.enable_impure(None);
|
eval_builder.enable_impure(None)
|
||||||
});
|
});
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use serde::de::value::{MapDeserializer, SeqDeserializer};
|
use serde::de::value::{MapDeserializer, SeqDeserializer};
|
||||||
use serde::de::{self, EnumAccess, VariantAccess};
|
use serde::de::{self, EnumAccess, VariantAccess};
|
||||||
pub use tvix_eval::Evaluation;
|
use tvix_eval::{EvalIO, EvaluationBuilder, Value};
|
||||||
use tvix_eval::{EvalIO, Value};
|
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ pub fn from_str<'code, T>(src: &'code str) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::Deserialize<'code>,
|
T: serde::Deserialize<'code>,
|
||||||
{
|
{
|
||||||
from_str_with_config(src, |_| /* no extra config */ ())
|
from_str_with_config(src, |b| /* no extra config */ b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the Nix code in `src`, with extra configuration for the
|
/// Evaluate the Nix code in `src`, with extra configuration for the
|
||||||
|
@ -44,13 +43,12 @@ where
|
||||||
pub fn from_str_with_config<'code, T, F>(src: &'code str, config: F) -> Result<T, Error>
|
pub fn from_str_with_config<'code, T, F>(src: &'code str, config: F) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::Deserialize<'code>,
|
T: serde::Deserialize<'code>,
|
||||||
F: FnOnce(&mut Evaluation<Box<dyn EvalIO>>),
|
F: for<'co, 'ro, 'env> FnOnce(
|
||||||
|
EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>>,
|
||||||
|
) -> EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>>,
|
||||||
{
|
{
|
||||||
// First step is to evaluate the Nix code ...
|
// First step is to evaluate the Nix code ...
|
||||||
let mut eval = Evaluation::new_pure();
|
let eval = config(EvaluationBuilder::new_pure().strict()).build();
|
||||||
config(&mut eval);
|
|
||||||
|
|
||||||
eval.strict = true;
|
|
||||||
let result = eval.evaluate(src, None);
|
let result = eval.evaluate(src, None);
|
||||||
|
|
||||||
if !result.errors.is_empty() {
|
if !result.errors.is_empty() {
|
||||||
|
|
|
@ -204,7 +204,7 @@ fn deserialize_enum_all() {
|
||||||
fn deserialize_with_config() {
|
fn deserialize_with_config() {
|
||||||
let result: String = from_str_with_config("builtins.testWithConfig", |eval| {
|
let result: String = from_str_with_config("builtins.testWithConfig", |eval| {
|
||||||
// Add a literal string builtin that just returns `"ok"`.
|
// Add a literal string builtin that just returns `"ok"`.
|
||||||
eval.src_builtins.push(("testWithConfig", "\"ok\""));
|
eval.add_src_builtin("testWithConfig", "\"ok\"")
|
||||||
})
|
})
|
||||||
.expect("should deserialize");
|
.expect("should deserialize");
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ fn deserialize_with_extra_builtin() {
|
||||||
let code = "builtins.prependHello \"world\"";
|
let code = "builtins.prependHello \"world\"";
|
||||||
|
|
||||||
let result: String = from_str_with_config(code, |eval| {
|
let result: String = from_str_with_config(code, |eval| {
|
||||||
eval.builtins.append(&mut test_builtins::builtins());
|
eval.add_builtins(test_builtins::builtins())
|
||||||
})
|
})
|
||||||
.expect("should deserialize");
|
.expect("should deserialize");
|
||||||
|
|
||||||
|
|
|
@ -265,18 +265,19 @@ fn eval(model: &Model) -> Output {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut eval = tvix_eval::Evaluation::new_pure();
|
let mut eval_builder = tvix_eval::Evaluation::builder_pure();
|
||||||
let source = eval.source_map();
|
let source = eval_builder.source_map().clone();
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let mut compiler_observer = DisassemblingObserver::new(source.clone(), &mut out.bytecode);
|
let mut compiler_observer = DisassemblingObserver::new(source.clone(), &mut out.bytecode);
|
||||||
eval.compiler_observer = Some(&mut compiler_observer);
|
eval_builder.set_compiler_observer(Some(&mut compiler_observer));
|
||||||
|
|
||||||
let mut runtime_observer = TracingObserver::new(&mut out.trace);
|
let mut runtime_observer = TracingObserver::new(&mut out.trace);
|
||||||
if model.trace {
|
if model.trace {
|
||||||
eval.runtime_observer = Some(&mut runtime_observer);
|
eval_builder.set_runtime_observer(Some(&mut runtime_observer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let eval = eval_builder.build();
|
||||||
eval.evaluate(&model.code, Some("/nixbolt".into()))
|
eval.evaluate(&model.code, Some("/nixbolt".into()))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue