-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add jdwp streams implemented abstract class
Codegen dataclassess (#79) * Wrote test to validate primitive type mapping * Added test for type alias definition * Built dataclasses code generator * Built test for dataclassess code generator * Changed to unittest * Updated struct dataclass function to support all types * feat: built dataclass generator * Added copyright * Added nested struct support * Added test for nested struct * Feat: update functions and put it in a class. * chore: update tests * Casted types to pass pyre check * Updated types * chore: Refactor typing.cast code * Added copyright * refactor: update tests to reflect new schema changes * fix: Fix pyre typing errors [jdwp] do not emit array length and union tag fields [jdwp] map union tags and array lengths to int [jdwp] fix type for union fields [jdwp] fix names of fields containing spaces [jdwp] generate projects.jdwp.runtime.structs [jdwp] add tests importing components of the runtime library implement async abstract methods, add TCP conn update files update conn and streams add buck deps add jvm connection and implement jvm streams add a base class for jdwp structs [jdwp] generate projects.jdwp.runtime.structs [jdwp] add tests importing components of the runtime library fix fail build
- Loading branch information
Showing
5 changed files
with
294 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
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 | ||
|
||
|
||
class JDWPOutputStream(JDWPOutputStreamBase): | ||
__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")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
||
import asyncio | ||
import typing | ||
import struct | ||
from projects.jdwp.runtime.jdwp_streams import JDWPInputStream, JDWPOutputStream | ||
from projects.jdwp.runtime.jdwpstruct import JDWPStruct | ||
|
||
|
||
class JVMConnection: | ||
next_packet_id: int = 0 | ||
|
||
def __init__(self, host: str, port: int): | ||
self.host: str = host | ||
self.port: int = port | ||
self.__reader: typing.Optional[asyncio.StreamReader] = None | ||
self.__writer: typing.Optional[asyncio.StreamWriter] = None | ||
self.__input_stream: typing.Optional[JDWPInputStream] = None | ||
self.__output_stream: typing.Optional[JDWPOutputStream] = None | ||
|
||
async def connect(self) -> None: | ||
try: | ||
self.__reader, self.__writer = await asyncio.open_connection( | ||
self.host, self.port | ||
) | ||
if self.__writer is None: | ||
raise Exception("Writer not initialized") | ||
self.__output_stream = JDWPOutputStream(self.__writer) | ||
except Exception as e: | ||
print(f"Error during connection: {e}") | ||
await self.close() | ||
|
||
async def close(self) -> None: | ||
for stream in (self.__reader, self.__writer): | ||
if stream is not None: | ||
if isinstance(stream, asyncio.streams.StreamWriter): | ||
stream.close() | ||
self.__input_stream, self.__output_stream = None, None | ||
|
||
async def handshake(self) -> None: | ||
try: | ||
handshake_bytes = b"JDWP-Handshake" | ||
if self.__output_stream is not None: | ||
await self.__output_stream._write_bytes(handshake_bytes) | ||
|
||
if self.__input_stream is not None and hasattr( | ||
self.__input_stream, "read_packet_data" | ||
): | ||
response_bytes = await self.__input_stream._read_bytes( | ||
len(handshake_bytes) | ||
) | ||
|
||
if response_bytes != handshake_bytes: | ||
raise Exception("Invalid handshake response") | ||
|
||
print(f"Handshake successful") | ||
except Exception as e: | ||
print(f"Error during handshake: {e}") | ||
await self.close() | ||
|
||
async def read_packet_header(self) -> typing.Tuple[int, int, bytes, int, int]: | ||
if self.__input_stream is None: | ||
raise Exception("Input stream not initialized") | ||
header_data = await self.__input_stream._read_bytes(10) | ||
return struct.unpack("!IIcBB", header_data) | ||
|
||
async def read_packet_data(self, length: int) -> bytes: | ||
if self.__input_stream is None: | ||
raise Exception("Input stream not initialized") | ||
return await self.__input_stream._read_bytes(length) | ||
|
||
async def write_packet_header( | ||
self, length: int, packet_id: int, flags: bytes, command_set: int, command: int | ||
) -> None: | ||
header_data = struct.pack( | ||
"!IIcBB", length, packet_id, flags, command_set, command | ||
) | ||
if self.__output_stream is None: | ||
raise Exception("Output stream not initialized") | ||
await self.__output_stream._write_bytes(header_data) | ||
|
||
async def write_packet_data(self, data: bytes) -> None: | ||
if self.__output_stream is None: | ||
raise Exception("Output stream not initialized") | ||
await self.__output_stream._write_bytes(data) | ||
|
||
@staticmethod | ||
def get_next_packet_id() -> int: | ||
JVMConnection.next_packet_id += 1 | ||
return JVMConnection.next_packet_id |