1use std::collections::{HashMap, HashSet};
4use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5use std::sync::{Arc, Condvar, Mutex, RwLock};
6
7use crate::env::async_hook::AsyncRuntime;
8
9use crate::env::error::EvalResult;
10use crate::mode::{ExecutionMode, TierState};
11use cljrs_gc::{GcConfig, GcPtr};
12use cljrs_reader::Form;
13use cljrs_value::{CljxFn, Namespace, Value, Var};
14#[derive(Debug, Clone)]
18pub enum RequireRefer {
19 None,
20 All,
21 Named(Vec<Arc<str>>),
22}
23
24#[derive(Debug, Clone)]
26pub struct RequireSpec {
27 pub ns: Arc<str>,
28 pub version: Option<Arc<str>>,
30 pub alias: Option<Arc<str>>,
31 pub refer: RequireRefer,
32}
33
34pub struct Frame {
38 pub bindings: Vec<(Arc<str>, Value)>,
39}
40
41impl Default for Frame {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl Frame {
48 pub fn new() -> Self {
49 Self {
50 bindings: Vec::new(),
51 }
52 }
53
54 pub fn bind(&mut self, name: Arc<str>, val: Value) {
55 self.bindings.push((name, val));
57 }
58
59 pub fn lookup(&self, name: &str) -> Option<&Value> {
60 tracing::trace!(target: "env", "lookup {}", name);
62 for (n, v) in self.bindings.iter().rev() {
63 if n.as_ref() == name {
64 return Some(v);
65 }
66 }
67 None
68 }
69}
70
71pub struct GlobalEnv {
75 id: u64,
82 pub namespaces: RwLock<HashMap<Arc<str>, GcPtr<Namespace>>>,
83 pub source_paths: RwLock<Vec<std::path::PathBuf>>,
85 pub loaded: Mutex<std::collections::HashSet<Arc<str>>>,
87 pub loading: Mutex<HashMap<Arc<str>, std::thread::ThreadId>>,
91 pub loading_done: Condvar,
93 pub builtin_sources: RwLock<HashMap<Arc<str>, &'static str>>,
96 pub gc_config: RwLock<Option<Arc<GcConfig>>>,
98 execution_mode: ExecutionMode,
101 tier_state: AtomicU8,
106 tiers: Arc<crate::tiered::tiers::Tiers>,
112 pub async_rt: RwLock<Option<Arc<dyn AsyncRuntime>>>,
115 pub version_cache: Mutex<HashMap<Arc<str>, Value>>,
119 pub deps_config: RwLock<Option<Arc<cljrs_project::config::DepsConfig>>>,
121 pub verify_commit_signatures: AtomicBool,
127 #[cfg(not(target_arch = "wasm32"))]
132 pub trusted_keys: RwLock<Arc<cljrs_project::vcs::TrustedKeys>>,
133 pub sig_verify_cache: Mutex<HashSet<(Arc<str>, Arc<str>)>>,
136 pub versioned_sources: RwLock<HashMap<Arc<str>, Arc<str>>>,
140 pub versioned_offline: AtomicBool,
145 pub native_provenance: RwLock<HashMap<Arc<str>, Arc<str>>>,
150 pub enforce_native_versions: AtomicBool,
155 pub provenance_warned: Mutex<HashSet<Arc<str>>>,
158 #[allow(clippy::type_complexity)]
164 pub pinned_native_loader: RwLock<Option<PinnedNativeLoader>>,
165 #[allow(clippy::type_complexity)]
173 pub native_require_loader: RwLock<Option<NativeRequireLoader>>,
174 #[allow(clippy::type_complexity)]
183 pub compiled_ns_loaders: RwLock<HashMap<Arc<str>, CompiledNsLoader>>,
184}
185
186pub type CompiledNsLoader = Arc<dyn Fn(&Arc<GlobalEnv>) -> EvalResult<()> + Send + Sync>;
190
191pub type PinnedNativeLoader =
194 Arc<dyn Fn(&Arc<GlobalEnv>, &str, &str) -> EvalResult<bool> + Send + Sync>;
195
196pub type NativeRequireLoader = Arc<dyn Fn(&Arc<GlobalEnv>, &str) -> EvalResult<bool> + Send + Sync>;
199
200static NEXT_GLOBAL_ENV_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
202
203impl std::fmt::Debug for GlobalEnv {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 write!(f, "GlobalEnv {{ ... }}")
206 }
207}
208
209impl GlobalEnv {
210 pub fn new(execution_mode: ExecutionMode) -> Arc<Self> {
215 let id = NEXT_GLOBAL_ENV_ID.fetch_add(1, Ordering::Relaxed);
216 Arc::new(Self {
217 id,
218 namespaces: RwLock::new(HashMap::new()),
219 source_paths: RwLock::new(Vec::new()),
220 loaded: Mutex::new(std::collections::HashSet::new()),
221 loading: Mutex::new(HashMap::new()),
222 loading_done: Condvar::new(),
223 builtin_sources: RwLock::new(HashMap::new()),
224 gc_config: RwLock::new(None),
225 execution_mode,
226 tier_state: AtomicU8::new(TierState::TreeWalk as u8),
227 tiers: crate::tiered::tiers::Tiers::new(id),
228 async_rt: RwLock::new(None),
229 version_cache: Mutex::new(HashMap::new()),
230 deps_config: RwLock::new(None),
231 verify_commit_signatures: AtomicBool::new(false),
232 #[cfg(not(target_arch = "wasm32"))]
233 trusted_keys: RwLock::new(Arc::new(cljrs_project::vcs::TrustedKeys::new())),
234 sig_verify_cache: Mutex::new(HashSet::new()),
235 versioned_sources: RwLock::new(HashMap::new()),
236 versioned_offline: AtomicBool::new(false),
237 native_provenance: RwLock::new(HashMap::new()),
238 enforce_native_versions: AtomicBool::new(false),
239 provenance_warned: Mutex::new(HashSet::new()),
240 pinned_native_loader: RwLock::new(None),
241 native_require_loader: RwLock::new(None),
242 compiled_ns_loaders: RwLock::new(HashMap::new()),
243 })
244 }
245
246 pub fn set_source_paths(&self, paths: Vec<std::path::PathBuf>) {
248 *self.source_paths.write().unwrap() = paths;
249 }
250
251 pub fn register_builtin_source(&self, ns: &str, src: &'static str) {
253 self.builtin_sources
254 .write()
255 .unwrap()
256 .insert(Arc::from(ns), src);
257 }
258
259 pub fn builtin_source(&self, ns: &str) -> Option<&'static str> {
261 self.builtin_sources.read().unwrap().get(ns).copied()
262 }
263
264 pub fn register_compiled_ns_loader(&self, ns: &str, loader: CompiledNsLoader) {
267 self.compiled_ns_loaders
268 .write()
269 .unwrap()
270 .insert(Arc::from(ns), loader);
271 }
272
273 pub fn compiled_ns_loader(&self, ns: &str) -> Option<CompiledNsLoader> {
275 self.compiled_ns_loaders.read().unwrap().get(ns).cloned()
276 }
277
278 pub fn mark_loaded(&self, ns: &str) {
280 self.loaded.lock().unwrap().insert(Arc::from(ns));
281 }
282
283 pub fn is_loaded(&self, ns: &str) -> bool {
285 self.loaded.lock().unwrap().contains(ns)
286 }
287
288 pub fn set_gc_config(&self, config: Arc<GcConfig>) {
290 *self.gc_config.write().unwrap() = Some(config);
291 }
292
293 pub fn gc_config(&self) -> Option<Arc<GcConfig>> {
295 self.gc_config.read().unwrap().clone()
296 }
297
298 pub fn resolve_alias(&self, current_ns: &str, alias: &str) -> Option<Arc<str>> {
300 let map = self.namespaces.read().unwrap();
301 let ns = map.get(current_ns)?;
302 let aliases = ns.get().aliases.lock().unwrap();
303 aliases.get(alias).cloned()
304 }
305
306 pub fn resolve_auto_keyword(&self, current_ns: &str, name: &str) -> Result<String, String> {
313 match name.split_once('/') {
314 Some((alias, kw_name)) => match self.resolve_alias(current_ns, alias) {
315 Some(ns) => Ok(format!("{ns}/{kw_name}")),
316 None => Err(format!(
317 "invalid token: ::{name} (no such namespace alias: {alias})"
318 )),
319 },
320 None => Ok(format!("{current_ns}/{name}")),
321 }
322 }
323
324 pub fn get_or_create_ns(&self, name: &str) -> GcPtr<Namespace> {
326 {
328 let map = self.namespaces.read().unwrap();
329 if let Some(ns) = map.get(name) {
330 return ns.clone();
331 }
332 }
333 let mut map = self.namespaces.write().unwrap();
335 if let Some(ns) = map.get(name) {
337 return ns.clone();
338 }
339 let ns = GcPtr::new(Namespace::new(name));
340 map.insert(Arc::from(name), ns.clone());
341 ns
342 }
343
344 pub fn intern(&self, ns_name: &str, name: Arc<str>, val: Value) -> GcPtr<Var> {
346 let ns = self.get_or_create_ns(ns_name);
347 let mut interns = ns.get().interns.lock().unwrap();
348 if let Some(var) = interns.get(&name) {
349 var.get().bind(val);
351 return var.clone();
352 }
353 let var = GcPtr::new(Var::new(ns_name, name.as_ref()));
354 var.get().bind(val);
355 interns.insert(name, var.clone());
356 var
357 }
358
359 pub fn lookup_var(&self, ns_name: &str, sym_name: &str) -> Option<GcPtr<Var>> {
361 let map = self.namespaces.read().unwrap();
362 let ns = map.get(ns_name)?;
363 let interns = ns.get().interns.lock().unwrap();
364 interns.get(sym_name).cloned()
365 }
366
367 pub fn lookup_in_ns(&self, ns_name: &str, sym_name: &str) -> Option<Value> {
370 let map = self.namespaces.read().unwrap();
371 let ns = map.get(ns_name)?;
372 let ns_ref = ns.get();
373 {
375 let interns = ns_ref.interns.lock().unwrap();
376 if let Some(var) = interns.get(sym_name) {
377 return crate::env::dynamics::deref_var(var);
378 }
379 }
380 {
382 let refers = ns_ref.refers.lock().unwrap();
383 if let Some(var) = refers.get(sym_name) {
384 return crate::env::dynamics::deref_var(var);
385 }
386 }
387 None
388 }
389
390 pub fn lookup_var_in_ns(&self, ns_name: &str, sym_name: &str) -> Option<GcPtr<Var>> {
392 let map = self.namespaces.read().unwrap();
393 let ns = map.get(ns_name)?;
394 let ns_ref = ns.get();
395 {
396 let interns = ns_ref.interns.lock().unwrap();
397 if let Some(var) = interns.get(sym_name) {
398 return Some(var.clone());
399 }
400 }
401 {
402 let refers = ns_ref.refers.lock().unwrap();
403 if let Some(var) = refers.get(sym_name) {
404 return Some(var.clone());
405 }
406 }
407 None
408 }
409
410 pub fn refer_all(&self, dst_ns: &str, src_ns: &str) {
412 let map = self.namespaces.read().unwrap();
413 let src = match map.get(src_ns) {
414 Some(ns) => ns.clone(),
415 None => return,
416 };
417 let dst = match map.get(dst_ns) {
418 Some(ns) => ns.clone(),
419 None => return,
420 };
421 let src_interns = src.get().interns.lock().unwrap();
422 let mut dst_refers = dst.get().refers.lock().unwrap();
423 for (name, var) in src_interns.iter() {
424 dst_refers.insert(name.clone(), var.clone());
425 }
426 }
427
428 pub fn refer_named(&self, dst_ns: &str, src_ns: &str, names: &[Arc<str>]) {
430 let map = self.namespaces.read().unwrap();
431 let src = match map.get(src_ns) {
432 Some(ns) => ns.clone(),
433 None => return,
434 };
435 let dst = match map.get(dst_ns) {
436 Some(ns) => ns.clone(),
437 None => return,
438 };
439 let src_interns = src.get().interns.lock().unwrap();
440 let mut dst_refers = dst.get().refers.lock().unwrap();
441 for name in names {
442 if let Some(var) = src_interns.get(name) {
443 dst_refers.insert(name.clone(), var.clone());
449 }
450 }
451 }
452
453 pub fn add_alias(&self, current_ns: &str, alias: &str, full_ns: &str) {
455 let ns_ptr = self.get_or_create_ns(current_ns);
456 let mut aliases = ns_ptr.get().aliases.lock().unwrap();
457 aliases.insert(Arc::from(alias), Arc::from(full_ns));
458 }
459
460 #[inline(always)]
462 pub fn id(&self) -> u64 {
463 self.id
464 }
465
466 #[inline(always)]
468 pub fn tiers(&self) -> &Arc<crate::tiered::tiers::Tiers> {
469 &self.tiers
470 }
471
472 #[inline(always)]
474 pub fn ir_cache(&self) -> &crate::tiered::ir_cache::IrCache {
475 self.tiers.ir_cache()
476 }
477
478 #[inline(always)]
480 pub fn jit(&self) -> &crate::tiered::jit_state::JitState {
481 self.tiers.jit()
482 }
483
484 #[inline(always)]
489 pub fn jit_backend(&self) -> Option<Arc<dyn crate::tiered::backend::JitBackend>> {
490 self.tiers.jit().backend().cloned()
491 }
492
493 #[inline(always)]
497 pub fn execution_mode(&self) -> ExecutionMode {
498 self.execution_mode
499 }
500
501 #[inline(always)]
503 pub fn tier_state(&self) -> TierState {
504 TierState::from_u8(self.tier_state.load(Ordering::Acquire))
505 }
506
507 pub fn set_tier_state(&self, tier: TierState) {
511 let _ = self.tier_state.fetch_max(tier as u8, Ordering::AcqRel);
512 }
513
514 #[inline(always)]
517 pub fn ir_enabled(&self) -> bool {
518 self.tier_state().ir_enabled()
519 }
520
521 #[inline(always)]
525 pub fn eval(&self, form: &Form, env: &mut Env) -> EvalResult {
526 crate::interp::eval::eval(form, env)
527 }
528
529 #[inline(always)]
535 pub fn call_cljrs_fn(&self, func: &CljxFn, args: &[Value], env: &mut Env) -> EvalResult {
536 match self.execution_mode {
537 ExecutionMode::TreeWalk => crate::interp::apply::call_cljrs_fn(func, args, env),
538 ExecutionMode::Tiered | ExecutionMode::TieredNoJit => {
539 crate::tiered::apply::call_cljrs_fn(func, args, env)
540 }
541 ExecutionMode::NoGcTransaction => crate::env::depth::call_cljrs_fn(func, args, env),
542 }
543 }
544
545 #[inline(always)]
550 pub fn on_fn_defined(&self, f: &CljxFn, env: &mut Env) {
551 if self.execution_mode.is_tiered() && self.ir_enabled() {
552 crate::tiered::ir_interp::eager_lower_fn(f, env);
553 }
554 }
555
556 pub fn set_async_runtime(&self, rt: Arc<dyn AsyncRuntime>) {
559 let mut guard = self.async_rt.write().unwrap();
560 if guard.is_none() {
561 *guard = Some(rt);
562 }
563 }
564
565 pub fn async_runtime(&self) -> Option<Arc<dyn AsyncRuntime>> {
567 self.async_rt.read().unwrap().clone()
568 }
569
570 pub fn get_ns_git_context(&self, ns_name: &str) -> Option<(Arc<str>, Arc<str>)> {
573 let map = self.namespaces.read().unwrap();
574 let ns = map.get(ns_name)?;
575 let ns_ref = ns.get();
576 let file = ns_ref.source_file.lock().unwrap().clone()?;
577 let repo = ns_ref.git_repo_root.lock().unwrap().clone()?;
578 Some((file, repo))
579 }
580
581 pub fn cache_versioned(&self, ns: &str, name: &str, commit: &str, val: Value) {
584 let key: Arc<str> = Arc::from(format!("{ns}/{name}@{commit}"));
585 self.version_cache.lock().unwrap().insert(key, val);
586 }
587
588 pub fn get_cached_versioned(&self, ns: &str, name: &str, commit: &str) -> Option<Value> {
590 let key = format!("{ns}/{name}@{commit}");
591 self.version_cache
592 .lock()
593 .unwrap()
594 .get(key.as_str())
595 .cloned()
596 }
597
598 pub fn cache_versioned_ns(&self, ns: &str, commit: &str) {
600 let key: Arc<str> = Arc::from(format!("{ns}@{commit}"));
601 self.version_cache.lock().unwrap().insert(key, Value::Nil);
602 }
603
604 pub fn record_versioned_source(&self, versioned_ns: &str, src: &str) {
607 self.versioned_sources
608 .write()
609 .unwrap()
610 .insert(Arc::from(versioned_ns), Arc::from(src));
611 }
612
613 pub fn versioned_sources_snapshot(&self) -> Vec<(Arc<str>, Arc<str>)> {
615 let map = self.versioned_sources.read().unwrap();
616 let mut entries: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
617 entries.sort_by(|a, b| a.0.cmp(&b.0));
618 entries
619 }
620
621 pub fn set_versioned_offline(&self, offline: bool) {
625 self.versioned_offline.store(offline, Ordering::Relaxed);
626 }
627
628 pub fn versioned_offline(&self) -> bool {
630 self.versioned_offline.load(Ordering::Relaxed)
631 }
632
633 pub fn set_native_provenance(&self, ns: &str, commit: &str) {
637 self.native_provenance
638 .write()
639 .unwrap()
640 .insert(Arc::from(ns), Arc::from(commit));
641 }
642
643 pub fn native_provenance_for(&self, ns: &str) -> Option<Arc<str>> {
645 self.native_provenance.read().unwrap().get(ns).cloned()
646 }
647
648 pub fn set_enforce_native_versions(&self, enforce: bool) {
650 self.enforce_native_versions
651 .store(enforce, Ordering::Relaxed);
652 }
653
654 pub fn enforce_native_versions(&self) -> bool {
656 self.enforce_native_versions.load(Ordering::Relaxed)
657 }
658
659 pub fn set_pinned_native_loader(&self, loader: PinnedNativeLoader) {
662 let mut guard = self.pinned_native_loader.write().unwrap();
663 if guard.is_none() {
664 *guard = Some(loader);
665 }
666 }
667
668 pub fn set_native_require_loader(&self, loader: NativeRequireLoader) {
671 let mut guard = self.native_require_loader.write().unwrap();
672 if guard.is_none() {
673 *guard = Some(loader);
674 }
675 }
676
677 pub fn check_commit_signature(&self, repo_root: &str, commit: &str) -> EvalResult<()> {
685 #[cfg(not(target_arch = "wasm32"))]
686 {
687 if !self.verify_commit_signatures.load(Ordering::Relaxed) {
688 return Ok(());
689 }
690 let key = (Arc::<str>::from(repo_root), Arc::<str>::from(commit));
691 if self.sig_verify_cache.lock().unwrap().contains(&key) {
692 return Ok(());
693 }
694 let trusted = self.trusted_keys.read().unwrap().clone();
695 cljrs_project::vcs::verify_commit_signature(
696 std::path::Path::new(repo_root),
697 commit,
698 &trusted,
699 )
700 .map_err(|e| match e {
701 cljrs_project::vcs::VcsError::SignatureVerificationFailed { commit: c, reason } => {
702 crate::env::error::EvalError::CommitSignatureVerificationFailed {
703 commit: c,
704 reason,
705 }
706 }
707 other => crate::env::error::EvalError::Runtime(format!("{other}")),
708 })?;
709 self.sig_verify_cache.lock().unwrap().insert(key);
710 }
711 let _ = (repo_root, commit);
712 Ok(())
713 }
714
715 #[cfg(not(target_arch = "wasm32"))]
721 pub fn load_trusted_signers(&self, config: &cljrs_project::config::DepsConfig) -> usize {
722 let mut keys = cljrs_project::vcs::TrustedKeys::new();
723 let mut loaded = 0usize;
724 for signer in &config.trusted_signers {
725 let result = match signer {
726 cljrs_project::config::TrustedSigner::Inline(text) => keys.add_key_text(text),
727 cljrs_project::config::TrustedSigner::File(path) => {
728 match std::fs::read_to_string(path) {
729 Ok(text) => keys.add_key_text(&text),
730 Err(e) => {
731 eprintln!(
732 "cljrs: warning: could not read trusted signer key {}: {e}",
733 path.display()
734 );
735 continue;
736 }
737 }
738 }
739 };
740 match result {
741 Ok(()) => loaded += 1,
742 Err(e) => eprintln!("cljrs: warning: invalid trusted signer key: {e}"),
743 }
744 }
745 *self.trusted_keys.write().unwrap() = Arc::new(keys);
746 loaded
747 }
748}
749
750pub struct Env {
754 pub frames: Vec<Frame>,
755 pub current_ns: Arc<str>,
756 pub globals: Arc<GlobalEnv>,
757 pub versioned_eval_commit: Option<Arc<str>>,
761 pub is_async: bool,
765}
766
767impl Env {
768 pub fn new(globals: Arc<GlobalEnv>, ns: &str) -> Self {
769 Self {
770 frames: Vec::new(),
771 current_ns: Arc::from(ns),
772 globals,
773 versioned_eval_commit: None,
774 is_async: false,
775 }
776 }
777
778 pub fn new_versioned(globals: Arc<GlobalEnv>, ns: &str, commit: &str) -> Self {
780 Self {
781 versioned_eval_commit: Some(Arc::from(commit)),
782 ..Self::new(globals, ns)
783 }
784 }
785
786 pub fn with_closure(globals: Arc<GlobalEnv>, ns: &str, f: &CljxFn) -> Self {
788 let mut env = Self::new(globals, ns);
789 if !f.closed_over_names.is_empty() {
790 env.push_frame();
791 for (name, val) in f.closed_over_names.iter().zip(f.closed_over_vals.iter()) {
792 env.bind(name.clone(), val.clone());
793 }
794 }
795 env
796 }
797
798 pub fn push_frame(&mut self) {
799 self.frames.push(Frame::new());
800 }
801
802 pub fn pop_frame(&mut self) {
803 self.frames.pop();
804 }
805
806 pub fn bind(&mut self, name: Arc<str>, val: Value) {
808 if let Some(frame) = self.frames.last_mut() {
809 frame.bind(name, val);
810 }
811 }
814
815 pub fn lookup(&self, name: &str) -> Option<Value> {
817 tracing::trace!(target: "env", "lookup {} in {} frames", name, self.frames.len());
818 for frame in self.frames.iter().rev() {
819 if let Some(v) = frame.lookup(name) {
820 return Some(v.clone());
821 }
822 }
823 self.globals.lookup_in_ns(&self.current_ns, name)
824 }
825
826 pub fn lookup_local_frames(&self, name: &str) -> Option<Value> {
830 for frame in self.frames.iter().rev() {
831 if let Some(v) = frame.lookup(name) {
832 return Some(v.clone());
833 }
834 }
835 None
836 }
837
838 pub fn lookup_var(&self, name: &str) -> Option<GcPtr<Var>> {
840 self.globals.lookup_var_in_ns(&self.current_ns, name)
841 }
842
843 pub fn all_local_bindings(&self) -> (Vec<Arc<str>>, Vec<Value>) {
846 let mut names = Vec::new();
847 let mut vals = Vec::new();
848 for frame in &self.frames {
850 for (n, v) in &frame.bindings {
851 names.push(n.clone());
852 vals.push(v.clone());
853 }
854 }
855 (names, vals)
856 }
857
858 pub fn child(&self) -> Self {
860 let (names, vals) = self.all_local_bindings();
861 let mut child = Self::new(self.globals.clone(), &self.current_ns);
862 child.is_async = self.is_async;
863 if !names.is_empty() {
864 child.push_frame();
865 for (n, v) in names.into_iter().zip(vals) {
866 child.bind(n, v);
867 }
868 }
869 child
870 }
871
872 #[inline(always)]
873 pub fn eval(&mut self, form: &Form) -> EvalResult {
874 let globals = self.globals.clone();
875 globals.eval(form, self)
876 }
877
878 #[inline(always)]
879 pub fn call_cljrs_fn(&mut self, func: &CljxFn, args: &[Value]) -> EvalResult {
880 let globals = self.globals.clone();
881 globals.call_cljrs_fn(func, args, self)
882 }
883
884 #[inline(always)]
885 pub fn on_fn_defined(&mut self, func: &CljxFn) {
886 let globals = self.globals.clone();
887 globals.on_fn_defined(func, self);
888 }
889}