use crate::commands::{command_sets, event_commands, event_kinds, step_depths, step_sizes, vm_commands};
use crate::connection::JdwpConnection;
use crate::eval::{write_tagged_value, write_untagged_value};
use crate::eventrequest::{EventFilters, SuspendPolicy};
use crate::protocol::{CommandPacket, JdwpResult};
use crate::reader::{read_i32, read_u64, read_u8, read_value_by_tag};
use crate::types::{FrameId, MethodId, ObjectId, ReferenceTypeId, ThreadId, Value, ValueData};
use bytes::BufMut;
const MOD_COUNT: u8 = 1;
const MOD_THREAD_ONLY: u8 = 3;
const MOD_CLASS_ONLY: u8 = 4;
const MOD_CLASS_EXCLUDE: u8 = 6;
const MOD_LOCATION_ONLY: u8 = 7;
const MOD_STEP: u8 = 10;
const MOD_INSTANCE_ONLY: u8 = 11;
const ARRAY_LENGTH: u8 = 1;
const ARRAY_GET_VALUES: u8 = 2;
const ARRAY_SET_VALUES: u8 = 3;
fn write_jdwp_string(packet: &mut CommandPacket, s: &str) {
let b = s.as_bytes();
packet.data.put_u32(u32::try_from(b.len()).unwrap_or(u32::MAX));
packet.data.extend_from_slice(b);
}
#[derive(Debug, Clone, Copy)]
pub enum StepDepth {
Into,
Over,
Out,
}
impl JdwpConnection {
pub async fn set_breakpoint_ex(
&mut self,
class_id: ReferenceTypeId,
method_id: MethodId,
bytecode_index: u64,
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::BREAKPOINT);
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_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);
if let Some(c) = filters.count {
packet.data.put_u8(MOD_COUNT);
packet.data.put_i32(c);
}
if let Some(t) = filters.thread {
packet.data.put_u8(MOD_THREAD_ONLY);
packet.data.put_u64(t);
}
if let Some(o) = filters.instance {
packet.data.put_u8(MOD_INSTANCE_ONLY);
packet.data.put_u64(o);
}
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
read_i32(&mut data)
}
pub async fn set_step(&mut self, thread: ThreadId, depth: StepDepth) -> JdwpResult<i32> {
self.set_step_ex(thread, depth, &[], &[]).await
}
pub async fn set_step_ex(
&mut self,
thread: ThreadId,
depth: StepDepth,
exclude: &[String],
only: &[String],
) -> 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::SINGLE_STEP);
packet.data.put_u8(SuspendPolicy::All as u8);
let n_mods = 1 + exclude.len() + only.len();
packet.data.put_i32(i32::try_from(n_mods).unwrap_or(i32::MAX));
packet.data.put_u8(MOD_STEP);
packet.data.put_u64(thread);
packet.data.put_i32(step_sizes::LINE);
packet.data.put_i32(match depth {
StepDepth::Into => step_depths::INTO,
StepDepth::Over => step_depths::OVER,
StepDepth::Out => step_depths::OUT,
});
for pat in only {
packet.data.put_u8(MOD_CLASS_ONLY);
write_jdwp_string(&mut packet, pat);
}
for pat in exclude {
packet.data.put_u8(MOD_CLASS_EXCLUDE);
write_jdwp_string(&mut packet, pat);
}
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
read_i32(&mut data)
}
pub async fn clear_step(&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::SINGLE_STEP);
packet.data.put_i32(request_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn clear_all_breakpoints(&mut self) -> JdwpResult<()> {
let id = self.next_id();
let packet =
CommandPacket::new(id, command_sets::EVENT_REQUEST, event_commands::CLEAR_ALL_BREAKPOINTS);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn get_array_length(&mut self, array_id: ObjectId) -> JdwpResult<i32> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::ARRAY_REFERENCE, ARRAY_LENGTH);
packet.data.put_u64(array_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
read_i32(&mut data)
}
pub async fn get_array_values(
&mut self,
array_id: ObjectId,
first: i32,
length: i32,
) -> JdwpResult<Vec<Value>> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::ARRAY_REFERENCE, ARRAY_GET_VALUES);
packet.data.put_u64(array_id);
packet.data.put_i32(first);
packet.data.put_i32(length);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let region_tag = read_u8(&mut data)?;
let count = read_i32(&mut data)?;
let is_object = matches!(region_tag, 76 | 115 | 116 | 103 | 108 | 99 | 91);
let mut out = Vec::with_capacity(usize::try_from(count.max(0)).unwrap_or(0));
for _ in 0..count {
if is_object {
let t = read_u8(&mut data)?;
out.push(Value { tag: t, data: read_value_by_tag(t, &mut data)? });
} else {
out.push(Value { tag: region_tag, data: read_value_by_tag(region_tag, &mut data)? });
}
}
Ok(out)
}
pub async fn set_array_values(
&mut self,
array_id: ObjectId,
first: i32,
values: &[Value],
) -> JdwpResult<()> {
self.guard_mutation("an array element write")?;
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::ARRAY_REFERENCE, ARRAY_SET_VALUES);
packet.data.put_u64(array_id);
packet.data.put_i32(first);
packet.data.put_i32(i32::try_from(values.len()).unwrap_or(i32::MAX));
for v in values {
write_untagged_value(&mut packet.data, v);
}
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
pub async fn create_string(&mut self, s: &str) -> JdwpResult<ObjectId> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::VIRTUAL_MACHINE, vm_commands::CREATE_STRING);
let bytes = s.as_bytes();
packet.data.put_i32(i32::try_from(bytes.len()).unwrap_or(i32::MAX));
packet.data.put_slice(bytes);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
read_u64(&mut data)
}
pub async fn set_frame_value(
&mut self,
thread_id: ThreadId,
frame_id: FrameId,
slot: i32,
value: &Value,
) -> JdwpResult<()> {
self.guard_mutation("a local variable write")?;
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::STACK_FRAME, 2 );
packet.data.put_u64(thread_id);
packet.data.put_u64(frame_id);
packet.data.put_i32(1); packet.data.put_i32(slot);
write_tagged_value(&mut packet.data, value);
let reply = self.send_command(packet).await?;
reply.check_error()?;
Ok(())
}
}
#[must_use]
pub const fn value_int(v: i32) -> Value {
Value { tag: 73, data: ValueData::Int(v) }
}
#[must_use]
pub const fn value_long(v: i64) -> Value {
Value { tag: 74, data: ValueData::Long(v) }
}
#[must_use]
pub const fn value_bool(v: bool) -> Value {
Value { tag: 90, data: ValueData::Boolean(v) }
}
#[must_use]
pub const fn value_float(v: f32) -> Value {
Value { tag: 70, data: ValueData::Float(v) }
}
#[must_use]
pub const fn value_double(v: f64) -> Value {
Value { tag: 68, data: ValueData::Double(v) }
}
#[must_use]
pub const fn value_char(v: u16) -> Value {
Value { tag: 67, data: ValueData::Char(v) }
}
#[must_use]
pub const fn value_null() -> Value {
Value { tag: 76, data: ValueData::Object(0) }
}
#[must_use]
pub const fn value_object(id: ObjectId) -> Value {
Value { tag: 76, data: ValueData::Object(id) }
}