Skip to content

fix: Custom reader should read till endblock #61

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
63 changes: 28 additions & 35 deletions javaobj/v1/unmarshaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,56 +514,51 @@ def do_object(self, parent=None, ident=0):
# TODO:
raise NotImplementedError("externalContents isn't implemented yet")

# Process class data for the entire hierarchy
self._read_serializable_data(java_object, classdesc, ident)

log_debug(">>> java_object: {0}".format(java_object), ident)
return java_object

def _read_serializable_data(self, java_object, classdesc, ident):
"""
Read serializable data following the Java specification more closely.
According to the spec, for each class in the hierarchy (from super to sub):
1. Read primitive fields
2. If SC_WRITE_METHOD is set, read custom writeObject data until TC_ENDBLOCKDATA
"""
if classdesc.superclass:
self._read_serializable_data(java_object, classdesc.superclass, ident + 1)

if classdesc.flags & ClassDescFlags.SC_SERIALIZABLE:
# TODO: look at ObjectInputStream.readSerialData()
# FIXME: Handle the SC_WRITE_METHOD flag

# create megalist
tempclass = classdesc
megalist = []
megatypes = []
log_debug("Constructing class...", ident)
while tempclass:
log_debug("Class: {0}".format(tempclass.name), ident + 1)
log_debug("Class: {0}".format(classdesc.name), ident + 1)
if classdesc.fields_names:
class_fields_str = " - ".join(
" ".join((str(field_type), field_name))
for field_type, field_name in zip(
tempclass.fields_types, tempclass.fields_names
classdesc.fields_types, classdesc.fields_names
)
)
if class_fields_str:
log_debug(class_fields_str, ident + 2)

fieldscopy = tempclass.fields_names[:]
fieldscopy.extend(megalist)
megalist = fieldscopy
log_debug(class_fields_str, ident + 2)

fieldscopy = tempclass.fields_types[:]
fieldscopy.extend(megatypes)
megatypes = fieldscopy
log_debug("Values count: {0}".format(len(classdesc.fields_names)), ident)
log_debug("Prepared list of values: {0}".format(classdesc.fields_names), ident)
log_debug("Prepared list of types: {0}".format(classdesc.fields_types), ident)

tempclass = tempclass.superclass

log_debug("Values count: {0}".format(len(megalist)), ident)
log_debug("Prepared list of values: {0}".format(megalist), ident)
log_debug("Prepared list of types: {0}".format(megatypes), ident)

for field_name, field_type in zip(megalist, megatypes):
for field_name, field_type in zip(classdesc.fields_names, classdesc.fields_types):
log_debug(
"Reading field: {0} - {1}".format(field_type, field_name)
)
res = self._read_value(field_type, ident, name=field_name)
java_object.__setattr__(field_name, res)

if (
classdesc.flags & ClassDescFlags.SC_SERIALIZABLE
and classdesc.flags & ClassDescFlags.SC_WRITE_METHOD
or classdesc.flags & ClassDescFlags.SC_EXTERNALIZABLE
and classdesc.flags & ClassDescFlags.SC_BLOCK_DATA
or classdesc.superclass is not None
and classdesc.superclass.flags & ClassDescFlags.SC_SERIALIZABLE
and classdesc.superclass.flags & ClassDescFlags.SC_WRITE_METHOD
):
has_write_method = classdesc.flags & ClassDescFlags.SC_SERIALIZABLE and classdesc.flags & ClassDescFlags.SC_WRITE_METHOD
has_block_data = classdesc.flags & ClassDescFlags.SC_EXTERNALIZABLE and classdesc.flags & ClassDescFlags.SC_BLOCK_DATA
if has_write_method or has_block_data:
# objectAnnotation
log_debug(
"java_object.annotations before: {0}".format(
Expand All @@ -572,6 +567,7 @@ def do_object(self, parent=None, ident=0):
ident,
)

opcode = None
while opcode != TerminalCode.TC_ENDBLOCKDATA:
opcode, obj = self._read_and_exec_opcode(ident=ident + 1)
# , expect=[self.TC_ENDBLOCKDATA, self.TC_BLOCKDATA,
Expand All @@ -593,9 +589,6 @@ def do_object(self, parent=None, ident=0):
log_debug("Java object has extra loading capability.")
java_object.__extra_loading__(self, ident)

log_debug(">>> java_object: {0}".format(java_object), ident)
return java_object

def do_string(self, parent=None, ident=0):
"""
Handles a TC_STRING opcode
Expand Down
Binary file added tests/issue60_custom_reader_endblock.ser
Binary file not shown.
72 changes: 72 additions & 0 deletions tests/java/src/test/java/Issue60CustomReaderEndblock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import java.io.*;
import java.util.List;

class SuperClass implements Serializable {
private List<String> superItems = null;

private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// Custom serialization logic that triggers SC_WRITE_METHOD flag
out.writeUTF("custom_marker");
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
// Custom deserialization logic
String marker = in.readUTF();
System.out.println("Read custom marker: " + marker);
}
}

class CustomClass extends SuperClass {
private static final long serialVersionUID = 1L;

private String name;
private List<String> items = null;
private int port = 443;

public CustomClass(String name) {
this.name = name;
}

private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// Custom serialization for child class too
out.writeInt(42);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
int customValue = in.readInt();
System.out.println("Read custom value: " + customValue);
}

@Override
public String toString() {
return "CustomClass{name='" + name + "', items=" + items + "', port=" + port + "}";
}
}

public class SerializationExample {
public static void main(String[] args) {
try {
// Create and serialize
CustomClass obj = new CustomClass("test");
System.out.println("Original: " + obj);

// Serialize to file
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("issue60_custom_reader_endblock.ser"))) {
oos.writeObject(obj);
}

// Deserialize from file
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("issue60_custom_reader_endblock.ser"))) {
CustomClass deserialized = (CustomClass) ois.readObject();
System.out.println("Deserialized: " + deserialized);
}

} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
11 changes: 11 additions & 0 deletions tests/test_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,17 @@ def test_qistoph_pr_27(self):
for key, value in pobj.items():
self.assertEqual(parent_map[key], value)

def test_read_custom(self):
"""
Tests to verify that the super-class is properly read when a custom writer is involved.
"""
ser = self.read_file("issue60_custom_reader_endblock.ser")
pobj = javaobj.loads(ser)
self.assertIsNone(pobj.superItems)
self.assertIsNone(pobj.items)
self.assertEquals(pobj.name, "test")
self.assertEquals(pobj.port, 443)


# ------------------------------------------------------------------------------

Expand Down
1 change: 0 additions & 1 deletion tests/test_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,6 @@ def test_writeObject(self):
self.assertEqual(expected["custom_obj"]["field_data"], child_data)
self.assertEqual(expected["custom_obj"]["annotations"], super_data)


# ------------------------------------------------------------------------------


Expand Down