Skip to content

Commit 58a9115

Browse files
Fede Atcalmant
authored andcommitted
adds suport for SC_WRITE_METHOD
1 parent cd94bff commit 58a9115

File tree

3 files changed

+85
-63
lines changed

3 files changed

+85
-63
lines changed

javaobj/v2/api.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,19 @@ def load_array(self, reader, field_type, size):
7878
:param size: Number of elements in the array
7979
"""
8080
return None
81+
82+
def load_custom_writeObject(self, parser, reader, name):
83+
"""
84+
Reads content stored from a custom writeObject.
85+
86+
This method is called only if the class description has both the
87+
``SC_SERIALIZABLE`` and ``SC_WRITE_METHOD`` flags set.
88+
89+
The stream parsing will stop and fail if this method returns None.
90+
91+
:param parser: The JavaStreamParser in use
92+
:param reader: The data stream reader
93+
:param name: The class description name
94+
:return: An array with the parsed fields or None
95+
"""
96+
return None

javaobj/v2/beans.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ class ContentType(IntEnum):
6060
BLOCKDATA = 6
6161
EXCEPTIONSTATE = 7
6262

63+
class ClassDataType(IntEnum):
64+
"""
65+
Class data types
66+
"""
67+
68+
NOWRCLASS = 0
69+
WRCLASS = 1
70+
EXTERNAL_CONTENTS = 2
71+
OBJECT_ANNOTATION = 3
72+
6373

6474
class ClassDescType(IntEnum):
6575
"""
@@ -181,7 +191,6 @@ def __hash__(self):
181191
def __eq__(self, other):
182192
return self.value == other
183193

184-
185194
class JavaField:
186195
"""
187196
Represents a field in a Java class description
@@ -304,6 +313,16 @@ def fields_types(self):
304313
"""
305314
return [field.type for field in self.fields]
306315

316+
@property
317+
def data_type(self):
318+
if (ClassDescFlags.SC_SERIALIZABLE & self.desc_flags):
319+
return ClassDataType.WRCLASS if (ClassDescFlags.SC_WRITE_METHOD & self.desc_flags) else ClassDataType.NOWRCLASS
320+
elif (ClassDescFlags.SC_EXTERNALIZABLE & self.desc_flags):
321+
return ClassDataType.OBJECT_ANNOTATION if (ClassDescFlags.SC_WRITE_METHOD & self.desc_flags) else ClassDataType.EXTERNAL_CONTENTS
322+
323+
raise ValueError("Unhandled Class Data Type")
324+
325+
307326
def is_array_class(self):
308327
# type: () -> bool
309328
"""

javaobj/v2/core.py

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
ExceptionRead,
4949
ClassDescType,
5050
FieldType,
51+
ClassDataType,
5152
)
5253
from .stream import DataStreamReader
5354
from ..constants import (
@@ -57,6 +58,7 @@
5758
TypeCode,
5859
PRIMITIVE_TYPES,
5960
)
61+
6062
from ..modifiedutf8 import decode_modified_utf8
6163

6264
# ------------------------------------------------------------------------------
@@ -276,7 +278,7 @@ def _do_null(self, _):
276278
"""
277279
return None
278280

279-
def _read_content(self, type_code, block_data):
281+
def _read_content(self, type_code, block_data, class_desc=None):
280282
# type: (int, bool) -> ParsedJavaContent
281283
"""
282284
Parses the next content
@@ -290,14 +292,17 @@ def _read_content(self, type_code, block_data):
290292
try:
291293
handler = self.__type_code_handlers[type_code]
292294
except KeyError:
295+
'''Looking for an external reader'''
296+
if class_desc and class_desc.data_type == ClassDataType.WRCLASS:
297+
return self._custom_readObject(class_desc.name)
293298
raise ValueError("Unknown type code: 0x{0:x}".format(type_code))
294299
else:
295300
try:
296301
return handler(type_code)
297302
except ExceptionRead as ex:
298303
return ex.exception_object
299304

300-
def _read_new_string(self, type_code):
305+
def _read_new_string(self, type_code, field_name=None):
301306
# type: (int) -> JavaString
302307
"""
303308
Reads a Java String
@@ -321,7 +326,7 @@ def _read_new_string(self, type_code):
321326
raise ValueError("Invalid string length: {0}".format(length))
322327
elif length < 65536:
323328
self._log.warning("Small string stored as a long one")
324-
329+
325330
# Parse the content
326331
data = self.__fd.read(length)
327332
java_str = JavaString(handle, data)
@@ -338,12 +343,10 @@ def _read_classdesc(self):
338343
type_code = self.__reader.read_byte()
339344
return self._do_classdesc(type_code)
340345

