1use std::collections::HashMap;
6
7use atomic_refcell::AtomicRefCell;
8use devtools_traits::{DebuggerValue, PropertyDescriptor};
9use malloc_size_of_derive::MallocSizeOf;
10use serde::Serialize;
11use serde_json::{Map, Value};
12
13use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry, new_actor_name};
14use crate::actors::property_iterator::{PropertyIteratorActor, PropertyIteratorEntry};
15use crate::actors::symbol_iterator::SymbolIteratorActor;
16use crate::protocol::ClientRequest;
17use crate::{StreamId, debugger_value_to_json};
18
19#[derive(Serialize)]
20#[serde(rename_all = "camelCase")]
21enum EnumIteratorType {
22 PropertyIterator,
23 SymbolIterator,
24}
25
26#[derive(Serialize)]
27struct EnumIterator {
28 actor: String,
29 #[serde(rename = "type")]
30 type_: EnumIteratorType,
31 count: u32,
32}
33
34#[derive(Serialize)]
35struct EnumReply {
36 from: String,
37 iterator: EnumIterator,
38}
39
40#[derive(Serialize)]
41struct PrototypeReply {
42 from: String,
43 prototype: Value,
44}
45
46#[derive(Serialize)]
47#[serde(rename_all = "camelCase")]
48pub(crate) struct ObjectPreview {
49 pub kind: String,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub size: Option<u32>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub entries: Option<Vec<(Value, Value)>>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub own_properties: Option<HashMap<String, ObjectPropertyDescriptor>>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub own_properties_length: Option<u32>,
58 #[serde(flatten)]
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub function: Option<FunctionPreview>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub length: Option<u32>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub items: Option<Vec<Value>>,
65}
66
67#[derive(Clone, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct FunctionPreview {
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub name: Option<String>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub display_name: Option<String>,
74 pub parameter_names: Vec<String>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub is_async: Option<bool>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub is_generator: Option<bool>,
79}
80
81#[derive(Serialize)]
82#[serde(rename_all = "camelCase")]
83pub(crate) struct ObjectActorMsg {
84 actor: String,
85 #[serde(rename = "type")]
86 type_: String,
87 class: String,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 own_property_length: Option<u32>,
90 extensible: bool,
91 frozen: bool,
92 sealed: bool,
93 #[serde(flatten)]
94 #[serde(skip_serializing_if = "Option::is_none")]
95 function: Option<FunctionPreview>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 preview: Option<ObjectPreview>,
98}
99
100#[derive(Serialize)]
101#[serde(rename_all = "camelCase")]
102pub(crate) struct ObjectPropertyDescriptor {
103 pub value: Value,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub configurable: Option<bool>,
106 pub enumerable: bool,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub writable: Option<bool>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub is_accessor: Option<bool>,
111}
112
113impl ObjectPropertyDescriptor {
114 pub(crate) fn from_property_descriptor(
115 registry: &ActorRegistry,
116 prop: &PropertyDescriptor,
117 ) -> Self {
118 Self {
119 value: debugger_value_to_json(registry, prop.value.clone()),
120 configurable: Some(prop.configurable),
121 enumerable: prop.enumerable,
122 writable: Some(prop.writable),
123 is_accessor: Some(prop.is_accessor),
124 }
125 }
126}
127
128#[derive(Clone, MallocSizeOf)]
129struct ObjectActorData {
130 class: String,
131 own_property_length: Option<u32>,
132 preview: Option<devtools_traits::ObjectPreview>,
133 prototype: Option<DebuggerValue>,
134}
135
136#[derive(MallocSizeOf)]
137pub(crate) struct ObjectActor {
138 name: String,
139 data: AtomicRefCell<ObjectActorData>,
140}
141
142impl Actor for ObjectActor {
143 fn name(&self) -> &str {
144 &self.name
145 }
146
147 fn handle_message(
149 &self,
150 request: ClientRequest,
151 registry: &ActorRegistry,
152 msg_type: &str,
153 _msg: &Map<String, Value>,
154 _id: StreamId,
155 ) -> Result<(), ActorError> {
156 match msg_type {
157 "enumProperties" => {
158 let preview = self.data.borrow().preview.clone();
159 let properties = preview.as_ref().map_or_else(Vec::new, |preview| {
160 if preview.kind == "ArrayLike" {
161 let mut props: Vec<PropertyDescriptor> = preview
164 .items
165 .as_ref()
166 .map(|items| {
167 items
168 .iter()
169 .enumerate()
170 .map(|(index, value)| PropertyDescriptor {
171 name: index.to_string(),
172 value: value.clone(),
173 configurable: true,
174 enumerable: true,
175 writable: true,
176 is_accessor: false,
177 })
178 .collect()
179 })
180 .unwrap_or_default();
181 if let Some(length) = preview.array_length {
183 props.push(PropertyDescriptor {
185 name: "length".to_string(),
186 value: DebuggerValue::NumberValue(length as f64),
187 configurable: false,
188 enumerable: false,
189 writable: true,
190 is_accessor: false,
191 });
192 }
193 props
194 } else {
195 preview.own_properties.clone().unwrap_or_default()
196 }
197 });
198 let entries = properties
199 .into_iter()
200 .map(PropertyIteratorEntry::Property)
201 .collect();
202 self.reply_property_iterator(request, registry, entries)?
203 },
204
205 "enumEntries" => {
206 let mut entries = Vec::new();
207 let preview = self.data.borrow().preview.clone();
208 if let Some(preview) = preview &&
209 let Some(map_entries) = preview.entries
210 {
211 for (key, value) in map_entries {
212 entries.push(PropertyIteratorEntry::MapEntry(key, value));
213 }
214 }
215 self.reply_property_iterator(request, registry, entries)?
216 },
217
218 "enumSymbols" => {
219 let symbol_iterator_actor = SymbolIteratorActor::register(registry);
220 let msg = EnumReply {
221 from: self.name().into(),
222 iterator: EnumIterator {
223 actor: symbol_iterator_actor.name().into(),
224 type_: EnumIteratorType::SymbolIterator,
225 count: 0,
226 },
227 };
228 request.reply_final(&msg)?
229 },
230
231 "prototype" => {
232 let prototype = self
233 .data
234 .borrow()
235 .prototype
236 .clone()
237 .unwrap_or(DebuggerValue::NullValue(false));
238 let msg = PrototypeReply {
239 from: self.name().into(),
240 prototype: debugger_value_to_json(registry, prototype),
241 };
242 request.reply_final(&msg)?
243 },
244
245 _ => return Err(ActorError::UnrecognizedPacketType),
246 };
247 Ok(())
248 }
249}
250
251impl ObjectActor {
252 fn reply_property_iterator(
253 &self,
254 request: ClientRequest,
255 registry: &ActorRegistry,
256 entries: Vec<PropertyIteratorEntry>,
257 ) -> Result<(), ActorError> {
258 let property_iterator_actor = PropertyIteratorActor::register(registry, entries);
259 let msg = EnumReply {
260 from: self.name().into(),
261 iterator: EnumIterator {
262 actor: property_iterator_actor.name().into(),
263 type_: EnumIteratorType::PropertyIterator,
264 count: property_iterator_actor.count(),
265 },
266 };
267 request.reply_final(&msg)
268 }
269
270 pub fn register(
271 registry: &ActorRegistry,
272 actor_name: Option<String>,
273 class: String,
274 own_property_length: Option<u32>,
275 preview: Option<devtools_traits::ObjectPreview>,
276 prototype: Option<DebuggerValue>,
277 ) -> String {
278 if let Some(name) = actor_name {
279 let actor = registry.find::<Self>(&name);
280 let mut data = actor.data.borrow_mut();
281
282 data.class = class;
283 data.own_property_length = own_property_length;
284
285 if preview.is_some() || data.preview.is_none() {
286 data.preview = preview;
287 }
288 if prototype.is_some() || data.prototype.is_none() {
289 data.prototype = prototype;
290 }
291
292 return name;
293 }
294
295 let name = new_actor_name::<Self>();
296 let actor = ObjectActor {
297 name: name.clone(),
298 data: AtomicRefCell::new(ObjectActorData {
299 class,
300 own_property_length,
301 preview,
302 prototype,
303 }),
304 };
305 registry.register(actor);
306 name
307 }
308}
309
310impl ActorEncode<ObjectActorMsg> for ObjectActor {
311 fn encode(&self, registry: &ActorRegistry) -> ObjectActorMsg {
312 let data = self.data.borrow().clone();
313 let mut msg = ObjectActorMsg {
314 actor: self.name().into(),
315 type_: "object".into(),
316 class: data.class.clone(),
317 extensible: true,
318 frozen: false,
319 sealed: false,
320 function: None,
321 preview: None,
322 own_property_length: data.own_property_length,
323 };
324
325 let Some(preview) = data.preview.clone() else {
328 return msg;
329 };
330
331 let function = preview.function.map(|function| FunctionPreview {
332 name: function.name.clone(),
333 display_name: function.display_name.clone(),
334 parameter_names: function.parameter_names.clone(),
335 is_async: function.is_async,
336 is_generator: function.is_generator,
337 });
338
339 if data.class == "Function" {
340 msg.function = function.clone();
341 }
342
343 let preview = ObjectPreview {
344 kind: preview.kind.clone(),
345 size: preview.size,
346 entries: preview.entries.map(|entries| {
347 entries
348 .iter()
349 .map(|(key, value)| {
350 (
351 debugger_value_to_json(registry, key.clone()),
352 debugger_value_to_json(registry, value.clone()),
353 )
354 })
355 .collect()
356 }),
357 own_properties: preview.own_properties.map(|own_properties| {
358 own_properties
359 .iter()
360 .map(|prop| {
361 (
362 prop.name.clone(),
363 ObjectPropertyDescriptor::from_property_descriptor(registry, prop),
364 )
365 })
366 .collect()
367 }),
368 own_properties_length: preview.own_properties_length,
369 function,
370 length: preview.array_length,
371 items: preview.items.map(|items| {
372 items
373 .iter()
374 .map(|item| debugger_value_to_json(registry, item.clone()))
375 .collect()
376 }),
377 };
378
379 msg.preview = Some(preview);
380 msg
381 }
382}