Skip to content

Commit ca4412f

Browse files
authored
Fix runtime null contracts and standard stream initialization (#226)
1 parent a848a99 commit ca4412f

54 files changed

Lines changed: 1332 additions & 99 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎rustjava-runtime/src/classes/java/io/buffered_output_stream.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ impl BufferedOutputStream {
120120
length: i32,
121121
) -> Result<()> {
122122
tracing::debug!("java.io.BufferedOutputStream::write({this:?}, {bytes:?}, {offset}, {length})");
123+
if bytes.is_null() {
124+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
125+
}
123126

124127
let source_length = jvm.array_length(&bytes).await? as i32;
125128
if offset < 0 || length < 0 || offset > source_length - length {

‎rustjava-runtime/src/classes/java/io/data_input_stream.rs‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,18 @@ impl DataInputStream {
214214

215215
async fn read_utf_from_input(jvm: &Jvm, _: &mut RuntimeContext, input: ClassInstanceRef<DataInput>) -> Result<ClassInstanceRef<String>> {
216216
tracing::debug!("java.io.DataInputStream::readUTF({input:?})");
217+
if input.is_null() {
218+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
219+
}
217220
jvm.invoke_virtual(&input, &input.class_definition().name(), "readUTF", "()Ljava/lang/String;", ())
218221
.await
219222
}
220223

221224
async fn read_fully(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, b: ClassInstanceRef<Array<i8>>) -> Result<()> {
222225
tracing::debug!("java.io.DataInputStream::readFully({this:?}, {b:?})");
226+
if b.is_null() {
227+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
228+
}
223229

224230
let length = jvm.array_length(&b).await?;
225231

@@ -239,6 +245,9 @@ impl DataInputStream {
239245
len: i32,
240246
) -> Result<()> {
241247
tracing::debug!("java.io.DataInputStream::readFully({this:?}, {b:?}, {off}, {len})");
248+
if b.is_null() {
249+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
250+
}
242251

243252
let mut read = 0;
244253
while read < len {
@@ -257,14 +266,23 @@ impl DataInputStream {
257266
async fn skip_bytes(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, n: i32) -> Result<i32> {
258267
tracing::debug!("java.io.DataInputStream::skipBytes({this:?}, {n:?})");
259268

260-
let r#in = jvm.get_field(&this, "java/io/DataInputStream", "in", "Ljava/io/InputStream;").await?;
269+
if n <= 0 {
270+
return Ok(0);
271+
}
272+
let r#in: ClassInstanceRef<InputStream> = jvm.get_field(&this, "java/io/DataInputStream", "in", "Ljava/io/InputStream;").await?;
273+
if r#in.is_null() {
274+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
275+
}
261276
let skipped: i64 = jvm.invoke_virtual(&r#in, "java/io/InputStream", "skip", "(J)J", (n as i64,)).await?;
262277

263278
Ok(skipped as _)
264279
}
265280

266281
async fn read_required_byte(jvm: &Jvm, this: &ClassInstanceRef<Self>) -> Result<u8> {
267-
let r#in = jvm.get_field(this, "java/io/DataInputStream", "in", "Ljava/io/InputStream;").await?;
282+
let r#in: ClassInstanceRef<InputStream> = jvm.get_field(this, "java/io/DataInputStream", "in", "Ljava/io/InputStream;").await?;
283+
if r#in.is_null() {
284+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
285+
}
268286
let value: i32 = jvm.invoke_virtual(&r#in, "java/io/InputStream", "read", "()I", ()).await?;
269287
if value == -1 {
270288
return Err(jvm.exception("java/io/EOFException", "End of stream").await);

‎rustjava-runtime/src/classes/java/io/data_output_stream.rs‎

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ impl DataOutputStream {
105105
async fn write(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, b: i32) -> Result<()> {
106106
tracing::debug!("java.io.DataOutputStream::write({this:?}, {b:?})");
107107

108-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
108+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
109+
if out.is_null() {
110+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
111+
}
109112
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "(I)V", (b,)).await?;
110113

111114
Ok(())
@@ -114,7 +117,10 @@ impl DataOutputStream {
114117
async fn write_byte(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, v: i32) -> Result<()> {
115118
tracing::debug!("java.io.DataOutputStream::writeByte({this:?}, {v:?})");
116119

117-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
120+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
121+
if out.is_null() {
122+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
123+
}
118124
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "(I)V", (v,)).await?;
119125

120126
Ok(())
@@ -137,7 +143,10 @@ impl DataOutputStream {
137143
let mut byte_array = jvm.instantiate_array("B", bytes.len() as _).await?;
138144
jvm.store_array(&mut byte_array, 0, cast_vec::<u8, i8>(bytes.to_vec())).await?;
139145

140-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
146+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
147+
if out.is_null() {
148+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
149+
}
141150
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (byte_array,)).await?;
142151

143152
Ok(())
@@ -156,7 +165,10 @@ impl DataOutputStream {
156165
let mut byte_array = jvm.instantiate_array("B", bytes.len() as _).await?;
157166
jvm.store_array(&mut byte_array, 0, cast_vec::<u8, i8>(bytes.to_vec())).await?;
158167

159-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
168+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
169+
if out.is_null() {
170+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
171+
}
160172
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (byte_array,)).await?;
161173

162174
Ok(())
@@ -169,7 +181,10 @@ impl DataOutputStream {
169181
let mut byte_array = jvm.instantiate_array("B", bytes.len() as _).await?;
170182
jvm.store_array(&mut byte_array, 0, cast_vec::<u8, i8>(bytes.to_vec())).await?;
171183

172-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
184+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
185+
if out.is_null() {
186+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
187+
}
173188
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (byte_array,)).await?;
174189

175190
Ok(())
@@ -189,23 +204,38 @@ impl DataOutputStream {
189204

190205
async fn write_bytes(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, s: ClassInstanceRef<String>) -> Result<()> {
191206
tracing::debug!("java.io.DataOutputStream::writeBytes({this:?}, {s:?})");
207+
if s.is_null() {
208+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
209+
}
192210

193211
let chars: ClassInstanceRef<Array<JavaChar>> = jvm.invoke_virtual(&s, "java/lang/String", "toCharArray", "()[C", ()).await?;
194212
let length = jvm.array_length(&chars).await?;
195213
let chars: Vec<JavaChar> = jvm.load_array(&chars, 0, length).await?;
214+
if chars.is_empty() {
215+
return Ok(());
216+
}
196217
let mut bytes = jvm.instantiate_array("B", chars.len()).await?;
197218
jvm.store_array(&mut bytes, 0, chars.into_iter().map(|value| value as i8)).await?;
198219

199-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
220+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
221+
if out.is_null() {
222+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
223+
}
200224
jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (bytes,)).await
201225
}
202226

203227
async fn write_chars(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, s: ClassInstanceRef<String>) -> Result<()> {
204228
tracing::debug!("java.io.DataOutputStream::writeChars({this:?}, {s:?})");
229+
if s.is_null() {
230+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
231+
}
205232

206233
let chars: ClassInstanceRef<Array<JavaChar>> = jvm.invoke_virtual(&s, "java/lang/String", "toCharArray", "()[C", ()).await?;
207234
let length = jvm.array_length(&chars).await?;
208235
let chars: Vec<JavaChar> = jvm.load_array(&chars, 0, length).await?;
236+
if chars.is_empty() {
237+
return Ok(());
238+
}
209239
let mut data = Vec::with_capacity(chars.len() * 2);
210240
for value in chars {
211241
data.push((value >> 8) as i8);
@@ -215,12 +245,18 @@ impl DataOutputStream {
215245
let mut bytes = jvm.instantiate_array("B", data.len()).await?;
216246
jvm.store_array(&mut bytes, 0, data).await?;
217247

218-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
248+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
249+
if out.is_null() {
250+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
251+
}
219252
jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (bytes,)).await
220253
}
221254

222255
async fn write_utf(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, s: ClassInstanceRef<String>) -> Result<()> {
223256
tracing::debug!("java.io.DataOutputStream::writeUTF({this:?}, {s:?})");
257+
if s.is_null() {
258+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
259+
}
224260

225261
let chars: ClassInstanceRef<Array<JavaChar>> = jvm.invoke_virtual(&s, "java/lang/String", "toCharArray", "()[C", ()).await?;
226262
let length = jvm.array_length(&chars).await?;
@@ -238,14 +274,20 @@ impl DataOutputStream {
238274
let mut bytes = jvm.instantiate_array("B", data.len()).await?;
239275
jvm.store_array(&mut bytes, 0, data).await?;
240276

241-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
277+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
278+
if out.is_null() {
279+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
280+
}
242281
jvm.invoke_virtual(&out, "java/io/OutputStream", "write", "([B)V", (bytes,)).await
243282
}
244283

245284
async fn close(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
246285
tracing::debug!("java.io.DataOutputStream::close({this:?})");
247286

248-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
287+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
288+
if out.is_null() {
289+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
290+
}
249291
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "close", "()V", []).await?;
250292

251293
Ok(())
@@ -254,7 +296,10 @@ impl DataOutputStream {
254296
async fn flush(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
255297
tracing::debug!("java.io.DataOutputStream::flush({this:?})");
256298

257-
let out = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
299+
let out: ClassInstanceRef<OutputStream> = jvm.get_field(&this, "java/io/DataOutputStream", "out", "Ljava/io/OutputStream;").await?;
300+
if out.is_null() {
301+
return Err(jvm.exception("java/lang/NullPointerException", "underlying stream is null").await);
302+
}
258303
let _: () = jvm.invoke_virtual(&out, "java/io/OutputStream", "flush", "()V", []).await?;
259304

260305
Ok(())

‎rustjava-runtime/src/classes/java/io/file.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ impl File {
8484

8585
async fn init(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, pathname: ClassInstanceRef<String>) -> Result<()> {
8686
tracing::debug!("java.io.File::<init>({this:?}, {pathname:?})");
87+
if pathname.is_null() {
88+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
89+
}
8790

8891
let _: () = jvm.invoke_special(&this, "java/lang/Object", "<init>", "()V", ()).await?;
8992

‎rustjava-runtime/src/classes/java/io/file_descriptor.rs‎

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,13 @@ impl FileDescriptor {
3232
async fn cl_init(jvm: &Jvm, runtime: &mut RuntimeContext) -> Result<()> {
3333
tracing::debug!("java.io.FileDescriptor::<clinit>()");
3434

35-
let stderr_fd = runtime.stderr();
36-
if let Ok(stderr_fd) = stderr_fd {
37-
let mut stderr = jvm.new_class("java/io/FileDescriptor", "()V", []).await?;
38-
jvm.put_field(&mut stderr, "java/io/FileDescriptor", "fd", "I", stderr_fd.id() as i32)
39-
.await?;
40-
41-
jvm.put_static_field("java/io/FileDescriptor", "err", "Ljava/io/FileDescriptor;", stderr)
42-
.await?;
43-
}
44-
45-
let stdin_fd = runtime.stdin();
46-
if let Ok(stdin_fd) = stdin_fd {
47-
let mut stdin = jvm.new_class("java/io/FileDescriptor", "()V", []).await?;
48-
jvm.put_field(&mut stdin, "java/io/FileDescriptor", "fd", "I", stdin_fd.id() as i32)
49-
.await?;
50-
51-
jvm.put_static_field("java/io/FileDescriptor", "in", "Ljava/io/FileDescriptor;", stdin)
52-
.await?;
53-
}
54-
55-
let stdout_fd = runtime.stdout();
56-
if let Ok(stdout_fd) = stdout_fd {
57-
let mut stdout = jvm.new_class("java/io/FileDescriptor", "()V", []).await?;
58-
jvm.put_field(&mut stdout, "java/io/FileDescriptor", "fd", "I", stdout_fd.id() as i32)
59-
.await?;
60-
61-
jvm.put_static_field("java/io/FileDescriptor", "out", "Ljava/io/FileDescriptor;", stdout)
35+
for (name, host_descriptor) in [("err", runtime.stderr()), ("in", runtime.stdin()), ("out", runtime.stdout())] {
36+
let mut descriptor = jvm.new_class("java/io/FileDescriptor", "()V", []).await?;
37+
if let Ok(host_descriptor) = host_descriptor {
38+
jvm.put_field(&mut descriptor, "java/io/FileDescriptor", "fd", "I", host_descriptor.id() as i32)
39+
.await?;
40+
}
41+
jvm.put_static_field("java/io/FileDescriptor", name, "Ljava/io/FileDescriptor;", descriptor)
6242
.await?;
6343
}
6444

‎rustjava-runtime/src/classes/java/io/file_input_stream.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ impl FileInputStream {
4444

4545
async fn init(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef<Self>, file: ClassInstanceRef<File>) -> Result<()> {
4646
tracing::debug!("java.io.FileInputStream::<init>({this:?}, {file:?})");
47+
if file.is_null() {
48+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
49+
}
4750

4851
let path = jvm.invoke_virtual(&file, "java/io/File", "getPath", "()Ljava/lang/String;", ()).await?;
4952
let path = JavaLangString::to_rust_string(jvm, &path).await?;
@@ -77,6 +80,9 @@ impl FileInputStream {
7780
fd: ClassInstanceRef<FileDescriptor>,
7881
) -> Result<()> {
7982
tracing::debug!("java.io.FileInputStream::<init>({this:?}, {fd:?})");
83+
if fd.is_null() {
84+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
85+
}
8086

8187
let _: () = jvm.invoke_special(&this, "java/io/InputStream", "<init>", "()V", ()).await?;
8288

@@ -114,6 +120,9 @@ impl FileInputStream {
114120
length: i32,
115121
) -> Result<i32> {
116122
tracing::debug!("java.io.FileInputStream::read({this:?}, {buf:?}, {offset:?}, {length:?})");
123+
if buf.is_null() {
124+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
125+
}
117126

118127
let fd = jvm.get_field(&this, "java/io/FileInputStream", "fd", "Ljava/io/FileDescriptor;").await?;
119128
let mut rust_file = FileDescriptor::file(jvm, context, fd).await?;

‎rustjava-runtime/src/classes/java/io/file_output_stream.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ impl FileOutputStream {
5555
append: bool,
5656
) -> Result<()> {
5757
tracing::debug!("java.io.FileOutputStream::<init>({this:?}, {file:?}, {append})");
58+
if file.is_null() {
59+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
60+
}
5861

5962
let path = jvm.invoke_virtual(&file, "java/io/File", "getPath", "()Ljava/lang/String;", ()).await?;
6063
let path = JavaLangString::to_rust_string(jvm, &path).await?;
@@ -89,6 +92,9 @@ impl FileOutputStream {
8992
file_descriptor: ClassInstanceRef<FileDescriptor>,
9093
) -> Result<()> {
9194
tracing::debug!("java.io.FileOutputStream::<init>({this:?}, {file_descriptor:?})");
95+
if file_descriptor.is_null() {
96+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
97+
}
9298

9399
let _: () = jvm.invoke_special(&this, "java/io/OutputStream", "<init>", "()V", ()).await?;
94100

@@ -108,6 +114,9 @@ impl FileOutputStream {
108114
length: i32,
109115
) -> Result<()> {
110116
tracing::debug!("java.io.FileOutputStream::write({this:?}, {buffer:?}, {offset:?}, {length:?})");
117+
if buffer.is_null() {
118+
return Err(jvm.exception("java/lang/NullPointerException", "argument is null").await);
119+
}
111120

112121
let fd = jvm.get_field(&this, "java/io/FileOutputStream", "fd", "Ljava/io/FileDescriptor;").await?;
113122
let mut file = FileDescriptor::file(jvm, context, fd).await?;

0 commit comments

Comments
 (0)