diff --git a/project/xcode/wren.xcodeproj/project.pbxproj b/project/xcode/wren.xcodeproj/project.pbxproj index 678282b8..7010e04c 100644 --- a/project/xcode/wren.xcodeproj/project.pbxproj +++ b/project/xcode/wren.xcodeproj/project.pbxproj @@ -22,6 +22,10 @@ 29205C9F1AB4E6430073018D /* wren_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C981AB4E6430073018D /* wren_vm.c */; }; 29512C811B91F8EB008C10E6 /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29512C801B91F8EB008C10E6 /* libuv.a */; }; 29512C821B91F901008C10E6 /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29512C801B91F8EB008C10E6 /* libuv.a */; }; + 29729F311BA70A620099CA20 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29729F2E1BA70A620099CA20 /* io.c */; }; + 29729F321BA70A620099CA20 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29729F2E1BA70A620099CA20 /* io.c */; }; + 29729F331BA70A620099CA20 /* io.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29729F301BA70A620099CA20 /* io.wren.inc */; }; + 29729F341BA70A620099CA20 /* io.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29729F301BA70A620099CA20 /* io.wren.inc */; }; 2986F6D71ACF93BA00BCE26C /* wren_primitive.c in Sources */ = {isa = PBXBuildFile; fileRef = 2986F6D51ACF93BA00BCE26C /* wren_primitive.c */; }; 29C8A9331AB71FFF00DEC81D /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A9311AB71FFF00DEC81D /* vm.c */; }; 29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 29DE39511AC3A50A00987D41 /* wren_meta.c */; }; @@ -75,6 +79,9 @@ 29512C7F1B91F86E008C10E6 /* api_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = api_test; sourceTree = BUILT_PRODUCTS_DIR; }; 29512C801B91F8EB008C10E6 /* libuv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libuv.a; path = ../../build/libuv.a; sourceTree = ""; }; 296371B31AC713D000079FDA /* wren_opcodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_opcodes.h; path = ../../src/vm/wren_opcodes.h; sourceTree = ""; }; + 29729F2E1BA70A620099CA20 /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io.c; path = ../../src/module/io.c; sourceTree = ""; }; + 29729F2F1BA70A620099CA20 /* io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../src/module/io.h; sourceTree = ""; }; + 29729F301BA70A620099CA20 /* io.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = io.wren.inc; path = ../../src/module/io.wren.inc; sourceTree = ""; }; 2986F6D51ACF93BA00BCE26C /* wren_primitive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wren_primitive.c; path = ../../src/vm/wren_primitive.c; sourceTree = ""; }; 2986F6D61ACF93BA00BCE26C /* wren_primitive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_primitive.h; path = ../../src/vm/wren_primitive.h; sourceTree = ""; }; 29AB1F061816E3AD004B501E /* wren */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wren; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -114,6 +121,9 @@ 2901D7611B74F3E20083A2C8 /* module */ = { isa = PBXGroup; children = ( + 29729F2F1BA70A620099CA20 /* io.h */, + 29729F2E1BA70A620099CA20 /* io.c */, + 29729F301BA70A620099CA20 /* io.wren.inc */, 291647C31BA5EA45006142EE /* scheduler.h */, 291647C21BA5EA45006142EE /* scheduler.c */, 291647CD1BA5ED26006142EE /* scheduler.wren.inc */, @@ -284,10 +294,12 @@ 291647C71BA5EC5E006142EE /* modules.c in Sources */, 29205C9A1AB4E6430073018D /* wren_core.c in Sources */, 2901D7641B74F4050083A2C8 /* timer.c in Sources */, + 29729F331BA70A620099CA20 /* io.wren.inc in Sources */, 29C8A9331AB71FFF00DEC81D /* vm.c in Sources */, 291647C41BA5EA45006142EE /* scheduler.c in Sources */, 29205C9B1AB4E6430073018D /* wren_debug.c in Sources */, 29205C9D1AB4E6430073018D /* wren_utils.c in Sources */, + 29729F311BA70A620099CA20 /* io.c in Sources */, 29205C9E1AB4E6430073018D /* wren_value.c in Sources */, 29205C9F1AB4E6430073018D /* wren_vm.c in Sources */, 29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */, @@ -299,7 +311,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 29729F341BA70A620099CA20 /* io.wren.inc in Sources */, 291647C81BA5EC5E006142EE /* modules.c in Sources */, + 29729F321BA70A620099CA20 /* io.c in Sources */, 291647D21BA5ED26006142EE /* timer.wren.inc in Sources */, 291647D01BA5ED26006142EE /* scheduler.wren.inc in Sources */, ); diff --git a/src/cli/modules.c b/src/cli/modules.c index 7f3bca56..af632fba 100644 --- a/src/cli/modules.c +++ b/src/cli/modules.c @@ -3,9 +3,11 @@ #include "modules.h" +#include "io.wren.inc" #include "scheduler.wren.inc" #include "timer.wren.inc" +#include "io.h" #include "scheduler.h" #include "timer.h" @@ -30,8 +32,9 @@ typedef struct // The array of built-in modules. static BuiltInModule modules[] = { + {"io", &ioModuleSource, ioBindForeign, NULL}, {"scheduler", &schedulerModuleSource, schedulerBindForeign, NULL}, - {"timer", &timerModuleSource, timerBindForeign, NULL}, + {"timer", &timerModuleSource, timerBindForeign, NULL}, // Sentinel marking the end of the list. {NULL, NULL, NULL, NULL} diff --git a/src/cli/vm.c b/src/cli/vm.c index d9a519da..64dcee5f 100644 --- a/src/cli/vm.c +++ b/src/cli/vm.c @@ -2,8 +2,8 @@ #include #include "modules.h" -#include "vm.h" #include "scheduler.h" +#include "vm.h" #define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary. diff --git a/src/module/io.c b/src/module/io.c new file mode 100644 index 00000000..9dd12ddf --- /dev/null +++ b/src/module/io.c @@ -0,0 +1,51 @@ +#include +#include + +#include "uv.h" + +#include "io.h" +#include "scheduler.h" +#include "vm.h" +#include "wren.h" + +#include + +// Called by libuv when the stat call for size completes. +static void sizeCallback(uv_fs_t* request) +{ + WrenValue* fiber = (WrenValue*)request->data; + + if (request->result != 0) + { + schedulerResumeString(fiber, uv_strerror((int)request->result)); + uv_fs_req_cleanup(request); + return; + } + + double size = (double)request->statbuf.st_size; + uv_fs_req_cleanup(request); + + schedulerResumeDouble(fiber, size); +} + +static void startSize(WrenVM* vm) +{ + const char* path = wrenGetArgumentString(vm, 1); + WrenValue* fiber = wrenGetArgumentValue(vm, 2); + + // Store the fiber to resume when the request completes. + uv_fs_t* request = (uv_fs_t*)malloc(sizeof(uv_fs_t)); + request->data = fiber; + + uv_fs_stat(getLoop(), request, path, sizeCallback); +} + +WrenForeignMethodFn ioBindForeign( + WrenVM* vm, const char* className, bool isStatic, const char* signature) +{ + if (strcmp(className, "File") != 0) return NULL; + + if (isStatic && strcmp(signature, "startSize_(_,_)") == 0) return startSize; + + return NULL; +} diff --git a/src/module/io.h b/src/module/io.h new file mode 100644 index 00000000..07f0aab1 --- /dev/null +++ b/src/module/io.h @@ -0,0 +1,9 @@ +#ifndef io_h +#define io_h + +#include "wren.h" + +WrenForeignMethodFn ioBindForeign( + WrenVM* vm, const char* className, bool isStatic, const char* signature); + +#endif diff --git a/src/module/io.wren b/src/module/io.wren new file mode 100644 index 00000000..a44c2d25 --- /dev/null +++ b/src/module/io.wren @@ -0,0 +1,15 @@ +import "scheduler" for Scheduler + +class File { + static size(path) { + if (!(path is String)) Fiber.abort("Path must be a string.") + + startSize_(path, Fiber.current) + var result = Scheduler.runNextScheduled_() + if (result is String) Fiber.abort(result) + + return result + } + + foreign static startSize_(path, fiber) +} diff --git a/src/module/io.wren.inc b/src/module/io.wren.inc new file mode 100644 index 00000000..91f91f2f --- /dev/null +++ b/src/module/io.wren.inc @@ -0,0 +1,17 @@ +// Generated automatically from src/module/io.wren. Do not edit. +static const char* ioModuleSource = +"import \"scheduler\" for Scheduler\n" +"\n" +"class File {\n" +" static size(path) {\n" +" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n" +"\n" +" startSize_(path, Fiber.current)\n" +" var result = Scheduler.runNextScheduled_()\n" +" if (result is String) Fiber.abort(result)\n" +"\n" +" return result\n" +" }\n" +"\n" +" foreign static startSize_(path, fiber)\n" +"}\n"; diff --git a/src/module/scheduler.c b/src/module/scheduler.c index 66fa418c..62b3e588 100644 --- a/src/module/scheduler.c +++ b/src/module/scheduler.c @@ -35,6 +35,18 @@ void schedulerResume(WrenValue* fiber) wrenReleaseValue(getVM(), fiber); } +void schedulerResumeDouble(WrenValue* fiber, double value) +{ + wrenCall(getVM(), resumeWithArg, NULL, "vd", fiber, value); + wrenReleaseValue(getVM(), fiber); +} + +void schedulerResumeString(WrenValue* fiber, const char* text) +{ + wrenCall(getVM(), resumeWithArg, NULL, "vs", fiber, text); + wrenReleaseValue(getVM(), fiber); +} + void schedulerReleaseMethods() { if (resume != NULL) wrenReleaseValue(getVM(), resume); diff --git a/src/module/scheduler.h b/src/module/scheduler.h index a083f2b4..a13b4b7d 100644 --- a/src/module/scheduler.h +++ b/src/module/scheduler.h @@ -7,6 +7,8 @@ WrenForeignMethodFn schedulerBindForeign( WrenVM* vm, const char* className, bool isStatic, const char* signature); void schedulerResume(WrenValue* fiber); +void schedulerResumeDouble(WrenValue* fiber, double value); +void schedulerResumeString(WrenValue* fiber, const char* text); void schedulerReleaseMethods(); diff --git a/test/io/file/size.wren b/test/io/file/size.wren new file mode 100644 index 00000000..b1a1c547 --- /dev/null +++ b/test/io/file/size.wren @@ -0,0 +1,18 @@ +import "io" for File +import "scheduler" for Scheduler + +System.print(File.size("test/io/file/size.wren")) // expect: 401 + +// Runs asynchronously. +Scheduler.add { + System.print("async") +} + +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