[m-rev.] for review: improve deconstruction (and printing) of foreign types in C# and Java grades

Julien Fischer jfischer at opturion.com
Wed Mar 13 16:30:08 AEDT 2019


For review by anyone.

----------------------------

Improve deconstruction (and printing) of foreign types in C# and Java grades.

Currently, we return a placeholder string ("foreignxx") if we attempt to look
up the functor of a foreign type in the C# or Java grades.  For C, we return a
string of the form:

    <<foreign(Name, Rep)>>

where Name is the type's Mercury name and Rep is its "value" as results from
casting it to (void *) and then using sprintf's %p conversion specifier.  Do
something similar for both the C# and Java grades, except that for Rep we
return the output of the calling the toString() (ToString()) method.  If the
object in question is a null reference then we return "null" for Rep.

Document how the predicates in the deconstruct module handle foreign types.

library/rtti_implementation.m:
    Return more information about foreign type functors.

library/deconstruct.m:
    Make the above changes to the documentation.

tests/hard_coded/csharp_print_foreign.{m,exp}:
tests/hard_coded/java_print_foreign.{m,exp}:
    Tests of the new functionality in the C# and Java grades.

tests/hard_coded/Mmakefile:
     Add the new tests.

     Add a new category of test programs that are only run in the C# grades.

Julien.

diff --git a/library/deconstruct.m b/library/deconstruct.m
index 3c8abec..a5446ce 100644
--- a/library/deconstruct.m
+++ b/library/deconstruct.m
@@ -92,6 +92,9 @@
      %   - for arrays, the string <<array>>.
      %   - for c_pointers, the string ptr(0xXXXX) where XXXX is the
      %     hexadecimal representation of the pointer.
+    %   - for foreign types, a string of the form <<foreign(Name, Rep)>> where
+    %     Name is the type's Mercury name and Rep is a target language specific
+    %     representation of the term's value.
      %   - for bitmaps, the bitmap converted to a length and a
      %     hexadecimal string inside angle brackets and quotes of the
      %     form """<[0-9]:[0-9A-F]*>""".
@@ -113,6 +116,7 @@
      %   - for tuples, the number of elements in the tuple.
      %   - for arrays, the number of elements in the array.
      %   - for c_pointers, zero.
+    %   - for foreign types, zero.
      %   - for bitmaps, zero.
      %
      % Note that in the current University of Melbourne implementation,
