jdwp_client/string.rs
1// StringReference command implementations
2//
3// Commands for working with String objects
4
5use crate::commands::{command_sets, string_reference_commands};
6use crate::connection::JdwpConnection;
7use crate::protocol::{CommandPacket, JdwpResult, ReplyPacket};
8use crate::reader::read_string;
9use crate::types::ObjectId;
10use bytes::BufMut;
11
12impl JdwpConnection {
13 /// Get the string value from a String object (StringReference.Value command)
14 ///
15 /// # Arguments
16 /// * `string_id` - The `ObjectId` of the String object
17 ///
18 /// # Returns
19 /// The actual string value
20 ///
21 /// # Example
22 /// ```no_run
23 /// # use jdwp_client::types::ObjectId;
24 /// # async fn demo(mut connection: jdwp_client::JdwpConnection, string_object_id: ObjectId)
25 /// # -> jdwp_client::JdwpResult<()> {
26 /// let value = connection.get_string_value(string_object_id).await?;
27 /// println!("String value: {}", value);
28 /// # Ok(()) }
29 /// ```
30 ///
31 /// # Errors
32 /// Returns a [`JdwpError`](crate::JdwpError) if the JDWP request fails or the reply cannot be parsed.
33 pub async fn get_string_value(&mut self, string_id: ObjectId) -> JdwpResult<String> {
34 let packet = self.string_value_request(string_id);
35 let reply = self.send_command(packet).await?;
36 Self::decode_string_value(&reply)
37 }
38
39 /// The contents of each of `string_ids`, read as **independent reads** (PERF-2, #129).
40 ///
41 /// The licence is real and narrower than it looks. A `java.lang.String` is immutable, so reading one
42 /// tells you nothing you need in order to read another and the order cannot matter — that is what makes
43 /// this a wave. What it does *not* license is reading a string the caller has not committed to
44 /// rendering: the id is still a weak reference, and a string read speculatively is a packet the
45 /// sequential path would never have sent. Committing is the caller's job, and `CONTEXT.md`'s
46 /// **speculative read** is the invariant that job protects.
47 ///
48 /// Positional and total — `result[i]` answers `string_ids[i]`, and one failure does not touch the rest.
49 /// A collected object answers `INVALID_OBJECT` in its own slot, exactly as it does read one at a time.
50 pub async fn read_string_values_independently(&self, string_ids: &[ObjectId]) -> Vec<JdwpResult<String>> {
51 let packets = string_ids.iter().map(|&id| self.string_value_request(id)).collect();
52 self.read_independently(packets)
53 .await
54 .into_iter()
55 .map(|reply| reply.and_then(|r| Self::decode_string_value(&r)))
56 .collect()
57 }
58
59 /// The request half of `StringReference.Value`.
60 ///
61 /// Split out, with [`decode_string_value`](Self::decode_string_value), so the wave form and the single
62 /// form cannot drift: **one encoder and one decoder, two schedulers.** `object.rs`'s
63 /// `reference_type_request` states the rule at length; this is the eighth command to follow it.
64 fn string_value_request(&self, string_id: ObjectId) -> CommandPacket {
65 let id = self.next_id();
66 let mut packet =
67 CommandPacket::new(id, command_sets::STRING_REFERENCE, string_reference_commands::VALUE);
68 // Write the string object ID
69 packet.data.put_u64(string_id);
70 packet
71 }
72
73 /// The decode half of `StringReference.Value`, error check included — so a wave and a single read agree
74 /// about what counts as a failure and not only about how to parse a success.
75 fn decode_string_value(reply: &ReplyPacket) -> JdwpResult<String> {
76 reply.check_error()?;
77 let mut data = reply.data();
78 read_string(&mut data)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84
85 use crate::commands::{command_sets, string_reference_commands};
86 use crate::protocol::CommandPacket;
87 use bytes::BufMut;
88
89 /// The request is exactly the 8-byte object id and nothing else, which is what lets a cassette census
90 /// read the id back out of a recorded request — `a_rendered_object_is_asked_for_its_type_once` does
91 /// that for `ObjectReference.ReferenceType`, and a wave of these is the next thing to need it.
92 #[test]
93 fn a_string_value_request_is_the_object_id_and_nothing_else() {
94 let mut packet =
95 CommandPacket::new(7, command_sets::STRING_REFERENCE, string_reference_commands::VALUE);
96 packet.data.put_u64(0x0102_0304_0506_0708);
97 assert_eq!(packet.command_set, command_sets::STRING_REFERENCE);
98 assert_eq!(packet.command, string_reference_commands::VALUE);
99 assert_eq!(&packet.data[..], &0x0102_0304_0506_0708_u64.to_be_bytes());
100 }
101}