feat: implement async file I/O with open/close/readBytes and scheduler error handling
Add foreign class File with static open, read, size methods and instance methods for close, size, readBytes. Implement async file operations using libuv callbacks that resume fibers on completion. Introduce schedulerResumeError and resumeError_ to propagate I/O errors as fiber errors. Add setExitCode to CLI vm for runtime error exit handling. Bump MAX_METHODS_PER_CLASS from 2 to 9 to accommodate new File methods.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import "io" for File
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
// See also: is_open.wren.
|
||||
|
||||
var file = File.open("test/io/file/close.wren")
|
||||
|
||||
System.print(file.close()) // expect: null
|
||||
|
||||
// Can call multiple times.
|
||||
file.close()
|
||||
|
||||
// If already closed, returns synchronously.
|
||||
Scheduler.add {
|
||||
System.print("does not print")
|
||||
}
|
||||
|
||||
file.close()
|
||||
System.print("sync") // expect: sync
|
||||
@@ -0,0 +1,11 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/descriptor.wren")
|
||||
|
||||
// We can't test for a specific value since it's up to the OS, but it should be
|
||||
// a positive number.
|
||||
System.print(file.descriptor is Num) // expect: true
|
||||
System.print(file.descriptor > 0) // expect: true
|
||||
|
||||
file.close()
|
||||
System.print(file.descriptor) // expect: -1
|
||||
@@ -0,0 +1 @@
|
||||
this is a text file
|
||||
@@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/is_open.wren")
|
||||
System.print(file.isOpen) // expect: true
|
||||
file.close()
|
||||
System.print(file.isOpen) // expect: false
|
||||
@@ -0,0 +1,5 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/open.wren")
|
||||
System.print(file is File) // expect: true
|
||||
System.print(file.isOpen) // expect: true
|
||||
@@ -0,0 +1,16 @@
|
||||
import "io" for File
|
||||
|
||||
var stash
|
||||
File.open("test/io/file/open_block.wren") {|file|
|
||||
System.print(file is File) // expect: true
|
||||
System.print(file.isOpen) // expect: true
|
||||
stash = file
|
||||
}
|
||||
|
||||
// Closes after block.
|
||||
System.print(stash.isOpen) // expect: false
|
||||
|
||||
// Returns null.
|
||||
System.print(File.open("test/io/file/open_block.wren") {|file|}) // expect: null
|
||||
|
||||
// TODO: Test a fiber aborting inside the block.
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("nonexistent") {|file|} // expect runtime error: no such file or directory
|
||||
@@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("test/io/file/open_block_wrong_block_type.wren",
|
||||
"no callable") // expect runtime error: String does not implement 'call(_)'.
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open(123) {|file|} // expect runtime error: Path must be a string.
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("nonexistent") // expect runtime error: no such file or directory
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open(123) // expect runtime error: Path must be a string.
|
||||
@@ -0,0 +1,8 @@
|
||||
import "io" for File
|
||||
|
||||
var text = File.read("test/io/file/file.txt")
|
||||
System.print(text) // expect: this is a text file
|
||||
System.print(text.count) // expect: 19
|
||||
|
||||
// TODO: A file containing line endings.
|
||||
// TODO: A file containing null bytes.
|
||||
@@ -0,0 +1,13 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
|
||||
System.print(file.readBytes(3)) // expect: thi
|
||||
|
||||
// Always reads from the beginning.
|
||||
System.print(file.readBytes(7)) // expect: this is
|
||||
|
||||
// Allows zero.
|
||||
System.print(file.readBytes(0).bytes.count) // expect: 0
|
||||
|
||||
file.close()
|
||||
@@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.close()
|
||||
|
||||
file.readBytes(3) // expect runtime error: File is not open.
|
||||
@@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes("not num") // expect runtime error: Count must be an integer.
|
||||
@@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes(-1) // expect runtime error: Count cannot be negative.
|
||||
@@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes(1.2) // expect runtime error: Count must be an integer.
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.read("nonexistent") // expect runtime error: no such file or directory
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.read(123) // expect runtime error: Path must be a string.
|
||||
@@ -1,7 +1,7 @@
|
||||
import "io" for File
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
System.print(File.size("test/io/file/size.wren")) // expect: 401
|
||||
System.print(File.size("test/io/file/size.wren")) // expect: 270
|
||||
|
||||
// Runs asynchronously.
|
||||
Scheduler.add {
|
||||
@@ -10,9 +10,4 @@ Scheduler.add {
|
||||
|
||||
System.print(File.size("test/io/file/size.wren"))
|
||||
// expect: async
|
||||
// expect: 401
|
||||
|
||||
var error = Fiber.new {
|
||||
System.print(File.size("nonexistent"))
|
||||
}.try()
|
||||
System.print(error) // expect: no such file or directory
|
||||
// expect: 270
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.close()
|
||||
|
||||
file.size // expect runtime error: File is not open.
|
||||
@@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.size("nonexistent") // expect runtime error: no such file or directory
|
||||
Reference in New Issue
Block a user