[m-rev.] for review: allow foreign types to be Java numeric primitive types
Julien Fischer
jfischer at opturion.com
Tue Apr 12 11:35:07 AEST 2016
For review by anyone.
Question: the remaining Java primitive types (bool and char) are not
supported by this change and I can't really see a need for them. In
the interests of completness should was also support them?
---------------
Allow foreign types to be Java numeric primitive types.
Currently we only allow non-primitive Java types to used with foreign_type
pragmas. This means that for foreign types that map on to Java numeric types
we have to use their boxed form (e.g. java.lang.Long, java.lang.Float etc) in
the foreign_type pragma. Doing so results in unnecessary boxing and is a
source of errors (e.g. accidently comparing for reference equality instead of
value equality).
This change causes the MLDS->Java code generator to recognise primitive numeric
foreign_types and generate code that works for them directly. (In effect
their handling is not really different to how the builtin types are handled.)
doc/reference.texi:
Change the language so Java foreign types are allowed to be a
primitive Java numeric type.
compiler/mlds_to_java.m:
Recognise foreign types that are defined as primitive numeric
types in Java and generate code that avoids the need for (some)
boxing of them.
library/array.m:
Handle arrays of Java bytes, shorts, longs and floats specially.
(int and double were already handled thus since the Mercury builtin
types int and float map on to them.)
NEWS:
Announce the above.
tests/hard_coded/Mmakefile:
tests/hard_coded/test_java_foreign_primitive.{m,exp}:
Test that foreign types work with primitive numeric types in Java.
Julien.
diff --git a/NEWS b/NEWS
index 5910514..ecc7d7d 100644
--- a/NEWS
+++ b/NEWS
@@ -104,6 +104,9 @@ Changes to the Mercury language:
any of its procedures, or for any of the procedures they call,
directly or indirectly.
+* The Java backend now supports defining foreign types as primitive Java
+ numeric types.
+
Changes to the Mercury standard library:
* We have added variants of the process_options predicates to the getopt
diff --git a/compiler/mlds_to_java.m b/compiler/mlds_to_java.m
index 068ad80..854be59 100644
--- a/compiler/mlds_to_java.m
+++ b/compiler/mlds_to_java.m
@@ -2770,10 +2770,15 @@ get_java_type_initializer(Type) = Initializer :-
Type = mlds_native_bool_type,
Initializer = "false"
;
+ Type = mlds_foreign_type(ForeignLangType),
+ ( if java_primitive_foreign_language_type(ForeignLangType, _, _, _)
+ then Initializer = "0"
+ else Initializer = "null"
+ )
+ ;
( Type = mlds_mercury_array_type(_)
; Type = mlds_cont_type(_)
; Type = mlds_commit_type
- ; Type = mlds_foreign_type(_)
; Type = mlds_class_type(_, _, _)
; Type = mlds_array_type(_)
; Type = mlds_mostly_generic_array_type(_)
@@ -4891,9 +4896,8 @@ java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
java_builtin_type(Type, "boolean", "java.lang.Boolean", "booleanValue") :-
Type = mlds_native_bool_type.
- % io.state and store.store(S) are dummy variables
- % for which we pass an arbitrary integer. For this
- % reason they should have the Java type `int'.
+ % io.state and store.store(S) are dummy variables for which we pass an
+ % arbitrary integer. For this reason they should have the Java type `int'.
%
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
% The test for defined/3 is logically redundant since all dummy
@@ -4902,6 +4906,59 @@ java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
Type = mercury_type(defined_type(_, _, _), TypeCtorCat, _),
TypeCtorCat = ctor_cat_builtin_dummy.
+ % Handle foreign types that map on to Java's primitive numeric types
+ % specially since we want to avoid boxing them where possible for
+ % performance reasons.
+ %
+java_builtin_type(Type, PrimitiveType, BoxedType, UnboxMethod) :-
+ Type = mlds_foreign_type(ForeignLangType),
+ java_primitive_foreign_language_type(ForeignLangType, PrimitiveType,
+ BoxedType, UnboxMethod).
+
+:- pred java_primitive_foreign_language_type(foreign_language_type::in, string::out,
+ string::out, string::out) is semidet.
+
+java_primitive_foreign_language_type(ForeignLangType, PrimitiveType,
+ BoxedType, UnboxMethod) :-
+ require_complete_switch [ForeignLangType] (
+ ForeignLangType = java(java_type(JavaForeignType))
+ ;
+ ForeignLangType = c(_),
+ unexpected($file, $pred, "foreign_type for C")
+ ;
+ ForeignLangType = csharp(_),
+ unexpected($file, $pred, "foreign_type for C#")
+ ;
+ ForeignLangType = erlang(_),
+ unexpected($file, $pred, "foreign_type for Erlang")
+ ),
+ PrimitiveType = string.strip(JavaForeignType),
+ (
+ PrimitiveType = "byte",
+ BoxedType = "java.lang.Byte",
+ UnboxMethod = "byteValue"
+ ;
+ PrimitiveType = "short",
+ BoxedType = "java.lang.Short",
+ UnboxMethod = "shortValue"
+ ;
+ PrimitiveType = "int",
+ BoxedType = "java.lang.Integer",
+ UnboxMethod = "intValue"
+ ;
+ PrimitiveType = "long",
+ BoxedType = "java.lang.Long",
+ UnboxMethod = "longValue"
+ ;
+ PrimitiveType = "float",
+ BoxedType = "java.lang.Float",
+ UnboxMethod = "floatValue"
+ ;
+ PrimitiveType = "double",
+ BoxedType = "java.lang.Double",
+ UnboxMethod = "doubleValue"
+ ).
+
:- pred output_std_unop(java_out_info::in, builtin_ops.unary_op::in,
mlds_rval::in, io::di, io::uo) is det.
diff --git a/doc/reference_manual.texi b/doc/reference_manual.texi
index 807d3c8..2209126 100644
--- a/doc/reference_manual.texi
+++ b/doc/reference_manual.texi
@@ -8495,7 +8495,9 @@ A Java @samp{pragma foreign_type} declaration has the form:
:- pragma foreign_type("Java", @var{MercuryTypeName}, "@var{JavaType}").
@end example
-The @var{JavaType} can be any accessible non-primitive Java type.
+The @var{JavaType} can be any accessible non-primitive Java type or one of the
+following primitive Java numeric types: @code{byte}, @code{short}, @code{int},
+ at code{long}, @code{float} or @code{double}.
The effect of this declaration is that Mercury values of type
@var{MercuryTypeName} will be passed to and from Java foreign_procs
diff --git a/library/array.m b/library/array.m
index 36bd599..fd604f5 100644
--- a/library/array.m
+++ b/library/array.m
@@ -1079,6 +1079,34 @@ ML_new_array(int Size, Object Item, boolean fill)
}
return as;
}
+ if (Item instanceof Byte) {
+ byte[] as = new byte[Size];
+ if (fill) {
+ java.util.Arrays.fill(as, (Byte) Item);
+ }
+ return as;
+ }
+ if (Item instanceof Short) {
+ short[] as = new short[Size];
+ if (fill) {
+ java.util.Arrays.fill(as, (Short) Item);
+ }
+ return as;
+ }
+ if (Item instanceof Long) {
+ long[] as = new long[Size];
+ if (fill) {
+ java.util.Arrays.fill(as, (Long) Item);
+ }
+ return as;
+ }
+ if (Item instanceof Float) {
+ float[] as = new float[Size];
+ if (fill) {
+ java.util.Arrays.fill(as, (Float) Item);
+ }
+ return as;
+ }
Object[] as = new Object[Size];
if (fill) {
java.util.Arrays.fill(as, Item);
@@ -1109,6 +1137,26 @@ ML_unsafe_new_array(int Size, Object Item, int IndexToSet)
as[IndexToSet] = (Boolean) Item;
return as;
}
+ if (Item instanceof Byte) {
+ byte[] as = new byte[Size];
+ as[IndexToSet] = (Byte) Item;
+ return as;
+ }
+ if (Item instanceof Short) {
+ short[] as = new short[Size];
+ as[IndexToSet] = (Short) Item;
+ return as;
+ }
+ if (Item instanceof Long) {
+ long[] as = new long[Size];
+ as[IndexToSet] = (Long) Item;
+ return as;
+ }
+ if (Item instanceof Float) {
+ float[] as = new float[Size];
+ as[IndexToSet] = (Float) Item;
+ return as;
+ }
Object[] as = new Object[Size];
as[IndexToSet] = Item;
return as;
@@ -1127,6 +1175,14 @@ ML_array_size(Object Array)
return ((char[]) Array).length;
} else if (Array instanceof boolean[]) {
return ((boolean[]) Array).length;
+ } else if (Array instanceof byte[]) {
+ return ((byte[]) Array).length;
+ } else if (Array instanceof short[]) {
+ return ((short[]) Array).length;
+ } else if (Array instanceof long[]) {
+ return ((long[]) Array).length;
+ } else if (Array instanceof float[]) {
+ return ((float[]) Array).length;
} else {
return ((Object[]) Array).length;
}
@@ -1183,6 +1239,46 @@ ML_array_resize(Object Array0, int Size, Object Item)
Array[i] = (Boolean) Item;
}
return Array;
+ }
+ if (Array0 instanceof byte[]) {
+ byte[] arr0 = (byte[]) Array0;
+ byte[] Array = new byte[Size];
+
+ System.arraycopy(arr0, 0, Array, 0, Math.min(arr0.length, Size));
+ for (int i = arr0.length; i < Size; i++) {
+ Array[i] = (Byte) Item;
+ }
+ return Array;
+ }
+ if (Array0 instanceof short[]) {
+ short[] arr0 = (short[]) Array0;
+ short[] Array = new short[Size];
+
+ System.arraycopy(arr0, 0, Array, 0, Math.min(arr0.length, Size));
+ for (int i = arr0.length; i < Size; i++) {
+ Array[i] = (Short) Item;
+ }
+ return Array;
+ }
+ if (Array0 instanceof long[]) {
+ long[] arr0 = (long[]) Array0;
+ long[] Array = new long[Size];
+
+ System.arraycopy(arr0, 0, Array, 0, Math.min(arr0.length, Size));
+ for (int i = arr0.length; i < Size; i++) {
+ Array[i] = (Long) Item;
+ }
+ return Array;
+ }
+ if (Array0 instanceof float[]) {
+ float[] arr0 = (float[]) Array0;
+ float[] Array = new float[Size];
+
+ System.arraycopy(arr0, 0, Array, 0, Math.min(arr0.length, Size));
+ for (int i = arr0.length; i < Size; i++) {
+ Array[i] = (Float) Item;
+ }
+ return Array;
} else {
Object[] arr0 = (Object[]) Array0;
Object[] Array = new Object[Size];
@@ -1601,6 +1697,14 @@ semidet_lookup(Array, Index, Item) :-
Item = ((char[]) Array)[Index];
} else if (Array instanceof boolean[]) {
Item = ((boolean[]) Array)[Index];
+ } else if (Array instanceof byte[]) {
+ Item = ((byte[]) Array)[Index];
+ } else if (Array instanceof short[]) {
+ Item = ((short[]) Array)[Index];
+ } else if (Array instanceof long[]) {
+ Item = ((long[]) Array)[Index];
+ } else if (Array instanceof float[]) {
+ Item = ((float[]) Array)[Index];
} else {
Item = ((Object[]) Array)[Index];
}
@@ -1667,6 +1771,14 @@ set(Index, Item, !Array) :-
((char[]) Array0)[Index] = (Character) Item;
} else if (Array0 instanceof boolean[]) {
((boolean[]) Array0)[Index] = (Boolean) Item;
+ } else if (Array0 instanceof byte[]) {
+ ((byte[]) Array0)[Index] = (Byte) Item;
+ } else if (Array0 instanceof short[]) {
+ ((short[]) Array0)[Index] = (Short) Item;
+ } else if (Array0 instanceof long[]) {
+ ((long[]) Array0)[Index] = (Long) Item;
+ } else if (Array0 instanceof float[]) {
+ ((float[]) Array0)[Index] = (Float) Item;
} else {
((Object[]) Array0)[Index] = Item;
}
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index be0059d..1e19c0e 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -463,7 +463,7 @@ else
CTGC_PROGS =
endif
-# Programs that deal with large amounts of datas need tail recursion. Don't
+# Programs that deal with large amounts of data need tail recursion. Don't
# execute them in grades that do not allow tail recursion, since their failures
# would be spurious.
@@ -477,13 +477,14 @@ else
BIG_DATA_PROGS =
endif
-# Tests of the Java foreign language interface only
-# work in Java grades
+# Tests of the Java foreign language interface only work in Java grades.
+#
ifeq "$(filter java%,$(GRADE))" ""
JAVA_PROGS =
else
JAVA_PROGS = \
- java_test
+ java_test \
+ test_java_foreign_primitive
endif
# Fact tables currently work only in the C grades.
diff --git a/tests/hard_coded/test_java_foreign_primitive.exp b/tests/hard_coded/test_java_foreign_primitive.exp
new file mode 100644
index 0000000..6ce7700
--- /dev/null
+++ b/tests/hard_coded/test_java_foreign_primitive.exp
@@ -0,0 +1,6 @@
+32
+32
+32
+32
+32.0
+32.0
diff --git a/tests/hard_coded/test_java_foreign_primitive.m b/tests/hard_coded/test_java_foreign_primitive.m
new file mode 100644
index 0000000..4da697c
--- /dev/null
+++ b/tests/hard_coded/test_java_foreign_primitive.m
@@ -0,0 +1,198 @@
+%----------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%----------------------------------------------------------------------------%
+%
+% Test support for primitive numeric types as foreign_types in the Java
+% grade.
+%
+%----------------------------------------------------------------------------%
+
+:- module test_java_foreign_primitive.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%----------------------------------------------------------------------------%
+%----------------------------------------------------------------------------%
+
+:- implementation.
+
+main(!IO) :-
+ io.print_line(byte_to_string(byte(16) `plus_byte` byte(16)), !IO),
+ io.print_line(short_to_string(short(16) `plus_short` short(16)), !IO),
+ io.print_line(int_to_string(int(16) `plus_int` int(16)), !IO),
+ io.print_line(long_to_string(long(16) `plus_long` long(16)), !IO),
+ io.print_line(float_to_string(float(16) `plus_float` float(16)), !IO),
+ io.print_line(double_to_string(double(16) `plus_double` double(16)), !IO).
+
+%----------------------------------------------------------------------------%
+
+:- type jbyte.
+:- type jshort.
+:- type jint.
+:- type jlong.
+:- type jfloat.
+:- type jdouble.
+
+:- pragma foreign_type("Java", jbyte, "byte").
+:- pragma foreign_type("Java", jshort, "short").
+:- pragma foreign_type("Java", jint, "int").
+:- pragma foreign_type("Java", jlong, "long").
+:- pragma foreign_type("Java", jfloat, "float").
+:- pragma foreign_type("Java", jdouble, "double").
+
+%----------------------------------------------------------------------------%
+
+:- func byte(int) = jbyte.
+:- pragma foreign_proc("Java",
+ byte(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = (byte) A;
+").
+
+:- func short(int) = jshort.
+:- pragma foreign_proc("Java",
+ short(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = (short) A;
+").
+
+:- func int(int) = jint.
+:- pragma foreign_proc("Java",
+ int(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = A;
+").
+
+:- func long(int) = jlong.
+:- pragma foreign_proc("Java",
+ long(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = A;
+").
+
+:- func float(int) = jfloat.
+:- pragma foreign_proc("Java",
+ float(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = (float) A;
+").
+
+:- func double(int) = jdouble.
+:- pragma foreign_proc("Java",
+ double(A::in) = (B::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ B = (double) A;
+").
+
+%----------------------------------------------------------------------------%
+
+:- func plus_byte(jbyte, jbyte) = jbyte.
+:- pragma foreign_proc("Java",
+ plus_byte(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = (byte) (A + B);
+").
+
+:- func plus_short(jshort, jshort) = jshort.
+:- pragma foreign_proc("Java",
+ plus_short(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = (short) (A + B);
+").
+
+:- func plus_int(jint, jint) = jint.
+:- pragma foreign_proc("Java",
+ plus_int(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = A + B;
+").
+
+:- func plus_long(jlong, jlong) = jlong.
+:- pragma foreign_proc("Java",
+ plus_long(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = A + B;
+").
+
+:- func plus_float(jfloat, jfloat) = jfloat.
+:- pragma foreign_proc("Java",
+ plus_float(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = A + B;
+").
+
+:- func plus_double(jdouble, jdouble) = jdouble.
+:- pragma foreign_proc("Java",
+ plus_double(A::in, B::in) = (C::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ C = A + B;
+").
+
+%----------------------------------------------------------------------------%
+
+:- func byte_to_string(jbyte) = string.
+:- pragma foreign_proc("Java",
+ byte_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Integer.toString(N);
+").
+
+:- func short_to_string(jshort) = string.
+:- pragma foreign_proc("Java",
+ short_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Integer.toString(N);
+").
+
+:- func int_to_string(jint) = string.
+:- pragma foreign_proc("Java",
+ int_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Integer.toString(N);
+").
+
+:- func long_to_string(jlong) = string.
+:- pragma foreign_proc("Java",
+ long_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Long.toString(N);
+").
+
+:- func float_to_string(jfloat) = string.
+:- pragma foreign_proc("Java",
+ float_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Float.toString(N);
+").
+
+:- func double_to_string(jdouble) = string.
+:- pragma foreign_proc("Java",
+ double_to_string(N::in) = (S::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ S = java.lang.Double.toString(N);
+").
+
+%----------------------------------------------------------------------------%
+:- end_module test_java_foreign_primitive.
+%----------------------------------------------------------------------------%
More information about the reviews
mailing list