nodejs/stdlib/
trace_events.rs1use crate::host::{type_error, with_host, JsObj};
13use fusevm::Value;
14use indexmap::IndexMap;
15use std::cell::RefCell;
16
17pub const METHODS: &[&str] = &["createTracing", "getEnabledCategories"];
19
20pub const TRACING_METHODS: &[&str] = &["enable", "disable"];
23
24thread_local! {
25 static ENABLED: RefCell<IndexMap<String, usize>> = RefCell::new(IndexMap::new());
29}
30
31pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
34 match method {
35 "createTracing" => Some(Ok(new_tracing(args.first()))),
36 "getEnabledCategories" => Some(Ok(get_enabled_categories())),
37 _ => None,
38 }
39}
40
41pub fn construct(args: &[Value]) -> Result<Value, String> {
44 Ok(new_tracing(args.first()))
45}
46
47fn new_tracing(options: Option<&Value>) -> Value {
51 let cats = read_categories(options);
52 with_host(|h| {
53 let joined = h.new_str(cats.join(","));
54 let cat_arr: Vec<Value> = cats.iter().map(|c| h.new_str(c.clone())).collect();
55 let cat_arr = h.new_array(cat_arr);
56 let mut m = IndexMap::new();
57 m.insert("@@native".to_string(), h.new_str("Tracing"));
58 m.insert("@@categories".to_string(), cat_arr);
59 m.insert("categories".to_string(), joined);
60 m.insert("enabled".to_string(), Value::Bool(false));
61 h.new_object(m)
62 })
63}
64
65fn get_enabled_categories() -> Value {
68 let joined = ENABLED.with(|e| {
69 e.borrow()
70 .iter()
71 .filter(|(_, &n)| n > 0)
72 .map(|(k, _)| k.clone())
73 .collect::<Vec<_>>()
74 .join(",")
75 });
76 if joined.is_empty() {
77 Value::Undef
78 } else {
79 with_host(|h| h.new_str(joined))
80 }
81}
82
83pub fn instance_call(recv: &Value, method: &str, _args: Vec<Value>) -> Result<Value, String> {
86 match method {
87 "enable" => {
88 set_enabled(recv, true);
89 Ok(recv.clone())
90 }
91 "disable" => {
92 set_enabled(recv, false);
93 Ok(recv.clone())
94 }
95 _ => Err(type_error(&format!("tracing.{method} is not a function"))),
96 }
97}
98
99fn set_enabled(recv: &Value, on: bool) {
102 let already = matches!(get_prop(recv, "enabled"), Some(Value::Bool(true)));
103 if already == on {
104 return;
105 }
106 let cats = categories_of(recv);
107 with_host(|h| {
108 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
109 p.insert("enabled".to_string(), Value::Bool(on));
110 }
111 });
112 ENABLED.with(|e| {
113 let mut e = e.borrow_mut();
114 for c in cats {
115 let slot = e.entry(c).or_insert(0);
116 if on {
117 *slot += 1;
118 } else if *slot > 0 {
119 *slot -= 1;
120 }
121 }
122 });
123}
124
125fn get_prop(recv: &Value, key: &str) -> Option<Value> {
128 with_host(|h| match h.get(recv) {
129 Some(JsObj::Object(p)) => p.get(key).cloned(),
130 _ => None,
131 })
132}
133
134fn categories_of(recv: &Value) -> Vec<String> {
136 with_host(|h| match h.get(recv) {
137 Some(JsObj::Object(p)) => match p.get("@@categories").and_then(|a| h.get(a)) {
138 Some(JsObj::Array(items)) => items.iter().map(|v| h.str_of(v)).collect(),
139 _ => Vec::new(),
140 },
141 _ => Vec::new(),
142 })
143}
144
145fn read_categories(options: Option<&Value>) -> Vec<String> {
148 with_host(|h| {
149 let Some(o) = options else { return Vec::new() };
150 let Some(JsObj::Object(p)) = h.get(o) else {
151 return Vec::new();
152 };
153 match p.get("categories").and_then(|c| h.get(c)) {
154 Some(JsObj::Array(items)) => items.iter().map(|v| h.str_of(v)).collect(),
155 _ => Vec::new(),
156 }
157 })
158}