use crate::commands::{
command_sets, object_reference_commands, reference_type_commands, stack_frame_commands,
};
use crate::connection::JdwpConnection;
use crate::protocol::{CommandPacket, JdwpResult};
use crate::reader::{read_string, read_u64, read_u8, read_value_by_tag};
use crate::types::{ClassId, FrameId, MethodId, ObjectId, ReferenceTypeId, ThreadId, Value, ValueData};
use bytes::BufMut;
const CLASS_TYPE_SUPERCLASS: u8 = 1;
const CLASS_TYPE_INVOKE_METHOD: u8 = 3;
const INVOKE_SINGLE_THREADED: i32 = 1;
impl JdwpConnection {
pub async fn get_signature(&mut self, ref_type_id: ReferenceTypeId) -> JdwpResult<String> {
if let Some(hit) = self.types().signature(ref_type_id) {
return Ok(hit);
}
let packet = self.signature_request(ref_type_id);
let reply = self.send_command(packet).await?;
let sig = Self::decode_signature_reply(&reply)?;
self.types().put_signature(ref_type_id, &sig);
Ok(sig)
}
pub async fn read_signatures_independently(
&self,
ref_type_ids: &[ReferenceTypeId],
) -> Vec<JdwpResult<String>> {
let mut wanted: Vec<ReferenceTypeId> = Vec::new();
for &id in ref_type_ids {
if self.types().signature(id).is_none() && !wanted.contains(&id) {
wanted.push(id);
}
}
if !wanted.is_empty() {
let packets = wanted.iter().map(|&id| self.signature_request(id)).collect();
for (&type_id, reply) in wanted.iter().zip(self.read_independently(packets).await) {
if let Ok(sig) = reply.and_then(|r| Self::decode_signature_reply(&r)) {
self.types().put_signature(type_id, &sig);
}
}
}
ref_type_ids
.iter()
.map(|&id| {
self.types().signature(id).ok_or_else(|| {
crate::protocol::JdwpError::Protocol(format!(
"the signature of type {id} could not be read"
))
})
})
.collect()
}
fn signature_request(&self, ref_type_id: ReferenceTypeId) -> CommandPacket {
let id = self.next_id();
let mut packet =
CommandPacket::new(id, command_sets::REFERENCE_TYPE, reference_type_commands::SIGNATURE);
packet.data.put_u64(ref_type_id);
packet
}
fn decode_signature_reply(reply: &crate::protocol::ReplyPacket) -> JdwpResult<String> {
reply.check_error()?;
let mut data = reply.data();
read_string(&mut data)
}
pub async fn get_superclass(&mut self, class_id: ClassId) -> JdwpResult<Option<ClassId>> {
match self.types().superclass(class_id) {
crate::connection::CachedSuperclass::Root => return Ok(None),
crate::connection::CachedSuperclass::Parent(p) => return Ok(Some(p)),
crate::connection::CachedSuperclass::Unknown => {}
}
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::CLASS_TYPE, CLASS_TYPE_SUPERCLASS);
packet.data.put_u64(class_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let sc = read_u64(&mut data)?;
let parent = if sc == 0 { None } else { Some(sc) };
self.types().put_superclass(class_id, parent);
Ok(parent)
}
pub async fn get_this_object(&mut self, thread_id: ThreadId, frame_id: FrameId) -> JdwpResult<ObjectId> {
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::STACK_FRAME, stack_frame_commands::THIS_OBJECT);
packet.data.put_u64(thread_id);
packet.data.put_u64(frame_id);
let reply = self.send_command(packet).await?;
reply.check_error()?;
let mut data = reply.data();
let _tag = read_u8(&mut data)?;
read_u64(&mut data)
}
pub async fn invoke_method(
&mut self,
object_id: ObjectId,
thread_id: ThreadId,
class_id: ClassId,
method_id: MethodId,
args: Vec<Value>,
) -> JdwpResult<(Value, ObjectId)> {
self.guard_mutation("an instance method invocation")?;
let id = self.next_id();
let mut packet =
CommandPacket::new(id, command_sets::OBJECT_REFERENCE, object_reference_commands::INVOKE_METHOD);
packet.data.put_u64(object_id);
packet.data.put_u64(thread_id);
packet.data.put_u64(class_id);
packet.data.put_u64(method_id);
packet.data.put_i32(i32::try_from(args.len()).unwrap_or(i32::MAX));
for a in &args {
write_tagged_value(&mut packet.data, a);
}
packet.data.put_i32(INVOKE_SINGLE_THREADED);
let reply = self.send_invoke(packet).await?;
reply.check_error()?;
read_invoke_reply(reply.data())
}
pub async fn invoke_static_method(
&mut self,
class_id: ClassId,
thread_id: ThreadId,
method_id: MethodId,
args: Vec<Value>,
) -> JdwpResult<(Value, ObjectId)> {
self.guard_mutation("a static method invocation")?;
let id = self.next_id();
let mut packet = CommandPacket::new(id, command_sets::CLASS_TYPE, CLASS_TYPE_INVOKE_METHOD);
packet.data.put_u64(class_id);
packet.data.put_u64(thread_id);
packet.data.put_u64(method_id);
packet.data.put_i32(i32::try_from(args.len()).unwrap_or(i32::MAX));
for a in &args {
write_tagged_value(&mut packet.data, a);
}
packet.data.put_i32(INVOKE_SINGLE_THREADED);
let reply = self.send_invoke(packet).await?;
reply.check_error()?;
read_invoke_reply(reply.data())
}
}
fn read_invoke_reply(mut data: &[u8]) -> JdwpResult<(Value, ObjectId)> {
let ret_tag = read_u8(&mut data)?;
let ret = Value { tag: ret_tag, data: read_value_by_tag(ret_tag, &mut data)? };
let _exc_tag = read_u8(&mut data)?;
let exc_id = read_u64(&mut data)?;
Ok((ret, exc_id))
}
pub(crate) fn write_tagged_value<B: BufMut>(buf: &mut B, v: &Value) {
buf.put_u8(v.tag);
write_untagged_value(buf, v);
}
pub(crate) fn write_untagged_value<B: BufMut>(buf: &mut B, v: &Value) {
match &v.data {
ValueData::Byte(x) => buf.put_i8(*x),
ValueData::Char(x) => buf.put_u16(*x),
ValueData::Float(x) => buf.put_f32(*x),
ValueData::Double(x) => buf.put_f64(*x),
ValueData::Int(x) => buf.put_i32(*x),
ValueData::Long(x) => buf.put_i64(*x),
ValueData::Short(x) => buf.put_i16(*x),
ValueData::Boolean(x) => buf.put_u8(u8::from(*x)),
ValueData::Object(x) => buf.put_u64(*x),
ValueData::Void => {}
}
}