[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