Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement async abstract methods, add TCP conn #91

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion projects/jdwp/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
python_binary(
name = "main",
main = "main.py",
deps = [],
deps = [":lib"],
)


python_library(
name = "lib",
srcs = glob(["**/*.py"]),
visibility = ["PUBLIC"],
deps = [ "//projects/jdwp/runtime:runtime"]
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib target is obsolete now, could you just remove it and instead make main depend on //projects/jdwp/runtime:runtime directly ?

18 changes: 15 additions & 3 deletions projects/jdwp/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.

import asyncio
from projects.jdwp.runtime.jvm_connection import JVMConnection

def main():
return None

async def main():
host = "localhost"
port = 8880

connection = JVMConnection(host, port)

await connection.connect()

await connection.handshake()

await connection.close()


if __name__ == "__main__":
main()
asyncio.run(main())
4 changes: 3 additions & 1 deletion projects/jdwp/runtime/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ python_library(
":type-aliases",
"async_streams.py",
"jdwpstruct.py",
"jdwp_streams.py",
"jvm_connection.py",
],
visibility = ["PUBLIC", ],
deps = [],
deps = ["//projects/jdwp/defs:defs"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency is probably unnecessary, runtime should not depend on defs directly

)
40 changes: 20 additions & 20 deletions projects/jdwp/runtime/async_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,85 +96,85 @@ async def read_long(
class JDWPOutputStreamBase(abc.ABC):
# Methods for OpaqueType
@abc.abstractmethod
def write_boolean(self, value: bool) -> None:
async def write_boolean(self, value: bool) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Output stream can (and should) be synchronous. StreamWriter.write is. Please remove changes in this file.

pass

@abc.abstractmethod
def write_location(self, value: typing.Any) -> None:
async def write_location(self, value: typing.Any) -> None:
pass

@abc.abstractmethod
def write_string(self, value: str) -> None:
async def write_string(self, value: str) -> None:
pass

# Methods for IdType
@abc.abstractmethod
def write_object_id(self, value: ObjectIDType) -> None:
async def write_object_id(self, value: ObjectIDType) -> None:
pass

@abc.abstractmethod
def write_thread_id(self, value: ThreadIDType) -> None:
async def write_thread_id(self, value: ThreadIDType) -> None:
pass

@abc.abstractmethod
def write_thread_group_id(self, value: ThreadGroupIDType) -> None:
async def write_thread_group_id(self, value: ThreadGroupIDType) -> None:
pass

@abc.abstractmethod
def write_string_id(self, value: StringIDType) -> None:
async def write_string_id(self, value: StringIDType) -> None:
pass

@abc.abstractmethod
def write_class_loader_id(self, value: ClassLoaderIDType) -> None:
async def write_class_loader_id(self, value: ClassLoaderIDType) -> None:
pass

@abc.abstractmethod
def write_class_object_id(self, value: ClassObjectIDType) -> None:
async def write_class_object_id(self, value: ClassObjectIDType) -> None:
pass

@abc.abstractmethod
def write_array_id(self, value: ArrayIDType) -> None:
async def write_array_id(self, value: ArrayIDType) -> None:
pass

@abc.abstractmethod
def write_reference_type_id(self, value: ReferenceTypeIDType) -> None:
async def write_reference_type_id(self, value: ReferenceTypeIDType) -> None:
pass

@abc.abstractmethod
def write_class_id(self, value: ClassIDType) -> None:
async def write_class_id(self, value: ClassIDType) -> None:
pass

@abc.abstractmethod
def write_interface_id(self, value: InterfaceIDType) -> None:
async def write_interface_id(self, value: InterfaceIDType) -> None:
pass

@abc.abstractmethod
def write_array_type_id(self, value: ArrayTypeIDType) -> None:
async def write_array_type_id(self, value: ArrayTypeIDType) -> None:
pass

@abc.abstractmethod
def write_method_id(self, value: MethodIDType) -> None:
async def write_method_id(self, value: MethodIDType) -> None:
pass

@abc.abstractmethod
def write_field_id(self, value: FieldIDType) -> None:
async def write_field_id(self, value: FieldIDType) -> None:
pass

@abc.abstractmethod
def write_frame_id(self, value: FrameIDType) -> None:
async def write_frame_id(self, value: FrameIDType) -> None:
pass

# Methods for IntegralType
@abc.abstractmethod
def write_byte(self, value: int) -> None:
async def write_byte(self, value: int) -> None:
pass

@abc.abstractmethod
def write_int(self, value: int) -> None:
async def write_int(self, value: int) -> None:
pass

@abc.abstractmethod
def write_long(
async def write_long(
self,
value: int,
) -> None:
Expand Down
181 changes: 181 additions & 0 deletions projects/jdwp/runtime/jdwp_streams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.

import struct
import typing
import asyncio
from projects.jdwp.defs.schema import IdType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably does not need to be imported.

from projects.jdwp.runtime.type_aliases import *
from projects.jdwp.runtime.async_streams import (
JDWPInputStreamBase,
JDWPOutputStreamBase,
)


class JDWPInputStream(JDWPInputStreamBase):
__tcp_connection: asyncio.StreamReader

def __init__(self, stream_reader: asyncio.StreamReader):
super().__init__()
self.__tcp_connection = stream_reader

async def read_boolean(self) -> bool:
data = await self._read_bytes(1)
return bool(data[0])

async def read_location(self) -> typing.Any:
pass

async def read_string(self) -> str:
length = await self.read_int()
string_data = await self._read_bytes(length)
return string_data.decode("utf-8")

async def read_object_id(self) -> ObjectIDType:
return await self._read_id(IdType.OBJECT_ID)

async def read_thread_id(self) -> ThreadIDType:
return await self._read_id(IdType.THREAD_ID)

async def read_thread_group_id(self) -> ThreadGroupIDType:
return await self._read_id(IdType.THREAD_GROUP_ID)

async def read_string_id(self) -> StringIDType:
return await self._read_id(IdType.STRING_ID)

async def read_class_loader_id(self) -> ClassLoaderIDType:
return await self._read_id(IdType.CLASS_LOADER_ID)

async def read_class_object_id(self) -> ClassObjectIDType:
return await self._read_id(IdType.CLASS_OBJECT_ID)

async def read_array_id(self) -> ArrayIDType:
return await self._read_id(IdType.ARRAY_ID)

async def read_reference_type_id(self) -> ReferenceTypeIDType:
return await self._read_id(IdType.REFERENCE_TYPE_ID)

async def read_class_id(self) -> ClassIDType:
return await self._read_id(IdType.CLASS_ID)

async def read_interface_id(self) -> InterfaceIDType:
return await self._read_id(IdType.INTERFACE_ID)

async def read_array_type_id(self) -> ArrayTypeIDType:
return await self._read_id(IdType.ARRAY_TYPE_ID)

async def read_method_id(self) -> MethodIDType:
return await self._read_id(IdType.METHOD_ID)

async def read_field_id(self) -> FieldIDType:
return await self._read_id(IdType.FIELD_ID)

async def read_frame_id(self) -> FrameIDType:
return await self._read_id(IdType.FRAME_ID)

async def read_byte(self) -> int:
data = await self._read_bytes(1)
return int.from_bytes(data, byteorder="big")

async def read_int(self) -> int:
data = await self._read_bytes(4)
return struct.unpack("!I", data)[0]

async def read_long(self) -> int:
data = await self._read_bytes(8)
return struct.unpack("!Q", data)[0]

async def _read_bytes(self, size: int) -> bytes:
try:
return await self.__tcp_connection.readexactly(size)
except Exception as e:
print(f"Error during data receiving: {e}")
return b""

async def _read_id(self, id_type: IdType) -> typing.Any:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you drop the IdType parameter please ? Perhaps drop this method entirely and make each of the methods calling it assert False and return XXXIdType(0)



class JDWPOutputStream(JDWPOutputStreamBase):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed my comment about JDWPBufferOutputStream. We need an output steam that appends everything to a byte buffer because we need to know the size of the entire message in order to write its header.

__tcp_connection: asyncio.StreamWriter

def __init__(self, socket_connection: asyncio.StreamWriter):
super().__init__()
self.__tcp_connection = socket_connection

async def write_boolean(self, value: bool) -> None:
await self._write_bytes(struct.pack("!B", int(value)))

async def write_int(self, value: int) -> None:
await self._write_bytes(struct.pack("!I", value))

async def write_array_id(self, value: ArrayIDType) -> None:
await self._write_id(value)

async def write_array_type_id(self, value: ArrayTypeIDType) -> None:
await self._write_id(value)

async def write_byte(self, value: int) -> None:
await self._write_bytes(struct.pack("!B", value))

async def write_class_id(self, value: ClassIDType) -> None:
await self._write_id(value)

async def write_class_loader_id(self, value: ClassLoaderIDType) -> None:
await self._write_id(value)

async def write_class_object_id(self, value: ClassObjectIDType) -> None:
await self._write_id(value)

async def write_field_id(self, value: FieldIDType) -> None:
await self._write_id(value)

async def write_frame_id(self, value: FrameIDType) -> None:
await self._write_id(value)

async def write_interface_id(self, value: InterfaceIDType) -> None:
await self._write_id(value)

async def write_location(self, value: typing.Any) -> None:
pass

async def write_method_id(self, value: MethodIDType) -> None:
await self._write_id(value)

async def write_string_id(self, value: StringIDType) -> None:
await self._write_id(value)

async def write_thread_group_id(self, value: ThreadGroupIDType) -> None:
await self._write_id(value)

async def write_long(self, value: int) -> None:
await self._write_bytes(struct.pack("!Q", value))

async def write_object_id(self, value: typing.Any) -> None:
await self._write_id(value)

async def write_thread_id(self, value: typing.Any) -> None:
await self._write_id(value)

async def write_string(self, value: str) -> None:
value_bytes = value.encode("utf-8")
length = len(value_bytes)

await self.write_int(length)

await self._write_bytes(value_bytes)

async def write_reference_type_id(self, value: ReferenceTypeIDType) -> None:
await self._write_id(value)

async def _write_bytes(self, data: bytes) -> None:
try:
self.__tcp_connection.write(data)
await self.__tcp_connection.drain()
except Exception as e:
print(f"Error during data sending: {e}")
await self.__tcp_connection.drain()

async def _write_id(self, value: typing.Any) -> None:
size = min(value.bit_length() // 8 + 1, 8)
await self._write_bytes(struct.pack("B", size))
await self._write_bytes(value.to_bytes(size, byteorder="big"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not the format, is it? Where does the size come from ? The size does not appear in the wire format, to learn the sizes we need to send IDSizes command and parse the response.

Loading