git.delta.rocks / fleet / refs/commits / 14689aa0c750

difftreelog

source

crates/nix-eval/src/lib.rs39.1 KiBsourcehistory
1use std::borrow::Cow;2use std::cell::RefCell;3use std::collections::HashMap;4use std::ffi::{CStr, CString, c_char, c_int, c_uint, c_void};5use std::ptr::{null, null_mut};6use std::sync::{Arc, LazyLock, OnceLock};7use std::{array, fmt, slice};89use anyhow::{Context, anyhow, bail};10use camino::{Utf8Path, Utf8PathBuf};11use itertools::Itertools;12use serde::Serialize;13use serde::de::DeserializeOwned;14use std::mem::transmute;1516pub use anyhow::Result;17use tracing::{Span, instrument, warn};1819use self::logging::{ErrorInfoBuilder, nix_logging_cxx};20use self::nix_cxx::set_fetcher_setting;21use self::nix_raw::{22	BindingsBuilder as c_bindings_builder, EvalState as c_eval_state, GC_SUCCESS,23	GC_allow_register_threads, GC_get_stack_base, GC_register_my_thread, GC_stack_base,24	GC_thread_is_registered, GC_unregister_my_thread, ListBuilder as c_list_builder, PrimOp,25	PrimOpFun, Store as c_store, StorePath as c_store_path, alloc_primop, alloc_value,26	bindings_builder_free, bindings_builder_insert, c_context, c_context_create, c_context_free,27	clear_err, copy_value, err_NIX_ERR_KEY, err_NIX_ERR_NIX_ERROR, err_NIX_ERR_OVERFLOW,28	err_NIX_ERR_UNKNOWN, err_NIX_OK, err_code, err_info_msg, err_msg, eval_state_build,29	eval_state_builder_load, eval_state_builder_new, eval_state_builder_set_eval_setting,30	expr_eval_from_string, fetchers_settings, fetchers_settings_free, fetchers_settings_new,31	flake_lock, flake_lock_flags, flake_lock_flags_free, flake_lock_flags_new, flake_reference,32	flake_reference_and_fragment_from_string, flake_reference_parse_flags,33	flake_reference_parse_flags_free, flake_reference_parse_flags_new,34	flake_reference_parse_flags_set_base_directory, flake_settings, flake_settings_free,35	flake_settings_new, gc_now as gc_now_raw, get_attr_byname, get_attr_name_byidx, get_attrs_size,36	get_list_byidx, get_list_size, get_string, get_type, has_attr_byname, init_bool, init_int,37	init_primop, init_string, libexpr_init, libstore_init, libutil_init, list_builder_free,38	list_builder_insert, locked_flake, locked_flake_free, locked_flake_get_output_attrs,39	make_attrs, make_bindings_builder, make_list, make_list_builder, realised_string,40	realised_string_free, realised_string_get_buffer_size, realised_string_get_buffer_start,41	realised_string_get_store_path, realised_string_get_store_path_count, register_primop,42	set_err_msg, setting_get, setting_set, state_free, store_copy_closure, store_free, store_open,43	store_parse_path, store_path_free, store_path_name, string_realise, value, value_call,44	value_decref, value_force, value_incref,45};4647// Contains macros helpers48pub mod drv;49pub mod logging;50#[doc(hidden)]51pub mod macros;52pub mod scheduler;5354#[doc(hidden)]55pub mod __macro_support {56	pub use std::collections::hash_map::HashMap;5758	pub use anyhow::Context;59	pub use tokio::task::block_in_place;60}61pub mod util;6263#[allow(64	non_upper_case_globals,65	non_camel_case_types,66	non_snake_case,67	dead_code68)]69mod nix_raw {70	include!(concat!(env!("OUT_DIR"), "/bindings.rs"));71}72#[cxx::bridge]73pub mod nix_cxx {74	struct AddFileToStoreResult {75		error: String,76		store_path: String,77		hash: String,78	}79	struct CxxProfileGeneration {80		id: u64,81		store_path: String,82		creation_time_unix: i64,83		current: bool,84	}85	struct CxxListGenerationsResult {86		error: String,87		generations: Vec<CxxProfileGeneration>,88	}89	struct CxxBuildResult {90		error: String,91		outputs: Vec<String>,92	}93	struct CxxPathInfo {94		error: String,95		nar_hash: String,96		nar_size: u64,97		references: Vec<String>,98		sigs: Vec<String>,99	}100	unsafe extern "C++" {101		type nix_fetchers_settings;102		type Store;103		include!("nix-eval/src/lib.hh");104105		#[allow(clippy::missing_safety_doc)]106		unsafe fn set_fetcher_setting(107			settings: *mut nix_fetchers_settings,108			setting: *const c_char,109			value: *const c_char,110		);111112		#[allow(clippy::missing_safety_doc)]113		unsafe fn switch_profile(store: *mut Store, profile: &str, store_path: &str) -> String;114115		#[allow(clippy::missing_safety_doc)]116		unsafe fn sign_closure(store: *mut Store, store_path: &str, key_file: &str) -> String;117118		#[allow(clippy::missing_safety_doc)]119		unsafe fn add_file_to_store(120			store: *mut Store,121			name: &str,122			path: &str,123		) -> AddFileToStoreResult;124125		fn list_generations(profile_path: &str) -> CxxListGenerationsResult;126127		#[allow(clippy::missing_safety_doc)]128		unsafe fn build_drv_outputs(129			store: *mut Store,130			drv_path: &str,131			output_names_joined: &str,132		) -> CxxBuildResult;133134		#[allow(clippy::missing_safety_doc)]135		unsafe fn substitute_paths(store: *mut Store, paths_joined: &str) -> CxxBuildResult;136137		#[allow(clippy::missing_safety_doc)]138		unsafe fn is_valid_path(store: *mut Store, path: &str) -> bool;139140		#[allow(clippy::missing_safety_doc)]141		unsafe fn query_path_info(store: *mut Store, path: &str) -> CxxPathInfo;142143		#[allow(clippy::missing_safety_doc)]144		unsafe fn add_temp_root(store: *mut Store, path: &str) -> String;145146		#[allow(clippy::missing_safety_doc)]147		unsafe fn compute_closure(store: *mut Store, path: &str) -> CxxBuildResult;148149		#[allow(clippy::missing_safety_doc)]150		unsafe fn nar_from_path(151			store: *mut Store,152			path: &str,153			sink_data: usize,154			sink: fn(usize, &[u8]) -> bool,155		) -> String;156	}157}158159#[derive(Debug, PartialEq, Eq)]160pub enum NixType {161	Thunk,162	Int,163	Float,164	Bool,165	String,166	Path,167	Null,168	Attrs,169	List,170	Function,171	External,172}173impl NixType {174	fn from_int(c: c_uint) -> Self {175		match c {176			0 => Self::Thunk,177			1 => Self::Int,178			2 => Self::Float,179			3 => Self::Bool,180			4 => Self::String,181			5 => Self::Path,182			6 => Self::Null,183			7 => Self::Attrs,184			8 => Self::List,185			9 => Self::Function,186			10 => Self::External,187			_ => unreachable!("unknown nix type: {c}"),188		}189	}190}191192enum FunctorKind {193	Function,194	Functor,195}196197#[derive(Debug)]198#[repr(i32)]199pub enum NixErrorKind {200	Unknown = err_NIX_ERR_UNKNOWN,201	Overflow = err_NIX_ERR_OVERFLOW,202	Key = err_NIX_ERR_KEY,203	Generic = err_NIX_ERR_NIX_ERROR,204}205impl NixErrorKind {206	fn from_int(v: c_int) -> Option<Self> {207		Some(match v {208			0 => return None,209			nix_raw::err_NIX_ERR_UNKNOWN => Self::Unknown,210			nix_raw::err_NIX_ERR_OVERFLOW => Self::Overflow,211			nix_raw::err_NIX_ERR_KEY => Self::Key,212			nix_raw::err_NIX_ERR_NIX_ERROR => Self::Generic,213			_ => {214				debug_assert!(false, "unexpected nix error kind: {v}");215				Self::Unknown216			}217		})218	}219}220221pub fn gc_now() {222	unsafe { gc_now_raw() };223}224225pub fn gc_register_my_thread() {226	assert_eq!(unsafe { GC_thread_is_registered() }, 0);227228	let mut sb = GC_stack_base {229		mem_base: null_mut(),230	};231	let r = unsafe { GC_get_stack_base(&mut sb) };232	if r as u32 != GC_SUCCESS {233		panic!("failed to get thread stack base");234	}235	unsafe { GC_register_my_thread(&sb) };236}237pub fn gc_unregister_my_thread() {238	assert_eq!(unsafe { GC_thread_is_registered() }, 1);239240	unsafe { GC_unregister_my_thread() };241}242243pub struct ThreadRegisterGuard {}244impl ThreadRegisterGuard {245	#[allow(clippy::new_without_default)]246	pub fn new() -> Self {247		gc_register_my_thread();248		Self {}249	}250}251impl Drop for ThreadRegisterGuard {252	fn drop(&mut self) {253		gc_unregister_my_thread();254	}255}256257#[repr(transparent)]258pub struct NixContext(*mut c_context);259impl NixContext {260	pub fn set_err_raw(&mut self, err: NixErrorKind, msg: &CStr) {261		unsafe { set_err_msg(self.0, err as c_int, msg.as_ptr()) };262	}263	pub fn set_err(&mut self, err: anyhow::Error) {264		let fmt = format!("{err:?}").replace("\0", "\\0");265		self.set_err_raw(266			NixErrorKind::Generic,267			&CString::new(fmt).expect("NUL bytes were just replaced"),268		);269	}270	pub fn new() -> Self {271		let ctx = unsafe { c_context_create() };272		Self(ctx)273	}274	fn error_kind(&self) -> Option<NixErrorKind> {275		let code = unsafe { err_code(self.0) };276		NixErrorKind::from_int(code)277	}278	fn error<'t>(&self) -> Option<(Cow<'t, str>, Option<Box<ErrorInfoBuilder>>)> {279		if let NixErrorKind::Generic = self.error_kind()? {280			let ei = unsafe { logging::nix_logging_cxx::extract_error_info(self.0) };281			let mut err_out = String::new();282			unsafe {283				err_info_msg(284					null_mut(),285					self.0,286					Some(copy_nix_str),287					(&raw mut err_out).cast(),288				)289			};290			return Some((Cow::Owned(err_out), Some(ei)));291		};292293		// TODO: Can throw error (resulting in panic) if unable to retrieve error. Should be able to resolve by passing context as a first argument,294		// but it looks ugly295		let str = unsafe { err_msg(null_mut(), self.0, null_mut()) };296		Some((unsafe { CStr::from_ptr(str) }.to_string_lossy(), None))297	}298	fn clean_err(&mut self) {299		unsafe {300			clear_err(self.0);301		}302	}303304	fn bail_if_error(&self) -> Result<()> {305		if let Some((err, stack)) = self.error() {306			let mut e = Err(anyhow!("{err}"));307			if let Some(stack) = stack {308				for ele in stack.stack_frames {309					e = e.with_context(|| {310						if ele.pos.is_empty() {311							ele.msg312						} else {313							format!("{} at {}", ele.msg, ele.pos)314						}315					})316				}317			}318			return e.context("<nix frames>");319		};320		Ok(())321	}322323	fn run_in_context<T>(&mut self, f: impl FnOnce(*mut c_context) -> T) -> Result<T> {324		self.clean_err();325		let o = f(self.0);326		self.bail_if_error()?;327		self.clean_err();328		Ok(o)329	}330}331332impl Default for NixContext {333	fn default() -> Self {334		Self::new()335	}336}337impl Drop for NixContext {338	fn drop(&mut self) {339		unsafe {340			c_context_free(self.0);341		}342	}343}344struct GlobalState {345	// Store should be valid as long as EvalState is valid346	#[allow(dead_code)]347	store: Arc<Store>,348	state: EvalState,349}350impl GlobalState {351	fn new() -> Result<Self> {352		let mut ctx = NixContext::new();353		let store = Arc::new(354			ctx.run_in_context(|c| unsafe { store_open(c, c"auto".as_ptr(), null_mut()) })355				.map(Store)?,356		);357358		let builder = ctx.run_in_context(|c| unsafe { eval_state_builder_new(c, store.0) })?;359		ctx.run_in_context(|c| unsafe { eval_state_builder_load(c, builder) })?;360		ctx.run_in_context(|c| unsafe {361			eval_state_builder_set_eval_setting(362				c,363				builder,364				c"lazy-trees".as_ptr(),365				c"true".as_ptr(),366			)367		})?;368		ctx.run_in_context(|c| unsafe {369			eval_state_builder_set_eval_setting(370				c,371				builder,372				c"lazy-locks".as_ptr(),373				c"true".as_ptr(),374			)375		})?;376		let state = ctx377			.run_in_context(|c| unsafe { eval_state_build(c, builder) })378			.map(EvalState)?;379380		Ok(Self { store, state })381	}382}383384struct ThreadState {385	ctx: NixContext,386}387impl ThreadState {388	fn new() -> Result<Self> {389		let ctx = NixContext::new();390391		Ok(Self { ctx })392	}393}394395static GLOBAL_STATE: LazyLock<GlobalState> =396	LazyLock::new(|| GlobalState::new().expect("global state init shouldn't fail"));397398thread_local! {399	static THREAD_STATE: RefCell<ThreadState> = RefCell::new(ThreadState::new().expect("thread state init shouldn't fail"));400}401pub(crate) fn with_default_context<T>(402	f: impl FnOnce(*mut c_context, *mut c_eval_state) -> T,403) -> Result<T> {404	let global = &GLOBAL_STATE.state;405	let (ctx, state) = THREAD_STATE.with_borrow_mut(|w| (w.ctx.0, global.0));406	let mut ctx = NixContext(ctx);407	let v = ctx.run_in_context(|c| f(c, state));408	// It is reused for thread409	std::mem::forget(ctx);410	v411}412413pub fn set_setting(s: &CStr, v: &CStr) -> Result<()> {414	with_default_context(|c, _| unsafe { setting_set(c, s.as_ptr(), v.as_ptr()) }).map(|_| ())415}416417pub fn get_setting(s: &CStr) -> Result<String> {418	let mut out = String::new();419	with_default_context(|c, _| unsafe {420		setting_get(c, s.as_ptr(), Some(copy_nix_str), (&raw mut out).cast())421	})?;422	Ok(out)423}424425#[derive(Debug)]426pub struct AddedFile {427	pub store_path: Utf8PathBuf,428	pub hash: String,429}430431#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]432pub struct ProfileGeneration {433	pub id: u64,434	pub store_path: Utf8PathBuf,435	pub creation_time_unix: i64,436	pub current: bool,437}438439#[instrument]440pub fn list_generations(profile_path: &str) -> Result<Vec<ProfileGeneration>> {441	let res = nix_cxx::list_generations(profile_path);442	if !res.error.is_empty() {443		bail!(444			"failed to list generations at {profile_path}: {}",445			res.error446		);447	}448	Ok(res449		.generations450		.into_iter()451		.map(|g| ProfileGeneration {452			id: g.id,453			store_path: Utf8PathBuf::from(g.store_path),454			creation_time_unix: g.creation_time_unix,455			current: g.current,456		})457		.collect())458}459460pub struct FetchSettings(*mut fetchers_settings);461impl FetchSettings {462	pub fn new() -> Self {463		Self::try_new().expect("allocation should not fail")464	}465	fn try_new() -> Result<Self> {466		with_default_context(|c, _| unsafe { fetchers_settings_new(c) }).map(Self)467	}468	pub fn set(&mut self, setting: &CStr, value: &CStr) {469		unsafe {470			set_fetcher_setting(self.0.cast(), setting.as_ptr(), value.as_ptr());471		};472	}473}474unsafe impl Send for FetchSettings {}475unsafe impl Sync for FetchSettings {}476477impl Default for FetchSettings {478	fn default() -> Self {479		Self::new()480	}481}482483impl Drop for FetchSettings {484	fn drop(&mut self) {485		unsafe { fetchers_settings_free(self.0) };486	}487}488pub struct FlakeSettings(*mut flake_settings);489impl FlakeSettings {490	pub fn new() -> Result<Self> {491		with_default_context(|c, _| unsafe { flake_settings_new(c) }).map(Self)492	}493}494unsafe impl Send for FlakeSettings {}495unsafe impl Sync for FlakeSettings {}496impl Drop for FlakeSettings {497	fn drop(&mut self) {498		unsafe {499			flake_settings_free(self.0);500		}501	}502}503504pub struct FlakeReferenceParseFlags(*mut flake_reference_parse_flags);505impl FlakeReferenceParseFlags {506	pub fn new(settings: &FlakeSettings) -> Result<Self> {507		with_default_context(|c, _| unsafe { flake_reference_parse_flags_new(c, settings.0) })508			.map(Self)509	}510	pub fn set_base_dir(&mut self, dir: &str) -> Result<()> {511		with_default_context(|c, _| {512			unsafe {513				flake_reference_parse_flags_set_base_directory(514					c,515					self.0,516					dir.as_ptr().cast(),517					dir.len(),518				)519			};520		})521	}522}523impl Drop for FlakeReferenceParseFlags {524	fn drop(&mut self) {525		unsafe {526			flake_reference_parse_flags_free(self.0);527		}528	}529}530pub struct FlakeLockFlags(*mut flake_lock_flags);531impl FlakeLockFlags {532	pub fn new(settings: &FlakeSettings) -> Result<Self> {533		let o = with_default_context(|c, _| unsafe { flake_lock_flags_new(c, settings.0) })534			.map(Self)?;535		// with_default_context(|c, _| unsafe { flake_lock_flags_set_mode_virtual(c, o.0) })?;536537		Ok(o)538	}539}540impl Drop for FlakeLockFlags {541	fn drop(&mut self) {542		unsafe {543			flake_lock_flags_free(self.0);544		}545	}546}547548pub(crate) unsafe extern "C" fn copy_nix_str(549	start: *const c_char,550	n: c_uint,551	user_data: *mut c_void,552) {553	let s = unsafe { slice::from_raw_parts(start.cast::<u8>(), n as usize) };554	let s = std::str::from_utf8(s).expect("c string has invalid utf-8");555	unsafe { *user_data.cast::<String>() = s.to_owned() };556}557558pub struct Store(*mut c_store);559unsafe impl Send for Store {}560unsafe impl Sync for Store {}561562#[derive(Debug, Clone)]563pub struct PathInfo {564	pub nar_hash: String,565	pub nar_size: u64,566	pub references: Vec<Utf8PathBuf>,567	pub sigs: Vec<String>,568}569570pub fn eval_store() -> Arc<Store> {571	GLOBAL_STATE.store.clone()572}573574impl Store {575	pub fn open(uri: &str) -> Result<Self> {576		let uri = CString::new(uri)?;577		let ptr = with_default_context(|c, _| unsafe { store_open(c, uri.as_ptr(), null_mut()) })?;578		if ptr.is_null() {579			bail!("failed to open store");580		}581		Ok(Store(ptr))582	}583584	pub fn parse_path(&self, path: &Utf8Path) -> Result<StorePath> {585		let path = CString::new(path.as_str()).expect("valid cstr");586		with_default_context(|c, _| {587			StorePath(unsafe { store_parse_path(c, self.0, path.as_ptr()) })588		})589	}590591	#[instrument(skip(self))]592	pub fn sign_closure(&self, path: &Utf8Path, key_file: &Utf8Path) -> Result<()> {593		let err = with_default_context(|_, _| unsafe {594			nix_cxx::sign_closure(self.as_ptr().cast(), path.as_str(), key_file.as_str())595		})?596		.to_string();597598		if err.is_empty() {599			Ok(())600		} else {601			bail!("failed to sign {path}: {err}");602		}603	}604605	#[instrument(skip(self, dst))]606	pub fn copy_to(&self, dst: &Store, path: &Utf8Path) -> Result<()> {607		let sp = self608			.parse_path(path)609			.context("failed to parse store path")?;610		let rc = with_default_context(|c, _| unsafe {611			store_copy_closure(c, self.as_ptr(), dst.0, sp.as_ptr())612		})?;613		if rc != err_NIX_OK {614			bail!("store_copy_closure failed (code {rc})");615		}616		Ok(())617	}618619	/// Would only work with local store.620	#[instrument(skip(self))]621	pub fn switch_profile(&self, profile: &str, path: &Utf8Path) -> Result<()> {622		let msg = unsafe { nix_cxx::switch_profile(self.as_ptr().cast(), profile, path.as_str()) };623		if msg.is_empty() {624			Ok(())625		} else {626			bail!("failed to switch profile {profile}: {msg}");627		}628	}629630	#[instrument(skip(self))]631	pub fn add_file(&self, name: &str, path: &Utf8Path) -> Result<AddedFile> {632		let msg = unsafe { nix_cxx::add_file_to_store(self.as_ptr().cast(), name, path.as_str()) };633		if !msg.error.is_empty() {634			bail!("failed to add {path} to store: {}", msg.error)635		}636		Ok(AddedFile {637			store_path: Utf8PathBuf::from(msg.store_path),638			hash: msg.hash,639		})640	}641642	#[instrument(skip(self, paths))]643	pub fn substitute_paths(&self, paths: &[Utf8PathBuf]) -> Result<Vec<Utf8PathBuf>> {644		let joined = paths.into_iter().join("\n");645		let res = unsafe { nix_cxx::substitute_paths(self.as_ptr().cast(), &joined) };646		if !res.error.is_empty() {647			warn!("substitute_paths reported: {}", res.error);648		}649		Ok(res.outputs.into_iter().map(Utf8PathBuf::from).collect())650	}651652	#[instrument(skip(self))]653	pub fn is_valid_path(&self, path: &Utf8Path) -> bool {654		unsafe { nix_cxx::is_valid_path(self.as_ptr().cast(), path.as_str()) }655	}656657	#[instrument(skip(self))]658	pub fn query_path_info(&self, path: &Utf8Path) -> Result<PathInfo> {659		let res = unsafe { nix_cxx::query_path_info(self.as_ptr().cast(), path.as_str()) };660		if !res.error.is_empty() {661			bail!("failed to query path info for {path}: {}", res.error);662		}663		Ok(PathInfo {664			nar_hash: res.nar_hash,665			nar_size: res.nar_size,666			references: res.references.into_iter().map(Utf8PathBuf::from).collect(),667			sigs: res.sigs,668		})669	}670671	/// The root is held for the lifetime of this process.672	#[instrument(skip(self))]673	pub fn add_temp_root(&self, path: &Utf8Path) -> Result<()> {674		let msg = unsafe { nix_cxx::add_temp_root(self.as_ptr().cast(), path.as_str()) };675		if !msg.is_empty() {676			bail!("failed to add temp root for {path}: {msg}");677		}678		Ok(())679	}680681	#[instrument(skip(self))]682	pub fn compute_closure(&self, path: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {683		let res = unsafe { nix_cxx::compute_closure(self.as_ptr().cast(), path.as_str()) };684		if !res.error.is_empty() {685			bail!("failed to compute closure of {path}: {}", res.error);686		}687		Ok(res.outputs.into_iter().map(Utf8PathBuf::from).collect())688	}689690	#[instrument(skip(self, out))]691	pub fn nar_from_path(&self, path: &Utf8Path, out: &mut dyn std::io::Write) -> Result<()> {692		struct SinkState<'a> {693			out: &'a mut dyn std::io::Write,694			error: Option<std::io::Error>,695		}696		fn sink(state: usize, data: &[u8]) -> bool {697			let state = unsafe { &mut *(state as *mut SinkState) };698			match state.out.write_all(data) {699				Ok(()) => true,700				Err(e) => {701					state.error = Some(e);702					false703				}704			}705		}706		let mut state = SinkState { out, error: None };707		let msg = unsafe {708			nix_cxx::nar_from_path(709				self.as_ptr().cast(),710				path.as_str(),711				(&raw mut state) as usize,712				sink,713			)714		};715		if let Some(e) = state.error {716			return Err(anyhow!(e).context(format!("nar sink failed for {path}")));717		}718		if !msg.is_empty() {719			bail!("failed to dump nar of {path}: {msg}");720		}721		Ok(())722	}723724	#[instrument(skip(self))]725	pub fn build_drv_outputs(726		&self,727		drv_path: &Utf8Path,728		output_names: &[String],729	) -> Result<Vec<String>> {730		let joined = output_names.join("\n");731		let res =732			unsafe { nix_cxx::build_drv_outputs(self.as_ptr().cast(), drv_path.as_str(), &joined) };733		if !res.error.is_empty() {734			bail!("build of {drv_path} failed: {}", res.error);735		}736		Ok(res.outputs)737	}738739	#[instrument(skip(self))]740	pub fn store_dir(&self) -> Result<Utf8PathBuf> {741		let mut out = String::new();742		with_default_context(|c, es| unsafe {743			nix_raw::store_get_storedir(c, self.as_ptr(), Some(copy_nix_str), (&raw mut out).cast())744		})?;745		let p = Utf8PathBuf::from(out);746		assert!(p.is_absolute());747		Ok(p)748	}749750	fn as_ptr(&self) -> *mut c_store {751		self.0752	}753}754impl Drop for Store {755	fn drop(&mut self) {756		unsafe { store_free(self.0) }757	}758}759760#[repr(transparent)]761pub struct EvalState(*mut c_eval_state);762unsafe impl Send for EvalState {}763unsafe impl Sync for EvalState {}764765impl Drop for EvalState {766	fn drop(&mut self) {767		unsafe {768			state_free(self.0);769		}770	}771}772773pub struct FlakeReference(*mut flake_reference);774impl FlakeReference {775	#[instrument(name = "new-flake-reference", skip(flake, parse, fetch))]776	pub fn new(777		s: &str,778		flake: &FlakeSettings,779		parse: &FlakeReferenceParseFlags,780		fetch: &FetchSettings,781	) -> Result<(Self, String)> {782		let mut out = null_mut();783		let mut fragment = String::new();784		// let fetch_settings = fetcher_settings;785		with_default_context(|c, _| unsafe {786			flake_reference_and_fragment_from_string(787				c,788				fetch.0,789				flake.0,790				parse.0,791				s.as_ptr().cast(),792				s.len(),793				&mut out,794				Some(copy_nix_str),795				(&raw mut fragment).cast(),796			)797		})?;798		assert!(!out.is_null());799800		Ok((Self(out), fragment))801	}802	#[instrument(name = "lock-flake", skip(self, fetch, flake, lock))]803	pub fn lock(804		&mut self,805		fetch: &FetchSettings,806		flake: &FlakeSettings,807		lock: &FlakeLockFlags,808	) -> Result<LockedFlake> {809		with_default_context(|c, es| unsafe { flake_lock(c, fetch.0, flake.0, es, lock.0, self.0) })810			.map(LockedFlake)811	}812}813unsafe impl Send for FlakeReference {}814unsafe impl Sync for FlakeReference {}815816pub struct LockedFlake(*mut locked_flake);817impl LockedFlake {818	pub fn get_attrs(&self, settings: &mut FlakeSettings) -> Result<Value> {819		with_default_context(|c, es| unsafe {820			locked_flake_get_output_attrs(c, settings.0, es, self.0)821		})822		.map(Value)823	}824}825unsafe impl Send for LockedFlake {}826unsafe impl Sync for LockedFlake {}827impl Drop for LockedFlake {828	fn drop(&mut self) {829		unsafe {830			locked_flake_free(self.0);831		};832	}833}834835type FieldName = [u8; 64];836fn init_field_name(v: &str) -> FieldName {837	let mut f = [0; 64];838	assert!(v.len() < 64, "max field name is 63 chars");839	assert!(840		v.bytes().all(|v| v != 0),841		"nul bytes are unsupported in field name"842	);843	f[0..v.len()].copy_from_slice(v.as_bytes());844	f845}846847pub struct RealisedString(*mut realised_string);848impl fmt::Debug for RealisedString {849	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {850		self.as_str().fmt(f)851	}852}853854impl RealisedString {855	pub fn as_str(&self) -> &str {856		let len = unsafe { realised_string_get_buffer_size(self.0) };857		let data: *const u8 = unsafe { realised_string_get_buffer_start(self.0) }.cast();858		let data = unsafe { slice::from_raw_parts(data, len) };859		std::str::from_utf8(data).expect("non-utf8 strings not supported")860	}861	pub fn path_count(&self) -> usize {862		unsafe { realised_string_get_store_path_count(self.0) }863	}864	pub fn path(&self, i: usize) -> String {865		assert!(i < self.path_count());866		let path = unsafe { realised_string_get_store_path(self.0, i) };867		let mut err_out = String::new();868		unsafe { store_path_name(path, Some(copy_nix_str), (&raw mut err_out).cast()) };869		err_out870	}871}872873unsafe impl Send for RealisedString {}874impl Drop for RealisedString {875	fn drop(&mut self) {876		unsafe { realised_string_free(self.0) }877	}878}879880#[repr(transparent)]881pub struct Value(*mut value);882883unsafe impl Send for Value {}884unsafe impl Sync for Value {}885886pub trait AsFieldName {887	fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T>;888	fn to_field_name(&self) -> Result<String>;889}890impl AsFieldName for Value {891	fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T> {892		let f = self.to_string()?;893		v(init_field_name(&f))894	}895	fn to_field_name(&self) -> Result<String> {896		self.to_string()897	}898}899impl<E> AsFieldName for E900where901	E: AsRef<str>,902{903	fn as_field_name<T>(&self, v: impl FnOnce(FieldName) -> Result<T>) -> Result<T> {904		let f = self.as_ref();905		v(init_field_name(f))906	}907	fn to_field_name(&self) -> Result<String> {908		Ok(self.as_ref().to_owned())909	}910}911912struct AttrsBuilder(*mut c_bindings_builder);913impl AttrsBuilder {914	fn new(capacity: usize) -> Self {915		with_default_context(|c, es| unsafe { make_bindings_builder(c, es, capacity) })916			.map(Self)917			.expect("alloc should not fail")918	}919	fn insert(&mut self, k: &impl AsFieldName, v: Value) {920		k.as_field_name(|name| {921			with_default_context(|c, _| unsafe {922				bindings_builder_insert(c, self.0, name.as_ptr().cast(), v.0);923				// bindings_builder_insert doesn't do incref924			})925		})926		.expect("builder insert shouldn't fail");927	}928}929impl Drop for AttrsBuilder {930	fn drop(&mut self) {931		unsafe { bindings_builder_free(self.0) };932	}933}934935struct ListBuilder(*mut c_list_builder, c_uint);936impl ListBuilder {937	fn new(capacity: usize) -> Self {938		with_default_context(|c, es| unsafe { make_list_builder(c, es, capacity) })939			.map(|l| Self(l, 0))940			.expect("alloc should not fail")941	}942}943impl ListBuilder {944	fn push(&mut self, v: Value) {945		with_default_context(|c, _| unsafe {946			list_builder_insert(947				c,948				self.0,949				{950					let v = self.1;951					self.1 += 1;952					v953				},954				v.0,955			)956		})957		.expect("list insert shouldn't fail");958	}959}960impl Drop for ListBuilder {961	fn drop(&mut self) {962		unsafe { list_builder_free(self.0) };963	}964}965966impl Value {967	pub fn new_primop(v: NativeFn) -> Self {968		let out = Self::new_uninit();969		with_default_context(|c, _| unsafe { init_primop(c, out.0, v.0) })970			.expect("primop initialization should not fail");971		out972	}973	pub fn new_attrs(v: HashMap<&str, Value>) -> Self {974		let out = Self::new_uninit();975		let mut b = AttrsBuilder::new(v.len());976		for (k, v) in v {977			b.insert(&k, v);978		}979		with_default_context(|c, _| unsafe { make_attrs(c, out.0, b.0) })980			.expect("attrs initialization should not fail");981982		out983	}984	fn new_list<T: Into<Self>>(v: Vec<T>) -> Self {985		let out = Self::new_uninit();986		let mut b = ListBuilder::new(v.len());987		for v in v {988			b.push(v.into());989		}990		with_default_context(|c, _| unsafe { make_list(c, b.0, out.0) })991			.expect("list initialization should not fail");992993		out994	}995	fn new_uninit() -> Self {996		let out = with_default_context(|c, es| unsafe { alloc_value(c, es) })997			.expect("value allocation should not fail");998		Self(out)999	}1000	pub fn new_str(v: &str) -> Self {1001		let s = CString::new(v).expect("string should not contain NULs");1002		let out = Self::new_uninit();1003		// String is copied, `s` is free to be dropped1004		with_default_context(|c, _| unsafe { init_string(c, out.0, s.as_ptr()) })1005			.expect("string initialization should not fail");1006		out1007	}1008	pub fn new_int(i: i64) -> Self {1009		let out = Self::new_uninit();1010		with_default_context(|c, _| unsafe { init_int(c, out.0, i) })1011			.expect("int initialization should not fail");1012		out1013	}1014	pub fn new_bool(v: bool) -> Self {1015		let out = Self::new_uninit();1016		with_default_context(|c, _| unsafe { init_bool(c, out.0, v) })1017			.expect("bool initialization should not fail");1018		out1019	}1020	// TODO: As far as I can see, there is no way to get Thunks from nix public C api, so this function is useless1021	// fn force(&mut self, st: &mut EvalState) -> Result<()> {1022	// 	with_default_context(|c, _| unsafe { value_force(c, st.0, self.0) })?;1023	// 	Ok(())1024	// }1025	pub fn type_of(&self) -> NixType {1026		let ty = with_default_context(|c, _| unsafe { get_type(c, self.0) })1027			.expect("get_type should not fail");1028		NixType::from_int(ty)1029	}1030	fn builtin_to_string(&self) -> Result<Self> {1031		let builtin = Self::eval("builtins.toString")?;1032		builtin.call(self.clone())1033	}1034	fn force(&mut self, s: *mut nix_raw::EvalState) -> Result<()> {1035		with_default_context(|c, _| unsafe { value_force(c, s, self.0) })?;1036		Ok(())1037	}1038	pub fn to_string(&self) -> Result<String> {1039		let ty = self.type_of();1040		if !matches!(ty, NixType::String) {1041			bail!("unexpected type: {ty:?}, expected string");1042		}1043		let mut str_out = String::new();1044		with_default_context(|c, _| unsafe {1045			get_string(c, self.0, Some(copy_nix_str), (&raw mut str_out).cast())1046		})?;10471048		Ok(str_out)1049	}1050	pub fn to_realised_string(&self) -> Result<RealisedString> {1051		with_default_context(|c, es| unsafe { string_realise(c, es, self.0, false) })1052			.map(RealisedString)10531054		// let store_paths = unsafe { nix_raw::realised_string_get_store_path_count(str) };1055		// for i in 0..store_paths {1056		// 	let store_path = unsafe { nix_raw::realised_string_get_store_path(str, i) };1057		// 	nix_raw::store_path_name(store_path, callback, user_data);1058		// }1059		// dbg!(store_paths);1060		// todo!();1061	}10621063	pub fn has_field(&self, field: &str) -> Result<bool> {1064		if !matches!(self.type_of(), NixType::Attrs) {1065			bail!("invalid type: expected attrs");1066		}10671068		let f = init_field_name(field);1069		with_default_context(|c, es| unsafe { has_attr_byname(c, self.0, es, f.as_ptr().cast()) })1070	}1071	// pub fn derivation_path(&self) {1072	// 	nix_raw::real1073	// }1074	pub fn list_fields(&self) -> Result<Vec<String>> {1075		if !matches!(self.type_of(), NixType::Attrs) {1076			bail!("invalid type: expected attrs");1077		}10781079		let len = with_default_context(|c, _| unsafe { get_attrs_size(c, self.0) })?;1080		let mut out = Vec::with_capacity(len as usize);10811082		for i in 0..len {1083			let name =1084				with_default_context(|c, es| unsafe { get_attr_name_byidx(c, self.0, es, i) })?;1085			let c = unsafe { CStr::from_ptr(name) };1086			out.push(c.to_str().expect("nix field names are utf-8").to_owned());1087		}1088		Ok(out)1089	}1090	pub fn get_elem(&self, v: usize) -> Result<Self> {1091		if !matches!(self.type_of(), NixType::List) {1092			bail!("invalid type: expected list");1093		}1094		let len = with_default_context(|c, _| unsafe { get_list_size(c, self.0) })? as usize;1095		if v >= len {1096			bail!("oob list get: {v} >= {len}");1097		}10981099		with_default_context(|c, es| unsafe { get_list_byidx(c, self.0, es, v as u32) }).map(Self)1100	}1101	pub fn attrs_update(self, other: Value /*, ignore_errors: bool*/) -> Result<Self> {1102		let attrs_update_fn = Self::eval("a: b: a // b")?;11031104		attrs_update_fn1105			.call(self)?1106			.call(other)1107			.context("attrs update")1108	}1109	pub fn get_field(&self, name: impl AsFieldName) -> Result<Self> {1110		if !matches!(self.type_of(), NixType::Attrs) {1111			bail!("invalid type: expected attrs");1112		}11131114		name.as_field_name(|name| {1115			with_default_context(|c, es| unsafe {1116				get_attr_byname(c, self.0, es, name.as_ptr().cast())1117			})1118			.map(Self)1119		})1120		.with_context(|| format!("getting field {:?}", name.to_field_name()))1121	}1122	pub fn call(&self, v: Value) -> Result<Self> {1123		let kind = self1124			.functor_kind()1125			.ok_or_else(|| anyhow!("can only call function or functor"))?;11261127		let function = match kind {1128			FunctorKind::Function => self.clone(),1129			FunctorKind::Functor => {1130				let f = self1131					.get_field("__functor")1132					.context("getting functor value")?;1133				assert_eq!(1134					f.type_of(),1135					NixType::Function,1136					"invalid functor encountered"1137				);1138				f1139			}1140		};11411142		let out = Value::new_uninit();1143		with_default_context(|c, es| unsafe { value_call(c, es, function.0, v.0, out.0) })?;11441145		Ok(out)1146	}1147	pub fn eval(v: &str) -> Result<Self> {1148		let s = CString::new(v).expect("expression shouldn't have internal NULs");1149		let out = Self::new_uninit();1150		with_default_context(|c, es| unsafe {1151			expr_eval_from_string(c, es, s.as_ptr(), c"/root".as_ptr(), out.0)1152		})?;1153		Ok(out)1154	}1155	#[instrument(name = "build", skip(self), fields(output))]1156	pub fn build(&self, output: &str) -> Result<Utf8PathBuf> {1157		if !self.is_derivation() {1158			bail!("expected derivation to build")1159		}1160		let output_name = self1161			.get_field("outputName")1162			.context("getting output name field")?1163			.to_string()?;1164		let v = if output_name != output {1165			let out = self.get_field(output).context("getting target output")?;1166			if !out.is_derivation() {1167				bail!("unknown output: {output}");1168			}1169			out1170		} else {1171			self.clone()1172		};11731174		let drv_path = Utf8PathBuf::from(1175			v.get_field("drvPath")1176				.context("getting drvPath")?1177				.to_string()?,1178		);1179		let graph = Arc::new(drv::DrvGraph::resolve(&eval_store(), &drv_path)?);1180		let _guard = logging::register_build_graph(&Span::current(), &graph);11811182		scheduler::build_graph_sync(graph.clone(), vec![output.to_owned()])?;11831184		let s = v.builtin_to_string()?;1185		let rs = s.to_realised_string()?;1186		let out_path = rs.as_str().to_owned();1187		Ok(Utf8PathBuf::from(out_path))1188	}1189	pub fn as_json<T: DeserializeOwned>(&self) -> Result<T> {1190		let to_json = Self::eval("builtins.toJSON")?;1191		let s = to_json.call(self.clone())?.to_string()?;1192		Ok(serde_json::from_str(&s)?)1193	}1194	pub fn serialized<T: Serialize>(v: &T) -> Result<Self> {1195		Self::eval(&nixlike::serialize(v)?)1196	}11971198	// Convert to string/evaluate derivations/etc1199	// fn to_string_weak(&self) -> Result<String> {1200	// 	// TODO: For now, it works exactly like to_string, see the comment for fn force()1201	// 	self.to_string()1202	// }12031204	fn is_derivation(&self) -> bool {1205		if !matches!(self.type_of(), NixType::Attrs) {1206			return false;1207		}1208		let Some(ty) = self.get_field("type").ok() else {1209			return false;1210		};1211		matches!(ty.to_string().as_deref(), Ok("derivation"))1212	}1213	fn functor_kind(&self) -> Option<FunctorKind> {1214		match self.type_of() {1215			NixType::Attrs => self1216				.has_field("__functor")1217				.expect("has_field shouldn't fail for attrs")1218				.then_some(FunctorKind::Functor),1219			NixType::Function => Some(FunctorKind::Function),1220			_ => None,1221		}1222	}1223	pub fn is_function(&self) -> bool {1224		self.functor_kind().is_some()1225	}1226	pub fn is_null(&self) -> bool {1227		matches!(self.type_of(), NixType::Null)1228	}1229	pub fn is_string(&self) -> bool {1230		matches!(self.type_of(), NixType::String)1231	}1232	pub fn is_attrs(&self) -> bool {1233		matches!(self.type_of(), NixType::Attrs)1234	}1235}12361237impl From<String> for Value {1238	fn from(value: String) -> Self {1239		Value::new_str(&value)1240	}1241}1242impl From<bool> for Value {1243	fn from(value: bool) -> Self {1244		Value::new_bool(value)1245	}1246}1247impl From<&str> for Value {1248	fn from(value: &str) -> Self {1249		Value::new_str(value)1250	}1251}1252impl<T> From<Vec<T>> for Value1253where1254	T: Into<Value>,1255{1256	fn from(value: Vec<T>) -> Self {1257		Value::new_list(value)1258	}1259}12601261impl Clone for Value {1262	fn clone(&self) -> Self {1263		with_default_context(|c, _| unsafe { value_incref(c, self.0) })1264			.expect("value incref should not fail");1265		Self(self.0)1266	}1267}1268impl Drop for Value {1269	fn drop(&mut self) {1270		with_default_context(|c, _| unsafe { value_decref(c, self.0) })1271			.expect("value drop should not fail");1272	}1273}12741275static TOKIO_FOR_NIX: OnceLock<Arc<tokio::runtime::Runtime>> = OnceLock::new();12761277pub fn init_libraries() {1278	unsafe { GC_allow_register_threads() };12791280	let mut ctx = NixContext::new();1281	ctx.run_in_context(|c| unsafe { libutil_init(c) })1282		.expect("util init should not fail");1283	ctx.run_in_context(|c| unsafe { libstore_init(c) })1284		.expect("store init should not fail");1285	ctx.run_in_context(|c| unsafe { libexpr_init(c) })1286		.expect("expr init should not fail");12871288	nix_logging_cxx::apply_tracing_logger();1289}12901291pub fn init_tokio_for_nix(tokio: Arc<tokio::runtime::Runtime>) {1292	TOKIO_FOR_NIX1293		.set(tokio)1294		.expect("tokio for nix should only be initialized once");1295}12961297pub fn await_in_nix<F: Send + 'static>(f: impl Future<Output = F> + Send + 'static) -> F {1298	// It should be possible to do Handle::current(), but some of the planned features don't work well with that1299	let runtime = TOKIO_FOR_NIX1300		.get()1301		.expect("init_tokio_for_nix was not called");1302	std::thread::spawn(move || runtime.block_on(f))1303		.join()1304		.expect("await_in_nix inner thread panicked")1305}13061307unsafe extern "C" fn nix_primop_closure_adapter<const N: usize>(1308	user_data: *mut c_void,1309	mut context: *mut c_context,1310	state: *mut nix_raw::EvalState,1311	args: *mut *mut value,1312	ret: *mut value,1313) {1314	let user_closure: &UserClosure<N> = unsafe { &*user_data.cast_const().cast() };1315	let args: [&Value; N] = array::from_fn(|i| {1316		let v: &mut Value = unsafe { &mut *args.add(i).cast() };1317		v as &Value1318	});1319	let ctx: &mut NixContext = unsafe { transmute(&mut context) };13201321	let state: &EvalState = unsafe { std::mem::transmute(&state) };13221323	match user_closure(state, args) {1324		Ok(v) => {1325			unsafe { copy_value(context, ret, v.0) };1326		}1327		Err(e) => {1328			ctx.set_err(e);1329		}1330	}1331}13321333type UserClosure<const N: usize> = Box<dyn Fn(&EvalState, [&Value; N]) -> Result<Value>>;13341335pub struct NativeFn(*mut PrimOp);1336impl NativeFn {1337	pub fn new<const N: usize>(1338		name: &'static CStr,1339		doc: &'static CStr,1340		args: [&'static CStr; N],1341		f: impl Fn(&EvalState, [&Value; N]) -> Result<Value> + 'static,1342	) -> Self {1343		// Double-boxing to make it thin pointer, as vtable gets outside of first Box1344		let closure: Box<UserClosure<N>> = Box::new(Box::new(f));1345		let f: PrimOpFun = Some(nix_primop_closure_adapter::<N>);1346		let mut args = args.into_iter().map(|v| v.as_ptr()).collect_vec();1347		args.push(null());1348		let args = args.as_mut_ptr();1349		let primop = unsafe {1350			alloc_primop(1351				null_mut(),1352				f,1353				N as i32,1354				name.as_ptr(),1355				args,1356				doc.as_ptr(),1357				Box::into_raw(closure).cast(),1358			)1359		};13601361		assert!(!primop.is_null(), "primop allocation should not fail");13621363		Self(primop)1364	}1365	pub fn register(self) {1366		unsafe { register_primop(null_mut(), self.0) };1367	}1368}13691370pub struct StorePath(*mut c_store_path);1371impl StorePath {1372	fn as_ptr(&self) -> *mut c_store_path {1373		self.01374	}1375}13761377impl Drop for StorePath {1378	fn drop(&mut self) {1379		unsafe { store_path_free(self.0) }1380	}1381}13821383#[test_log::test]1384fn test_native() -> Result<()> {1385	init_libraries();1386	NativeFn::new(1387		c"__uppercaseSuffix2",1388		c"make string uppercase and add suffix",1389		[c"str", c"suffix"],1390		|_, [str, suffix]: [&Value; 2]| {1391			let str = str.to_string()?;1392			let suffix = suffix.to_string()?;1393			Ok(Value::new_str(&format!("{}{suffix}", str.to_uppercase())))1394		},1395	)1396	.register();13971398	let mut fetch_settings = FetchSettings::new();1399	fetch_settings.set(c"warn-dirty", c"false");14001401	let manifest = format!("git+file://{}/../../", env!("CARGO_MANIFEST_DIR"));1402	let flake = FlakeSettings::new()?;1403	let parse = FlakeReferenceParseFlags::new(&flake)?;1404	let (mut r, _) = FlakeReference::new(&manifest, &flake, &parse, &fetch_settings)?;1405	let lock = FlakeLockFlags::new(&flake)?;1406	let locked = r.lock(&fetch_settings, &flake, &lock)?;1407	let attrs = locked.get_attrs(&mut FlakeSettings::new()?)?;14081409	let builtins = Value::eval("builtins")?;1410	assert_eq!(builtins.type_of(), NixType::Attrs);14111412	assert_eq!(attrs.type_of(), NixType::Attrs);1413	let test_data = nix_go!(attrs.testData);14141415	let test_string: String = nix_go_json!(test_data.testString);1416	assert_eq!(test_string, "hello");14171418	let s = nix_go!(attrs.packages["x86_64-linux"].fleet.drvPath);1419	let s = CString::new(s.to_string()?).expect("path str is cstring");14201421	let uppercase_suffix = Value::new_primop(NativeFn::new(1422		c"uppercase_suffix",1423		c"make string uppercase and add suffix",1424		[c"str", c"suffix"],1425		|es, [str, suffix]: [&Value; 2]| {1426			let str = str.to_string()?;1427			let suffix = suffix.to_string()?;1428			Ok(Value::new_str(&format!("{}{suffix}", str.to_uppercase())))1429		},1430	));14311432	let test_result: String = nix_go_json!(test_data.testPrimop(uppercase_suffix));1433	assert_eq!(test_result, "PREFIX_BODY_SUFFIX");1434	let test_result: String = nix_go_json!(builtins.uppercaseSuffix2("test")("suffix"));1435	assert_eq!(test_result, "TESTsuffix");14361437	let drv_path =1438		nix_go!(attrs.packages["x86_64-linux"]["fleet-install-secrets"].drvPath).to_string()?;1439	let graph = drv::DrvGraph::resolve(&drv_path)?;1440	eprintln!(1441		"fleet-install-secrets dependency graph: {} nodes",1442		graph.nodes.len()1443	);1444	for (path, node) in &graph.nodes {1445		if !node.input_drvs.is_empty() {1446			eprintln!("  {} ({} deps)", node.name, node.input_drvs.len());1447		}1448	}14491450	Ok(())1451}14521453// pub struct GcAlloc;1454// unsafe impl GlobalAlloc for GcAlloc {1455// 	unsafe fn alloc(&self, l: Layout) -> *mut u8 {1456// 		let ptr = unsafe { GC_malloc(l.size()) };1457// 		ptr.cast()1458// 	}1459// 	unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {1460// 		// unsafe { GC_free(ptr.cast()) };1461// 	}1462//1463// 	unsafe fn realloc(&self, ptr: *mut u8, _: Layout, new_size: usize) -> *mut u8 {1464// 		let ptr = unsafe { GC_realloc(ptr.cast(), new_size) };1465// 		ptr.cast()1466// 	}1467// }1468//1469// #[global_allocator]1470// static GC: GcAlloc = GcAlloc;