Skip to content

Commit f22a5fc

Browse files
committed
Ported implementation of jdeserialize
* Will be useful to debug the current implementation * Can possibly become the next core implementation jdeserialize source: https://github.com/frohoff/jdeserialize
1 parent e042c2c commit f22a5fc

File tree

4 files changed

+1074
-0
lines changed

4 files changed

+1074
-0
lines changed

javaobj/deserialize/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Debugging module, port of the jdeserialize project from Java
3+
=> https://github.com/frohoff/jdeserialize
4+
"""

javaobj/deserialize/beans.py

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Definition of the beans used in javaobj
4+
"""
5+
6+
from enum import Enum, IntEnum
7+
from typing import Any, Dict, List, Optional, Set
8+
import logging
9+
10+
from .constants import *
11+
from ..modifiedutf8 import decode_modified_utf8
12+
13+
14+
class ContentType(IntEnum):
15+
"""
16+
Types of objects
17+
"""
18+
19+
INSTANCE = 0
20+
CLASS = 1
21+
ARRAY = 2
22+
STRING = 3
23+
ENUM = 4
24+
CLASSDESC = 5
25+
BLOCKDATA = 6
26+
EXCEPTIONSTATE = 7
27+
28+
29+
class ClassDescType(IntEnum):
30+
"""
31+
Types of class descriptions
32+
"""
33+
34+
NORMALCLASS = 0
35+
PROXYCLASS = 1
36+
37+
38+
class FieldType(IntEnum):
39+
"""
40+
Types of class fields
41+
"""
42+
43+
BYTE = TYPE_BYTE
44+
CHAR = TYPE_CHAR
45+
DOUBLE = TYPE_DOUBLE
46+
FLOAT = TYPE_FLOAT
47+
INTEGER = TYPE_INTEGER
48+
LONG = TYPE_LONG
49+
SHORT = TYPE_SHORT
50+
BOOLEAN = TYPE_BOOLEAN
51+
ARRAY = TYPE_ARRAY
52+
OBJECT = TYPE_OBJECT
53+
54+
55+
class Content:
56+
"""
57+
Generic representation of data parsed from the stream
58+
"""
59+
60+
def __init__(self, content_type: ContentType):
61+
self.type: ContentType = content_type
62+
self.is_exception: bool = False
63+
self.handle: int = 0
64+
65+
def validate(self) -> None:
66+
"""
67+
Validity check on the object
68+
"""
69+
pass
70+
71+
72+
class ExceptionState(Content):
73+
"""
74+
Representation of a failed parsing
75+
"""
76+
77+
def __init__(self, exception_object: Content, data: bytes):
78+
super().__init__(ContentType.EXCEPTIONSTATE)
79+
self.exception_object = exception_object
80+
self.stream_data = data
81+
self.handle = exception_object.handle
82+
83+
84+
class ExceptionRead(Exception):
85+
"""
86+
Exception used to indicate that an exception object has been parsed
87+
"""
88+
89+
def __init__(self, content: Content):
90+
self.exception_object = content
91+
92+
93+
class JavaString(Content):
94+
"""
95+
Represents a Java string
96+
"""
97+
98+
def __init__(self, handle: int, data: bytes):
99+
super().__init__(ContentType.STRING)
100+
self.handle = handle
101+
value, length = decode_modified_utf8(data)
102+
self.value: str = value
103+
self.length: int = length
104+
105+
def __str__(self) -> str:
106+
return "[String {0:x}: {1}]".format(self.handle, self.value)
107+
108+
__repr__ = __str__
109+
110+
111+
class JavaField:
112+
"""
113+
Represents a field in a Java class description
114+
"""
115+
116+
def __init__(
117+
self,
118+
field_type: FieldType,
119+
name: str,
120+
class_name: Optional[JavaString] = None,
121+
):
122+
self.type = field_type
123+
self.name = name
124+
self.class_name: JavaString = class_name
125+
self.is_inner_class_reference = False
126+
127+
if self.class_name:
128+
self.validate(self.class_name.value)
129+
130+
def validate(self, java_type: str) -> None:
131+
"""
132+
Validates the type given as parameter
133+
"""
134+
if self.type == FieldType.OBJECT:
135+
if not java_type:
136+
raise ValueError("Class name can't be empty")
137+
138+
if java_type[0] != "L" or java_type[-1] != ";":
139+
raise ValueError(
140+
"Invalid object field type: {0}".format(java_type)
141+
)
142+
143+
144+
class JavaClassDesc(Content):
145+
"""
146+
Represents the description of a class
147+
"""
148+
149+
def __init__(self, class_desc_type: ClassDescType):
150+
super().__init__(ContentType.CLASSDESC)
151+
152+
# Type of class description
153+
self.class_type: ClassDescType = class_desc_type
154+
155+
# Class name
156+
self.name: Optional[str] = None
157+
158+
# Serial version UID
159+
self.serial_version_uid: int = 0
160+
161+
# Description flags byte
162+
self.desc_flags: int = 0
163+
164+
# Fields in the class
165+
self.fields: List[JavaField] = []
166+
167+
# Inner classes
168+
self.inner_classes: List[JavaClassDesc] = []
169+
170+
# List of annotations objects
171+
self.annotations: List[Content] = []
172+
173+
# The super class of this one, if any
174+
self.super_class: JavaClassDesc = None
175+
176+
# List of the interfaces of the class
177+
self.interfaces: List[str] = []
178+
179+
# Set of enum constants
180+
self.enum_constants: Set[str] = set()
181+
182+
# Flag to indicate if this is an inner class
183+
self.is_inner_class: bool = False
184+
185+
# Flag to indicate if this is a local inner class
186+
self.is_local_inner_class: bool = False
187+
188+
# Flag to indicate if this is a static member class
189+
self.is_static_member_class: bool = False
190+
191+
def __str__(self):
192+
return "[classdesc 0x{0:x}: name {1}, uid {2}]".format(
193+
self.handle, self.name, self.serial_version_uid
194+
)
195+
196+
__repr__ = __str__
197+
198+
def is_array_class(self) -> bool:
199+
"""
200+
Determines if this is an array type
201+
"""
202+
return self.name.startswith("[") if self.name else False
203+
204+
def get_hierarchy(self, classes: List["JavaClassDesc"]) -> None:
205+
"""
206+
Generates a list of class descriptions in this class's hierarchy, in
207+
the order described by the Object Stream Serialization Protocol.
208+
This is the order in which fields are read from the stream.
209+
210+
:param classes: A list to be filled in with the hierarchy
211+
"""
212+
if self.super_class is not None:
213+
if self.super_class.class_type == ClassDescType.PROXYCLASS:
214+
logging.warning("Hit a proxy class in super class hierarchy")
215+
else:
216+
self.super_class.get_hierarchy(classes)
217+
218+
classes.append(self)
219+
220+
def validate(self):
221+
"""
222+
Checks the validity of this class description
223+
"""
224+
serial_or_extern = SC_SERIALIZABLE | SC_EXTERNALIZABLE
225+
if (self.desc_flags & serial_or_extern) == 0 and self.fields:
226+
raise ValueError(
227+
"Non-serializable, non-externalizable class has fields"
228+
)
229+
230+
if self.desc_flags & serial_or_extern == serial_or_extern:
231+
raise ValueError("Class is both serializable and externalizable")
232+
233+
if self.desc_flags & SC_ENUM:
234+
if self.fields or self.interfaces:
235+
raise ValueError(
236+
"Enums shouldn't implement interfaces "
237+
"or have non-constant fields"
238+
)
239+
else:
240+
if self.enum_constants:
241+
raise ValueError(
242+
"Non-enum classes shouldn't have enum constants"
243+
)
244+
245+
246+
class JavaInstance(Content):
247+
"""
248+
Represents an instance of Java object
249+
"""
250+
251+
def __init__(self):
252+
super().__init__(ContentType.INSTANCE)
253+
self.classdesc: JavaClassDesc = None
254+
self.field_data: Dict[JavaClassDesc, Dict[JavaField, Any]] = {}
255+
self.annotations: Dict[JavaClassDesc, List[Content]] = {}
256+
257+
def __str__(self):
258+
return "[instance 0x{0:x}: type {1}]".format(
259+
self.handle, self.classdesc.name
260+
)
261+
262+
__repr__ = __str__
263+
264+
265+
class JavaClass(Content):
266+
"""
267+
Represents a stored Java class
268+
"""
269+
270+
def __init__(self, handle: int, class_desc: JavaClassDesc):
271+
super().__init__(ContentType.CLASS)
272+
self.handle = handle
273+
self.classdesc = class_desc
274+
275+
def __str__(self):
276+
return "[class 0x{0:x}: {1}]".format(self.handle, self.classdesc)
277+
278+
__repr__ = __str__
279+
280+
281+
class JavaEnum(Content):
282+
"""
283+
Represents an enumeration value
284+
"""
285+
286+
def __init__(
287+
self, handle: int, class_desc: JavaClassDesc, value: JavaString
288+
):
289+
super().__init__(ContentType.ENUM)
290+
self.handle = handle
291+
self.class_desc = class_desc
292+
self.value = value
293+
294+
def __str__(self):
295+
return "[Enum 0x{0:x}: {1}]".format(self.handle, self.value)
296+
297+
__repr__ = __str__
298+
299+
300+
class JavaArray(Content):
301+
"""
302+
Represents a Java array
303+
"""
304+
305+
def __init__(
306+
self,
307+
handle: int,
308+
class_desc: JavaClassDesc,
309+
field_type: FieldType,
310+
content: List[Any],
311+
):
312+
super().__init__(ContentType.ARRAY)
313+
self.handle = handle
314+
self.class_desc = class_desc
315+
self.field_type = field_type
316+
self.content = content
317+
318+
def __str__(self):
319+
return "[array 0x{0:x}: {1} items]".format(
320+
self.handle, len(self.content)
321+
)
322+
323+
__repr__ = __str__
324+
325+
326+
class BlockData(Content):
327+
"""
328+
Represents a data block
329+
"""
330+
331+
def __init__(self, data: bytes):
332+
super().__init__(ContentType.BLOCKDATA)
333+
self.data = data
334+
335+
def __str__(self):
336+
return "[blockdata 0x{0:x}: {1} bytes]".format(
337+
self.handle, len(self.data)
338+
)
339+
340+
__repr__ = __str__

0 commit comments

Comments
 (0)