diff options
Diffstat (limited to 'tvix/eval/src/builtins')
-rw-r--r-- | tvix/eval/src/builtins/impure.rs | 38 | ||||
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 135 |
2 files changed, 100 insertions, 73 deletions
diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs index 5edd81999a7c..8433c04c419d 100644 --- a/tvix/eval/src/builtins/impure.rs +++ b/tvix/eval/src/builtins/impure.rs @@ -1,17 +1,17 @@ use std::{ - cell::RefCell, - collections::{BTreeMap, HashMap}, + collections::BTreeMap, env, fs::File, io::{self, Read}, - rc::Rc, + rc::{Rc, Weak}, time::{SystemTime, UNIX_EPOCH}, }; use crate::{ + compiler::GlobalsMap, errors::ErrorKind, observer::NoOpObserver, - value::{Builtin, NixAttrs, NixString, Thunk}, + value::{Builtin, NixAttrs, Thunk}, vm::VM, SourceCode, Value, }; @@ -69,10 +69,10 @@ fn impure_builtins() -> Vec<Builtin> { /// Return all impure builtins, that is all builtins which may perform I/O /// outside of the VM and so cannot be used in all contexts (e.g. WASM). -pub(super) fn builtins() -> BTreeMap<NixString, Value> { - let mut map: BTreeMap<NixString, Value> = impure_builtins() +pub(super) fn builtins() -> BTreeMap<&'static str, Value> { + let mut map: BTreeMap<&'static str, Value> = impure_builtins() .into_iter() - .map(|b| (b.name().into(), Value::Builtin(b))) + .map(|b| (b.name(), Value::Builtin(b))) .collect(); // currentTime pins the time at which evaluation was started @@ -84,7 +84,7 @@ pub(super) fn builtins() -> BTreeMap<NixString, Value> { Err(err) => -(err.duration().as_secs() as i64), }; - map.insert(NixString::from("currentTime"), Value::Integer(seconds)); + map.insert("currentTime", Value::Integer(seconds)); } map @@ -94,10 +94,13 @@ pub(super) fn builtins() -> BTreeMap<NixString, Value> { /// it needs to capture the [crate::SourceCode] structure to correctly track /// source code locations while invoking a compiler. // TODO: need to be able to pass through a CompilationObserver, too. -pub fn builtins_import( - globals: Rc<RefCell<HashMap<&'static str, Value>>>, - source: SourceCode, -) -> Builtin { +pub fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builtin { + // This (very cheap, once-per-compiler-startup) clone exists + // solely in order to keep the borrow checker happy. It + // resolves the tension between the requirements of + // Rc::new_cyclic() and Builtin::new() + let globals = globals.clone(); + Builtin::new( "import", &[true], @@ -126,11 +129,18 @@ pub fn builtins_import( }); } - let result = crate::compile( + let result = crate::compiler::compile( &parsed.tree().expr().unwrap(), Some(path.clone()), file, - globals.clone(), + // The VM must ensure that a strong reference to the + // globals outlives any self-references (which are + // weak) embedded within the globals. If the + // expect() below panics, it means that did not + // happen. + globals + .upgrade() + .expect("globals dropped while still in use"), &mut NoOpObserver::default(), ) .map_err(|err| ErrorKind::ImportCompilerError { diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 91fe27aa4695..7dd7ee946d32 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -3,9 +3,12 @@ //! See //tvix/eval/docs/builtins.md for a some context on the //! available builtins in Nix. +use crate::compiler::{GlobalsMap, GlobalsMapFunc}; +use crate::source::SourceCode; use std::cmp::{self, Ordering}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::path::PathBuf; +use std::rc::Rc; use regex::Regex; @@ -690,70 +693,84 @@ fn placeholders() -> Vec<Builtin> { // we set TVIX_CURRENT_SYSTEM in build.rs pub const CURRENT_PLATFORM: &str = env!("TVIX_CURRENT_SYSTEM"); -fn builtins_set() -> NixAttrs { - let mut map: BTreeMap<NixString, Value> = BTreeMap::new(); - - // Pure-value builtins - map.insert( - "nixVersion".into(), - Value::String("2.3-compat-tvix-0.1".into()), - ); - - map.insert("langVersion".into(), Value::Integer(6)); +/// Set of Nix builtins that are globally available. +pub fn global_builtins(source: SourceCode) -> GlobalsMapFunc { + Box::new(move |globals: &std::rc::Weak<GlobalsMap>| { + let mut map: BTreeMap<&'static str, Value> = BTreeMap::new(); - map.insert( - "currentSystem".into(), - crate::systems::llvm_triple_to_nix_double(CURRENT_PLATFORM).into(), - ); + // Pure-value builtins + map.insert("nixVersion", Value::String("2.3-compat-tvix-0.1".into())); - let mut add_builtins = |builtins: Vec<Builtin>| { - for builtin in builtins { - map.insert(builtin.name().into(), Value::Builtin(builtin)); - } - }; + map.insert("langVersion", Value::Integer(6)); - add_builtins(pure_builtins()); - add_builtins(placeholders()); + map.insert( + "currentSystem", + crate::systems::llvm_triple_to_nix_double(CURRENT_PLATFORM).into(), + ); - #[cfg(feature = "impure")] - { - map.extend(impure::builtins()); - } - - NixAttrs::from_map(map) -} - -/// Set of Nix builtins that are globally available. -pub fn global_builtins() -> HashMap<&'static str, Value> { - let builtins = builtins_set(); - let mut globals: HashMap<&'static str, Value> = HashMap::new(); - - // known global builtins from the builtins set. - for global in &[ - "abort", - "baseNameOf", - "derivation", - "derivationStrict", - "dirOf", - "fetchGit", - "fetchMercurial", - "fetchTarball", - "fromTOML", - "import", - "isNull", - "map", - "placeholder", - "removeAttrs", - "scopedImport", - "throw", - "toString", - ] { - if let Some(builtin) = builtins.select(global) { - globals.insert(global, builtin.clone()); + let mut add_builtins = |builtins: Vec<Builtin>| { + for builtin in builtins { + map.insert(builtin.name(), Value::Builtin(builtin)); + } + }; + + add_builtins(pure_builtins()); + add_builtins(placeholders()); + + #[cfg(feature = "impure")] + { + map.extend(impure::builtins()); + + // We need to insert import into the builtins, but the + // builtins passed to import must have import *in it*. + let import = Value::Builtin(crate::builtins::impure::builtins_import( + globals, + source.clone(), + )); + + map.insert("import", import); + }; + + let mut globals: GlobalsMap = HashMap::new(); + + let builtins = Rc::new(NixAttrs::from_map( + map.into_iter().map(|(k, v)| (k.into(), v)).collect(), + )); + + // known global builtins from the builtins set. + for global in &[ + "abort", + "baseNameOf", + "derivation", + "derivationStrict", + "dirOf", + "fetchGit", + "fetchMercurial", + "fetchTarball", + "fromTOML", + "import", + "isNull", + "map", + "placeholder", + "removeAttrs", + "scopedImport", + "throw", + "toString", + ] { + if let Some(builtin) = builtins.select(global) { + let builtin = builtin.clone(); + globals.insert( + global, + Rc::new(move |c, s| c.emit_constant(builtin.clone(), &s)), + ); + } } - } - globals.insert("builtins", Value::attrs(builtins)); + globals.insert( + "builtins", + Rc::new(move |c, s| c.emit_constant(Value::attrs(builtins.as_ref().clone()), &s)), + ); - globals + globals + }) } |