341-
def _do_classdesc(self, type_code, must_be_new=False):
346+
def _do_classdesc(self, type_code):
342347
# type: (int, bool) -> JavaClassDesc
343348
"""
344349
Parses a class description
345-
346-
:param must_be_new: Check if the class description is really a new one
347350
"""
348351
if type_code == TerminalCode.TC_CLASSDESC:
349352
# Do the real job
@@ -352,57 +355,47 @@ def _do_classdesc(self, type_code, must_be_new=False):
352355
handle = self._new_handle()
353356
desc_flags = self.__reader.read_byte()
354357
nb_fields = self.__reader.read_short()
358+
355359
if nb_fields < 0:
356360
raise ValueError("Invalid field count: {0}".format(nb_fields))
357361

358362
fields = [] # type: List[JavaField]
359363
for _ in range(nb_fields):
360364
field_type = self.__reader.read_byte()
361-
if field_type in PRIMITIVE_TYPES:
362-
# Primitive type
363-
field_name = self.__reader.read_UTF()
364-
fields.append(JavaField(FieldType(field_type), field_name))
365-
elif field_type in (TypeCode.TYPE_OBJECT, TypeCode.TYPE_ARRAY,):
366-
# Array or object type
367-
field_name = self.__reader.read_UTF()
365+
field_name = self.__reader.read_UTF()
366+
class_name = None
367+
368+
if field_type in (TypeCode.TYPE_OBJECT, TypeCode.TYPE_ARRAY):
368369
# String type code
369370
str_type_code = self.__reader.read_byte()
370371
class_name = self._read_new_string(str_type_code)
371-
fields.append(
372-
JavaField(
373-
FieldType(field_type), field_name, class_name,
374-
),
375-
)
376-
else:
372+
elif field_type not in PRIMITIVE_TYPES:
377373
raise ValueError(
378374
"Invalid field type char: 0x{0:x}".format(field_type)
379375
)
380376

377+
fields.append(JavaField(
378+
FieldType(field_type), field_name, class_name
379+
))
380+
381381
# Setup the class description bean
382382
class_desc = JavaClassDesc(ClassDescType.NORMALCLASS)
383383
class_desc.name = name
384384
class_desc.serial_version_uid = serial_version_uid
385385
class_desc.handle = handle
386386
class_desc.desc_flags = desc_flags
387387
class_desc.fields = fields
388-
class_desc.annotations = self._read_class_annotations()
388+
class_desc.annotations = self._read_class_annotations(class_desc)
389389
class_desc.super_class = self._read_classdesc()
390390

391391
# Store the reference to the parsed bean
392392
self._set_handle(handle, class_desc)
393393
return class_desc
394394
elif type_code == TerminalCode.TC_NULL:
395395
# Null reference
396-
if must_be_new:
397-
raise ValueError("Got Null instead of a new class description")
398396
return None
399397
elif type_code == TerminalCode.TC_REFERENCE:
400398
# Reference to an already loading class description
401-
if must_be_new:
402-
raise ValueError(
403-
"Got a reference instead of a new class description"
404-
)
405-
406399
previous = self._do_reference()
407400
if not isinstance(previous, JavaClassDesc):
408401
raise ValueError("Referenced object is not a class description")
@@ -424,10 +417,20 @@ def _do_classdesc(self, type_code, must_be_new=False):
424417
# Store the reference to the parsed bean
425418
self._set_handle(handle, class_desc)
426419
return class_desc
427-
420+
428421
raise ValueError("Expected a valid class description starter")
429422

