Fix serialization of class fields with same names.
Dex allows for fields to share a name if they have
different types. openJdk serialization code was not
handling this case properly.
Bug: 29721023
Change-Id: I3b8ea027f0af829e9f158d4bcb11a2052e34c10f
(cherry picked from commit 0885fcbbbf2fdc46ffe847aca9d3c339f4c7bb41)
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
index af5fce5..e81224a 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
@@ -17,8 +17,10 @@
package org.apache.harmony.tests.java.io;
+import dalvik.system.DexFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
@@ -27,6 +29,9 @@
import java.io.ObjectStreamClass;
import java.io.ObjectStreamException;
import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import junit.framework.TestCase;
@@ -210,4 +215,54 @@
// Excpected
}
}
+
+ // http://b/29721023
+ public void test_sameName() throws Exception {
+ // Load class from dex, it's not possible to create a class with same-named
+ // fields in java (but it's allowed in dex).
+ File sameFieldNames = File.createTempFile("sameFieldNames", ".dex");
+ InputStream dexIs = this.getClass().getClassLoader().
+ getResourceAsStream("tests/api/java/io/sameFieldNames.dex");
+ assertNotNull(dexIs);
+
+ Class<?> clazz = null;
+
+ // Get the class object
+ try {
+ Files.copy(dexIs, sameFieldNames.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ DexFile dexFile = new DexFile(sameFieldNames);
+ clazz = dexFile.loadClass("sameFieldNames", getClass().getClassLoader());
+ dexFile.close();
+ } finally {
+ if (sameFieldNames.exists()) {
+ sameFieldNames.delete();
+ }
+ }
+
+ // Create class instance, fill it with content
+ Object o1 = clazz.getConstructor().newInstance();
+ int v = 123;
+ for(Field f : clazz.getFields()) {
+ if (f.getType() == Integer.class) {
+ f.set(o1, new Integer(v++));
+ } else if (f.getType() == Long.class) {
+ f.set(o1, new Long(v++));
+ }
+ }
+
+ // Serialize and deserialize
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o1);
+ oos.close();
+ ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
+ baos.toByteArray()));
+ Object o2 = ois.readObject();
+ ois.close();
+
+ // Compare content
+ for(Field f : clazz.getFields()) {
+ assertEquals(f.get(o1), f.get(o2));
+ }
+ }
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
index 7ae4617..948059b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
@@ -17,16 +17,22 @@
package org.apache.harmony.tests.java.io;
-import junit.framework.TestCase;
+import dalvik.system.DexFile;
import java.io.Externalizable;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.Serializable;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import junit.framework.TestCase;
public class ObjectStreamClassTest extends TestCase {
@@ -294,4 +300,27 @@
hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
true /* checkSuperclass */));
}
+
+ // http://b/29721023
+ public void testClassWithSameFieldName() throws Exception {
+ // Load class from dex, it's not possible to create a class with same-named
+ // fields in java (but it's allowed in dex).
+ File sameFieldNames = File.createTempFile("sameFieldNames", ".dex");
+ InputStream dexIs = this.getClass().getClassLoader().
+ getResourceAsStream("tests/api/java/io/sameFieldNames.dex");
+ assertNotNull(dexIs);
+
+ try {
+ Files.copy(dexIs, sameFieldNames.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ DexFile dexFile = new DexFile(sameFieldNames);
+ Class<?> clazz = dexFile.loadClass("sameFieldNames", getClass().getClassLoader());
+ ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
+ assertEquals(4, osc.getFields().length);
+ dexFile.close();
+ } finally {
+ if (sameFieldNames.exists()) {
+ sameFieldNames.delete();
+ }
+ }
+ }
}
diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex
new file mode 100644
index 0000000..ada4935
--- /dev/null
+++ b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex
Binary files differ
diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali
new file mode 100644
index 0000000..657b965
--- /dev/null
+++ b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali
@@ -0,0 +1,17 @@
+# Source for sameFieldNames.dex
+.class public LsameFieldNames;
+.super Ljava/lang/Object;
+.implements Ljava/io/Serializable;
+
+# Test multiple fields with the same name and different types.
+# (Invalid in Java language but valid in bytecode.)
+.field public a:J
+.field public a:I
+.field public a:Ljava/lang/Integer;
+.field public a:Ljava/lang/Long;
+
+.method public constructor <init>()V
+ .registers 2
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
diff --git a/ojluni/src/main/java/java/io/ObjectStreamClass.java b/ojluni/src/main/java/java/io/ObjectStreamClass.java
index a157f4d..ffbde98e 100755
--- a/ojluni/src/main/java/java/io/ObjectStreamClass.java
+++ b/ojluni/src/main/java/java/io/ObjectStreamClass.java
@@ -2246,13 +2246,9 @@
ObjectStreamField f = fields[i], m = null;
for (int j = 0; j < localFields.length; j++) {
ObjectStreamField lf = localFields[j];
- if (f.getName().equals(lf.getName())) {
- if ((f.isPrimitive() || lf.isPrimitive()) &&
- f.getTypeCode() != lf.getTypeCode())
- {
- throw new InvalidClassException(localDesc.name,
- "incompatible types for field " + f.getName());
- }
+ // Android-changed: We can have fields with a same name and a different type.
+ if (f.getName().equals(lf.getName()) &&
+ f.getSignature().equals(lf.getSignature())) {
if (lf.getField() != null) {
m = new ObjectStreamField(
lf.getField(), lf.isUnshared(), false);