feat: add File.stat() method and Stat class for filesystem metadata queries

Implement asynchronous stat() on File class returning a new Stat object with device, inode, mode, linkCount, user, group, specialDevice, size, blockSize, and blockCount fields populated from libuv uv_fs_stat callback. Register foreign method statPath_ in module registry, bump MAX_METHODS_PER_CLASS to 10, add Stat class to io.wren with indexed field accessors, update documentation index with Stat page, and include test coverage for stat on existing file and nonexistent path.
This commit is contained in:
Bob Nystrom
2016-01-01 17:58:44 +00:00
parent 3746b56ed9
commit f112722d00
11 changed files with 195 additions and 11 deletions
+3 -1
View File
@@ -12,6 +12,7 @@ extern void fileAllocate(WrenVM* vm);
extern void fileFinalize(void* data);
extern void fileOpen(WrenVM* vm);
extern void fileSizePath(WrenVM* vm);
extern void fileStatPath(WrenVM* vm);
extern void fileClose(WrenVM* vm);
extern void fileDescriptor(WrenVM* vm);
extern void fileReadBytes(WrenVM* vm);
@@ -29,7 +30,7 @@ extern void timerStartTimer(WrenVM* vm);
// If you add a new method to the longest class below, make sure to bump this.
// Note that it also includes an extra slot for the sentinel value indicating
// the end of the list.
#define MAX_METHODS_PER_CLASS 9
#define MAX_METHODS_PER_CLASS 10
// The maximum number of foreign classes a single built-in module defines.
//
@@ -97,6 +98,7 @@ static ModuleRegistry modules[] =
FINALIZER(fileFinalize)
STATIC_METHOD("open_(_,_)", fileOpen)
STATIC_METHOD("sizePath_(_,_)", fileSizePath)
STATIC_METHOD("statPath_(_,_)", fileStatPath)
METHOD("close_(_)", fileClose)
METHOD("descriptor", fileDescriptor)
METHOD("readBytes_(_,_,_)", fileReadBytes)
+56
View File
@@ -188,6 +188,62 @@ void fileSizePath(WrenVM* vm)
uv_fs_stat(getLoop(), request, path, fileSizeCallback);
}
// Called by libuv when the stat call completes.
static void fileStatPathCallback(uv_fs_t* request)
{
if (handleRequestError(request)) return;
WrenVM* vm = getVM();
wrenEnsureSlots(vm, 4);
wrenSetSlotNewList(vm, 2);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_dev);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_ino);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_mode);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_nlink);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_uid);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_gid);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_rdev);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_size);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_blksize);
wrenInsertInList(vm, 2, -1, 3);
wrenSetSlotDouble(vm, 3, (double)request->statbuf.st_blocks);
wrenInsertInList(vm, 2, -1, 3);
// TODO: Include access, modification, and change times once we figure out
// how we want to represent it.
// time_t st_atime; /* time of last access */
// time_t st_mtime; /* time of last modification */
// time_t st_ctime; /* time of last status change */
schedulerResume(freeRequest(request), true);
schedulerFinishResume();
}
void fileStatPath(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
uv_fs_stat(getLoop(), request, path, fileStatPathCallback);
}
static void fileCloseCallback(uv_fs_t* request)
{
if (handleRequestError(request)) return;
+25
View File
@@ -44,6 +44,13 @@ foreign class File {
return Scheduler.runNextScheduled_()
}
static stat(path) {
if (!(path is String)) Fiber.abort("Path must be a string.")
statPath_(path, Fiber.current)
return Stat.new_(Scheduler.runNextScheduled_())
}
construct new_(fd) {}
close() {
@@ -80,12 +87,30 @@ foreign class File {
foreign static open_(path, fiber)
foreign static sizePath_(path, fiber)
foreign static statPath_(path, fiber)
foreign close_(fiber)
foreign readBytes_(count, start, fiber)
foreign size_(fiber)
}
class Stat {
construct new_(fields) {
_fields = fields
}
device { _fields[0] }
inode { _fields[1] }
mode { _fields[2] }
linkCount { _fields[3] }
user { _fields[4] }
group { _fields[5] }
specialDevice { _fields[6] }
size { _fields[7] }
blockSize { _fields[8] }
blockCount { _fields[9] }
}
class Stdin {
static readLine() {
if (__isClosed == true) {
+25
View File
@@ -46,6 +46,13 @@ static const char* ioModuleSource =
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" static stat(path) {\n"
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
"\n"
" statPath_(path, Fiber.current)\n"
" return Stat.new_(Scheduler.runNextScheduled_())\n"
" }\n"
"\n"
" construct new_(fd) {}\n"
"\n"
" close() {\n"
@@ -82,12 +89,30 @@ static const char* ioModuleSource =
"\n"
" foreign static open_(path, fiber)\n"
" foreign static sizePath_(path, fiber)\n"
" foreign static statPath_(path, fiber)\n"
"\n"
" foreign close_(fiber)\n"
" foreign readBytes_(count, start, fiber)\n"
" foreign size_(fiber)\n"
"}\n"
"\n"
"class Stat {\n"
" construct new_(fields) {\n"
" _fields = fields\n"
" }\n"
"\n"
" device { _fields[0] }\n"
" inode { _fields[1] }\n"
" mode { _fields[2] }\n"
" linkCount { _fields[3] }\n"
" user { _fields[4] }\n"
" group { _fields[5] }\n"
" specialDevice { _fields[6] }\n"
" size { _fields[7] }\n"
" blockSize { _fields[8] }\n"
" blockCount { _fields[9] }\n"
"}\n"
"\n"
"class Stdin {\n"
" static readLine() {\n"
" if (__isClosed == true) {\n"