430-
def _read_class_annotations(self):
423+
424+
def _custom_readObject(self, class_name):
425+
self.__fd.seek(-1, os.SEEK_CUR)
426+
for transformer in self.__transformers:
427+
class_data = transformer.load_custom_writeObject(self, self.__reader, class_name)
428+
if class_data:
429+
return class_data
430+
raise ValueError("Custom readObject can not be processed")
431+
432+
433+
def _read_class_annotations(self, class_desc=None):
431434
# type: () -> List[ParsedJavaContent]
432435
"""
433436
Reads the annotations associated to a class
@@ -442,8 +445,8 @@ def _read_class_annotations(self):
442445
# Reset references
443446
self._reset()
444447
continue
448+
java_object = self._read_content(type_code, True, class_desc)
445449

446-
java_object = self._read_content(type_code, True)
447450
if java_object is not None and java_object.is_exception:
448451
raise ExceptionRead(java_object)
449452

@@ -503,40 +506,24 @@ def _read_class_data(self, instance):
503506

504507
for cd in classes:
505508
values = {} # type: Dict[JavaField, Any]
506-
if cd.desc_flags & ClassDescFlags.SC_SERIALIZABLE:
507-
if cd.desc_flags & ClassDescFlags.SC_EXTERNALIZABLE:
508-
raise ValueError(
509-
"SC_EXTERNALIZABLE & SC_SERIALIZABLE encountered"
510-
)
511-
512-
for field in cd.fields:
513-
values[field] = self._read_field_value(field.type)
514-
515-
all_data[cd] = values
516-
517-
if cd.desc_flags & ClassDescFlags.SC_WRITE_METHOD:
518-
if cd.desc_flags & ClassDescFlags.SC_ENUM:
519-
raise ValueError(
520-
"SC_ENUM & SC_WRITE_METHOD encountered!"
521-
)
522-
523-
annotations[cd] = self._read_class_annotations()
524-
elif cd.desc_flags & ClassDescFlags.SC_EXTERNALIZABLE:
525-
if cd.desc_flags & ClassDescFlags.SC_SERIALIZABLE:
526-
raise ValueError(
527-
"SC_EXTERNALIZABLE & SC_SERIALIZABLE encountered"
528-
)
529-
530-
if cd.desc_flags & ClassDescFlags.SC_BLOCK_DATA:
509+
cd.validate()
510+
if cd.data_type == ClassDataType.NOWRCLASS or cd.data_type == ClassDataType.WRCLASS:
511+
if cd.data_type == ClassDataType.NOWRCLASS:
512+
for field in cd.fields:
513+
values[field] = self._read_field_value(field.type)
514+
all_data[cd] = values
515+
else:
516+
annotations[cd] = self._read_class_annotations(cd)
517+
else:
518+
if cd.data_type == ClassDataType.OBJECT_ANNOTATION:
531519
# Call the transformer if possible
532520
if not instance.load_from_blockdata(self, self.__reader):
533521
# Can't read :/
534522
raise ValueError(
535523
"hit externalizable with nonzero SC_BLOCK_DATA; "
536524
"can't interpret data"
537525
)
538-
539-
annotations[cd] = self._read_class_annotations()
526+
annotations[cd] = self._read_class_annotations(cd)
540527

541528
# Fill the instance object
542529
instance.annotations = annotations
@@ -568,11 +555,11 @@ def _read_field_value(self, field_type):
568555
return self.__reader.read_bool()
569556
elif field_type in (FieldType.OBJECT, FieldType.ARRAY):
570557
sub_type_code = self.__reader.read_byte()
571-
if (
572-
field_type == FieldType.ARRAY
573-
and sub_type_code != TerminalCode.TC_ARRAY
574-
):
575-
raise ValueError("Array type listed, but type code != TC_ARRAY")
558+
if field_type == FieldType.ARRAY:
559+
if sub_type_code == TerminalCode.TC_REFERENCE:
560+
return self._do_classdesc(sub_type_code)
561+
elif sub_type_code != TerminalCode.TC_ARRAY:
562+
raise ValueError("Array type listed, but type code != TC_ARRAY")
576563

577564
content = self._read_content(sub_type_code, False)
578565
if content is not None and content.is_exception:

0 commit comments

Comments
 (0)