[m-rev.] for review: deep copy for java

Peter Wang novalazy at gmail.com
Mon Jun 15 15:43:13 AEST 2009


Branches: main

Complete the deep copy implementation for Java.

library/builtin.m:
        Implement deep copy using reflection to instantiate new objects and
        set fields.  Only Mercury objects are copied, in line with deep copy
        in C grades.

compiler/ml_type_gen.m:
        Generate zero-argument constructors for Java classes corresponding to
        Mercury types.  The deep copy implementation needs to be able to
        instantiate the classes without knowing the constructor arguments.

diff --git a/compiler/ml_type_gen.m b/compiler/ml_type_gen.m
index b5fbd59..9e79d62 100644
--- a/compiler/ml_type_gen.m
+++ b/compiler/ml_type_gen.m
@@ -721,12 +721,19 @@ ml_gen_du_ctor_member(ModuleInfo, BaseClassId,
BaseClassQualifier,
             % also need to generate an additional zero-argument constructor,
             % which is used to construct the class that is used for
             % reserved_objects.
+            %
+            % The implementation of deep copy in Java grades also depends on
+            % zero-argument constructors.
             (
-                TagVal = shared_with_reserved_addresses_tag(RAs,
-                    single_functor_tag),
-                some [RA] (
-                    list.member(RA, RAs),
-                    RA = reserved_object(_, _, _)
+                (
+                    Target = target_java
+                ;
+                    TagVal = shared_with_reserved_addresses_tag(RAs,
+                        single_functor_tag),
+                    some [RA] (
+                        list.member(RA, RAs),
+                        RA = reserved_object(_, _, _)
+                    )
                 ),
                 Members = [_ | _]
             ->
diff --git a/library/builtin.m b/library/builtin.m
index 91cb38f..325ed67 100644
--- a/library/builtin.m
+++ b/library/builtin.m
@@ -783,53 +783,65 @@ special__Compare____tuple_0_0(ref object[] result,

 :- pragma foreign_code("Java",
 "
-public static java.lang.Object
-deep_copy(java.lang.Object original) {
-    java.lang.Object clone;
-
-    if (original == null) {
-        return null;
-    }
-
-    java.lang.Class cls = original.getClass();
-
-    if (cls.getName().equals(""java.lang.String"")) {
-        return new java.lang.String((java.lang.String) original);
-    }
-
-    if (cls.isArray()) {
-        int length = java.lang.reflect.Array.getLength(original);
-        clone = java.lang.reflect.Array.newInstance(
-                cls.getComponentType(), length);
-        for (int i = 0; i < length; i++) {
-            java.lang.Object X, Y;
-            X = java.lang.reflect.Array.get(original, i);
-            Y = deep_copy(X);
-            java.lang.reflect.Array.set(clone, i, Y);
-        }
-        return clone;
-    }
-
     /*
-    ** XXX Two possible approaches are possible here:
+    ** Two other approaches to implement deep copy might be:
     **
     ** 1. Get all mercury objects to implement the Serializable interface.
     **    Then this whole function could be replaced with code that writes
     **    the Object out via an ObjectOutputStream into a byte array (or
     **    something), then reads it back in again, thus creating a copy.
-    ** 2. Call cls.getConstructors(), then iterate through the resulting
-    **    array until one of them allows instantiation with all parameters
-    **    set to 0 or null (or some sort of recursive call that attempts to
-    **    instantiate the parameters).
-    **    This approach is of course not guaranteed to work all the time.
-    **    Then we can just copy the fields across using Reflection.
+    **    This would copy non-Mercury objects as well, though.
     **
-    ** For now, we're just throwing an exception.
+    ** 2. Get all mercury objects to implement a clone() method (either the
+    **    one in Object or our own). The MLDS doesn't have method calls
+    **    and probably we wouldn't want to clutter it up, so we might try
+    **    to generate it directly in mlds_to_java.m, but then it's quite
+    **    messy.
     */

-    throw new java.lang.RuntimeException(
-        ""deep copy not yet fully implemented"");
-}
+    public static <T> T
+    deep_copy(final T original) throws
+        java.lang.InstantiationException, java.lang.IllegalAccessException
+    {
+        if (original == null) {
+            return null;
+        }
+
+        final java.lang.Class<T> cls = (java.lang.Class<T>)
original.getClass();
+
+        if (cls.isArray()) {
+            int length = java.lang.reflect.Array.getLength(original);
+            T clone = (T) java.lang.reflect.Array.newInstance(
+                cls.getComponentType(), length);
+            for (int i = 0; i < length; i++) {
+                java.lang.Object X = java.lang.reflect.Array.get(original, i);
+                java.lang.Object Y = deep_copy(X);
+                java.lang.reflect.Array.set(clone, i, Y);
+            }
+            return clone;
+        }
+
+        // We'll only copy objects of Mercury-defined types.  We could copy
+        // more but that's what we do for C backends and it's enough.
+        if (!(original instanceof MercuryType)) {
+            return original;
+        }
+
+        // Make a new instance of the class and fill in the fields.
+        // This requires the class have a default constructor.
+        // (alternatively, we could use the Objenesis library).
+        T clone = cls.newInstance();
+        java.lang.Class<?> c = cls;
+        while (c != Object.class && c != null) {
+            for (java.lang.reflect.Field field : c.getDeclaredFields()) {
+                java.lang.Object X = field.get(original);
+                java.lang.Object Y = deep_copy(X);
+                field.set(clone, Y);
+            }
+            c = c.getSuperclass();
+        }
+        return clone;
+    }
 ").

 :- pragma foreign_code("Erlang", "
@@ -925,14 +937,26 @@ deep_copy(java.lang.Object original) {
     copy(X::ui, Y::uo),
     [may_call_mercury, thread_safe, promise_pure, terminates],
 "
-    Y = deep_copy(X);
+    try {
+        Y = deep_copy(X);
+    } catch (java.lang.InstantiationException E) {
+        throw new RuntimeException(E);
+    } catch (java.lang.IllegalAccessException E) {
+        throw new RuntimeException(E);
+    }
 ").

 :- pragma foreign_proc("Java",
     copy(X::in, Y::uo),
     [may_call_mercury, thread_safe, promise_pure, terminates],
 "
-    Y = deep_copy(X);
+    try {
+        Y = deep_copy(X);
+    } catch (java.lang.InstantiationException E) {
+        throw new RuntimeException(E);
+    } catch (java.lang.IllegalAccessException E) {
+        throw new RuntimeException(E);
+    }
 ").

 :- pragma foreign_proc("Erlang",
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list