Skip to main content

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}