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