Explorar o código

Improved File class. Added initial support for gc callback refactoring.

Marco Bambini %!s(int64=4) %!d(string=hai) anos
pai
achega
f37782a9f3

+ 171 - 47
src/optionals/gravity_opt_file.c

@@ -17,45 +17,42 @@
 static gravity_class_t              *gravity_class_file = NULL;
 static uint32_t                     refcount = 0;
 
-// MARK: - Implementation -
+// MARK: Instance -
+
+typedef struct {
+    gravity_class_t         *isa;   // to be an object
+    gravity_gc_t            gc;     // to be collectable by the garbage collector
+    FILE                    *file;  // real FILE instance
+} gravity_file_t;
+
+#define VALUE_AS_FILE(x)    ((gravity_file_t *)VALUE_AS_OBJECT(x))
+
+static uint32_t gravity_ifile_free (gravity_vm *vm, gravity_object_t *object) {
+    UNUSED_PARAM(vm);
+    
+    gravity_file_t *instance = (gravity_file_t *)object;
+    DEBUG_FREE("FREE %s", gravity_object_debug(object, true));
+    if (instance->file) fclose(instance->file);
+    mem_free((void *)object);
+    
+    return 0;
+}
+
+static gravity_file_t *gravity_ifile_new (gravity_vm *vm, FILE *f) {
+    if (!f) return NULL;
+    
+    gravity_file_t *instance = (gravity_file_t *)mem_alloc(NULL, sizeof(gravity_file_t));
+    if (!instance) return NULL;
+    
+    instance->isa = gravity_class_file;
+    instance->gc.free = gravity_ifile_free;
+    instance->file = f;
 
-/*
-     GRAVITY EXAMPLE
-     ==============
-    
-     func main() {
-         var target_file = "FULL_PATH_TO_A_TEXT_FILE_HERE";
-         var target_folder = "FULL_PATH_TO_A_FOLDER_HERE";
- 
-         // FILE TEST
-         var size = File.size(target_file);
-         var exists = File.exists(target_file);
-         var is_dir = File.is_directory(target_file);
-         var data = File.read(target_file);
-         
-         System.print("File: " + target_file);
-         System.print("Size: " + size);
-         System.print("Exists: " + exists);
-         System.print("Is Directory: " + is_dir);
-         System.print("Data: " + data);
-         
-         // FOLDER TEST
-         func closure (file_name, full_path, is_directory) {
-             if (is_directory) {
-                 System.print("+ \(file_name)");
-             } else {
-                 System.print("    \(file_name)");
-             }
-         }
-         
-         var recursive = true;
-         var n = File.directory_scan(target_folder, recursive, closure);
-         
-         // return the number of file processed
-         return n;
-     }
- 
- */
+    if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
+    return instance;
+}
+
+// MARK: - Implementation -
 
 static bool internal_file_size (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     // 1 parameter of type string is required
@@ -237,25 +234,143 @@ static bool internal_file_directory_scan (gravity_vm *vm, gravity_value_t *args,
     // do not report directory name in the first scan
     scan_directory(vm, path, recursive, closure, &n, false);
     
+    RETURN_VALUE(VALUE_FROM_INT(n), rindex);
+}
+
+// MARK: -
+
+static bool internal_file_open (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var file = File.open("path_to_file", "mode")
+    
     /*
-     func closure (var filename, var full_path) {
-        Console.write(filename);
-     }
-     
-     var skipdot = true;
-     var recursive = true;
-     File.directory_scan("/Users/marco/Desktop/", true, recursive, closure);
+     mode is a string:
+        r or rb: Open file for reading.
+        w or wb: Truncate to zero length or create file for writing.
+        a or ab: Append; open or create file for writing at end-of-file.
+        r+ or rb+ or r+b: Open file for update (reading and writing).
+        w+ or wb+ or w+b: Truncate to zero length or create file for update.
+        a+ or ab+ or a+b: Append; open or create file for update, writing at end-of-file.
      */
     
-    RETURN_VALUE(VALUE_FROM_INT(n), rindex);
+    // 1 parameter of type string is required
+    if (nargs > 1 && !VALUE_ISA_STRING(args[1])) {
+        RETURN_ERROR("A path parameter of type String is required.");
+    }
+    char *path = VALUE_AS_STRING(args[1])->s;
+    
+    char *mode = "r";
+    if (nargs > 2 && VALUE_ISA_STRING(args[2])) {
+        mode = VALUE_AS_STRING(args[2])->s;
+    }
+    
+    FILE *file = fopen(path, mode);
+    if (file == NULL) {
+        RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    }
+    
+    gravity_file_t *instance = gravity_ifile_new(vm, file);
+    if (instance == NULL) {
+        RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    }
+    
+    RETURN_VALUE(VALUE_FROM_OBJECT((gravity_object_t*) instance), rindex);
+}
+
+static bool internal_file_iread (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var data = file.read(N)
+    
+    // 1 parameter of type int is required
+    if (nargs < 1 && !VALUE_ISA_INT(args[1])) {
+        RETURN_ERROR("A path parameter of type Int is required.");
+    }
+    
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    gravity_int_t n = VALUE_AS_INT(args[1]);
+    
+    char *b = (char *)mem_alloc(NULL, n);
+    if (!b) {
+        RETURN_ERROR("Not enought memory to allocate required buffer.");
+    }
+    
+    size_t nread = fread(b, (size_t)n, 1, instance->file);
+    gravity_string_t *result = gravity_string_new(vm, b, (uint32_t)nread, (uint32_t)n);
+    
+    RETURN_VALUE(VALUE_FROM_OBJECT(result), rindex);
+}
+
+static bool internal_file_iwrite (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var written = file.write(data)
+    
+    // 1 parameter of type int is required
+    if (nargs < 1 && !VALUE_ISA_STRING(args[1])) {
+        RETURN_ERROR("A parameter of type String is required.");
+    }
+    
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    gravity_string_t *data = VALUE_AS_STRING(args[1]);
+    
+    size_t nwritten = fwrite(data->s, data->len, 1, instance->file);
+    RETURN_VALUE(VALUE_FROM_INT(nwritten), rindex);
+}
+
+static bool internal_file_iseek (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var result = file.seek(offset, whence)
+    
+    // 2 parameters of type int are required
+    if (nargs != 3 && !VALUE_ISA_INT(args[1]) && !VALUE_ISA_INT(args[2])) {
+        RETURN_ERROR("An offset parameter of type Int and a whence parameter of type Int are required.");
+    }
+    
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    gravity_int_t offset = VALUE_AS_INT(args[1]);
+    gravity_int_t whence = VALUE_AS_INT(args[2]);
+    
+    int result = fseek(instance->file, (long)offset, (int)whence);
+    RETURN_VALUE(VALUE_FROM_INT(result), rindex);
 }
 
+static bool internal_file_ierror (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var error = file.error();
+    
+    UNUSED_PARAM(nargs);
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    int result = ferror(instance->file);
+    RETURN_VALUE(VALUE_FROM_INT(result), rindex);
+}
+
+static bool internal_file_iflush (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // var error = file.flush();
+    
+    UNUSED_PARAM(nargs);
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    int result = fflush(instance->file);
+    RETURN_VALUE(VALUE_FROM_INT(result), rindex);
+}
+
+static bool internal_file_iclose (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    UNUSED_PARAM(nargs);
+    
+    // var bool = file.close()
+    gravity_file_t *instance = VALUE_AS_FILE(args[0]);
+    
+    bool result = false;
+    if (instance->file) {
+        fclose(instance->file);
+        instance->file = NULL;
+        result = true;
+    }
+    
+    RETURN_VALUE(VALUE_FROM_BOOL(result), rindex);
+}
+
+
 // MARK: - Internals -
 
 static void create_optional_class (void) {
     gravity_class_file = gravity_class_new_pair(NULL, GRAVITY_CLASS_FILE_NAME, NULL, 0, 0);
     gravity_class_t *meta = gravity_class_get_meta(gravity_class_file);
-
+    
+    // class methods
     gravity_class_bind(meta, "size", NEW_CLOSURE_VALUE(internal_file_size));
     gravity_class_bind(meta, "exists", NEW_CLOSURE_VALUE(internal_file_exists));
     gravity_class_bind(meta, "delete", NEW_CLOSURE_VALUE(internal_file_delete));
@@ -265,6 +380,15 @@ static void create_optional_class (void) {
     gravity_class_bind(meta, "is_directory", NEW_CLOSURE_VALUE(internal_file_is_directory));
     gravity_class_bind(meta, "directory_create", NEW_CLOSURE_VALUE(internal_file_directory_create));
     gravity_class_bind(meta, "directory_scan", NEW_CLOSURE_VALUE(internal_file_directory_scan));
+    
+    // instance methods
+    gravity_class_bind(meta, "open", NEW_CLOSURE_VALUE(internal_file_open));
+    gravity_class_bind(gravity_class_file, "read", NEW_CLOSURE_VALUE(internal_file_iread));
+    gravity_class_bind(gravity_class_file, "write", NEW_CLOSURE_VALUE(internal_file_iwrite));
+    gravity_class_bind(gravity_class_file, "seek", NEW_CLOSURE_VALUE(internal_file_iseek));
+    gravity_class_bind(gravity_class_file, "error", NEW_CLOSURE_VALUE(internal_file_ierror));
+    gravity_class_bind(gravity_class_file, "flush", NEW_CLOSURE_VALUE(internal_file_iflush));
+    gravity_class_bind(gravity_class_file, "close", NEW_CLOSURE_VALUE(internal_file_iclose));
 
     SETMETA_INITED(gravity_class_file);
 }

+ 7 - 4
src/shared/gravity_value.c

@@ -1579,8 +1579,9 @@ const char *gravity_object_debug (gravity_object_t *obj, bool is_free) {
 
 void gravity_object_free (gravity_vm *vm, gravity_object_t *obj) {
     if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
-
-    if (OBJECT_ISA_CLASS(obj)) gravity_class_free(vm, (gravity_class_t *)obj);
+    
+    if (obj->gc.free) obj->gc.free(vm, obj);
+    else if (OBJECT_ISA_CLASS(obj)) gravity_class_free(vm, (gravity_class_t *)obj);
     else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_free(vm, (gravity_function_t *)obj);
     else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_free(vm, (gravity_closure_t *)obj);
     else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_free(vm, (gravity_instance_t *)obj);
@@ -1600,7 +1601,8 @@ uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
     // check if object has already been visited (to avoid infinite loop)
     if (obj->gc.visited) return 0;
     
-    if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
+    if (obj->gc.size) return obj->gc.size(vm, obj);
+    else if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
     else if (OBJECT_ISA_FUNCTION(obj)) return gravity_function_size(vm, (gravity_function_t *)obj);
     else if (OBJECT_ISA_CLOSURE(obj)) return gravity_closure_size(vm, (gravity_closure_t *)obj);
     else if (OBJECT_ISA_INSTANCE(obj)) return gravity_instance_size(vm, (gravity_instance_t *)obj);
@@ -1617,7 +1619,8 @@ uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
 void gravity_object_blacken (gravity_vm *vm, gravity_object_t *obj) {
     if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
 
-    if (OBJECT_ISA_CLASS(obj)) gravity_class_blacken(vm, (gravity_class_t *)obj);
+    if (obj->gc.blacken) obj->gc.blacken(vm, obj);
+    else if (OBJECT_ISA_CLASS(obj)) gravity_class_blacken(vm, (gravity_class_t *)obj);
     else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_blacken(vm, (gravity_function_t *)obj);
     else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_blacken(vm, (gravity_closure_t *)obj);
     else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_blacken(vm, (gravity_instance_t *)obj);

+ 6 - 2
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VERSION						"0.8.0"     // git tag 0.8.0
-#define GRAVITY_VERSION_NUMBER				0x000800    // git push --tags
+#define GRAVITY_VERSION						"0.8.1"     // git tag 0.8.0
+#define GRAVITY_VERSION_NUMBER				0x000801    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 #ifndef GRAVITY_ENABLE_DOUBLE
@@ -227,6 +227,7 @@ typedef struct gravity_vm                gravity_vm;        // vm is an opaque d
 #endif
 
 typedef bool (*gravity_c_internal)(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex);
+typedef uint32_t (*gravity_gc_callback)(gravity_vm *vm, gravity_object_t *obj);
 
 typedef enum {
     EXEC_TYPE_SPECIAL_GETTER = 0,       // index inside special gravity_function_t union to represent getter func
@@ -243,6 +244,9 @@ typedef enum {
 typedef struct gravity_gc_s {
     bool                    isdark;         // flag to check if object is reachable
     bool                    visited;        // flag to check if object has already been counted in memory size
+    gravity_gc_callback     free;           // free callback
+    gravity_gc_callback     size;           // size callback
+    gravity_gc_callback     blacken;        // blacken callback
     gravity_object_t        *next;          // to track next object in the linked list
 } gravity_gc_t;
 

+ 1 - 1
src/utils/gravity_utils.c

@@ -92,7 +92,7 @@ double millitime (nanotime_t tstart, nanotime_t tend) {
 
 int64_t file_size (const char *path) {
     #ifdef WIN32
-    WIN32_FILE_ATTRIBUTE_DATA   fileInfo;
+    WIN32_FILE_ATTRIBUTE_DATA fileInfo;
     if (GetFileAttributesExA(path, GetFileExInfoStandard, (void*)&fileInfo) == 0) return -1;
     return (int64_t)(((__int64)fileInfo.nFileSizeHigh) << 32 ) + fileInfo.nFileSizeLow;
     #else