From 9cfffc440fb63d009f58fa81fcbac3d4e2993dda Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Fri, 9 Jan 2026 16:52:15 +0800 Subject: [PATCH] refactor: tidy --- nix-js/runtime-ts/src/type-assert.ts | 4 +- nix-js/runtime-ts/src/types/global.d.ts | 2 - nix-js/src/context.rs | 51 ++++++---- nix-js/src/runtime.rs | 120 ++++++++++++------------ 4 files changed, 94 insertions(+), 83 deletions(-) diff --git a/nix-js/runtime-ts/src/type-assert.ts b/nix-js/runtime-ts/src/type-assert.ts index 2a2782e..74cc33f 100644 --- a/nix-js/runtime-ts/src/type-assert.ts +++ b/nix-js/runtime-ts/src/type-assert.ts @@ -20,7 +20,7 @@ const typeName = (value: NixValue): string => { if (typeof val === "object") return "attribute set"; throw new TypeError(`Unknown Nix type: ${typeof val}`); -} +}; /** * Force a value and assert it's a list @@ -52,7 +52,7 @@ export const force_function = (value: NixValue): NixFunction => { */ export const force_attrs = (value: NixValue): NixAttrs => { const forced = force(value); - if (!isAttrs(forced)) { + if (typeof forced !== "object" || Array.isArray(forced) || forced === null) { throw new TypeError(`Expected attribute set, got ${typeName(forced)}`); } return forced; diff --git a/nix-js/runtime-ts/src/types/global.d.ts b/nix-js/runtime-ts/src/types/global.d.ts index 354754e..2ee569e 100644 --- a/nix-js/runtime-ts/src/types/global.d.ts +++ b/nix-js/runtime-ts/src/types/global.d.ts @@ -1,7 +1,5 @@ import type { NixRuntime } from ".."; -export {}; - declare global { var Nix: NixRuntime; namespace Deno { diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index fded8d1..86e62b2 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -18,7 +18,7 @@ mod downgrade; pub struct Context { ctx: Pin>, - pub(crate) runtime: Runtime, + runtime: Runtime, } impl Default for Context { @@ -27,10 +27,20 @@ impl Default for Context { } } +pub(crate) struct CtxPtr(NonNull); +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 { pub fn new() -> Self { let mut ctx = Box::pin(Ctx::new()); - let ptr = unsafe { NonNull::new_unchecked(Pin::get_unchecked_mut(ctx.as_mut())) }; + let ptr = unsafe { CtxPtr(NonNull::new_unchecked(Pin::get_unchecked_mut(ctx.as_mut()))) }; let runtime = Runtime::new(ptr); Self { ctx, runtime } @@ -45,16 +55,24 @@ impl Context { if !root.errors().is_empty() { return Err(Error::parse_error(root.errors().iter().join("; "))); } - let root = ctx.as_mut().downgrade_ctx().downgrade(root.tree().expr().unwrap())?; + 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)] + pub(crate) fn eval_js(&mut self, code: String) -> Result { + self.runtime.eval(code) + } } #[pin_project::pin_project(PinnedDrop)] -pub struct Ctx { +pub(crate) struct Ctx { irs: Vec, symbols: DefaultStringInterner, global: NonNull>, @@ -72,22 +90,22 @@ impl PinnedDrop for Ctx { } } -pub struct PathDropGuard<'ctx> { +pub(crate) struct PathDropGuard<'ctx> { ctx: Pin<&'ctx mut Ctx>, } impl<'ctx> PathDropGuard<'ctx> { - pub fn new(path: PathBuf, mut ctx: Pin<&'ctx mut Ctx>) -> Self { + pub(crate) fn new(path: PathBuf, mut ctx: Pin<&'ctx mut Ctx>) -> Self { ctx.as_mut().project().path_stack.push(path); Self { ctx } } - pub fn new_cwd(mut ctx: Pin<&'ctx mut Ctx>) -> Self { + pub(crate) fn new_cwd(mut ctx: Pin<&'ctx mut Ctx>) -> Self { let cwd = std::env::current_dir().unwrap(); let virtual_file = cwd.join("__eval__.nix"); ctx.as_mut().project().path_stack.push(virtual_file); Self { ctx } } - pub fn as_ctx<'a>(&'a mut self) -> &'a mut Pin<&'ctx mut Ctx> { + pub(crate) fn as_ctx<'a>(&'a mut self) -> &'a mut Pin<&'ctx mut Ctx> { &mut self.ctx } } @@ -165,7 +183,6 @@ impl Ctx { } pub fn downgrade_ctx<'a>(self: Pin<&'a mut Self>) -> DowngradeCtx<'a> { - // SAFETY: `global` is readonly let global_ref = unsafe { self.global.as_ref() }; DowngradeCtx::new(self, global_ref) } @@ -827,14 +844,14 @@ mod test { let main_path = temp_dir.path().join("main.nix"); let main_content = r#" -let - lib = import ./lib.nix; - helper = import ./subdir/helper.nix; -in { - result1 = lib.multiply 3 4; - result2 = helper.subtract 10 3; -} -"#; + let + lib = import ./lib.nix; + helper = import ./subdir/helper.nix; + in { + result1 = lib.multiply 3 4; + result2 = helper.subtract 10 3; + } + "#; std::fs::write(&main_path, main_content).unwrap(); let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display()); diff --git a/nix-js/src/runtime.rs b/nix-js/src/runtime.rs index ead7f6b..dd87ac9 100644 --- a/nix-js/src/runtime.rs +++ b/nix-js/src/runtime.rs @@ -1,36 +1,37 @@ use std::borrow::Cow; -use std::cell::RefCell; use std::pin::Pin; -use std::ptr::NonNull; use std::sync::Once; -use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpState, RuntimeOptions, v8}; +use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpDecl, OpState, RuntimeOptions, v8}; use deno_error::js_error_wrapper; use crate::codegen::{CodegenContext, Compile}; -use crate::context::{Ctx, PathDropGuard}; +use crate::context::{CtxPtr, PathDropGuard}; use crate::error::{Error, Result}; use crate::ir::DowngradeContext; use crate::value::{AttrSet, Const, List, Symbol, Value}; type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>; +type LocalValue<'a> = v8::Local<'a, v8::Value>; +type LocalSymbol<'a> = v8::Local<'a, v8::Symbol>; -fn runtime_extension(ctx: NonNull) -> Extension { +fn runtime_extension(ctx: CtxPtr) -> Extension { const ESM: &[ExtensionFileSource] = &deno_core::include_js_files!(nix_runtime dir "runtime-ts/dist", "runtime.js"); + const OPS: &[OpDecl] = &[ + op_import(), + op_read_file(), + op_path_exists(), + op_resolve_path(), + ]; Extension { name: "nix_runtime", esm_files: Cow::Borrowed(ESM), esm_entry_point: Some("ext:nix_runtime/runtime.js"), - ops: Cow::Owned(vec![ - op_import(), - op_read_file(), - op_path_exists(), - op_resolve_path(), - ]), + ops: Cow::Borrowed(OPS), op_state_fn: Some(Box::new(move |state| { - state.put(RefCell::new(ctx)); + state.put(ctx); })), enabled: true, ..Default::default() @@ -58,19 +59,18 @@ impl std::error::Error for SimpleErrorWrapper { } } +js_error_wrapper!(SimpleErrorWrapper, NixError, "EvalError"); impl From for NixError { fn from(value: String) -> Self { NixError(SimpleErrorWrapper(value)) } } -js_error_wrapper!(SimpleErrorWrapper, NixError, "EvalError"); - #[deno_core::op2] #[string] fn op_import(state: &mut OpState, #[string] path: String) -> std::result::Result { - let mut ptr = state.borrow::>>().borrow_mut(); - let ctx = unsafe { Pin::new_unchecked(ptr.as_mut()) }; + let ptr = state.borrow_mut::(); + let ctx = unsafe { ptr.as_mut() }; let current_dir = ctx.get_current_dir(); let absolute_path = current_dir @@ -126,7 +126,7 @@ fn op_resolve_path( state: &mut OpState, #[string] path: String, ) -> std::result::Result { - let ptr = state.borrow::>>().borrow(); + let ptr = state.borrow::(); let ctx = unsafe { ptr.as_ref() }; // If already absolute, return as-is @@ -144,14 +144,14 @@ fn op_resolve_path( .map_err(|e| -> NixError { format!("Failed to resolve path {}: {}", path, e).into() }) } -pub struct Runtime { +pub(crate) struct Runtime { js_runtime: JsRuntime, is_thunk_symbol: v8::Global, primop_metadata_symbol: v8::Global, } impl Runtime { - pub fn new(ctx: NonNull) -> Self { + pub(crate) fn new(ctx: CtxPtr) -> Self { // Initialize V8 once static INIT: Once = Once::new(); INIT.call_once(|| { @@ -178,7 +178,7 @@ impl Runtime { } } - pub fn eval(&mut self, script: String) -> Result { + pub(crate) fn eval(&mut self, script: String) -> Result { let global_value = self .js_runtime .execute_script("", script) @@ -190,13 +190,16 @@ impl Runtime { let is_thunk_symbol = v8::Local::new(scope, &self.is_thunk_symbol); let primop_metadata_symbol = v8::Local::new(scope, &self.primop_metadata_symbol); - Ok(to_value(local_value, scope, is_thunk_symbol, primop_metadata_symbol)) + Ok(to_value( + local_value, + scope, + is_thunk_symbol, + primop_metadata_symbol, + )) } /// get (IS_THUNK, PRIMOP_METADATA) - fn get_symbols( - scope: &v8::PinnedRef<'_, v8::HandleScope<'_>>, - ) -> (v8::Global, v8::Global) { + fn get_symbols(scope: &ScopeRef) -> (v8::Global, v8::Global) { let global = scope.get_current_context().global(scope); let nix_key = v8::String::new(scope, "Nix").unwrap(); let nix_obj = global @@ -220,10 +223,10 @@ impl Runtime { } fn to_value<'a>( - val: v8::Local<'a, v8::Value>, + val: LocalValue<'a>, scope: &ScopeRef<'a, '_>, - is_thunk_symbol: v8::Local<'a, v8::Symbol>, - primop_metadata_symbol: v8::Local<'a, v8::Symbol>, + is_thunk_symbol: LocalSymbol<'a>, + primop_metadata_symbol: LocalSymbol<'a>, ) -> Value { match () { _ if val.is_big_int() => { @@ -257,10 +260,8 @@ fn to_value<'a>( Value::List(List::new(list)) } _ if val.is_function() => { - if let Some(name) = primop_app_name(val, scope, primop_metadata_symbol) { - Value::PrimOpApp(name) - } else if let Some(name) = primop_name(val, scope, primop_metadata_symbol) { - Value::PrimOp(name) + if let Some(primop) = to_primop(val, scope, primop_metadata_symbol) { + primop } else { Value::Func } @@ -280,7 +281,10 @@ fn to_value<'a>( let key = keys.get_index(scope, i).unwrap(); let val = val.get(scope, key).unwrap(); let key = key.to_rust_string_lossy(scope); - (Symbol::new(key), to_value(val, scope, is_thunk_symbol, primop_metadata_symbol)) + ( + Symbol::new(key), + to_value(val, scope, is_thunk_symbol, primop_metadata_symbol), + ) }) .collect(); Value::AttrSet(AttrSet::new(attrs)) @@ -289,7 +293,7 @@ fn to_value<'a>( } } -fn is_thunk<'a>(val: v8::Local<'a, v8::Value>, scope: &ScopeRef<'a, '_>, symbol: v8::Local<'a, v8::Symbol>) -> bool { +fn is_thunk<'a>(val: LocalValue<'a>, scope: &ScopeRef<'a, '_>, symbol: LocalSymbol<'a>) -> bool { if !val.is_object() { return false; } @@ -298,39 +302,31 @@ fn is_thunk<'a>(val: v8::Local<'a, v8::Value>, scope: &ScopeRef<'a, '_>, symbol: matches!(obj.get(scope, symbol.into()), Some(v) if v.is_true()) } -fn primop_name<'a>(val: v8::Local<'a, v8::Value>, scope: &ScopeRef<'a, '_>, symbol: v8::Local<'a, v8::Symbol>) -> Option { +fn to_primop<'a>( + val: LocalValue<'a>, + scope: &ScopeRef<'a, '_>, + symbol: LocalSymbol<'a>, +) -> Option { if !val.is_function() { return None; } let obj = val.to_object(scope).unwrap(); + let metadata = obj.get(scope, symbol.into())?.to_object(scope)?; - if let Some(metadata) = obj.get(scope, symbol.into()) - && let Some(metadata_obj) = metadata.to_object(scope) - && let Some(name_key) = v8::String::new(scope, "name") - && let Some(name_val) = metadata_obj.get(scope, name_key.into()) - { - Some(name_val.to_rust_string_lossy(scope)) + let name_key = v8::String::new(scope, "name")?; + let name = metadata + .get(scope, name_key.into())? + .to_rust_string_lossy(scope); + + let applied_key = v8::String::new(scope, "applied")?; + let applied_val = metadata.get(scope, applied_key.into())?; + let applied = applied_val.to_number(scope)?.value(); + + if applied == 0.0 { + Some(Value::PrimOp(name)) } else { - None - } -} - -fn primop_app_name<'a>(val: v8::Local<'a, v8::Value>, scope: &ScopeRef<'a, '_>, symbol: v8::Local<'a, v8::Symbol>) -> Option { - let name = primop_name(val, scope, symbol)?; - - let obj = val.to_object(scope).unwrap(); - - if let Some(metadata) = obj.get(scope, symbol.into()) - && let Some(metadata_obj) = metadata.to_object(scope) - && let Some(applied_key) = v8::String::new(scope, "applied") - && let Some(applied_val) = metadata_obj.get(scope, applied_key.into()) - && let Some(applied_num) = applied_val.to_number(scope) - && applied_num.value() > 0.0 - { - Some(name) - } else { - None + Some(Value::PrimOpApp(name)) } } @@ -343,10 +339,10 @@ mod test { fn to_value_working() { let mut ctx = Context::new(); assert_eq!( - ctx.runtime.eval( + ctx.eval_js( "({ - test: [1., 9223372036854775807n, true, false, 'hello world!'] - })" + test: [1., 9223372036854775807n, true, false, 'hello world!'] + })" .into(), ) .unwrap(),