diff --git a/library/rtti_implementation.m b/library/rtti_implementation.m
index 85b9b42..42f582d 100644
--- a/library/rtti_implementation.m
+++ b/library/rtti_implementation.m
@@ -3142,7 +3142,10 @@ deconstruct_2(Term, TypeInfo, TypeCtorInfo, TypeCtorRep, NonCanon,
      ;
          (
              TypeCtorRep = tcr_foreign,
-            Functor = "<<foreignxx>>"
+            TypeCtorName = TypeCtorInfo ^ type_ctor_name,
+            TargetLangRep = get_target_lang_rep(Term),
+            string.format("<<foreign(%s, %s)>>",
+                [s(TypeCtorName), s(TargetLangRep)], Functor)
          ;
              TypeCtorRep = tcr_stable_foreign,
              Functor = "<<stable_foreign>>"
@@ -3956,6 +3959,43 @@ same_pointer_value_untyped(_, _) :-
      % matching foreign_proc version.
      private_builtin.sorry("same_pointer_value_untyped").

+:- func get_target_lang_rep(T) = string.
+
+:- pragma foreign_proc("C",
+    get_target_lang_rep(Term::in) = (S::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    // This should be kept in sync with the MR_TYEPCTOR_REP_FOREIGN
+    // case in runtime/mercury_ml_expand_body.h.
+    char buf[256];
+    MR_snprintf(buf, 256, ""%p"", (void *) Term);
+    MR_make_aligned_string_copy(S, buf);
+").
+
+:- pragma foreign_proc("C#",
+    get_target_lang_rep(Term::in) = (S::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    if (Term == null) {
+        S = ""null"";
+    } else {
+        S = Term.ToString();
+    }
+").
+
+:- pragma foreign_proc("Java",
+    get_target_lang_rep(Term::in) = (S::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    if (Term == null) {
+        S = ""null"";
+    } else {
+        S = Term.toString();
+    }
+").
+
+get_target_lang_rep(_) = "some_foreign_value".
+
  %---------------------------------------------------------------------------%
  %---------------------------------------------------------------------------%

diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index d99efb8..1a51cc9 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -562,10 +562,20 @@ ifeq "$(filter java%,$(GRADE))" ""
  	JAVA_PROGS =
  else
  	JAVA_PROGS = \
+		java_print_foreign  \
  		java_test \
  		test_java_foreign_primitive
  endif

+# Tests of the C# foreign language interface only work in the C# grades.
+
+ifeq "$(filter csharp%,$(GRADE))" ""
+	CSHARP_PROGS =
+else
+	CSHARP_PROGS = \
+		csharp_print_foreign
+endif
+
  # Fact tables currently work only in the C grades.
  # The foreign_type_assertion test is currently meaningful only in C grades.
  # Tests of the C foreign language interface only work in C grades.
@@ -837,6 +847,7 @@ PROGS = \
  	$(STATIC_LINK_PROGS) \
  	$(C_ONLY_PROGS) \
  	$(JAVA_PROGS) \
+	$(CSHARP_PROGS) \
  	$(SOLVER_PROGS) \
  	$(FOREIGN_ENUM_PROGS) \
  	$(TRAILED_PROGS) \
diff --git a/tests/hard_coded/csharp_print_foreign.exp b/tests/hard_coded/csharp_print_foreign.exp
index e69de29..fea695b 100644
--- a/tests/hard_coded/csharp_print_foreign.exp
+++ b/tests/hard_coded/csharp_print_foreign.exp
@@ -0,0 +1,8 @@
+'<<foreign(foreign_bool, True)>>'
+'<<foreign(foreign_char, A)>>'
+'<<foreign(foreign_int, 561)>>'
+'<<foreign(foreign_long, 561)>>'
+'<<foreign(foreign_float, 3.402823E+38)>>'
+'<<foreign(foreign_double, 1.79769313486232E+308)>>'
+'<<foreign(uuid, 00000000-0000-0000-0000-000000000000)>>'
+'<<foreign(object, null)>>'
diff --git a/tests/hard_coded/csharp_print_foreign.m b/tests/hard_coded/csharp_print_foreign.m
index e69de29..5387006 100644
--- a/tests/hard_coded/csharp_print_foreign.m
+++ b/tests/hard_coded/csharp_print_foreign.m
@@ -0,0 +1,109 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test io.print etc with foreign types in the C# grades.
+%
+%---------------------------------------------------------------------------%
+
+:- module csharp_print_foreign.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+main(!IO) :-
+   io.print_line(get_foreign_bool, !IO),
+   io.print_line(get_foreign_char, !IO),
+   io.print_line(get_foreign_int, !IO),
+   io.print_line(get_foreign_long, !IO),
+   io.print_line(get_foreign_float, !IO),
+   io.print_line(get_foreign_double, !IO),
+   io.print_line(get_uuid, !IO),
+   io.print_line(null_object, !IO).
+
+:- type foreign_bool.
+:- pragma foreign_type("C#", foreign_bool, "bool").
+:- func get_foreign_bool = foreign_bool.
+:- pragma foreign_proc("C#",
+    get_foreign_bool = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = true;
+").
+
+:- type foreign_int.
+:- pragma foreign_type("C#", foreign_int, "int").
+:- func get_foreign_int = foreign_int.
+:- pragma foreign_proc("C#",
+    get_foreign_int = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 561;
+").
+
+:- type foreign_char.
+:- pragma foreign_type("C#", foreign_char, "char").
+:- func get_foreign_char = foreign_char.
+:- pragma foreign_proc("C#",
+    get_foreign_char = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 'A';
+").
+
+:- type foreign_long.
+:- pragma foreign_type("C#", foreign_long, "long").
+:- func get_foreign_long = foreign_long.
+:- pragma foreign_proc("C#",
+    get_foreign_long = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 561L;
+").
+
+:- type foreign_float.
+:- pragma foreign_type("C#", foreign_float, "float").
+:- func get_foreign_float = foreign_float.
+:- pragma foreign_proc("C#",
+    get_foreign_float = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = System.Single.MaxValue;
+").
+
+:- type foreign_double.
+:- pragma foreign_type("C#", foreign_double, "double").
+:- func get_foreign_double = foreign_double.
+:- pragma foreign_proc("C#",
+    get_foreign_double = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = System.Double.MaxValue;
+").
+
+:- type uuid.
+:- pragma foreign_type("C#", uuid, "System.Guid").
+:- func get_uuid = uuid.
+:- pragma foreign_proc("C#",
+    get_uuid = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = System.Guid.Empty;
+").
+
+:- type object.
+:- pragma foreign_type("C#", object, "System.Object").
+:- func null_object = object.
+:- pragma foreign_proc("C#",
+    null_object = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = null;
+").
diff --git a/tests/hard_coded/java_print_foreign.exp b/tests/hard_coded/java_print_foreign.exp
index e69de29..e92084a 100644
--- a/tests/hard_coded/java_print_foreign.exp
+++ b/tests/hard_coded/java_print_foreign.exp
@@ -0,0 +1,10 @@
+'<<foreign(foreign_bool, true)>>'
+'<<foreign(foreign_char, A)>>'
+'<<foreign(foreign_int, 561)>>'
+'<<foreign(foreign_long, 561)>>'
+'<<foreign(foreign_float, 3.4028235E38)>>'
+'<<foreign(foreign_double, 1.7976931348623157E308)>>'
+'<<foreign(uuid, 5f4fe224-2d69-49f0-9c72-acb9ec8400ef)>>'
+'<<foreign(uuid, null)>>'
+'<<foreign(local_date, +999999999-12-31)>>'
+'<<foreign(local_date, null)>>'
diff --git a/tests/hard_coded/java_print_foreign.m b/tests/hard_coded/java_print_foreign.m
index e69de29..34f95bb 100644
--- a/tests/hard_coded/java_print_foreign.m
+++ b/tests/hard_coded/java_print_foreign.m
@@ -0,0 +1,129 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test io.print etc with foreign types in the Java grades.
+%
+%---------------------------------------------------------------------------%
+
+:- module java_print_foreign.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+main(!IO) :-
+   io.print_line(get_foreign_bool, !IO),
+   io.print_line(get_foreign_char, !IO),
+   io.print_line(get_foreign_int, !IO),
+   io.print_line(get_foreign_long, !IO),
+   io.print_line(get_foreign_float, !IO),
+   io.print_line(get_foreign_double, !IO),
+   io.print_line(get_uuid, !IO),
+   io.print_line(null_uuid, !IO),
+   io.print_line(get_local_date, !IO),
+   io.print_line(null_local_date, !IO).
+
+:- type foreign_bool.
+:- pragma foreign_type("Java", foreign_bool, "boolean").
+:- func get_foreign_bool = foreign_bool.
+:- pragma foreign_proc("Java",
+    get_foreign_bool = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = true;
+").
+
+:- type foreign_int.
+:- pragma foreign_type("Java", foreign_int, "int").
+:- func get_foreign_int = foreign_int.
+:- pragma foreign_proc("Java",
+    get_foreign_int = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 561;
+").
+
+:- type foreign_char.
+:- pragma foreign_type("Java", foreign_char, "char").
+:- func get_foreign_char = foreign_char.
+:- pragma foreign_proc("Java",
+    get_foreign_char = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 'A';
+").
+
+:- type foreign_long.
+:- pragma foreign_type("Java", foreign_long, "long").
+:- func get_foreign_long = foreign_long.
+:- pragma foreign_proc("Java",
+    get_foreign_long = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = 561L;
+").
+
+:- type foreign_float.
+:- pragma foreign_type("Java", foreign_float, "float").
+:- func get_foreign_float = foreign_float.
+:- pragma foreign_proc("Java",
+    get_foreign_float = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = java.lang.Float.MAX_VALUE;
+").
+
+:- type foreign_double.
+:- pragma foreign_type("Java", foreign_double, "double").
+:- func get_foreign_double = foreign_double.
+:- pragma foreign_proc("Java",
+    get_foreign_double = (V::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    V = java.lang.Double.MAX_VALUE;
+").
+
+:- type uuid.
+:- pragma foreign_type("Java", uuid, "java.util.UUID").
+:- func get_uuid = uuid.
+:- pragma foreign_proc("Java",
+    get_uuid = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = java.util.UUID.fromString(""5f4fe224-2d69-49f0-9c72-acb9ec8400ef"");
+").
+
+:- func null_uuid = uuid.
+:- pragma foreign_proc("Java",
+    null_uuid = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = null;
+").
+
+:- type local_date.
+:- pragma foreign_type("Java", local_date, "java.time.LocalDate").
+:- func get_local_date = local_date.
+:- pragma foreign_proc("Java",
+    get_local_date = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = java.time.LocalDate.MAX;
+").
+
+:- func null_local_date = local_date.
+:- pragma foreign_proc("Java",
+    null_local_date = (U::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U = null;
+").
+
+


More information about the reviews mailing list