refactor: RuntimeContext

This commit is contained in:
2026-01-24 15:21:54 +08:00
parent f46ee9d48f
commit 10430e2006
8 changed files with 246 additions and 239 deletions

View File

@@ -316,8 +316,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
.attrpath .attrpath
.iter() .iter()
.map(|attr| match attr { .map(|attr| match attr {
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(), Attr::Str(sym, _) => ctx.get_sym(*sym).escape_quote(),
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx), Attr::Dynamic(expr_id, _) => ctx.get_ir(*expr_id).compile(ctx),
}) })
.join(","); .join(",");
let span_str = encode_span(self.span, ctx); let span_str = encode_span(self.span, ctx);
@@ -431,8 +431,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
.rhs .rhs
.iter() .iter()
.map(|attr| match attr { .map(|attr| match attr {
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(), Attr::Str(sym, _) => ctx.get_sym(*sym).escape_quote(),
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx), Attr::Dynamic(expr_id, _) => ctx.get_ir(*expr_id).compile(ctx),
}) })
.join(","); .join(",");
format!("Nix.hasAttr({lhs},[{attrpath}])") format!("Nix.hasAttr({lhs},[{attrpath}])")

View File

@@ -14,45 +14,8 @@ use crate::ir::{
Thunk, ToIr as _, synthetic_span, Thunk, ToIr as _, synthetic_span,
}; };
use crate::runtime::{Runtime, RuntimeContext}; use crate::runtime::{Runtime, RuntimeContext};
use crate::store::{StoreBackend, StoreConfig}; use crate::store::{Store, StoreBackend, StoreConfig};
use crate::value::Value; use crate::value::Value;
use std::sync::Arc;
mod private {
use super::*;
use std::ptr::NonNull;
pub struct CtxPtr(NonNull<Ctx>);
impl CtxPtr {
pub fn new(ctx: &mut Ctx) -> Self {
unsafe { CtxPtr(NonNull::new_unchecked(ctx)) }
}
fn as_ref(&self) -> &Ctx {
// SAFETY: This is safe since inner `NonNull<Ctx>` is obtained from `&mut Ctx`
unsafe { self.0.as_ref() }
}
fn as_mut(&mut self) -> &mut Ctx {
// SAFETY: This is safe since inner `NonNull<Ctx>` is obtained from `&mut Ctx`
unsafe { self.0.as_mut() }
}
}
impl RuntimeContext for CtxPtr {
fn get_current_dir(&self) -> &Path {
self.as_ref().get_current_dir()
}
fn add_source(&mut self, source: Source) {
self.as_mut().sources.push(source);
}
fn compile_code(&mut self, source: Source) -> Result<String> {
self.as_mut().compile_code(source)
}
fn get_source(&self, id: usize) -> Source {
self.as_ref().get_source(id)
}
}
}
use private::CtxPtr;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct SccInfo { pub(crate) struct SccInfo {
@@ -62,7 +25,7 @@ pub(crate) struct SccInfo {
pub struct Context { pub struct Context {
ctx: Ctx, ctx: Ctx,
runtime: Runtime<CtxPtr>, runtime: Runtime<Ctx>,
} }
impl Context { impl Context {
@@ -79,14 +42,9 @@ impl Context {
tracing::debug!("Compiling code"); tracing::debug!("Compiling code");
let code = self.compile_code(source)?; let code = self.compile_code(source)?;
self.runtime
.op_state()
.borrow_mut()
.put(self.ctx.store.clone());
tracing::debug!("Executing JavaScript"); tracing::debug!("Executing JavaScript");
self.runtime self.runtime
.eval(format!("Nix.force({code})"), CtxPtr::new(&mut self.ctx)) .eval(format!("Nix.force({code})"), &mut self.ctx)
} }
pub fn compile_code(&mut self, source: Source) -> Result<String> { pub fn compile_code(&mut self, source: Source) -> Result<String> {
@@ -95,7 +53,7 @@ impl Context {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn eval_js(&mut self, code: String) -> Result<Value> { pub(crate) fn eval_js(&mut self, code: String) -> Result<Value> {
self.runtime.eval(code, CtxPtr::new(&mut self.ctx)) self.runtime.eval(code, &mut self.ctx)
} }
pub fn get_store_dir(&self) -> &str { pub fn get_store_dir(&self) -> &str {
@@ -108,7 +66,7 @@ pub(crate) struct Ctx {
symbols: DefaultStringInterner, symbols: DefaultStringInterner,
global: NonNull<HashMap<SymId, ExprId>>, global: NonNull<HashMap<SymId, ExprId>>,
sources: Vec<Source>, sources: Vec<Source>,
store: Arc<StoreBackend>, store: StoreBackend,
} }
impl Ctx { impl Ctx {
@@ -197,7 +155,7 @@ impl Ctx {
} }
let config = StoreConfig::from_env(); let config = StoreConfig::from_env();
let store = Arc::new(StoreBackend::new(config)?); let store = StoreBackend::new(config)?;
Ok(Self { Ok(Self {
symbols, symbols,
@@ -207,9 +165,7 @@ impl Ctx {
store, store,
}) })
} }
}
impl Ctx {
pub(crate) fn downgrade_ctx<'a>(&'a mut self) -> DowngradeCtx<'a> { pub(crate) fn downgrade_ctx<'a>(&'a mut self) -> DowngradeCtx<'a> {
let global_ref = unsafe { self.global.as_ref() }; let global_ref = unsafe { self.global.as_ref() };
DowngradeCtx::new(self, global_ref) DowngradeCtx::new(self, global_ref)
@@ -279,6 +235,24 @@ impl CodegenContext for Ctx {
} }
} }
impl RuntimeContext for Ctx {
fn get_current_dir(&self) -> &Path {
self.get_current_dir()
}
fn add_source(&mut self, source: Source) {
self.sources.push(source);
}
fn compile_code(&mut self, source: Source) -> Result<String> {
self.compile_code(source)
}
fn get_source(&self, id: usize) -> Source {
self.get_source(id)
}
fn get_store(&self) -> &dyn Store {
self.store.as_store()
}
}
struct DependencyTracker { struct DependencyTracker {
graph: DiGraphMap<ExprId, ()>, graph: DiGraphMap<ExprId, ()>,
current_binding: Option<ExprId>, current_binding: Option<ExprId>,
@@ -460,16 +434,18 @@ impl DowngradeContext for DowngradeCtx<'_> {
use crate::ir::{Attr, Select}; use crate::ir::{Attr, Select};
let select = Select { let select = Select {
expr: namespace, expr: namespace,
attrpath: vec![Attr::Str(sym)], attrpath: vec![Attr::Str(sym, synthetic_span())],
default: result, // Link to outer With or None default: result, // Link to outer With or None
span, span,
}; };
result = Some(self.new_expr(select.to_ir())); result = Some(self.new_expr(select.to_ir()));
} }
result.ok_or_else(|| { result.ok_or_else(|| {
Error::downgrade_error(format!("'{}' not found", self.get_sym(sym))) Error::downgrade_error(
.with_span(span) format!("'{}' not found", self.get_sym(sym)),
.with_source(self.get_current_source()) self.get_current_source(),
span,
)
}) })
} }

View File

@@ -134,10 +134,10 @@ impl Error {
.into() .into()
} }
pub fn downgrade_error(msg: String) -> Box<Self> { pub fn downgrade_error(msg: String, src: Source, span: rnix::TextRange) -> Box<Self> {
Error::DowngradeError { Error::DowngradeError {
src: None, src: Some(src.into()),
span: None, span: Some(text_range_to_source_span(span)),
message: msg, message: msg,
} }
.into() .into()

View File

@@ -3,6 +3,9 @@ use deno_core::op2;
use serde::Serialize; use serde::Serialize;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use crate::runtime::OpStateExt;
use crate::runtime::RuntimeContext;
mod archive; mod archive;
pub(crate) mod cache; pub(crate) mod cache;
mod download; mod download;
@@ -53,16 +56,13 @@ pub struct FetchHgResult {
#[op2] #[op2]
#[serde] #[serde]
pub fn op_fetch_url( pub fn op_fetch_url<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] url: String, #[string] url: String,
#[string] expected_hash: Option<String>, #[string] expected_hash: Option<String>,
#[string] name: Option<String>, #[string] name: Option<String>,
executable: bool, executable: bool,
) -> Result<FetchUrlResult, NixError> { ) -> Result<FetchUrlResult, NixError> {
use crate::store::StoreBackend;
use std::sync::Arc;
let _span = tracing::info_span!("op_fetch_url", url = %url).entered(); let _span = tracing::info_span!("op_fetch_url", url = %url).entered();
info!("fetchurl started"); info!("fetchurl started");
@@ -128,9 +128,9 @@ pub fn op_fetch_url(
} }
} }
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store = ctx.get_store();
let store_path = store let store_path = store
.as_store()
.add_to_store(&file_name, &data, false, vec![]) .add_to_store(&file_name, &data, false, vec![])
.map_err(|e| NixError::from(e.to_string()))?; .map_err(|e| NixError::from(e.to_string()))?;
@@ -160,16 +160,13 @@ pub fn op_fetch_url(
#[op2] #[op2]
#[serde] #[serde]
pub fn op_fetch_tarball( pub fn op_fetch_tarball<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] url: String, #[string] url: String,
#[string] expected_hash: Option<String>, #[string] expected_hash: Option<String>,
#[string] expected_nar_hash: Option<String>, #[string] expected_nar_hash: Option<String>,
#[string] name: Option<String>, #[string] name: Option<String>,
) -> Result<FetchTarballResult, NixError> { ) -> Result<FetchTarballResult, NixError> {
use crate::store::StoreBackend;
use std::sync::Arc;
let _span = tracing::info_span!("op_fetch_tarball", url = %url).entered(); let _span = tracing::info_span!("op_fetch_tarball", url = %url).entered();
info!("fetchTarball started"); info!("fetchTarball started");
@@ -264,9 +261,9 @@ pub fn op_fetch_tarball(
} }
info!("Adding to store"); info!("Adding to store");
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store = ctx.get_store();
let store_path = store let store_path = store
.as_store()
.add_to_store_from_path(&dir_name, &extracted_path, vec![]) .add_to_store_from_path(&dir_name, &extracted_path, vec![])
.map_err(|e| NixError::from(e.to_string()))?; .map_err(|e| NixError::from(e.to_string()))?;
@@ -292,7 +289,7 @@ pub fn op_fetch_tarball(
#[op2] #[op2]
#[serde] #[serde]
pub fn op_fetch_git( pub fn op_fetch_git<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] url: String, #[string] url: String,
#[string] git_ref: Option<String>, #[string] git_ref: Option<String>,
@@ -302,19 +299,17 @@ pub fn op_fetch_git(
all_refs: bool, all_refs: bool,
#[string] name: Option<String>, #[string] name: Option<String>,
) -> Result<FetchGitResult, NixError> { ) -> Result<FetchGitResult, NixError> {
use crate::store::StoreBackend;
use std::sync::Arc;
let _span = tracing::info_span!("op_fetch_git", url = %url).entered(); let _span = tracing::info_span!("op_fetch_git", url = %url).entered();
info!("fetchGit started"); info!("fetchGit started");
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?; let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
let dir_name = name.unwrap_or_else(|| "source".to_string()); let dir_name = name.unwrap_or_else(|| "source".to_string());
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store = ctx.get_store();
git::fetch_git( git::fetch_git(
&cache, &cache,
store.as_store(), store,
&url, &url,
git_ref.as_deref(), git_ref.as_deref(),
rev.as_deref(), rev.as_deref(),
@@ -377,11 +372,11 @@ fn base64_decode(input: &str) -> Result<Vec<u8>, String> {
Ok(output) Ok(output)
} }
pub fn register_ops() -> Vec<deno_core::OpDecl> { pub fn register_ops<Ctx: RuntimeContext>() -> Vec<deno_core::OpDecl> {
vec![ vec![
op_fetch_url(), op_fetch_url::<Ctx>(),
op_fetch_tarball(), op_fetch_tarball::<Ctx>(),
op_fetch_git(), op_fetch_git::<Ctx>(),
op_fetch_hg(), op_fetch_hg(),
] ]
} }

View File

@@ -120,7 +120,7 @@ impl AttrSet {
if let Some(attr) = path.next() { if let Some(attr) = path.next() {
// If the path is not yet exhausted, we need to recurse deeper. // If the path is not yet exhausted, we need to recurse deeper.
match attr { match attr {
Attr::Str(ident) => { Attr::Str(ident, span) => {
// If the next attribute is a static string. // If the next attribute is a static string.
if let Some(&id) = self.stcs.get(&ident) { if let Some(&id) = self.stcs.get(&ident) {
// If a sub-attrset already exists, recurse into it. // If a sub-attrset already exists, recurse into it.
@@ -132,8 +132,11 @@ impl AttrSet {
// This path segment exists but is not an attrset, which is an error. // This path segment exists but is not an attrset, which is an error.
Error::downgrade_error(format!( Error::downgrade_error(format!(
"attribute '{}' already defined but is not an attribute set", "attribute '{}' already defined but is not an attribute set",
format_symbol(ctx.get_sym(ident)) format_symbol(ctx.get_sym(ident)),
)) ),
ctx.get_current_source(),
span
)
}) })
.and_then(|attrs| attrs._insert(path, name, value, ctx)); .and_then(|attrs| attrs._insert(path, name, value, ctx));
ctx.replace_expr(id, ir); ctx.replace_expr(id, ir);
@@ -144,7 +147,7 @@ impl AttrSet {
let mut attrs = AttrSet { let mut attrs = AttrSet {
stcs: Default::default(), stcs: Default::default(),
dyns: Default::default(), dyns: Default::default(),
span: synthetic_span(), span,
}; };
attrs._insert(path, name, value, ctx)?; attrs._insert(path, name, value, ctx)?;
let attrs = ctx.new_expr(attrs.to_ir()); let attrs = ctx.new_expr(attrs.to_ir());
@@ -152,14 +155,14 @@ impl AttrSet {
} }
Ok(()) Ok(())
} }
Attr::Dynamic(dynamic) => { Attr::Dynamic(dynamic, span) => {
// If the next attribute is a dynamic expression, we must create a new sub-attrset. // If the next attribute is a dynamic expression, we must create a new sub-attrset.
// We cannot merge with existing dynamic attributes at this stage. // We cannot merge with existing dynamic attributes at this stage.
// FIXME: span // FIXME: span
let mut attrs = AttrSet { let mut attrs = AttrSet {
stcs: Default::default(), stcs: Default::default(),
dyns: Default::default(), dyns: Default::default(),
span: synthetic_span(), span,
}; };
attrs._insert(path, name, value, ctx)?; attrs._insert(path, name, value, ctx)?;
self.dyns.push((dynamic, ctx.new_expr(attrs.to_ir()))); self.dyns.push((dynamic, ctx.new_expr(attrs.to_ir())));
@@ -169,15 +172,19 @@ impl AttrSet {
} else { } else {
// This is the final attribute in the path, so insert the value here. // This is the final attribute in the path, so insert the value here.
match name { match name {
Attr::Str(ident) => { Attr::Str(ident, span) => {
if self.stcs.insert(ident, value).is_some() { if self.stcs.insert(ident, value).is_some() {
return Err(Error::downgrade_error(format!( return Err(Error::downgrade_error(
"attribute '{}' already defined", format!(
format_symbol(ctx.get_sym(ident)) "attribute '{}' already defined",
))); format_symbol(ctx.get_sym(ident)),
),
ctx.get_current_source(),
span,
));
} }
} }
Attr::Dynamic(dynamic) => { Attr::Dynamic(dynamic, _) => {
self.dyns.push((dynamic, value)); self.dyns.push((dynamic, value));
} }
} }
@@ -215,10 +222,10 @@ pub struct ArgId(pub usize);
pub enum Attr { pub enum Attr {
/// A dynamic attribute key, which is an expression that must evaluate to a string. /// A dynamic attribute key, which is an expression that must evaluate to a string.
/// Example: `attrs.${key}` /// Example: `attrs.${key}`
Dynamic(ExprId), Dynamic(ExprId, TextRange),
/// A static attribute key. /// A static attribute key.
/// Example: `attrs.key` /// Example: `attrs.key`
Str(SymId), Str(SymId, TextRange),
} }
/// The kinds of binary operations supported in Nix. /// The kinds of binary operations supported in Nix.

View File

@@ -19,9 +19,11 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for Expr {
Assert(assert) => assert.downgrade(ctx), Assert(assert) => assert.downgrade(ctx),
Error(error) => { Error(error) => {
let span = error.syntax().text_range(); let span = error.syntax().text_range();
Err(self::Error::downgrade_error(error.to_string()) Err(self::Error::downgrade_error(
.with_span(span) error.to_string(),
.with_source(ctx.get_current_source())) ctx.get_current_source(),
span,
))
} }
IfElse(ifelse) => ifelse.downgrade(ctx), IfElse(ifelse) => ifelse.downgrade(ctx),
Select(select) => select.downgrade(ctx), Select(select) => select.downgrade(ctx),
@@ -320,7 +322,8 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
let body_sym = ctx.new_sym("body".to_string()); let body_sym = ctx.new_sym("body".to_string());
let select = Select { let select = Select {
expr: attrset_expr, expr: attrset_expr,
attrpath: vec![Attr::Str(body_sym)], // FIXME: span
attrpath: vec![Attr::Str(body_sym, synthetic_span())],
default: None, default: None,
span, span,
}; };
@@ -397,13 +400,9 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
scc_info, scc_info,
required, required,
optional, optional,
} = downgrade_pattern_bindings( } = downgrade_pattern_bindings(pat_entries, alias, arg, ctx, |ctx, _| {
pat_entries, self.body().unwrap().downgrade(ctx)
alias, })?;
arg,
ctx,
|ctx, _| self.body().unwrap().downgrade(ctx),
)?;
param = Some(Param { param = Some(Param {
required, required,

View File

@@ -79,21 +79,21 @@ pub fn downgrade_inherit(
for attr in inherit.attrs() { for attr in inherit.attrs() {
let span = attr.syntax().text_range(); let span = attr.syntax().text_range();
let ident = match downgrade_attr(attr, ctx)? { let ident = match downgrade_attr(attr, ctx)? {
Attr::Str(ident) => ident, Attr::Str(ident, _) => ident,
_ => { _ => {
// `inherit` does not allow dynamic attributes. // `inherit` does not allow dynamic attributes.
return Err(Error::downgrade_error( return Err(Error::downgrade_error(
"dynamic attributes not allowed in inherit".to_string(), "dynamic attributes not allowed in inherit".to_string(),
) ctx.get_current_source(),
.with_span(span) span,
.with_source(ctx.get_current_source())); ));
} }
}; };
let expr = if let Some(expr) = from { let expr = if let Some(expr) = from {
let select_expr = ctx.new_expr( let select_expr = ctx.new_expr(
Select { Select {
expr, expr,
attrpath: vec![Attr::Str(ident)], attrpath: vec![Attr::Str(ident, span)],
default: None, default: None,
span, span,
} }
@@ -111,10 +111,14 @@ pub fn downgrade_inherit(
}; };
match stcs.entry(ident) { match stcs.entry(ident) {
Entry::Occupied(occupied) => { Entry::Occupied(occupied) => {
return Err(Error::downgrade_error(format!( return Err(Error::downgrade_error(
"attribute '{}' already defined", format!(
format_symbol(ctx.get_sym(*occupied.key())) "attribute '{}' already defined",
)) format_symbol(ctx.get_sym(*occupied.key()))
),
ctx.get_current_source(),
span,
)
.with_span(span) .with_span(span)
.with_source(ctx.get_current_source())); .with_source(ctx.get_current_source()));
} }
@@ -129,20 +133,24 @@ pub fn downgrade_inherit(
pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result<Attr> { pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result<Attr> {
use ast::Attr::*; use ast::Attr::*;
use ast::InterpolPart::*; use ast::InterpolPart::*;
let span = attr.syntax().text_range();
match attr { match attr {
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))), Ident(ident) => Ok(Attr::Str(
ctx.new_sym(ident.to_string()),
ident.syntax().text_range(),
)),
Str(string) => { Str(string) => {
let parts = string.normalized_parts(); let parts = string.normalized_parts();
let span = string.syntax().text_range();
if parts.is_empty() { if parts.is_empty() {
Ok(Attr::Str(ctx.new_sym("".to_string()))) Ok(Attr::Str(ctx.new_sym("".to_string()), span))
} else if parts.len() == 1 { } else if parts.len() == 1 {
// If the string has only one part, it's either a literal or a single interpolation. // If the string has only one part, it's either a literal or a single interpolation.
match parts.into_iter().next().unwrap() { match parts.into_iter().next().unwrap() {
Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))), Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident), span)),
Interpolation(interpol) => { Interpolation(interpol) => Ok(Attr::Dynamic(
Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) interpol.expr().unwrap().downgrade(ctx)?,
} span,
)),
} }
} else { } else {
// If the string has multiple parts, it's an interpolated string that must be concatenated. // If the string has multiple parts, it's an interpolated string that must be concatenated.
@@ -155,10 +163,14 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Resul
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
Ok(Attr::Dynamic( Ok(Attr::Dynamic(
ctx.new_expr(ConcatStrings { parts, span }.to_ir()), ctx.new_expr(ConcatStrings { parts, span }.to_ir()),
span,
)) ))
} }
} }
Dynamic(dynamic) => Ok(Attr::Dynamic(dynamic.expr().unwrap().downgrade(ctx)?)), Dynamic(dynamic) => Ok(Attr::Dynamic(
dynamic.expr().unwrap().downgrade(ctx)?,
dynamic.syntax().text_range(),
)),
} }
} }
@@ -194,12 +206,14 @@ pub fn downgrade_static_attrpathvalue(
) -> Result<()> { ) -> Result<()> {
let attrpath_node = value.attrpath().unwrap(); let attrpath_node = value.attrpath().unwrap();
let path = downgrade_attrpath(attrpath_node.clone(), ctx)?; let path = downgrade_attrpath(attrpath_node.clone(), ctx)?;
if path.iter().any(|attr| matches!(attr, Attr::Dynamic(_))) { if let Some(&Attr::Dynamic(_, span)) =
path.iter().find(|attr| matches!(attr, Attr::Dynamic(..)))
{
return Err(Error::downgrade_error( return Err(Error::downgrade_error(
"dynamic attributes not allowed in let bindings".to_string(), "dynamic attributes not allowed in let bindings".to_string(),
) ctx.get_current_source(),
.with_span(attrpath_node.syntax().text_range()) span,
.with_source(ctx.get_current_source())); ));
} }
let value = value.value().unwrap().downgrade(ctx)?; let value = value.value().unwrap().downgrade(ctx)?;
attrs.insert(path, value, ctx) attrs.insert(path, value, ctx)
@@ -231,47 +245,54 @@ pub fn downgrade_pattern_bindings<Ctx>(
where where
Ctx: DowngradeContext, Ctx: DowngradeContext,
{ {
let mut param_syms = Vec::new(); struct Param {
let mut param_defaults = Vec::new(); sym: SymId,
let mut param_spans = Vec::new(); sym_span: TextRange,
let mut seen_params = HashSet::new(); default: Option<ast::Expr>,
span: TextRange,
for entry in pat_entries {
let sym = ctx.new_sym(entry.ident().unwrap().to_string());
let span = entry.ident().unwrap().syntax().text_range();
if !seen_params.insert(sym) {
return Err(Error::downgrade_error(format!(
"duplicate parameter '{}'",
format_symbol(ctx.get_sym(sym))
))
.with_span(span)
.with_source(ctx.get_current_source()));
}
let default_ast = entry.default();
param_syms.push(sym);
param_defaults.push(default_ast);
param_spans.push(span);
} }
let mut seen_params = HashSet::new();
let (params, mut binding_keys) = pat_entries
.into_iter()
.map(|entry| {
let ident = entry.ident().unwrap();
let sym_span = ident.syntax().text_range();
let sym = ctx.new_sym(ident.syntax().text().to_string());
let default = entry.default();
let span = entry.syntax().text_range();
if !seen_params.insert(sym) {
return Err(Error::downgrade_error(
format!("duplicate parameter '{}'", format_symbol(ctx.get_sym(sym))),
ctx.get_current_source(),
span,
));
}
Ok((
Param {
sym,
sym_span,
default,
span,
},
sym,
))
})
.collect::<Result<(Vec<_>, Vec<_>)>>()?;
let mut binding_keys: Vec<SymId> = param_syms.clone();
if let Some(alias_sym) = alias { if let Some(alias_sym) = alias {
binding_keys.push(alias_sym); binding_keys.push(alias_sym);
} }
let (required, optional) = let (required, optional) = params.iter().partition_map(|Param { sym, default, .. }| {
param_syms use itertools::Either::*;
.iter() if default.is_none() {
.zip(param_defaults.iter()) Left(sym)
.partition_map(|(&sym, default)| { } else {
use itertools::Either::*; Right(sym)
if default.is_none() { }
Left(sym) });
} else {
Right(sym)
}
});
// Get the owner from outer tracker's current_binding // Get the owner from outer tracker's current_binding
let owner = ctx.get_current_binding(); let owner = ctx.get_current_binding();
@@ -282,16 +303,18 @@ where
|ctx, sym_to_slot| { |ctx, sym_to_slot| {
let mut bindings = HashMap::new(); let mut bindings = HashMap::new();
for ((sym, default_ast), span) in param_syms for Param {
.iter() sym,
.zip(param_defaults.iter()) sym_span,
.zip(param_spans.iter()) default,
span,
} in params
{ {
let slot = *sym_to_slot.get(sym).unwrap(); let slot = *sym_to_slot.get(&sym).unwrap();
ctx.set_current_binding(Some(slot)); ctx.set_current_binding(Some(slot));
let default = if let Some(default_expr) = default_ast { let default = if let Some(default) = default {
Some(default_expr.clone().downgrade(ctx)?) Some(default.clone().downgrade(ctx)?)
} else { } else {
None None
}; };
@@ -299,13 +322,13 @@ where
let select_expr = ctx.new_expr( let select_expr = ctx.new_expr(
Select { Select {
expr: arg, expr: arg,
attrpath: vec![Attr::Str(*sym)], attrpath: vec![Attr::Str(sym, sym_span)],
default, default,
span: *span, span,
} }
.to_ir(), .to_ir(),
); );
bindings.insert(*sym, select_expr); bindings.insert(sym, select_expr);
ctx.set_current_binding(None); ctx.set_current_binding(None);
} }
@@ -393,12 +416,11 @@ where
.to_ir(), .to_ir(),
); );
} else { } else {
return Err(Error::downgrade_error(format!( return Err(Error::downgrade_error(
"binding '{}' not found", format!("binding '{}' not found", format_symbol(ctx.get_sym(sym))),
format_symbol(ctx.get_sym(sym)) ctx.get_current_source(),
)) synthetic_span(),
.with_span(synthetic_span()) ));
.with_source(ctx.get_current_source()));
} }
} }
@@ -433,12 +455,14 @@ where
if let ast::Attr::Ident(ident) = attr { if let ast::Attr::Ident(ident) = attr {
let sym = ctx.new_sym(ident.to_string()); let sym = ctx.new_sym(ident.to_string());
if !binding_syms.insert(sym) { if !binding_syms.insert(sym) {
return Err(Error::downgrade_error(format!( return Err(Error::downgrade_error(
"attribute '{}' already defined", format!(
format_symbol(ctx.get_sym(sym)) "attribute '{}' already defined",
)) format_symbol(ctx.get_sym(sym))
.with_span(ident.syntax().text_range()) ),
.with_source(ctx.get_current_source())); ctx.get_current_source(),
ident.syntax().text_range(),
));
} }
} }
} }
@@ -453,12 +477,14 @@ where
if let Some(ast::Attr::Ident(ident)) = attrs_vec.first() { if let Some(ast::Attr::Ident(ident)) = attrs_vec.first() {
let sym = ctx.new_sym(ident.to_string()); let sym = ctx.new_sym(ident.to_string());
if !binding_syms.insert(sym) { if !binding_syms.insert(sym) {
return Err(Error::downgrade_error(format!( return Err(Error::downgrade_error(
"attribute '{}' already defined", format!(
format_symbol(ctx.get_sym(sym)) "attribute '{}' already defined",
)) format_symbol(ctx.get_sym(sym))
.with_span(ident.syntax().text_range()) ),
.with_source(ctx.get_current_source())); ctx.get_current_source(),
ident.syntax().text_range(),
));
} }
} }
} else if attrs_vec.len() > 1 { } else if attrs_vec.len() > 1 {

View File

@@ -8,6 +8,7 @@ use deno_error::JsErrorClass;
use itertools::Itertools as _; use itertools::Itertools as _;
use crate::error::{Error, Result, Source}; use crate::error::{Error, Result, Source};
use crate::store::Store;
use crate::value::{AttrSet, List, Symbol, Value}; use crate::value::{AttrSet, List, Symbol, Value};
type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>; type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>;
@@ -19,6 +20,23 @@ pub(crate) trait RuntimeContext: 'static {
fn add_source(&mut self, path: Source); fn add_source(&mut self, path: Source);
fn compile_code(&mut self, source: Source) -> Result<String>; fn compile_code(&mut self, source: Source) -> Result<String>;
fn get_source(&self, id: usize) -> Source; fn get_source(&self, id: usize) -> Source;
fn get_store(&self) -> &dyn Store;
}
pub(crate) trait OpStateExt<Ctx: RuntimeContext> {
fn get_ctx(&self) -> &Ctx;
fn get_ctx_mut(&mut self) -> &mut Ctx;
}
impl<Ctx: RuntimeContext> OpStateExt<Ctx> for OpState {
fn get_ctx(&self) -> &Ctx {
self.try_borrow::<&'static mut Ctx>()
.expect("RuntimeContext not set")
}
fn get_ctx_mut(&mut self) -> &mut Ctx {
self.try_borrow_mut::<&'static mut Ctx>()
.expect("RuntimeContext not set")
}
} }
fn runtime_extension<Ctx: RuntimeContext>() -> Extension { fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
@@ -30,16 +48,16 @@ fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
op_path_exists(), op_path_exists(),
op_resolve_path(), op_resolve_path(),
op_sha256_hex(), op_sha256_hex(),
op_make_store_path(), op_make_store_path::<Ctx>(),
op_output_path_name(), op_output_path_name(),
op_make_fixed_output_path(), op_make_fixed_output_path::<Ctx>(),
op_add_path(), op_add_path::<Ctx>(),
op_store_path(), op_store_path::<Ctx>(),
op_to_file(), op_to_file::<Ctx>(),
op_copy_path_to_store(), op_copy_path_to_store::<Ctx>(),
op_get_env(), op_get_env(),
]; ];
ops.extend(crate::fetcher::register_ops()); ops.extend(crate::fetcher::register_ops::<Ctx>());
Extension { Extension {
name: "nix_runtime", name: "nix_runtime",
@@ -85,7 +103,7 @@ fn op_import<Ctx: RuntimeContext>(
#[string] path: String, #[string] path: String,
) -> std::result::Result<String, NixError> { ) -> std::result::Result<String, NixError> {
let _span = tracing::info_span!("op_import", path = %path).entered(); let _span = tracing::info_span!("op_import", path = %path).entered();
let ctx = state.borrow_mut::<Ctx>(); let ctx: &mut Ctx = state.get_ctx_mut();
let current_dir = ctx.get_current_dir(); let current_dir = ctx.get_current_dir();
let mut absolute_path = current_dir let mut absolute_path = current_dir
@@ -169,17 +187,15 @@ fn op_sha256_hex(#[string] data: String) -> String {
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_make_store_path( fn op_make_store_path<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] ty: String, #[string] ty: String,
#[string] hash_hex: String, #[string] hash_hex: String,
#[string] name: String, #[string] name: String,
) -> String { ) -> String {
use crate::store::StoreBackend; let ctx: &Ctx = state.get_ctx();
use std::sync::Arc; let store = ctx.get_store();
let store_dir = store.get_store_dir();
let store = state.borrow::<Arc<StoreBackend>>();
let store_dir = store.as_store().get_store_dir();
crate::nix_hash::make_store_path(store_dir, &ty, &hash_hex, &name) crate::nix_hash::make_store_path(store_dir, &ty, &hash_hex, &name)
} }
@@ -191,19 +207,18 @@ fn op_output_path_name(#[string] drv_name: String, #[string] output_name: String
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_make_fixed_output_path( fn op_make_fixed_output_path<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] hash_algo: String, #[string] hash_algo: String,
#[string] hash: String, #[string] hash: String,
#[string] hash_mode: String, #[string] hash_mode: String,
#[string] name: String, #[string] name: String,
) -> String { ) -> String {
use crate::store::StoreBackend;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::sync::Arc;
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store_dir = store.as_store().get_store_dir(); let store = ctx.get_store();
let store_dir = store.get_store_dir();
if hash_algo == "sha256" && hash_mode == "recursive" { if hash_algo == "sha256" && hash_mode == "recursive" {
crate::nix_hash::make_store_path(store_dir, "source", &hash, &name) crate::nix_hash::make_store_path(store_dir, "source", &hash, &name)
@@ -220,18 +235,16 @@ fn op_make_fixed_output_path(
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_add_path( fn op_add_path<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] path: String, #[string] path: String,
#[string] name: Option<String>, #[string] name: Option<String>,
recursive: bool, recursive: bool,
#[string] sha256: Option<String>, #[string] sha256: Option<String>,
) -> std::result::Result<String, NixError> { ) -> std::result::Result<String, NixError> {
use crate::store::StoreBackend;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
let path_obj = Path::new(&path); let path_obj = Path::new(&path);
@@ -272,10 +285,10 @@ fn op_add_path(
))); )));
} }
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store = ctx.get_store();
let store_path = store let store_path = store
.as_store()
.add_to_store_from_path(&computed_name, path_obj, vec![]) .add_to_store_from_path(&computed_name, path_obj, vec![])
.map_err(|e| NixError::from(format!("failed to add path to store: {}", e)))?; .map_err(|e| NixError::from(format!("failed to add path to store: {}", e)))?;
@@ -319,20 +332,19 @@ fn compute_nar_hash(path: &std::path::Path) -> std::result::Result<String, NixEr
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_store_path( fn op_store_path<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] path: String, #[string] path: String,
) -> std::result::Result<String, NixError> { ) -> std::result::Result<String, NixError> {
use crate::store::{StoreBackend, validate_store_path}; use crate::store::validate_store_path;
use std::sync::Arc;
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store_dir = store.as_store().get_store_dir(); let store = ctx.get_store();
let store_dir = store.get_store_dir();
validate_store_path(store_dir, &path).map_err(|e| NixError::from(e.to_string()))?; validate_store_path(store_dir, &path).map_err(|e| NixError::from(e.to_string()))?;
store store
.as_store()
.ensure_path(&path) .ensure_path(&path)
.map_err(|e| NixError::from(e.to_string()))?; .map_err(|e| NixError::from(e.to_string()))?;
@@ -341,18 +353,15 @@ fn op_store_path(
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_to_file( fn op_to_file<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] name: String, #[string] name: String,
#[string] contents: String, #[string] contents: String,
#[serde] references: Vec<String>, #[serde] references: Vec<String>,
) -> std::result::Result<String, NixError> { ) -> std::result::Result<String, NixError> {
use crate::store::StoreBackend; let ctx: &Ctx = state.get_ctx();
use std::sync::Arc; let store = ctx.get_store();
let store = state.borrow::<Arc<StoreBackend>>();
let store_path = store let store_path = store
.as_store()
.add_text_to_store(&name, &contents, references) .add_text_to_store(&name, &contents, references)
.map_err(|e| NixError::from(format!("builtins.toFile failed: {}", e)))?; .map_err(|e| NixError::from(format!("builtins.toFile failed: {}", e)))?;
@@ -361,13 +370,11 @@ fn op_to_file(
#[deno_core::op2] #[deno_core::op2]
#[string] #[string]
fn op_copy_path_to_store( fn op_copy_path_to_store<Ctx: RuntimeContext>(
state: &mut OpState, state: &mut OpState,
#[string] path: String, #[string] path: String,
) -> std::result::Result<String, NixError> { ) -> std::result::Result<String, NixError> {
use crate::store::StoreBackend;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
let path_obj = Path::new(&path); let path_obj = Path::new(&path);
@@ -381,9 +388,9 @@ fn op_copy_path_to_store(
.unwrap_or("source") .unwrap_or("source")
.to_string(); .to_string();
let store = state.borrow::<Arc<StoreBackend>>(); let ctx: &Ctx = state.get_ctx();
let store = ctx.get_store();
let store_path = store let store_path = store
.as_store()
.add_to_store_from_path(&name, path_obj, vec![]) .add_to_store_from_path(&name, path_obj, vec![])
.map_err(|e| NixError::from(format!("failed to copy path to store: {}", e)))?; .map_err(|e| NixError::from(format!("failed to copy path to store: {}", e)))?;
@@ -436,11 +443,8 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
}) })
} }
pub(crate) fn op_state(&mut self) -> std::rc::Rc<std::cell::RefCell<OpState>> { pub(crate) fn eval(&mut self, script: String, ctx: &mut Ctx) -> Result<Value> {
self.js_runtime.op_state() let ctx: &'static mut Ctx = unsafe { &mut *(ctx as *mut Ctx) };
}
pub(crate) fn eval(&mut self, script: String, ctx: Ctx) -> Result<Value> {
self.js_runtime.op_state().borrow_mut().put(ctx); self.js_runtime.op_state().borrow_mut().put(ctx);
let global_value = self let global_value = self
@@ -450,7 +454,7 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
// Get current source from Context // Get current source from Context
let op_state = self.js_runtime.op_state(); let op_state = self.js_runtime.op_state();
let op_state_borrow = op_state.borrow(); let op_state_borrow = op_state.borrow();
let ctx = op_state_borrow.borrow::<Ctx>(); let ctx: &Ctx = op_state_borrow.get_ctx();
let msg = e.get_message().to_string(); let msg = e.get_message().to_string();
let mut span = None; let mut span = None;