use crate::commands::{command_sets, event_commands, event_kinds};
use crate::connection::JdwpConnection;
use crate::protocol::{CommandPacket, JdwpResult};
use crate::reader::read_i32;
use crate::types::{FieldId, MethodId, ObjectId, ReferenceTypeId, ThreadId};
use bytes::BufMut;
#[derive(Debug, Clone, Copy, Default)]
pub struct EventFilters {
pub count: Option<i32>,
pub thread: Option<ThreadId>,
pub instance: Option<ObjectId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SuspendPolicy {
None = 0,
EventThread = 1,
All = 2,
}
mod mod_kinds {
pub const COUNT: u8 = 1;
pub const THREAD_ONLY: u8 = 3;
pub const CLASS_ONLY: u8 = 4;
pub const CLASS_MATCH: u8 = 5;
pub const CLASS_EXCLUDE: u8 = 6;
pub const INSTANCE_ONLY: u8 = 11;
pub const LOCATION_ONLY: u8 = 7;
pub const EXCEPTION_ONLY: u8 = 8;
pub const FIELD_ONLY: u8 = 9;
}
impl JdwpConnection {
pub async fn set_breakpoint(
&mut self,
class_id: ReferenceTypeId,
method_id: MethodId,
bytecode_index: u64,
suspend_policy: SuspendPolicy,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(event_kinds::BREAKPOINT);
packet.data.put_u8(suspend_policy as u8);
packet.data.put_i32(1);
packet.data.put_u8(mod_kinds::LOCATION_ONLY);
packet.data.put_u8(1);
packet.data.put_u64(class_id);
packet.data.put_u64(method_id);
packet.data.put_u64(bytecode_index);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn clear_breakpoint(&mut self, request_id: i32) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(event_kinds::BREAKPOINT);
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn set_class_prepare(
&mut self,
class_pattern: &str,
suspend_policy: SuspendPolicy,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(event_kinds::CLASS_PREPARE);
packet.data.put_u8(suspend_policy as u8);
packet.data.put_i32(1);
packet.data.put_u8(mod_kinds::CLASS_MATCH);
let pat = class_pattern.as_bytes();
packet.data.put_u32(u32::try_from(pat.len()).unwrap_or(u32::MAX));
packet.data.extend_from_slice(pat);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn clear_class_prepare(&mut self, request_id: i32) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(event_kinds::CLASS_PREPARE);
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn set_exception_request(
&mut self,
ref_type: Option<ReferenceTypeId>,
caught: bool,
uncaught: bool,
suspend_policy: SuspendPolicy,
) -> JdwpResult<i32> {
self.set_exception_request_ex(ref_type, caught, uncaught, suspend_policy, EventFilters::default())
.await
}
pub async fn set_exception_request_ex(
&mut self,
ref_type: Option<ReferenceTypeId>,
caught: bool,
uncaught: bool,
suspend_policy: SuspendPolicy,
filters: EventFilters,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(event_kinds::EXCEPTION);
packet.data.put_u8(suspend_policy as u8);
let n_mods = 1
+ i32::from(filters.count.is_some())
+ i32::from(filters.thread.is_some())
+ i32::from(filters.instance.is_some());
packet.data.put_i32(n_mods);
packet.data.put_u8(mod_kinds::EXCEPTION_ONLY);
packet.data.put_u64(ref_type.unwrap_or(0));
packet.data.put_u8(u8::from(caught));
packet.data.put_u8(u8::from(uncaught));
write_count_thread(&mut packet, filters.count, filters.thread);
write_instance_only(&mut packet, filters.instance);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn clear_exception_request(&mut self, request_id: i32) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(event_kinds::EXCEPTION);
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn set_field_watch(
&mut self,
ref_type: ReferenceTypeId,
field_id: FieldId,
kind: WatchKind,
suspend_policy: SuspendPolicy,
) -> JdwpResult<i32> {
self.set_field_watch_ex(ref_type, field_id, kind, suspend_policy, EventFilters::default()).await
}
pub async fn set_field_watch_ex(
&mut self,
ref_type: ReferenceTypeId,
field_id: FieldId,
kind: WatchKind,
suspend_policy: SuspendPolicy,
filters: EventFilters,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(kind.event_kind());
packet.data.put_u8(suspend_policy as u8);
let n_mods = 1
+ i32::from(filters.count.is_some())
+ i32::from(filters.thread.is_some())
+ i32::from(filters.instance.is_some());
packet.data.put_i32(n_mods);
packet.data.put_u8(mod_kinds::FIELD_ONLY);
packet.data.put_u64(ref_type);
packet.data.put_u64(field_id);
write_count_thread(&mut packet, filters.count, filters.thread);
write_instance_only(&mut packet, filters.instance);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn set_method_exit_request(
&mut self,
class_pattern: &str,
with_return_value: bool,
suspend_policy: SuspendPolicy,
count: Option<i32>,
thread: Option<ThreadId>,
) -> JdwpResult<i32> {
self.set_method_exit_request_ex(
class_pattern,
with_return_value,
suspend_policy,
&[],
EventFilters { count, thread, instance: None },
)
.await
}
pub async fn set_method_exit_request_ex(
&mut self,
class_pattern: &str,
with_return_value: bool,
suspend_policy: SuspendPolicy,
exclude: &[String],
filters: EventFilters,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(method_exit_kind(with_return_value));
packet.data.put_u8(suspend_policy as u8);
let n_mods = 1
+ i32::from(filters.count.is_some())
+ i32::from(filters.thread.is_some())
+ i32::try_from(exclude.len()).unwrap_or(0);
packet.data.put_i32(n_mods);
packet.data.put_u8(mod_kinds::CLASS_MATCH);
let pat = class_pattern.as_bytes();
packet.data.put_u32(u32::try_from(pat.len()).unwrap_or(u32::MAX));
packet.data.extend_from_slice(pat);
for p in exclude {
packet.data.put_u8(mod_kinds::CLASS_EXCLUDE);
let b = p.as_bytes();
packet.data.put_u32(u32::try_from(b.len()).unwrap_or(u32::MAX));
packet.data.extend_from_slice(b);
}
write_count_thread(&mut packet, filters.count, filters.thread);
write_instance_only(&mut packet, filters.instance);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn clear_method_exit_request(
&mut self,
request_id: i32,
with_return_value: bool,
) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(method_exit_kind(with_return_value));
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn can_get_method_return_values(&mut self) -> JdwpResult<bool> {
let v = self.get_version().await?;
Ok(v.jdwp_major > 1 || (v.jdwp_major == 1 && v.jdwp_minor >= 6))
}
pub async fn set_monitor_request(
&mut self,
kind: MonitorKind,
suspend_policy: SuspendPolicy,
monitor_class: Option<ReferenceTypeId>,
filters: EventFilters,
) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::SET);
packet.data.put_u8(kind.event_kind());
packet.data.put_u8(suspend_policy as u8);
let n_mods = i32::from(monitor_class.is_some())
+ i32::from(filters.count.is_some())
+ i32::from(filters.thread.is_some())
+ i32::from(filters.instance.is_some());
packet.data.put_i32(n_mods);
if let Some(t) = monitor_class {
packet.data.put_u8(mod_kinds::CLASS_ONLY);
packet.data.put_u64(t);
}
write_count_thread(&mut packet, filters.count, filters.thread);
write_instance_only(&mut packet, filters.instance);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let request_id = read_i32(&mut data)?;
Ok(request_id)
}
pub async fn clear_monitor_request(&mut self, request_id: i32, kind: MonitorKind) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(kind.event_kind());
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn clear_field_watch(&mut self, request_id: i32, kind: WatchKind) -> JdwpResult<()> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR);
packet.data.put_u8(kind.event_kind());
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
}
fn write_count_thread(packet: &mut CommandPacket, count: Option<i32>, thread: Option<ThreadId>) {
if let Some(c) = count {
packet.data.put_u8(mod_kinds::COUNT);
packet.data.put_i32(c);
}
if let Some(t) = thread {
packet.data.put_u8(mod_kinds::THREAD_ONLY);
packet.data.put_u64(t);
}
}
fn write_instance_only(packet: &mut CommandPacket, instance: Option<ObjectId>) {
if let Some(o) = instance {
packet.data.put_u8(mod_kinds::INSTANCE_ONLY);
packet.data.put_u64(o);
}
}
const fn method_exit_kind(with_return_value: bool) -> u8 {
if with_return_value {
event_kinds::METHOD_EXIT_WITH_RETURN_VALUE
} else {
event_kinds::METHOD_EXIT
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonitorKind {
Blocked,
Acquired,
Wait,
Waited,
}
impl MonitorKind {
pub const ALL: [Self; 4] = [Self::Blocked, Self::Acquired, Self::Wait, Self::Waited];
#[must_use]
pub const fn event_kind(self) -> u8 {
match self {
Self::Blocked => event_kinds::MONITOR_CONTENDED_ENTER,
Self::Acquired => event_kinds::MONITOR_CONTENDED_ENTERED,
Self::Wait => event_kinds::MONITOR_WAIT,
Self::Waited => event_kinds::MONITOR_WAITED,
}
}
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Blocked => "blocked",
Self::Acquired => "acquired",
Self::Wait => "wait",
Self::Waited => "waited",
}
}
#[must_use]
pub const fn partner(self) -> Self {
match self {
Self::Blocked => Self::Acquired,
Self::Acquired => Self::Blocked,
Self::Wait => Self::Waited,
Self::Waited => Self::Wait,
}
}
#[must_use]
pub const fn class_filter_tests_monitor(self) -> bool {
matches!(self, Self::Wait | Self::Waited)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WatchKind {
Access,
Modify,
}
impl WatchKind {
#[must_use]
pub const fn event_kind(self) -> u8 {
match self {
Self::Access => event_kinds::FIELD_ACCESS,
Self::Modify => event_kinds::FIELD_MODIFICATION,
}
}
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Access => "access",
Self::Modify => "modify",
}
}
}