refactor: avoid Pin hack
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1053,7 +1053,6 @@ dependencies = [
|
|||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"nix-js-macros",
|
"nix-js-macros",
|
||||||
"pin-project",
|
|
||||||
"regex",
|
"regex",
|
||||||
"rnix",
|
"rnix",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ anyhow = "1.0"
|
|||||||
rustyline = "14.0"
|
rustyline = "14.0"
|
||||||
|
|
||||||
derive_more = { version = "2", features = ["full"] }
|
derive_more = { version = "2", features = ["full"] }
|
||||||
pin-project = "1"
|
thiserror = "2"
|
||||||
|
|
||||||
hashbrown = "0.16"
|
hashbrown = "0.16"
|
||||||
string-interner = "0.19"
|
string-interner = "0.19"
|
||||||
|
|
||||||
thiserror = "2"
|
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
|
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@@ -9,109 +8,87 @@ use string_interner::DefaultStringInterner;
|
|||||||
use crate::codegen::{CodegenContext, Compile};
|
use crate::codegen::{CodegenContext, Compile};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ir::{Builtin, DowngradeContext, ExprId, Ir, SymId};
|
use crate::ir::{Builtin, DowngradeContext, ExprId, Ir, SymId};
|
||||||
use crate::runtime::Runtime;
|
use crate::runtime::{Runtime, RuntimeCtx};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
use downgrade::DowngradeCtx;
|
use downgrade::DowngradeCtx;
|
||||||
|
use drop_guard::{PathDropGuard, PathStackProvider};
|
||||||
|
|
||||||
mod downgrade;
|
mod downgrade;
|
||||||
|
mod drop_guard;
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
use super::*;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
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 PathStackProvider for CtxPtr {
|
||||||
|
fn path_stack(&mut self) -> &mut Vec<PathBuf> {
|
||||||
|
&mut self.as_mut().path_stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeCtx for CtxPtr {
|
||||||
|
fn get_current_dir(&self) -> PathBuf {
|
||||||
|
self.as_ref().get_current_dir()
|
||||||
|
}
|
||||||
|
fn push_path_stack(&mut self, path: PathBuf) -> impl DerefMut<Target = Self> {
|
||||||
|
PathDropGuard::new(path, self)
|
||||||
|
}
|
||||||
|
fn compile_code(&mut self, expr: &str) -> Result<String> {
|
||||||
|
self.as_mut().compile_code(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use private::CtxPtr;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
ctx: Pin<Box<Ctx>>,
|
ctx: Ctx,
|
||||||
runtime: Runtime,
|
runtime: Runtime<CtxPtr>,
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct CtxPtr(NonNull<Ctx>);
|
|
||||||
impl CtxPtr {
|
|
||||||
pub(crate) unsafe fn as_ref(&self) -> &Ctx {
|
|
||||||
unsafe { self.0.as_ref() }
|
|
||||||
}
|
|
||||||
pub(crate) unsafe fn as_mut(&mut self) -> Pin<&mut Ctx> {
|
|
||||||
unsafe { Pin::new_unchecked(self.0.as_mut()) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let mut ctx = Box::pin(Ctx::new());
|
let ctx = Ctx::new();
|
||||||
let ptr = unsafe { CtxPtr(NonNull::new_unchecked(Pin::get_unchecked_mut(ctx.as_mut()))) };
|
let runtime = Runtime::new()?;
|
||||||
let runtime = Runtime::new(ptr)?;
|
|
||||||
|
|
||||||
Ok(Self { ctx, runtime })
|
Ok(Self { ctx, runtime })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_code(&mut self, expr: &str) -> Result<Value> {
|
pub fn eval_code(&mut self, expr: &str) -> Result<Value> {
|
||||||
// Initialize `path_stack` with current directory for relative path resolution
|
// Initialize `path_stack` with current directory for relative path resolution
|
||||||
let mut guard = PathDropGuard::new_cwd(self.ctx.as_mut())?;
|
let mut guard = PathDropGuard::new_cwd(&mut self.ctx)?;
|
||||||
let ctx = guard.as_ctx();
|
let ctx = guard.as_ctx();
|
||||||
|
|
||||||
let root = rnix::Root::parse(expr);
|
let code = ctx.compile_code(expr)?;
|
||||||
if !root.errors().is_empty() {
|
self.runtime.eval(code, CtxPtr::new(&mut self.ctx))
|
||||||
return Err(Error::parse_error(root.errors().iter().join("; ")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
// Always `Some` since there is no parse error
|
|
||||||
let root = ctx
|
|
||||||
.as_mut()
|
|
||||||
.downgrade_ctx()
|
|
||||||
.downgrade(root.tree().expr().unwrap())?;
|
|
||||||
let code = ctx.get_ir(root).compile(Pin::get_ref(ctx.as_ref()));
|
|
||||||
let code = format!("Nix.force({})", code);
|
|
||||||
println!("[DEBUG] generated code: {}", &code);
|
|
||||||
self.runtime.eval(code)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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)
|
self.runtime.eval(code, CtxPtr::new(&mut self.ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project::pin_project(PinnedDrop)]
|
|
||||||
pub(crate) struct Ctx {
|
pub(crate) struct Ctx {
|
||||||
irs: Vec<Ir>,
|
irs: Vec<Ir>,
|
||||||
symbols: DefaultStringInterner,
|
symbols: DefaultStringInterner,
|
||||||
global: NonNull<HashMap<SymId, ExprId>>,
|
global: NonNull<HashMap<SymId, ExprId>>,
|
||||||
path_stack: Vec<PathBuf>,
|
path_stack: Vec<PathBuf>,
|
||||||
#[pin]
|
|
||||||
_marker: std::marker::PhantomPinned,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pin_project::pinned_drop]
|
|
||||||
impl PinnedDrop for Ctx {
|
|
||||||
fn drop(self: Pin<&mut Self>) {
|
|
||||||
unsafe {
|
|
||||||
drop(Box::from_raw(self.global.as_ptr()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct PathDropGuard<'ctx> {
|
|
||||||
ctx: Pin<&'ctx mut Ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> PathDropGuard<'ctx> {
|
|
||||||
pub(crate) fn new(path: PathBuf, mut ctx: Pin<&'ctx mut Ctx>) -> Self {
|
|
||||||
ctx.as_mut().project().path_stack.push(path);
|
|
||||||
Self { ctx }
|
|
||||||
}
|
|
||||||
pub(crate) fn new_cwd(mut ctx: Pin<&'ctx mut Ctx>) -> Result<Self> {
|
|
||||||
let cwd = std::env::current_dir()
|
|
||||||
.map_err(|err| Error::downgrade_error(format!("cannot get cwd: {err}")))?;
|
|
||||||
let virtual_file = cwd.join("__eval__.nix");
|
|
||||||
ctx.as_mut().project().path_stack.push(virtual_file);
|
|
||||||
Ok(Self { ctx })
|
|
||||||
}
|
|
||||||
pub(crate) fn as_ctx<'a>(&'a mut self) -> &'a mut Pin<&'ctx mut Ctx> {
|
|
||||||
&mut self.ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for PathDropGuard<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.ctx.as_mut().project().path_stack.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Ctx {
|
impl Default for Ctx {
|
||||||
@@ -165,7 +142,6 @@ impl Default for Ctx {
|
|||||||
irs,
|
irs,
|
||||||
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
||||||
path_stack: Vec::new(),
|
path_stack: Vec::new(),
|
||||||
_marker: std::marker::PhantomPinned,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +151,7 @@ impl Ctx {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn downgrade_ctx<'a>(self: Pin<&'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)
|
||||||
}
|
}
|
||||||
@@ -190,6 +166,23 @@ impl Ctx {
|
|||||||
.expect("path in path_stack should always have a parent dir. this is a bug")
|
.expect("path in path_stack should always have a parent dir. this is a bug")
|
||||||
.to_path_buf()
|
.to_path_buf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_code(&mut self, expr: &str) -> Result<String> {
|
||||||
|
let root = rnix::Root::parse(expr);
|
||||||
|
if !root.errors().is_empty() {
|
||||||
|
return Err(Error::parse_error(root.errors().iter().join("; ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
// Always `Some` since there is no parse error
|
||||||
|
let root = self
|
||||||
|
.downgrade_ctx()
|
||||||
|
.downgrade(root.tree().expr().unwrap())?;
|
||||||
|
let code = self.get_ir(root).compile(self);
|
||||||
|
let code = format!("Nix.force({})", code);
|
||||||
|
println!("[DEBUG] generated code: {}", &code);
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodegenContext for Ctx {
|
impl CodegenContext for Ctx {
|
||||||
@@ -202,6 +195,12 @@ impl CodegenContext for Ctx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PathStackProvider for Ctx {
|
||||||
|
fn path_stack(&mut self) -> &mut Vec<PathBuf> {
|
||||||
|
&mut self.path_stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use crate::codegen::CodegenContext;
|
use crate::codegen::CodegenContext;
|
||||||
@@ -32,14 +30,14 @@ impl<'a, 'ctx> ScopeGuard<'a, 'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DowngradeCtx<'ctx> {
|
pub struct DowngradeCtx<'ctx> {
|
||||||
ctx: Pin<&'ctx mut Ctx>,
|
ctx: &'ctx mut Ctx,
|
||||||
irs: Vec<Option<Ir>>,
|
irs: Vec<Option<Ir>>,
|
||||||
scopes: Vec<Scope<'ctx>>,
|
scopes: Vec<Scope<'ctx>>,
|
||||||
arg_id: usize,
|
arg_id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> DowngradeCtx<'ctx> {
|
impl<'ctx> DowngradeCtx<'ctx> {
|
||||||
pub fn new(ctx: Pin<&'ctx mut Ctx>, global: &'ctx HashMap<SymId, ExprId>) -> Self {
|
pub fn new(ctx: &'ctx mut Ctx, global: &'ctx HashMap<SymId, ExprId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scopes: vec![Scope::Global(global)],
|
scopes: vec![Scope::Global(global)],
|
||||||
irs: vec![],
|
irs: vec![],
|
||||||
@@ -62,7 +60,7 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_sym(&mut self, sym: String) -> SymId {
|
fn new_sym(&mut self, sym: String) -> SymId {
|
||||||
self.ctx.as_mut().project().symbols.get_or_intern(sym)
|
self.ctx.symbols.get_or_intern(sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sym(&self, id: SymId) -> &str {
|
fn get_sym(&self, id: SymId) -> &str {
|
||||||
@@ -144,8 +142,6 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
fn downgrade(mut self, root: rnix::ast::Expr) -> Result<ExprId> {
|
fn downgrade(mut self, root: rnix::ast::Expr) -> Result<ExprId> {
|
||||||
let root = root.downgrade(&mut self)?;
|
let root = root.downgrade(&mut self)?;
|
||||||
self.ctx
|
self.ctx
|
||||||
.as_mut()
|
|
||||||
.project()
|
|
||||||
.irs
|
.irs
|
||||||
.extend(self.irs.into_iter().map(Option::unwrap));
|
.extend(self.irs.into_iter().map(Option::unwrap));
|
||||||
Ok(root)
|
Ok(root)
|
||||||
|
|||||||
41
nix-js/src/context/drop_guard.rs
Normal file
41
nix-js/src/context/drop_guard.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub trait PathStackProvider {
|
||||||
|
fn path_stack(&mut self) -> &mut Vec<PathBuf>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PathDropGuard<'ctx, Ctx: PathStackProvider> {
|
||||||
|
ctx: &'ctx mut Ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Ctx: PathStackProvider> PathDropGuard<'ctx, Ctx> {
|
||||||
|
pub fn new(path: PathBuf, ctx: &'ctx mut Ctx) -> Self {
|
||||||
|
ctx.path_stack().push(path);
|
||||||
|
Self { ctx }
|
||||||
|
}
|
||||||
|
pub fn new_cwd(ctx: &'ctx mut Ctx) -> Result<Self> {
|
||||||
|
let cwd = std::env::current_dir()
|
||||||
|
.map_err(|err| Error::downgrade_error(format!("cannot get cwd: {err}")))?;
|
||||||
|
let virtual_file = cwd.join("__eval__.nix");
|
||||||
|
ctx.path_stack().push(virtual_file);
|
||||||
|
Ok(Self { ctx })
|
||||||
|
}
|
||||||
|
pub fn as_ctx(&mut self) -> &mut Ctx {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: PathStackProvider> Deref for PathDropGuard<'_, Ctx> {
|
||||||
|
type Target = Ctx;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Ctx: PathStackProvider> DerefMut for PathDropGuard<'_, Ctx> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
|||||||
path_str
|
path_str
|
||||||
} else {
|
} else {
|
||||||
let current_dir = ctx.get_current_dir();
|
let current_dir = ctx.get_current_dir();
|
||||||
|
dbg!(¤t_dir);
|
||||||
|
|
||||||
current_dir
|
current_dir
|
||||||
.join(&path_str)
|
.join(&path_str)
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ where
|
|||||||
Ctx: DowngradeContext,
|
Ctx: DowngradeContext,
|
||||||
F: FnOnce(&mut Ctx, &[SymId]) -> Result<R>,
|
F: FnOnce(&mut Ctx, &[SymId]) -> Result<R>,
|
||||||
{
|
{
|
||||||
// 1. Collect all top-level binding keys
|
// Collect all top-level binding keys
|
||||||
let mut binding_syms = HashSet::new();
|
let mut binding_syms = HashSet::new();
|
||||||
|
|
||||||
for entry in &entries {
|
for entry in &entries {
|
||||||
@@ -271,14 +271,14 @@ where
|
|||||||
|
|
||||||
let binding_keys: Vec<_> = binding_syms.into_iter().collect();
|
let binding_keys: Vec<_> = binding_syms.into_iter().collect();
|
||||||
|
|
||||||
// 2. Reserve slots for bindings
|
// Reserve slots for bindings
|
||||||
let slots_iter = ctx.reserve_slots(binding_keys.len());
|
let slots_iter = ctx.reserve_slots(binding_keys.len());
|
||||||
let slots_clone = slots_iter.clone();
|
let slots_clone = slots_iter.clone();
|
||||||
|
|
||||||
// 3. Create let scope bindings
|
// Create let scope bindings
|
||||||
let let_bindings: HashMap<_, _> = binding_keys.iter().copied().zip(slots_iter).collect();
|
let let_bindings: HashMap<_, _> = binding_keys.iter().copied().zip(slots_iter).collect();
|
||||||
|
|
||||||
// 4. Process entries in let scope
|
// Process entries in let scope
|
||||||
let body = ctx.with_let_scope(let_bindings, |ctx| {
|
let body = ctx.with_let_scope(let_bindings, |ctx| {
|
||||||
// Collect all bindings in a temporary AttrSet
|
// Collect all bindings in a temporary AttrSet
|
||||||
let mut temp_attrs = AttrSet {
|
let mut temp_attrs = AttrSet {
|
||||||
@@ -313,6 +313,5 @@ where
|
|||||||
body_fn(ctx, &binding_keys)
|
body_fn(ctx, &binding_keys)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// 5. Return the slots and body
|
|
||||||
Ok((slots_clone.collect(), body))
|
Ok((slots_clone.collect(), body))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::pin::Pin;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpDecl, OpState, RuntimeOptions, v8};
|
use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpState, RuntimeOptions, v8};
|
||||||
use deno_error::JsErrorClass;
|
use deno_error::JsErrorClass;
|
||||||
|
|
||||||
use crate::codegen::{CodegenContext, Compile};
|
|
||||||
use crate::context::{CtxPtr, PathDropGuard};
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ir::DowngradeContext;
|
|
||||||
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>>;
|
||||||
type LocalValue<'a> = v8::Local<'a, v8::Value>;
|
type LocalValue<'a> = v8::Local<'a, v8::Value>;
|
||||||
type LocalSymbol<'a> = v8::Local<'a, v8::Symbol>;
|
type LocalSymbol<'a> = v8::Local<'a, v8::Symbol>;
|
||||||
|
|
||||||
fn runtime_extension(ctx: CtxPtr) -> Extension {
|
pub(crate) trait RuntimeCtx: 'static {
|
||||||
|
fn get_current_dir(&self) -> PathBuf;
|
||||||
|
fn push_path_stack(&mut self, path: PathBuf) -> impl DerefMut<Target = Self>;
|
||||||
|
fn compile_code(&mut self, code: &str) -> Result<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_extension<Ctx: RuntimeCtx>() -> Extension {
|
||||||
const ESM: &[ExtensionFileSource] =
|
const ESM: &[ExtensionFileSource] =
|
||||||
&deno_core::include_js_files!(nix_runtime dir "runtime-ts/dist", "runtime.js");
|
&deno_core::include_js_files!(nix_runtime dir "runtime-ts/dist", "runtime.js");
|
||||||
const OPS: &[OpDecl] = &[
|
let ops = vec![
|
||||||
op_import(),
|
op_import::<Ctx>(),
|
||||||
op_read_file(),
|
op_read_file(),
|
||||||
op_path_exists(),
|
op_path_exists(),
|
||||||
op_resolve_path(),
|
op_resolve_path::<Ctx>(),
|
||||||
];
|
];
|
||||||
|
|
||||||
Extension {
|
Extension {
|
||||||
name: "nix_runtime",
|
name: "nix_runtime",
|
||||||
esm_files: Cow::Borrowed(ESM),
|
esm_files: Cow::Borrowed(ESM),
|
||||||
esm_entry_point: Some("ext:nix_runtime/runtime.js"),
|
esm_entry_point: Some("ext:nix_runtime/runtime.js"),
|
||||||
ops: Cow::Borrowed(OPS),
|
ops: Cow::Owned(ops),
|
||||||
op_state_fn: Some(Box::new(move |state| {
|
|
||||||
state.put(ctx);
|
|
||||||
})),
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@@ -67,9 +69,11 @@ use private::NixError;
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result<String, NixError> {
|
fn op_import<Ctx: RuntimeCtx>(
|
||||||
let ptr = state.borrow_mut::<CtxPtr>();
|
state: &mut OpState,
|
||||||
let ctx = unsafe { ptr.as_mut() };
|
#[string] path: String,
|
||||||
|
) -> std::result::Result<String, NixError> {
|
||||||
|
let ctx = state.borrow_mut::<Ctx>();
|
||||||
|
|
||||||
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
|
||||||
@@ -80,30 +84,13 @@ fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result
|
|||||||
absolute_path.push("default.nix")
|
absolute_path.push("default.nix")
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut guard = PathDropGuard::new(absolute_path.clone(), ctx);
|
|
||||||
let ctx = guard.as_ctx();
|
|
||||||
|
|
||||||
let content = std::fs::read_to_string(&absolute_path)
|
let content = std::fs::read_to_string(&absolute_path)
|
||||||
.map_err(|e| format!("Failed to read {}: {}", absolute_path.display(), e))?;
|
.map_err(|e| format!("Failed to read {}: {}", absolute_path.display(), e))?;
|
||||||
|
|
||||||
let root = rnix::Root::parse(&content);
|
let mut guard = ctx.push_path_stack(absolute_path);
|
||||||
if !root.errors().is_empty() {
|
let ctx = guard.deref_mut();
|
||||||
return Err(format!(
|
|
||||||
"Parse error in {}: {:?}",
|
|
||||||
absolute_path.display(),
|
|
||||||
root.errors()
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let expr = root.tree().expr().ok_or("No expression in file")?;
|
Ok(ctx.compile_code(&content).map_err(|err| err.to_string())?)
|
||||||
let expr_id = ctx
|
|
||||||
.as_mut()
|
|
||||||
.downgrade_ctx()
|
|
||||||
.downgrade(expr)
|
|
||||||
.map_err(|e| format!("Downgrade error: {}", e))?;
|
|
||||||
|
|
||||||
Ok(ctx.get_ir(expr_id).compile(Pin::get_ref(ctx.as_ref())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
@@ -119,12 +106,11 @@ fn op_path_exists(#[string] path: String) -> bool {
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
fn op_resolve_path(
|
fn op_resolve_path<Ctx: RuntimeCtx>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> std::result::Result<String, NixError> {
|
) -> std::result::Result<String, NixError> {
|
||||||
let ptr = state.borrow::<CtxPtr>();
|
let ctx = state.borrow::<Ctx>();
|
||||||
let ctx = unsafe { ptr.as_ref() };
|
|
||||||
|
|
||||||
// If already absolute, return as-is
|
// If already absolute, return as-is
|
||||||
if path.starts_with('/') {
|
if path.starts_with('/') {
|
||||||
@@ -141,14 +127,15 @@ fn op_resolve_path(
|
|||||||
.map_err(|e| format!("Failed to resolve path {}: {}", path, e))?)
|
.map_err(|e| format!("Failed to resolve path {}: {}", path, e))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Runtime {
|
pub(crate) struct Runtime<Ctx: RuntimeCtx> {
|
||||||
js_runtime: JsRuntime,
|
js_runtime: JsRuntime,
|
||||||
is_thunk_symbol: v8::Global<v8::Symbol>,
|
is_thunk_symbol: v8::Global<v8::Symbol>,
|
||||||
primop_metadata_symbol: v8::Global<v8::Symbol>,
|
primop_metadata_symbol: v8::Global<v8::Symbol>,
|
||||||
|
_marker: PhantomData<Ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runtime {
|
impl<Ctx: RuntimeCtx> Runtime<Ctx> {
|
||||||
pub(crate) fn new(ctx: CtxPtr) -> Result<Self> {
|
pub(crate) fn new() -> Result<Self> {
|
||||||
// Initialize V8 once
|
// Initialize V8 once
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
INIT.call_once(|| {
|
INIT.call_once(|| {
|
||||||
@@ -159,7 +146,7 @@ impl Runtime {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut js_runtime = JsRuntime::new(RuntimeOptions {
|
let mut js_runtime = JsRuntime::new(RuntimeOptions {
|
||||||
extensions: vec![runtime_extension(ctx)],
|
extensions: vec![runtime_extension::<Ctx>()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,10 +159,13 @@ impl Runtime {
|
|||||||
js_runtime,
|
js_runtime,
|
||||||
is_thunk_symbol,
|
is_thunk_symbol,
|
||||||
primop_metadata_symbol,
|
primop_metadata_symbol,
|
||||||
|
_marker: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eval(&mut self, script: String) -> Result<Value> {
|
pub(crate) fn eval(&mut self, script: String, ctx: Ctx) -> Result<Value> {
|
||||||
|
self.js_runtime.op_state().borrow_mut().put(ctx);
|
||||||
|
|
||||||
let global_value = self
|
let global_value = self
|
||||||
.js_runtime
|
.js_runtime
|
||||||
.execute_script("<eval>", script)
|
.execute_script("<eval>", script)
|
||||||
|
|||||||
Reference in New Issue
Block a user