From 10430e200683c9e7965dc9090d0dede4e86e324f Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sat, 24 Jan 2026 15:21:54 +0800 Subject: [PATCH] refactor: RuntimeContext --- nix-js/src/codegen.rs | 8 +- nix-js/src/context.rs | 84 ++++++---------- nix-js/src/error.rs | 6 +- nix-js/src/fetcher.rs | 39 ++++---- nix-js/src/ir.rs | 35 ++++--- nix-js/src/ir/downgrade.rs | 21 ++-- nix-js/src/ir/utils.rs | 194 +++++++++++++++++++++---------------- nix-js/src/runtime.rs | 98 ++++++++++--------- 8 files changed, 246 insertions(+), 239 deletions(-) diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 2ba1330..cec2772 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -316,8 +316,8 @@ impl Compile for Select { .attrpath .iter() .map(|attr| match attr { - Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(), - Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx), + Attr::Str(sym, _) => ctx.get_sym(*sym).escape_quote(), + Attr::Dynamic(expr_id, _) => ctx.get_ir(*expr_id).compile(ctx), }) .join(","); let span_str = encode_span(self.span, ctx); @@ -431,8 +431,8 @@ impl Compile for HasAttr { .rhs .iter() .map(|attr| match attr { - Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(), - Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx), + Attr::Str(sym, _) => ctx.get_sym(*sym).escape_quote(), + Attr::Dynamic(expr_id, _) => ctx.get_ir(*expr_id).compile(ctx), }) .join(","); format!("Nix.hasAttr({lhs},[{attrpath}])") diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index f00d4eb..93b2341 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -14,45 +14,8 @@ use crate::ir::{ Thunk, ToIr as _, synthetic_span, }; use crate::runtime::{Runtime, RuntimeContext}; -use crate::store::{StoreBackend, StoreConfig}; +use crate::store::{Store, StoreBackend, StoreConfig}; use crate::value::Value; -use std::sync::Arc; - -mod private { - use super::*; - use std::ptr::NonNull; - - pub struct CtxPtr(NonNull); - 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` is obtained from `&mut Ctx` - unsafe { self.0.as_ref() } - } - fn as_mut(&mut self) -> &mut Ctx { - // SAFETY: This is safe since inner `NonNull` 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 { - self.as_mut().compile_code(source) - } - fn get_source(&self, id: usize) -> Source { - self.as_ref().get_source(id) - } - } -} -use private::CtxPtr; #[derive(Debug)] pub(crate) struct SccInfo { @@ -62,7 +25,7 @@ pub(crate) struct SccInfo { pub struct Context { ctx: Ctx, - runtime: Runtime, + runtime: Runtime, } impl Context { @@ -79,14 +42,9 @@ impl Context { tracing::debug!("Compiling code"); let code = self.compile_code(source)?; - self.runtime - .op_state() - .borrow_mut() - .put(self.ctx.store.clone()); - tracing::debug!("Executing JavaScript"); 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 { @@ -95,7 +53,7 @@ impl Context { #[allow(dead_code)] pub(crate) fn eval_js(&mut self, code: String) -> Result { - self.runtime.eval(code, CtxPtr::new(&mut self.ctx)) + self.runtime.eval(code, &mut self.ctx) } pub fn get_store_dir(&self) -> &str { @@ -108,7 +66,7 @@ pub(crate) struct Ctx { symbols: DefaultStringInterner, global: NonNull>, sources: Vec, - store: Arc, + store: StoreBackend, } impl Ctx { @@ -197,7 +155,7 @@ impl Ctx { } let config = StoreConfig::from_env(); - let store = Arc::new(StoreBackend::new(config)?); + let store = StoreBackend::new(config)?; Ok(Self { symbols, @@ -207,9 +165,7 @@ impl Ctx { store, }) } -} -impl Ctx { pub(crate) fn downgrade_ctx<'a>(&'a mut self) -> DowngradeCtx<'a> { let global_ref = unsafe { self.global.as_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 { + 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 { graph: DiGraphMap, current_binding: Option, @@ -460,16 +434,18 @@ impl DowngradeContext for DowngradeCtx<'_> { use crate::ir::{Attr, Select}; let select = Select { expr: namespace, - attrpath: vec![Attr::Str(sym)], + attrpath: vec![Attr::Str(sym, synthetic_span())], default: result, // Link to outer With or None span, }; result = Some(self.new_expr(select.to_ir())); } result.ok_or_else(|| { - Error::downgrade_error(format!("'{}' not found", self.get_sym(sym))) - .with_span(span) - .with_source(self.get_current_source()) + Error::downgrade_error( + format!("'{}' not found", self.get_sym(sym)), + self.get_current_source(), + span, + ) }) } diff --git a/nix-js/src/error.rs b/nix-js/src/error.rs index 2f36a3a..9ffb048 100644 --- a/nix-js/src/error.rs +++ b/nix-js/src/error.rs @@ -134,10 +134,10 @@ impl Error { .into() } - pub fn downgrade_error(msg: String) -> Box { + pub fn downgrade_error(msg: String, src: Source, span: rnix::TextRange) -> Box { Error::DowngradeError { - src: None, - span: None, + src: Some(src.into()), + span: Some(text_range_to_source_span(span)), message: msg, } .into() diff --git a/nix-js/src/fetcher.rs b/nix-js/src/fetcher.rs index 0592755..54ffecf 100644 --- a/nix-js/src/fetcher.rs +++ b/nix-js/src/fetcher.rs @@ -3,6 +3,9 @@ use deno_core::op2; use serde::Serialize; use tracing::{debug, info, warn}; +use crate::runtime::OpStateExt; +use crate::runtime::RuntimeContext; + mod archive; pub(crate) mod cache; mod download; @@ -53,16 +56,13 @@ pub struct FetchHgResult { #[op2] #[serde] -pub fn op_fetch_url( +pub fn op_fetch_url( state: &mut OpState, #[string] url: String, #[string] expected_hash: Option, #[string] name: Option, executable: bool, ) -> Result { - use crate::store::StoreBackend; - use std::sync::Arc; - let _span = tracing::info_span!("op_fetch_url", url = %url).entered(); info!("fetchurl started"); @@ -128,9 +128,9 @@ pub fn op_fetch_url( } } - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); let store_path = store - .as_store() .add_to_store(&file_name, &data, false, vec![]) .map_err(|e| NixError::from(e.to_string()))?; @@ -160,16 +160,13 @@ pub fn op_fetch_url( #[op2] #[serde] -pub fn op_fetch_tarball( +pub fn op_fetch_tarball( state: &mut OpState, #[string] url: String, #[string] expected_hash: Option, #[string] expected_nar_hash: Option, #[string] name: Option, ) -> Result { - use crate::store::StoreBackend; - use std::sync::Arc; - let _span = tracing::info_span!("op_fetch_tarball", url = %url).entered(); info!("fetchTarball started"); @@ -264,9 +261,9 @@ pub fn op_fetch_tarball( } info!("Adding to store"); - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); let store_path = store - .as_store() .add_to_store_from_path(&dir_name, &extracted_path, vec![]) .map_err(|e| NixError::from(e.to_string()))?; @@ -292,7 +289,7 @@ pub fn op_fetch_tarball( #[op2] #[serde] -pub fn op_fetch_git( +pub fn op_fetch_git( state: &mut OpState, #[string] url: String, #[string] git_ref: Option, @@ -302,19 +299,17 @@ pub fn op_fetch_git( all_refs: bool, #[string] name: Option, ) -> Result { - use crate::store::StoreBackend; - use std::sync::Arc; - let _span = tracing::info_span!("op_fetch_git", url = %url).entered(); info!("fetchGit started"); let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?; let dir_name = name.unwrap_or_else(|| "source".to_string()); - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); git::fetch_git( &cache, - store.as_store(), + store, &url, git_ref.as_deref(), rev.as_deref(), @@ -377,11 +372,11 @@ fn base64_decode(input: &str) -> Result, String> { Ok(output) } -pub fn register_ops() -> Vec { +pub fn register_ops() -> Vec { vec![ - op_fetch_url(), - op_fetch_tarball(), - op_fetch_git(), + op_fetch_url::(), + op_fetch_tarball::(), + op_fetch_git::(), op_fetch_hg(), ] } diff --git a/nix-js/src/ir.rs b/nix-js/src/ir.rs index a0f8645..37edead 100644 --- a/nix-js/src/ir.rs +++ b/nix-js/src/ir.rs @@ -120,7 +120,7 @@ impl AttrSet { if let Some(attr) = path.next() { // If the path is not yet exhausted, we need to recurse deeper. match attr { - Attr::Str(ident) => { + Attr::Str(ident, span) => { // If the next attribute is a static string. if let Some(&id) = self.stcs.get(&ident) { // 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. Error::downgrade_error(format!( "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)); ctx.replace_expr(id, ir); @@ -144,7 +147,7 @@ impl AttrSet { let mut attrs = AttrSet { stcs: Default::default(), dyns: Default::default(), - span: synthetic_span(), + span, }; attrs._insert(path, name, value, ctx)?; let attrs = ctx.new_expr(attrs.to_ir()); @@ -152,14 +155,14 @@ impl AttrSet { } Ok(()) } - Attr::Dynamic(dynamic) => { + Attr::Dynamic(dynamic, span) => { // 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. // FIXME: span let mut attrs = AttrSet { stcs: Default::default(), dyns: Default::default(), - span: synthetic_span(), + span, }; attrs._insert(path, name, value, ctx)?; self.dyns.push((dynamic, ctx.new_expr(attrs.to_ir()))); @@ -169,15 +172,19 @@ impl AttrSet { } else { // This is the final attribute in the path, so insert the value here. match name { - Attr::Str(ident) => { + Attr::Str(ident, span) => { if self.stcs.insert(ident, value).is_some() { - return Err(Error::downgrade_error(format!( - "attribute '{}' already defined", - format_symbol(ctx.get_sym(ident)) - ))); + return Err(Error::downgrade_error( + format!( + "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)); } } @@ -215,10 +222,10 @@ pub struct ArgId(pub usize); pub enum Attr { /// A dynamic attribute key, which is an expression that must evaluate to a string. /// Example: `attrs.${key}` - Dynamic(ExprId), + Dynamic(ExprId, TextRange), /// A static attribute key. /// Example: `attrs.key` - Str(SymId), + Str(SymId, TextRange), } /// The kinds of binary operations supported in Nix. diff --git a/nix-js/src/ir/downgrade.rs b/nix-js/src/ir/downgrade.rs index f0542c4..3d9b4eb 100644 --- a/nix-js/src/ir/downgrade.rs +++ b/nix-js/src/ir/downgrade.rs @@ -19,9 +19,11 @@ impl Downgrade for Expr { Assert(assert) => assert.downgrade(ctx), Error(error) => { let span = error.syntax().text_range(); - Err(self::Error::downgrade_error(error.to_string()) - .with_span(span) - .with_source(ctx.get_current_source())) + Err(self::Error::downgrade_error( + error.to_string(), + ctx.get_current_source(), + span, + )) } IfElse(ifelse) => ifelse.downgrade(ctx), Select(select) => select.downgrade(ctx), @@ -320,7 +322,8 @@ impl Downgrade for ast::LegacyLet { let body_sym = ctx.new_sym("body".to_string()); let select = Select { expr: attrset_expr, - attrpath: vec![Attr::Str(body_sym)], + // FIXME: span + attrpath: vec![Attr::Str(body_sym, synthetic_span())], default: None, span, }; @@ -397,13 +400,9 @@ impl Downgrade for ast::Lambda { scc_info, required, optional, - } = downgrade_pattern_bindings( - pat_entries, - alias, - arg, - ctx, - |ctx, _| self.body().unwrap().downgrade(ctx), - )?; + } = downgrade_pattern_bindings(pat_entries, alias, arg, ctx, |ctx, _| { + self.body().unwrap().downgrade(ctx) + })?; param = Some(Param { required, diff --git a/nix-js/src/ir/utils.rs b/nix-js/src/ir/utils.rs index f0e6c51..4f8f1ed 100644 --- a/nix-js/src/ir/utils.rs +++ b/nix-js/src/ir/utils.rs @@ -79,21 +79,21 @@ pub fn downgrade_inherit( for attr in inherit.attrs() { let span = attr.syntax().text_range(); let ident = match downgrade_attr(attr, ctx)? { - Attr::Str(ident) => ident, + Attr::Str(ident, _) => ident, _ => { // `inherit` does not allow dynamic attributes. return Err(Error::downgrade_error( "dynamic attributes not allowed in inherit".to_string(), - ) - .with_span(span) - .with_source(ctx.get_current_source())); + ctx.get_current_source(), + span, + )); } }; let expr = if let Some(expr) = from { let select_expr = ctx.new_expr( Select { expr, - attrpath: vec![Attr::Str(ident)], + attrpath: vec![Attr::Str(ident, span)], default: None, span, } @@ -111,10 +111,14 @@ pub fn downgrade_inherit( }; match stcs.entry(ident) { Entry::Occupied(occupied) => { - return Err(Error::downgrade_error(format!( - "attribute '{}' already defined", - format_symbol(ctx.get_sym(*occupied.key())) - )) + return Err(Error::downgrade_error( + format!( + "attribute '{}' already defined", + format_symbol(ctx.get_sym(*occupied.key())) + ), + ctx.get_current_source(), + span, + ) .with_span(span) .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 { use ast::Attr::*; use ast::InterpolPart::*; - let span = attr.syntax().text_range(); 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) => { let parts = string.normalized_parts(); + let span = string.syntax().text_range(); 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 { // If the string has only one part, it's either a literal or a single interpolation. match parts.into_iter().next().unwrap() { - Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))), - Interpolation(interpol) => { - Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?)) - } + Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident), span)), + Interpolation(interpol) => Ok(Attr::Dynamic( + interpol.expr().unwrap().downgrade(ctx)?, + span, + )), } } else { // 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::>>()?; Ok(Attr::Dynamic( 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<()> { let attrpath_node = value.attrpath().unwrap(); 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( "dynamic attributes not allowed in let bindings".to_string(), - ) - .with_span(attrpath_node.syntax().text_range()) - .with_source(ctx.get_current_source())); + ctx.get_current_source(), + span, + )); } let value = value.value().unwrap().downgrade(ctx)?; attrs.insert(path, value, ctx) @@ -231,47 +245,54 @@ pub fn downgrade_pattern_bindings( where Ctx: DowngradeContext, { - let mut param_syms = Vec::new(); - let mut param_defaults = Vec::new(); - let mut param_spans = Vec::new(); - let mut seen_params = HashSet::new(); - - 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); + struct Param { + sym: SymId, + sym_span: TextRange, + default: Option, + span: TextRange, } + 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::, Vec<_>)>>()?; - let mut binding_keys: Vec = param_syms.clone(); if let Some(alias_sym) = alias { binding_keys.push(alias_sym); } - let (required, optional) = - param_syms - .iter() - .zip(param_defaults.iter()) - .partition_map(|(&sym, default)| { - use itertools::Either::*; - if default.is_none() { - Left(sym) - } else { - Right(sym) - } - }); + let (required, optional) = params.iter().partition_map(|Param { sym, default, .. }| { + use itertools::Either::*; + if default.is_none() { + Left(sym) + } else { + Right(sym) + } + }); // Get the owner from outer tracker's current_binding let owner = ctx.get_current_binding(); @@ -282,16 +303,18 @@ where |ctx, sym_to_slot| { let mut bindings = HashMap::new(); - for ((sym, default_ast), span) in param_syms - .iter() - .zip(param_defaults.iter()) - .zip(param_spans.iter()) + for Param { + sym, + sym_span, + 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)); - let default = if let Some(default_expr) = default_ast { - Some(default_expr.clone().downgrade(ctx)?) + let default = if let Some(default) = default { + Some(default.clone().downgrade(ctx)?) } else { None }; @@ -299,13 +322,13 @@ where let select_expr = ctx.new_expr( Select { expr: arg, - attrpath: vec![Attr::Str(*sym)], + attrpath: vec![Attr::Str(sym, sym_span)], default, - span: *span, + span, } .to_ir(), ); - bindings.insert(*sym, select_expr); + bindings.insert(sym, select_expr); ctx.set_current_binding(None); } @@ -393,12 +416,11 @@ where .to_ir(), ); } else { - return Err(Error::downgrade_error(format!( - "binding '{}' not found", - format_symbol(ctx.get_sym(sym)) - )) - .with_span(synthetic_span()) - .with_source(ctx.get_current_source())); + return Err(Error::downgrade_error( + format!("binding '{}' not found", format_symbol(ctx.get_sym(sym))), + ctx.get_current_source(), + synthetic_span(), + )); } } @@ -433,12 +455,14 @@ where if let ast::Attr::Ident(ident) = attr { let sym = ctx.new_sym(ident.to_string()); if !binding_syms.insert(sym) { - return Err(Error::downgrade_error(format!( - "attribute '{}' already defined", - format_symbol(ctx.get_sym(sym)) - )) - .with_span(ident.syntax().text_range()) - .with_source(ctx.get_current_source())); + return Err(Error::downgrade_error( + format!( + "attribute '{}' already defined", + format_symbol(ctx.get_sym(sym)) + ), + ctx.get_current_source(), + ident.syntax().text_range(), + )); } } } @@ -453,12 +477,14 @@ where if let Some(ast::Attr::Ident(ident)) = attrs_vec.first() { let sym = ctx.new_sym(ident.to_string()); if !binding_syms.insert(sym) { - return Err(Error::downgrade_error(format!( - "attribute '{}' already defined", - format_symbol(ctx.get_sym(sym)) - )) - .with_span(ident.syntax().text_range()) - .with_source(ctx.get_current_source())); + return Err(Error::downgrade_error( + format!( + "attribute '{}' already defined", + format_symbol(ctx.get_sym(sym)) + ), + ctx.get_current_source(), + ident.syntax().text_range(), + )); } } } else if attrs_vec.len() > 1 { diff --git a/nix-js/src/runtime.rs b/nix-js/src/runtime.rs index eb367d4..7a93793 100644 --- a/nix-js/src/runtime.rs +++ b/nix-js/src/runtime.rs @@ -8,6 +8,7 @@ use deno_error::JsErrorClass; use itertools::Itertools as _; use crate::error::{Error, Result, Source}; +use crate::store::Store; use crate::value::{AttrSet, List, Symbol, Value}; 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 compile_code(&mut self, source: Source) -> Result; fn get_source(&self, id: usize) -> Source; + fn get_store(&self) -> &dyn Store; +} + +pub(crate) trait OpStateExt { + fn get_ctx(&self) -> &Ctx; + fn get_ctx_mut(&mut self) -> &mut Ctx; +} + +impl OpStateExt 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() -> Extension { @@ -30,16 +48,16 @@ fn runtime_extension() -> Extension { op_path_exists(), op_resolve_path(), op_sha256_hex(), - op_make_store_path(), + op_make_store_path::(), op_output_path_name(), - op_make_fixed_output_path(), - op_add_path(), - op_store_path(), - op_to_file(), - op_copy_path_to_store(), + op_make_fixed_output_path::(), + op_add_path::(), + op_store_path::(), + op_to_file::(), + op_copy_path_to_store::(), op_get_env(), ]; - ops.extend(crate::fetcher::register_ops()); + ops.extend(crate::fetcher::register_ops::()); Extension { name: "nix_runtime", @@ -85,7 +103,7 @@ fn op_import( #[string] path: String, ) -> std::result::Result { let _span = tracing::info_span!("op_import", path = %path).entered(); - let ctx = state.borrow_mut::(); + let ctx: &mut Ctx = state.get_ctx_mut(); let current_dir = ctx.get_current_dir(); let mut absolute_path = current_dir @@ -169,17 +187,15 @@ fn op_sha256_hex(#[string] data: String) -> String { #[deno_core::op2] #[string] -fn op_make_store_path( +fn op_make_store_path( state: &mut OpState, #[string] ty: String, #[string] hash_hex: String, #[string] name: String, ) -> String { - use crate::store::StoreBackend; - use std::sync::Arc; - - let store = state.borrow::>(); - let store_dir = store.as_store().get_store_dir(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); + let store_dir = store.get_store_dir(); 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] #[string] -fn op_make_fixed_output_path( +fn op_make_fixed_output_path( state: &mut OpState, #[string] hash_algo: String, #[string] hash: String, #[string] hash_mode: String, #[string] name: String, ) -> String { - use crate::store::StoreBackend; use sha2::{Digest, Sha256}; - use std::sync::Arc; - let store = state.borrow::>(); - let store_dir = store.as_store().get_store_dir(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); + let store_dir = store.get_store_dir(); if hash_algo == "sha256" && hash_mode == "recursive" { crate::nix_hash::make_store_path(store_dir, "source", &hash, &name) @@ -220,18 +235,16 @@ fn op_make_fixed_output_path( #[deno_core::op2] #[string] -fn op_add_path( +fn op_add_path( state: &mut OpState, #[string] path: String, #[string] name: Option, recursive: bool, #[string] sha256: Option, ) -> std::result::Result { - use crate::store::StoreBackend; use sha2::{Digest, Sha256}; use std::fs; use std::path::Path; - use std::sync::Arc; let path_obj = Path::new(&path); @@ -272,10 +285,10 @@ fn op_add_path( ))); } - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); let store_path = store - .as_store() .add_to_store_from_path(&computed_name, path_obj, vec![]) .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( state: &mut OpState, #[string] path: String, ) -> std::result::Result { - use crate::store::{StoreBackend, validate_store_path}; - use std::sync::Arc; + use crate::store::validate_store_path; - let store = state.borrow::>(); - let store_dir = store.as_store().get_store_dir(); + let ctx: &Ctx = state.get_ctx(); + 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()))?; store - .as_store() .ensure_path(&path) .map_err(|e| NixError::from(e.to_string()))?; @@ -341,18 +353,15 @@ fn op_store_path( #[deno_core::op2] #[string] -fn op_to_file( +fn op_to_file( state: &mut OpState, #[string] name: String, #[string] contents: String, #[serde] references: Vec, ) -> std::result::Result { - use crate::store::StoreBackend; - use std::sync::Arc; - - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); let store_path = store - .as_store() .add_text_to_store(&name, &contents, references) .map_err(|e| NixError::from(format!("builtins.toFile failed: {}", e)))?; @@ -361,13 +370,11 @@ fn op_to_file( #[deno_core::op2] #[string] -fn op_copy_path_to_store( +fn op_copy_path_to_store( state: &mut OpState, #[string] path: String, ) -> std::result::Result { - use crate::store::StoreBackend; use std::path::Path; - use std::sync::Arc; let path_obj = Path::new(&path); @@ -381,9 +388,9 @@ fn op_copy_path_to_store( .unwrap_or("source") .to_string(); - let store = state.borrow::>(); + let ctx: &Ctx = state.get_ctx(); + let store = ctx.get_store(); let store_path = store - .as_store() .add_to_store_from_path(&name, path_obj, vec![]) .map_err(|e| NixError::from(format!("failed to copy path to store: {}", e)))?; @@ -436,11 +443,8 @@ impl Runtime { }) } - pub(crate) fn op_state(&mut self) -> std::rc::Rc> { - self.js_runtime.op_state() - } - - pub(crate) fn eval(&mut self, script: String, ctx: Ctx) -> Result { + pub(crate) fn eval(&mut self, script: String, ctx: &mut Ctx) -> Result { + let ctx: &'static mut Ctx = unsafe { &mut *(ctx as *mut Ctx) }; self.js_runtime.op_state().borrow_mut().put(ctx); let global_value = self @@ -450,7 +454,7 @@ impl Runtime { // Get current source from Context let op_state = self.js_runtime.op_state(); let op_state_borrow = op_state.borrow(); - let ctx = op_state_borrow.borrow::(); + let ctx: &Ctx = op_state_borrow.get_ctx(); let msg = e.get_message().to_string(); let mut span = None;