[m-rev.] for review: java I/O improvements, putback clarifications
Peter Wang
novalazy at gmail.com
Tue Aug 25 14:44:15 AEST 2009
Only the documentation parts really require review.
Branches: main
library/io.m:
Document the effect of io.putback_byte on the stream position, based
on the behaviour of ungetc().
Document the effect of io.seek_binary_input on the pushback buffer.
Implement io.read_line_as_string and io.read_file_as_string
efficiently for Java.
Make the globals ML_next_stream_id and MR_io_exception in Java
thread-safe.
Make the type of ML_io_stream_db more specific in Java.
Use the FileChannel class unconditionally. We no longer support
Java < 1.5 so we don't need to use reflection to access that class at
runtime.
Throw Mercury-catchable exceptions on Java I/O errors.
Add class qualification or `may_not_duplicate' attributes on
foreign_procs to prevent problems with intermodule optimisation.
tests/hard_coded/Mmakefile:
tests/hard_coded/stream_putback.data:
tests/hard_coded/stream_putback.exp:
tests/hard_coded/stream_putback.m:
tests/hard_coded/stream_putback_binary.exp:
tests/hard_coded/stream_putback_binary.m:
Add test cases for io.putback_char, io.putback_byte and seeking.
diff --git a/library/io.m b/library/io.m
index 5d518a1..18fc281 100644
--- a/library/io.m
+++ b/library/io.m
@@ -930,6 +930,10 @@
% On some systems only one byte of pushback is guaranteed.
% `io.putback_byte' will throw an io.error exception if ungetc() fails.
%
+ % Pushing back a byte decrements the file position by one, except when
+ % the file position is already zero, in which case the new file position
+ % is unspecified.
+ %
:- pred io.putback_byte(int::in, io::di, io::uo) is det.
% Un-reads a byte from specified binary input stream.
@@ -940,6 +944,10 @@
% On some systems only one byte of pushback is guaranteed.
% `io.putback_byte' will throw an io.error exception if ungetc() fails.
%
+ % Pushing back a byte decrements the file position by one, except when
+ % the file position is already zero, in which case the new file position
+ % is unspecified.
+ %
:- pred io.putback_byte(io.binary_input_stream::in, int::in,
io::di, io::uo) is det.
@@ -1050,6 +1058,8 @@
% on a specified binary input stream. Attempting to seek on a pipe
% or tty results in implementation dependent behaviour.
%
+ % A successful seek undoes any effects of io.putback_byte on the stream.
+ %
:- pred io.seek_binary_input(io.binary_input_stream::in, io.whence::in,
int::in, io::di, io::uo) is det.
@@ -1793,7 +1803,7 @@
:- pragma foreign_code("Java",
"
- static java.lang.Object ML_io_stream_db =
+ static tree234.Tree234_2 ML_io_stream_db =
new tree234.Tree234_2.Empty_0();
static java.lang.Object ML_io_user_globals =
new tree234.Tree234_2.Empty_0();
@@ -2182,7 +2192,7 @@ io.read_line_as_string(input_stream(Stream), Result, !IO) :-
string::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
- io.read_line_as_string_2(Stream::in, _Bool::in, Res :: out,
+ io.read_line_as_string_2(Stream::in, _FirstCall::in, Res::out,
RetString::out, IO0::di, IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
@@ -2250,6 +2260,24 @@ io.read_line_as_string(input_stream(Stream), Result, !IO) :-
MR_update_io(IO0, IO);
").
+:- pragma foreign_proc("Java",
+ io.read_line_as_string_2(Stream::in, _FirstCall::in, Res::out,
+ RetString::out, IO0::di, IO::uo),
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+ does_not_affect_liveness, may_not_duplicate],
+"
+ try {
+ StringBuilder sb = new StringBuilder();
+ Res = Stream.read_line(sb);
+ RetString = sb.toString();
+ } catch (java.io.IOException e) {
+ io.MR_io_exception.set(e);
+ Res = -3;
+ RetString = """";
+ }
+ IO = IO0;
+").
+
io.read_line_as_string_2(Stream, FirstCall, Res, String, !IO) :-
% XXX This is terribly inefficient, a better approach would be to
% use a buffer like what is done for io.read_file_as_string.
@@ -2312,6 +2340,23 @@ io.read_file_as_string(Result, !IO) :-
io.input_stream(Stream, !IO),
io.read_file_as_string(Stream, Result, !IO).
+:- pragma foreign_proc("Java",
+ io.read_file_as_string(InputStream::in, Result::out, _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
+ may_not_duplicate],
+"
+ io.MR_MercuryFileStruct File = ((io.Input_stream_0) InputStream).F1;
+ StringBuilder sb = new StringBuilder();
+
+ try {
+ File.read_file(sb);
+ Result = ML_make_io_maybe_partial_res_1_ok_string(sb.toString());
+ } catch (java.io.IOException e) {
+ Result = ML_make_io_maybe_partial_res_1_error_string(sb.toString(),
+ e, ""io.read_file_as_string failed: "");
+ }
+").
+
:- pragma foreign_proc("Erlang",
io.read_file_as_string(InputStream::in, Result::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
@@ -2493,7 +2538,7 @@ io.output_clear_err(output_stream(Stream), !IO) :-
:- pragma foreign_proc("Java",
io.clear_err(_Stream::in, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
// XXX as for .NET above
").
@@ -2552,7 +2597,7 @@ io.check_err(Stream, Res, !IO) :-
:- pragma foreign_proc("Java",
ferror(_Stream::in, RetVal::out, _RetStr::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"{
// XXX see clearerr
RetVal = 0;
@@ -2600,9 +2645,9 @@ io.make_err_msg(Msg0, Msg, !IO) :-
:- pragma foreign_proc("Java",
io.get_system_error(Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"{
- Error = MR_io_exception;
-}").
+"
+ Error = io.MR_io_exception.get();
+").
:- pragma foreign_proc("Erlang",
io.get_system_error(Error::out, _IO0::di, _IO::uo),
@@ -2635,13 +2680,13 @@ io.make_err_msg(Msg0, Msg, !IO) :-
:- pragma foreign_proc("Java",
make_err_msg(Error::in, Msg0::in, Msg::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure],
-"{
+"
if (Error.getMessage() != null) {
Msg = Msg0 + Error.getMessage();
} else {
Msg = Msg0;
}
-}").
+").
:- pragma foreign_proc("Erlang",
make_err_msg(Error::in, Msg0::in, Msg::out, _IO0::di, _IO::uo),
@@ -2794,9 +2839,13 @@ io.output_stream_file_size(output_stream(Stream), Size, !IO) :-
:- pragma foreign_proc("Java",
io.stream_file_size(Stream::in, Size::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
+ try {
Size = Stream.size();
+ } catch (java.io.IOException e) {
+ Size = -1;
+ }
").
:- pragma foreign_proc("Erlang",
@@ -3337,7 +3386,8 @@ io.check_file_accessibility(FileName, AccessTypes, Result, !IO) :-
:- pragma foreign_proc("Java",
io.check_file_accessibility_2(FileName::in, AccessTypes::in,
Result::out, _IO0::di, _IO::uo),
- [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+ [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+ may_not_duplicate],
"
java.lang.String permissions = null;
@@ -3702,6 +3752,25 @@ make_io_res_1_ok_string(String) = ok(String).
make_io_res_1_error_string(Error, Msg0, error(make_io_error(Msg)), !IO) :-
io.make_err_msg(Error, Msg0, Msg, !IO).
+:- func make_io_maybe_partial_res_1_ok_string(string)
+ = io.maybe_partial_res(string).
+:- pragma foreign_export("Java",
+ (make_io_maybe_partial_res_1_ok_string(in) = out),
+ "ML_make_io_maybe_partial_res_1_ok_string").
+
+make_io_maybe_partial_res_1_ok_string(String) = ok(String).
+
+:- pred make_io_maybe_partial_res_1_error_string(string::in,
+ io.system_error::in, string::in, io.maybe_partial_res(string)::out,
+ io::di, io::uo) is det.
+:- pragma foreign_export("Java",
+ make_io_maybe_partial_res_1_error_string(in, in, in, out, di, uo),
+ "ML_make_io_maybe_partial_res_1_error_string").
+
+make_io_maybe_partial_res_1_error_string(Partial, Error, Msg0, Res, !IO) :-
+ io.make_err_msg(Error, Msg0, Msg, !IO),
+ Res = error(Partial, make_io_error(Msg)).
+
%-----------------------------------------------------------------------------%
:- type file_id ---> file_id.
@@ -3783,8 +3852,7 @@ compare_file_id(Result, FileId1, FileId2) :-
compare_file_id_2(_Res::out, _FileId1::in, _FileId2::in),
[will_not_call_mercury, promise_pure, thread_safe],
"
- throw new java.lang.RuntimeException(
- ""File IDs are not supported by Java."");
+ io.ML_throw_io_error(""File IDs are not supported by Java."");
").
:- pragma foreign_proc("Erlang",
@@ -5009,21 +5077,21 @@ io.unlock_stream_db(!IO).
io.get_stream_db_with_locking(StreamDb::out),
[will_not_call_mercury, tabled_for_io],
"
- StreamDb = ML_io_stream_db;
+ StreamDb = io.ML_io_stream_db;
").
:- pragma foreign_proc("Java",
io.get_stream_db(StreamDb::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- StreamDb = ML_io_stream_db;
+ StreamDb = io.ML_io_stream_db;
").
:- pragma foreign_proc("Java",
io.set_stream_db(StreamDb::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- ML_io_stream_db = StreamDb;
+ io.ML_io_stream_db = (tree234.Tree234_2) StreamDb;
").
% XXX the following Erlang implementation doesn't work with multiple threads
@@ -5230,14 +5298,14 @@ io.unlock_globals :-
io.unsafe_get_globals(Globals::out, _IOState0::di, _IOState::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- Globals = ML_io_user_globals;
+ Globals = io.ML_io_user_globals;
").
:- pragma foreign_proc("Java",
io.unsafe_set_globals(Globals::in, _IOState0::di, _IOState::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- ML_io_user_globals = Globals;
+ io.ML_io_user_globals = Globals;
").
% XXX the following Erlang implementation doesn't work with multiple threads
@@ -5644,6 +5712,10 @@ namespace mercury {
}
").
+:- pragma foreign_decl("Java", local, "
+ import java.util.concurrent.atomic.AtomicInteger;
+").
+
:- pragma foreign_code("Java",
"
/* All text files (including stdin/stdout/stderr) are run through
@@ -5654,9 +5726,8 @@ namespace mercury {
** Binary files are opened via RandomAccessFile, which supports seek,
** but not character encoding/decoding or buffering.
** Binary stdin and stdout are a special case. They are opened via
- ** FileInput/OutputStreams and for Java versions >= 1.4, seeking is
- ** controlled through use of FileChannels. (obtained by Reflection so
- ** as not to break the compilation using earlier versions of Java)
+ ** FileInput/OutputStreams and seeking is controlled through use of
+ ** FileChannels (requring Java versions >= 1.4).
**
** The use of the methods in this implementation is not very flexible.
** You may not perform text mode operations on a binary file or vice
@@ -5675,13 +5746,15 @@ namespace mercury {
private static final int SEEK_CUR = 1;
private static final int SEEK_END = 2;
- public static int ML_next_stream_id = 0;
+ public static final AtomicInteger ML_next_stream_id
+ = new AtomicInteger();
public int id;
// line_number is only valid for text streams
public int line_number = 1;
// pushback is non-null only for input streams
+ // (both text and binary)
private java.util.Stack<Integer> pushback = null;
// input is non-null only for text input streams
@@ -5700,15 +5773,10 @@ namespace mercury {
// binary_output is non-null only for binary_stdout
private java.io.FileOutputStream binary_output = null;
- // For Java versions < 1.4, there is no way to retrieve
- // the current offset into binary stdin/out.
- // In this case, position records the current offset,
- // otherwise this field is not used for anything.
- int position = 0;
-
- // channel is non-null only for binary stdin/out using
- // Java versions >= 1.4 (those supporting FileChannel).
- private java.lang.Object channel = null;
+ // channel is non-null for any binary stream.
+ // It is used for positioning the stream. Read/write operations
+ // use randomaccess, binary_input, binary_output instead.
+ private java.nio.channels.FileChannel channel = null;
/*
** This constructor is for text input streams.
@@ -5728,8 +5796,9 @@ namespace mercury {
** This constructor handles binary files (in or out) but does
** not cover mercury_stdin_binary/mercury_stdout_binary.
*/
- public MR_MercuryFileStruct(java.lang.String file, char mode) {
- id = ML_next_stream_id++;
+ public MR_MercuryFileStruct(java.lang.String file, char mode)
+ {
+ id = ML_next_stream_id.getAndAdd(1);
String openstring;
if (mode == 'r') {
@@ -5742,27 +5811,27 @@ namespace mercury {
// There is no such mode as ""w"", which could be a problem
// for write-only files.
} else {
- throw new RuntimeException(""Invalid file opening mode"");
+ openstring = """";
+ io.ML_throw_io_error(""Invalid file opening mode"");
}
try {
randomaccess = new java.io.RandomAccessFile(file, openstring);
+ channel = randomaccess.getChannel();
if (mode == 'w') {
// Truncate an existing file.
randomaccess.setLength(0);
} else if (mode == 'a') {
- seek(SEEK_END, 0);
+ seek_binary(SEEK_END, 0);
}
} catch (java.lang.Exception e) {
- throw new RuntimeException(e.getMessage());
- // We can't just throw e or the Java compiler
- // will complain about unreported exceptions.
+ io.ML_throw_io_error(e.getMessage());
}
}
public MR_MercuryFileStruct(java.io.InputStream stream,
boolean openAsBinary)
{
- id = ML_next_stream_id++;
+ id = ML_next_stream_id.getAndAdd(1);
mode = INPUT;
pushback = new java.util.Stack<Integer>();
@@ -5772,14 +5841,14 @@ namespace mercury {
/* open stdin as binary */
binary_input = new java.io.FileInputStream(
java.io.FileDescriptor.in);
- channel = get_channel(binary_input);
+ channel = binary_input.getChannel();
}
}
public MR_MercuryFileStruct(java.io.OutputStream stream,
boolean openAsBinary)
{
- id = ML_next_stream_id++;
+ id = ML_next_stream_id.getAndAdd(1);
mode = OUTPUT;
if (!openAsBinary) {
@@ -5788,121 +5857,42 @@ namespace mercury {
/* open stdout as binary */
binary_output = new java.io.FileOutputStream(
java.io.FileDescriptor.out);
- channel = get_channel(binary_output);
+ channel = binary_output.getChannel();
}
}
/*
- ** size():
+ ** size(): [Java]
**
- ** Returns the length of a binary file. XXX Note that this method
- ** will return -1 for mercury_stdin_binary and the current position
- ** for mercury_stdout_binary in Java versions < 1.4.
+ ** Returns the length of a file.
*/
- public int size() {
- if (randomaccess != null) {
- try {
- return (int) randomaccess.length();
- } catch (java.io.IOException e) {
- return -1;
- }
- }
-
- try {
- java.lang.reflect.Method size_mth =
- channel.getClass().getMethod(""size"");
- return ((Long) size_mth.invoke(channel)).intValue();
- } catch (java.lang.Exception e) {
- if (binary_output != null) {
- return position;
- } else {
- return -1;
- }
- }
+ public int size()
+ throws java.io.IOException
+ {
+ return (int) channel.size();
}
/*
- ** seek():
- **
- ** Seek relative to start, current position or end depending on the
- ** flag. This function only works for binary files.
- **
- ** The binary versions of stdin and stdout are treated specially
- ** as follows.
- **
- ** For Java versions >= 1.4:
- ** Reflection is used to obtain and use the necessary FileChannel
- ** object needed to perform seeking on each.
- ** For older versions:
- ** For mercury_stdin_binary, seek may be only be done forwards
- ** from the current position and for mercury_stdout_binary seek is not
- ** supported at all.
+ ** getOffset():
+ ** Returns the current position in a binary file.
*/
- public void seek(int flag, int offset) {
- if (channel != null) {
- channelSeek(flag, offset);
- return;
- }
-
- if (input != null || output != null) {
- throw new java.lang.RuntimeException(
- ""Java text streams are not seekable"");
- }
- if (binary_output != null) {
- throw new java.lang.RuntimeException(
- ""for Java versions < 1.4, "" +
- ""mercury_stdout_binary is not seekable"");
- }
- if (binary_input != null &&
- (flag != SEEK_CUR || offset < 0))
+ public int getOffset()
+ throws java.io.IOException
{
- throw new java.lang.RuntimeException(
- ""for Java versions < 1.4, "" +
- ""mercury_stdin_binary may only seek forwards "" +
- ""from the current position"");
- }
-
- pushback = new java.util.Stack<Integer>();
-
- try {
- switch (flag) {
- case SEEK_SET:
- randomaccess.seek(offset);
- break;
- case SEEK_CUR:
- if (randomaccess != null) {
- randomaccess.seek(
- randomaccess.getFilePointer() + offset);
- } else {
- binary_input.skip(offset);
- }
- break;
- case SEEK_END:
- randomaccess.seek(randomaccess.length() + offset);
- break;
- default:
- throw new java.lang.
- RuntimeException(""Invalid seek flag"");
- }
- } catch (java.lang.Exception e) {
- throw new java.lang.RuntimeException(e.getMessage());
- }
+ return (int) channel.position() - pushback.size();
}
/*
- ** channelSeek():
+ ** seek(): [Java]
**
** Seek relative to start, current position or end depending on the
- ** flag. This function is a special case of seek() above, used when
- ** a FileChannel is present and usable. At present, this is only the
- ** case for binary stdin/out using Java versions >= 1.4. FileChannel's
- ** position() method must be called using Reflection so that this code
- ** will still compile for Java versions < 1.4.
+ ** flag. This function only works for binary files.
*/
- private void channelSeek(int flag, int offset) {
- pushback = new java.util.Stack<Integer>();
+ public void seek_binary(int flag, int offset)
+ throws java.io.IOException
+ {
+ long position;
- try {
switch (flag) {
case SEEK_SET:
position = offset;
@@ -5918,66 +5908,35 @@ namespace mercury {
RuntimeException(""Invalid seek flag"");
}
- // Simulate
- // channel.position(position);
- // using reflection.
- Class[] argTypes = {Long.TYPE};
- java.lang.reflect.Method seek_mth =
- channel.getClass().getMethod(""position"", argTypes);
-
- Object[] args = {new Long(position)};
- seek_mth.invoke(channel, args);
- }
- catch (java.lang.Exception e) {
- throw new java.lang.RuntimeException(e.getMessage());
- }
+ channel.position(position);
+ pushback = new java.util.Stack<Integer>();
}
- /*
- ** getOffset():
- ** Returns the current position in a binary file.
- */
- public int getOffset() {
- if (randomaccess != null) {
+ public void seek_binary_or_throw(int flag, int offset) {
try {
- return (int) randomaccess.getFilePointer();
+ seek_binary(flag, offset);
} catch (java.io.IOException e) {
- return -1;
- }
- }
-
- try {
- java.lang.reflect.Method posn_mth =
- channel.getClass().getMethod(""position"");
- return ((Long) posn_mth.invoke(channel)).intValue();
- } catch (java.lang.Exception e) {
- if (binary_input != null || binary_output != null) {
- return position;
- } else {
- return -1;
- }
+ io.ML_throw_io_error(e.getMessage());
}
}
/*
- ** read_char():
+ ** read_char(): [Java]
**
** Reads one character in from a text input file using the default
** charset decoding. Returns -1 at end of file.
*/
- public int read_char() {
+ public int read_char()
+ throws java.io.IOException
+ {
int c;
if (input == null) {
- throw new java.lang.RuntimeException(
+ io.ML_throw_io_error(
""read_char_code may only be called"" +
"" on text input streams"");
}
if (pushback.empty()) {
- try {
c = input.read();
- } catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(e.getMessage());
- }
} else {
c = pushback.pop();
}
@@ -5990,41 +5949,102 @@ namespace mercury {
}
/*
- ** read_byte():
+ ** read_line(): [Java]
+ **
+ ** Reads in a line of a text input file using the default
+ ** charset decoding.
+ */
+ public int read_line(StringBuilder sb)
+ throws java.io.IOException
+ {
+ int c;
+
+ while (!pushback.empty()) {
+ c = pushback.pop();
+ sb.append((char) c);
+ if (c == '\\n') {
+ line_number++;
+ return 0; /* ok */
+ }
+ }
+
+ while ((c = input.read()) != -1) {
+ sb.append((char) c);
+ if (c == '\\n') {
+ line_number++;
+ return 0; /* ok */
+ }
+ }
+
+ if (sb.length() > 0) {
+ return 0; /* ok */
+ } else {
+ return -1; /* eof */
+ }
+ }
+
+ /*
+ ** read_file(): [Java]
+ **
+ ** Reads in the rest of a text input file using the default
+ ** charset decoding.
+ */
+ public void read_file(StringBuilder sb)
+ throws java.io.IOException
+ {
+ if (input == null) {
+ io.ML_throw_io_error(
+ ""read_file_as_string may only be called"" +
+ "" on text input streams"");
+ }
+
+ while (!pushback.empty()) {
+ sb.append((char) read_char());
+ }
+
+ char[] chars = new char[1024];
+ int n = 0;
+ while ((n = input.read(chars)) > -1) {
+ for (int i = 0; i < n; i++) {
+ if (chars[i] == '\\n') {
+ line_number++;
+ }
+ }
+ sb.append(chars, 0, n);
+ }
+ }
+
+ /*
+ ** read_byte(): [Java]
**
** Reads one byte in from a binary file. Returns -1 at end of file.
*/
- public int read_byte() {
+ public int read_byte()
+ throws java.io.IOException
+ {
int c;
if (mode == OUTPUT) {
- throw new java.lang.RuntimeException(
- ""Attempted to read output stream"");
+ io.ML_throw_io_error(""Attempted to read output stream"");
}
if (randomaccess == null && binary_input == null) {
- throw new java.lang.RuntimeException(
- ""read_byte_val may only be called"" +
+ io.ML_throw_io_error(""read_byte_val may only be called"" +
"" on binary input streams"");
}
if (pushback.empty()) {
- try {
if (binary_input != null) {
c = binary_input.read();
} else {
c = randomaccess.read();
}
- } catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(e.getMessage());
- }
} else {
c = pushback.pop();
}
- position++;
return c;
}
/*
- ** ungetc():
+ ** ungetc(): [Java]
**
** Pushes an integer, which may represent either a byte or a character,
** onto the pushback stack. This stack is the same, regardless of
@@ -6032,19 +6052,17 @@ namespace mercury {
*/
public void ungetc(int c) {
if (mode == OUTPUT) {
- throw new java.lang.RuntimeException(
+ io.ML_throw_io_error(
""Attempted to unget char to output stream"");
}
if (c == '\\n') {
line_number--;
}
-
pushback.push(c);
- position--;
}
/*
- ** put():
+ ** put(): [Java]
**
** Write one unit to an output stream. If the file is text, the int
** will hold a character. If the file is binary, this will be a
@@ -6052,16 +6070,13 @@ namespace mercury {
** 16 bits hold a char, in latter we take only the lowest 8 bits
** for a byte.
*/
- public void put(int c) {
+ public void put(int c)
+ throws java.io.IOException
+ {
if (mode == INPUT) {
- throw new java.lang.RuntimeException(
- ""Attempted to write to input stream"");
- }
- if (c == '\\n') {
- line_number++;
+ io.ML_throw_io_error(""Attempted to write to input stream"");
}
- try {
if (output != null) {
output.write(c);
if (c == '\\n') {
@@ -6071,35 +6086,46 @@ namespace mercury {
randomaccess.write(c);
} else {
binary_output.write(c);
- position++;
}
+
+ /* Don't increment until the write succeeds. */
+ if (c == '\\n') {
+ line_number++;
+ }
+ }
+
+ public void put_or_throw(int c) {
+ try {
+ put(c);
} catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(e.getMessage());
+ io.ML_throw_io_error(e.getMessage());
}
}
/*
- ** write():
+ ** write(): [Java]
**
** Writes a string to a file stream. For text files, this string
** is encoded as a byte stream using default encoding. For binary
** files, the lower order 8 bits of each character are written.
*/
- public void write(java.lang.String s) {
+ public void write(java.lang.String s)
+ throws java.io.IOException
+ {
if (mode == INPUT) {
- throw new java.lang.RuntimeException(
- ""Attempted to write to input stream"");
+ io.ML_throw_io_error(""Attempted to write to input stream"");
}
- try {
if (output != null) {
+ output.write(s);
+
int old_line_number = line_number;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '\\n') {
line_number++;
}
}
- output.write(s);
+
/* Flush if we saw a newline. */
if (old_line_number != line_number) {
output.flush();
@@ -6110,18 +6136,26 @@ namespace mercury {
put((byte) s.charAt(i));
}
}
+ }
+
+ public void write_or_throw(java.lang.String s) {
+ try {
+ write(s);
} catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(e.getMessage());
+ io.ML_throw_io_error(e.getMessage());
}
}
- public void flush() {
+ /*
+ ** flush(): [Java]
+ */
+ public void flush()
+ throws java.io.IOException
+ {
if (mode == INPUT) {
- throw new java.lang.RuntimeException(
- ""Attempted to flush input stream"");
+ io.ML_throw_io_error(""Attempted to flush input stream"");
}
- try {
if (output != null) {
output.flush();
}
@@ -6129,13 +6163,22 @@ namespace mercury {
binary_output.flush();
}
// else randomaccess is already unbuffered.
+ }
+
+ public void flush_or_throw() {
+ try {
+ flush();
} catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(e.getMessage());
+ io.ML_throw_io_error(e.getMessage());
}
}
- public void close() {
- try {
+ /*
+ ** close(): [Java]
+ */
+ public void close()
+ throws java.io.IOException
+ {
if (input != null) {
input.close();
}
@@ -6151,8 +6194,13 @@ namespace mercury {
if (binary_output != null) {
binary_output.close();
}
+ }
+
+ public void close_or_throw() {
+ try {
+ close();
} catch (java.io.IOException e) {
- throw new java.lang.RuntimeException(""Error closing stream"");
+ io.ML_throw_io_error(e.getMessage());
}
}
} // class MR_MercuryFileStruct
@@ -6196,34 +6244,6 @@ namespace mercury {
}
}
} // class StreamPipe
-
- /*
- ** get_channel():
- **
- ** Given some object, attempts to call getChannel() using Reflection
- ** and returns the result, or null if getChannel() is not available.
- ** The reason we do this is that FileChannels were not supported until
- ** Java v1.4, so users with older versions of Java would not be able to
- ** compile this code if the call to getChannel were made directly.
- ** This way, older versions of Java will still be able to compile,
- ** but will not support binary seeking on stdin/out properly, since
- ** this can't be done without FileChannel.
- */
- static java.lang.Object/*FileChannel*/ get_channel(java.lang.Object o)
- {
- try {
- // Simulate
- // return o.getChannel();
- // using reflection.
- java.lang.reflect.Method getChannel_mth =
- o.getClass().getMethod(""getChannel"");
-
- return getChannel_mth.invoke(o);
- }
- catch (java.lang.Exception e) {
- return null;
- }
- }
").
:- pragma foreign_code("C", "
@@ -6410,6 +6430,7 @@ static MR_MercuryFileStruct mercury_stdout_binary =
new MR_MercuryFileStruct(java.lang.System.out, true);
// Note: these are set again in io.init_state.
+// XXX these should be thread-local
static MR_MercuryFileStruct mercury_current_text_input =
mercury_stdin;
static MR_MercuryFileStruct mercury_current_text_output =
@@ -6419,8 +6440,7 @@ static MR_MercuryFileStruct mercury_current_binary_input =
static MR_MercuryFileStruct mercury_current_binary_output =
mercury_stdout_binary;
-// XXX not thread-safe!
-static java.lang.Exception MR_io_exception;
+static ThreadLocal<Exception> MR_io_exception = new ThreadLocal<Exception>();
").
:- pragma foreign_decl("Erlang", local, "
@@ -6836,6 +6856,7 @@ static MR_MercuryFileStruct mercury_open(string filename, string openmode,
:- pred throw_io_error(string::in) is erroneous.
:- pragma foreign_export("C", throw_io_error(in), "ML_throw_io_error").
:- pragma foreign_export("IL", throw_io_error(in), "ML_throw_io_error").
+:- pragma foreign_export("Java", throw_io_error(in), "ML_throw_io_error").
throw_io_error(Message) :-
throw(io_error(Message)).
@@ -7441,28 +7462,38 @@ io.putback_byte(binary_input_stream(Stream), Character, !IO) :-
:- pragma foreign_proc("Java",
io.read_char_code_2(File::in, CharCode::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure],
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
+ try {
CharCode = File.read_char();
+ } catch (java.io.IOException e) {
+ io.MR_io_exception.set(e);
+ CharCode = -2;
+ }
").
:- pragma foreign_proc("Java",
io.read_byte_val_2(File::in, ByteVal::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure],
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
+ try {
ByteVal = File.read_byte();
+ } catch (java.io.IOException e) {
+ io.MR_io_exception.set(e);
+ ByteVal = -2;
+ }
").
:- pragma foreign_proc("Java",
io.putback_char_2(File::in, Character::in, _IO0::di, _IO::uo),
- [may_call_mercury, promise_pure, terminates],
+ [may_call_mercury, promise_pure, thread_safe, terminates, tabled_for_io],
"
File.ungetc(Character);
").
:- pragma foreign_proc("Java",
io.putback_byte_2(File::in, Byte::in, _IO0::di, _IO::uo),
- [may_call_mercury, promise_pure, terminates],
+ [may_call_mercury, promise_pure, thread_safe, terminates, tabled_for_io],
"
File.ungetc(Byte);
").
@@ -7683,53 +7714,53 @@ io.write_bitmap(Bitmap, Start, NumBytes, !IO) :-
io.write_string(Message::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_text_output.write(Message);
+ io.mercury_current_text_output.write_or_throw(Message);
").
:- pragma foreign_proc("Java",
io.write_char(Chr::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_text_output.write(java.lang.Character.toString(Chr));
+ io.mercury_current_text_output.write_or_throw(Character.toString(Chr));
").
:- pragma foreign_proc("Java",
io.write_int(Val::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_text_output.write(java.lang.Integer.toString(Val));
+ io.mercury_current_text_output.write_or_throw(Integer.toString(Val));
").
:- pragma foreign_proc("Java",
io.write_float(Val::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_text_output.write(java.lang.Double.toString(Val));
+ io.mercury_current_text_output.write_or_throw(Double.toString(Val));
").
:- pragma foreign_proc("Java",
io.write_byte(Byte::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_binary_output.put((byte) Byte);
+ io.mercury_current_binary_output.put_or_throw((byte) Byte);
").
:- pragma foreign_proc("Java",
io.write_bytes(Message::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
-"{
- mercury_current_binary_output.write(Message);
-}").
+"
+ io.mercury_current_binary_output.write_or_throw(Message);
+").
:- pragma foreign_proc("Java",
io.flush_output(_IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_text_output.flush();
+ io.mercury_current_text_output.flush_or_throw();
").
:- pragma foreign_proc("Java",
io.flush_binary_output(_IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- mercury_current_binary_output.flush();
+ io.mercury_current_binary_output.flush_or_throw();
").
:- pragma foreign_proc("Erlang",
@@ -8121,7 +8152,7 @@ io.flush_binary_output(binary_output_stream(Stream), !IO) :-
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
terminates],
"
- Stream.seek(Flag, Off);
+ Stream.seek_binary_or_throw(Flag, Off);
").
:- pragma foreign_proc("Java",
@@ -8129,63 +8160,67 @@ io.flush_binary_output(binary_output_stream(Stream), !IO) :-
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
terminates],
"
+ try {
Offset = Stream.getOffset();
+ } catch (java.io.IOException e) {
+ return -1;
+ }
").
:- pragma foreign_proc("Java",
io.write_string_2(Stream::in, Message::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.write(Message);
+ Stream.write_or_throw(Message);
").
:- pragma foreign_proc("Java",
io.write_char_2(Stream::in, Character::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.put(Character);
+ Stream.put_or_throw(Character);
").
:- pragma foreign_proc("Java",
io.write_int_2(Stream::in, Val::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
"
- Stream.write(java.lang.String.valueOf(Val));
+ Stream.write_or_throw(java.lang.String.valueOf(Val));
").
:- pragma foreign_proc("Java",
io.write_float_2(Stream::in, Val::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
"
- Stream.write(java.lang.String.valueOf(Val));
+ Stream.write_or_throw(java.lang.String.valueOf(Val));
").
:- pragma foreign_proc("Java",
io.write_byte_2(Stream::in, Byte::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.put(Byte);
+ Stream.put_or_throw(Byte);
").
:- pragma foreign_proc("Java",
io.write_bytes_2(Stream::in, Message::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.write(Message);
+ Stream.write_or_throw(Message);
").
:- pragma foreign_proc("Java",
io.flush_output_2(Stream::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.flush();
+ Stream.flush_or_throw();
").
:- pragma foreign_proc("Java",
io.flush_binary_output_2(Stream::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io, terminates],
"
- Stream.flush();
+ Stream.flush_or_throw();
").
:- pragma foreign_proc("Erlang",
@@ -8310,7 +8345,7 @@ io.stdin_stream = input_stream(io.stdin_stream_2).
[will_not_call_mercury, promise_pure, thread_safe,
does_not_affect_liveness],
"
- Stream = mercury_stdin;
+ Stream = io.mercury_stdin;
").
io.stdin_stream(input_stream(Stream), !IO) :-
@@ -8352,7 +8387,7 @@ io.stdout_stream = output_stream(io.stdout_stream_2).
[will_not_call_mercury, promise_pure, thread_safe,
does_not_affect_liveness],
"
- Stream = mercury_stdout;
+ Stream = io.mercury_stdout;
").
io.stdout_stream(output_stream(Stream), !IO) :-
@@ -8394,7 +8429,7 @@ io.stderr_stream = output_stream(io.stderr_stream_2).
[will_not_call_mercury, promise_pure, thread_safe,
does_not_affect_liveness],
"
- Stream = mercury_stderr;
+ Stream = io.mercury_stderr;
").
io.stderr_stream(output_stream(Stream), !IO) :-
@@ -8819,70 +8854,70 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
io.stdin_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
- Stream = mercury_stdin;
+ Stream = io.mercury_stdin;
").
:- pragma foreign_proc("Java",
io.stdout_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
- Stream = mercury_stdout;
+ Stream = io.mercury_stdout;
").
:- pragma foreign_proc("Java",
io.stderr_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
- Stream = mercury_stderr;
+ Stream = io.mercury_stderr;
").
:- pragma foreign_proc("Java",
io.stdin_binary_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
- Stream = mercury_stdin_binary;
+ Stream = io.mercury_stdin_binary;
").
:- pragma foreign_proc("Java",
io.stdout_binary_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
- Stream = mercury_stdout_binary;
+ Stream = io.mercury_stdout_binary;
").
:- pragma foreign_proc("Java",
io.input_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- Stream = mercury_current_text_input;
+ Stream = io.mercury_current_text_input;
").
:- pragma foreign_proc("Java",
io.output_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- Stream = mercury_current_text_output;
+ Stream = io.mercury_current_text_output;
").
:- pragma foreign_proc("Java",
io.binary_input_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- Stream = mercury_current_binary_input;
+ Stream = io.mercury_current_binary_input;
").
:- pragma foreign_proc("Java",
io.binary_output_stream_2(Stream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- Stream = mercury_current_binary_output;
+ Stream = io.mercury_current_binary_output;
").
:- pragma foreign_proc("Java",
io.get_line_number(LineNum::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- LineNum = mercury_current_text_input.line_number;
+ LineNum = io.mercury_current_text_input.line_number;
").
:- pragma foreign_proc("Java",
@@ -8896,43 +8931,43 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
io.set_line_number(LineNum::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- mercury_current_text_input.line_number = LineNum;
+ io.mercury_current_text_input.line_number = LineNum;
").
:- pragma foreign_proc("Java",
io.set_line_number_2(Stream::in, LineNum::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
-"{
+"
Stream.line_number = LineNum;
-}").
+").
:- pragma foreign_proc("Java",
io.get_output_line_number(LineNum::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- LineNum = mercury_current_text_output.line_number;
+ LineNum = io.mercury_current_text_output.line_number;
").
:- pragma foreign_proc("Java",
io.get_output_line_number_2(Stream::in, LineNum::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
-"{
+"
LineNum = Stream.line_number;
-}").
+").
:- pragma foreign_proc("Java",
io.set_output_line_number(LineNum::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- mercury_current_text_output.line_number = LineNum;
+ io.mercury_current_text_output.line_number = LineNum;
").
:- pragma foreign_proc("Java",
io.set_output_line_number_2(Stream::in, LineNum::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
-"{
+"
Stream.line_number = LineNum;
-}").
+").
% io.set_input_stream(NewStream, OldStream, IO0, IO1)
% Changes the current input stream to the stream specified.
@@ -8942,16 +8977,16 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
io.set_input_stream_2(NewStream::in, OutStream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- OutStream = mercury_current_text_input;
- mercury_current_text_input = NewStream;
+ OutStream = io.mercury_current_text_input;
+ io.mercury_current_text_input = NewStream;
").
:- pragma foreign_proc("Java",
io.set_output_stream_2(NewStream::in, OutStream::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- OutStream = mercury_current_text_output;
- mercury_current_text_output = NewStream;
+ OutStream = io.mercury_current_text_output;
+ io.mercury_current_text_output = NewStream;
").
:- pragma foreign_proc("Java",
@@ -8959,8 +8994,8 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- OutStream = mercury_current_binary_input;
- mercury_current_binary_input = NewStream;
+ OutStream = io.mercury_current_binary_input;
+ io.mercury_current_binary_input = NewStream;
").
:- pragma foreign_proc("Java",
@@ -8968,8 +9003,8 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
- OutStream = mercury_current_binary_output;
- mercury_current_binary_output = NewStream;
+ OutStream = io.mercury_current_binary_output;
+ io.mercury_current_binary_output = NewStream;
").
:- pragma foreign_proc("Erlang",
@@ -9238,7 +9273,8 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
:- pragma foreign_proc("Java",
io.do_open_text(FileName::in, Mode::in, ResultCode::out,
StreamId::out, Stream::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+ may_not_duplicate],
"
try {
if (Mode.charAt(0) == 'r') {
@@ -9251,13 +9287,14 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
Stream = new MR_MercuryFileStruct(
new java.io.FileOutputStream(FileName, true));
} else {
- throw new java.lang.RuntimeException(""io.do_open_text: "" +
+ io.ML_throw_io_error(""io.do_open_text: "" +
""Invalid open mode"" + "" \\"""" + Mode + ""\\"""");
+ throw new Error(""unreachable"");
}
StreamId = Stream.id;
ResultCode = 0;
} catch (java.lang.Exception e) {
- MR_io_exception = e;
+ io.MR_io_exception.set(e);
Stream = null;
StreamId = -1;
ResultCode = -1;
@@ -9267,14 +9304,15 @@ io.set_binary_output_stream(binary_output_stream(NewStream),
:- pragma foreign_proc("Java",
io.do_open_binary(FileName::in, Mode::in, ResultCode::out,
StreamId::out, Stream::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+ may_not_duplicate],
"
try {
Stream = new MR_MercuryFileStruct(FileName, Mode.charAt(0));
StreamId = Stream.id;
ResultCode = 0;
} catch (java.lang.Exception e) {
- MR_io_exception = e;
+ io.MR_io_exception.set(e);
Stream = null;
StreamId = -1;
ResultCode = -1;
@@ -9361,7 +9399,7 @@ io.close_binary_output(binary_output_stream(Stream), !IO) :-
io.close_stream(Stream::in, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
"
- Stream.close();
+ Stream.close_or_throw();
").
:- pragma foreign_proc("Erlang",
@@ -9905,7 +9943,7 @@ io.setenv(Var, Value) :-
// implemented directly for Java.
// This implementation is included only to suppress warnings.
- throw new RuntimeException(
+ io.ML_throw_io_error(
""io.putenv/1 not implemented for Java: "" + VarAndValue);
").
@@ -10079,7 +10117,8 @@ io.make_temp(Dir, Prefix, Name, !IO) :-
:- pragma foreign_proc("Java",
io.make_temp(FileName::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+ may_not_duplicate],
"
try {
java.io.File tmpdir = new java.io.File(
@@ -10087,15 +10126,15 @@ io.make_temp(Dir, Prefix, Name, !IO) :-
FileName = java.io.File.createTempFile(""mtmp"", null, tmpdir).
getName();
} catch (java.lang.Exception e) {
- throw new RuntimeException(e.getMessage());
- // This is done instead of just 'throw e' because otherwise
- // the java compiler complains about unreported exceptions.
+ io.ML_throw_io_error(e.getMessage());
+ FileName = null;
}
").
:- pragma foreign_proc("Java",
io.make_temp(Dir::in, Prefix::in, FileName::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+ may_not_duplicate],
"
try {
if (Prefix.length() < 3) {
@@ -10110,9 +10149,8 @@ io.make_temp(Dir, Prefix, Name, !IO) :-
FileName = java.io.File.createTempFile(Prefix, null,
new java.io.File(Dir)).getName();
} catch (java.lang.Exception e) {
- throw new RuntimeException(e.getMessage());
- // This is done instead of just 'throw e' because otherwise
- // the java compiler complains about unreported exceptions.
+ io.ML_throw_io_error(e.getMessage());
+ FileName = null;
}
").
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 7b7bc37..1c0834f 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -214,6 +214,8 @@ ORDINARY_PROGS= \
static_no_tag \
stream_format \
stream_ignore_ws \
+ stream_putback \
+ stream_putback_binary \
stream_test \
string_alignment \
string_alignment_bug \
diff --git a/tests/hard_coded/stream_putback.data b/tests/hard_coded/stream_putback.data
new file mode 100644
index 0000000..8baef1b
--- /dev/null
+++ b/tests/hard_coded/stream_putback.data
@@ -0,0 +1 @@
+abc
diff --git a/tests/hard_coded/stream_putback.exp b/tests/hard_coded/stream_putback.exp
new file mode 100644
index 0000000..628426f
--- /dev/null
+++ b/tests/hard_coded/stream_putback.exp
@@ -0,0 +1,7 @@
+Read: 'a'
+Put back: 'X'
+Read: 'X'
+Read: 'b'
+Read: 'c'
+Read: '\n'
+Read: eof
diff --git a/tests/hard_coded/stream_putback.m b/tests/hard_coded/stream_putback.m
new file mode 100644
index 0000000..2db0c6d
--- /dev/null
+++ b/tests/hard_coded/stream_putback.m
@@ -0,0 +1,68 @@
+%-----------------------------------------------------------------------------%
+
+:- module stream_putback.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module require.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+
+main(!IO) :-
+ io.open_input("stream_putback.data", OpenRes, !IO),
+ (
+ OpenRes = ok(Stream),
+ print_read(Stream, !IO), % a
+ % Can only expect one pushback.
+ print_putback(Stream, 'X', !IO),
+ print_read(Stream, !IO), % X
+ print_read(Stream, !IO), % b
+ print_read(Stream, !IO), % c
+ print_read(Stream, !IO), % \n
+ print_read(Stream, !IO), % eof
+ io.close_input(Stream, !IO)
+ ;
+ OpenRes = error(Error),
+ error(io.error_message(Error))
+ ).
+
+:- pred print_read(io.input_stream::in, io::di, io::uo) is det.
+
+print_read(Stream, !IO) :-
+ io.read_char(Stream, ReadRes, !IO),
+ (
+ ReadRes = ok(Char),
+ io.write_string("Read: ", !IO),
+ io.write(Char, !IO),
+ io.nl(!IO)
+ ;
+ ReadRes = eof,
+ io.write_string("Read: eof\n", !IO)
+ ;
+ ReadRes = error(Error),
+ error(io.error_message(Error))
+ ).
+
+:- pred print_putback(io.input_stream::in, char::in, io::di, io::uo) is det.
+
+print_putback(Stream, Char, !IO) :-
+ io.putback_char(Stream, Char, !IO),
+ io.write_string("Put back: ", !IO),
+ io.write(Char, !IO),
+ io.nl(!IO).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/stream_putback_binary.exp b/tests/hard_coded/stream_putback_binary.exp
new file mode 100644
index 0000000..fa4327a
--- /dev/null
+++ b/tests/hard_coded/stream_putback_binary.exp
@@ -0,0 +1,32 @@
+Position: 0
+
+Read: 'a'
+Position: 1
+
+Seek: +1
+Position: 2
+
+Read: 'c'
+Position: 3
+
+Put back: 'X'
+Position: 2
+
+Read: 'X'
+Position: 3
+
+Read: '\n'
+Position: 4
+
+Read: eof
+Position: 4
+
+Put back: 'Y'
+Position: 3
+
+Seek: -2
+Position: 1
+
+Read: 'b'
+Position: 2
+
diff --git a/tests/hard_coded/stream_putback_binary.m b/tests/hard_coded/stream_putback_binary.m
new file mode 100644
index 0000000..3744d64
--- /dev/null
+++ b/tests/hard_coded/stream_putback_binary.m
@@ -0,0 +1,88 @@
+%-----------------------------------------------------------------------------%
+
+:- module stream_putback_binary.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module require.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+
+main(!IO) :-
+ io.open_binary_input("stream_putback.data", OpenRes, !IO),
+ (
+ OpenRes = ok(Stream),
+ print_position(Stream, !IO), % pos 0
+ print_read_byte(Stream, !IO), % pos 1
+ print_relseek(Stream, 1, !IO), % pos 2
+ print_read_byte(Stream, !IO), % pos 3
+ print_putback(Stream, 'X', !IO), % pos 2
+ print_read_byte(Stream, !IO), % X, pos 3
+ print_read_byte(Stream, !IO), % \n, pos 4
+ print_read_byte(Stream, !IO), % eof, pos 4
+ print_putback(Stream, 'Y', !IO), % pos 3
+ print_relseek(Stream, -2, !IO), % pos 1; pushback dropped
+ print_read_byte(Stream, !IO), % b, pos 2
+ io.close_binary_input(Stream, !IO)
+ ;
+ OpenRes = error(Error),
+ error(io.error_message(Error))
+ ).
+
+:- pred print_read_byte(io.binary_input_stream::in, io::di, io::uo) is det.
+
+print_read_byte(Stream, !IO) :-
+ io.read_byte(Stream, ReadRes, !IO),
+ (
+ ReadRes = ok(Byte),
+ io.write_string("Read: ", !IO),
+ io.write(det_from_int(Byte) : char, !IO),
+ io.nl(!IO)
+ ;
+ ReadRes = eof,
+ io.write_string("Read: eof\n", !IO)
+ ;
+ ReadRes = error(Error),
+ error(io.error_message(Error))
+ ),
+ print_position(Stream, !IO).
+
+:- pred print_putback(io.binary_input_stream::in, char::in, io::di, io::uo)
+ is det.
+
+print_putback(Stream, Char, !IO) :-
+ io.putback_byte(Stream, char.to_int(Char), !IO),
+ io.write_string("Put back: ", !IO),
+ io.write(Char, !IO),
+ io.nl(!IO),
+ print_position(Stream, !IO).
+
+:- pred print_relseek(io.binary_input_stream::in, int::in, io::di, io::uo)
+ is det.
+
+print_relseek(Stream, Offset, !IO) :-
+ io.seek_binary_input(Stream, cur, Offset, !IO),
+ io.format("Seek: %+d\n", [i(Offset)], !IO),
+ print_position(Stream, !IO).
+
+:- pred print_position(io.binary_input_stream::in, io::di, io::uo) is det.
+
+print_position(Stream, !IO) :-
+ io.binary_input_stream_offset(Stream, Offset, !IO),
+ io.format("Position: %d\n\n", [i(Offset)], !IO).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
--------------------------------------------------------------------------
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