Преглед изворни кода

Several fixes to the optional ENV class. Unit test added.

Marco Bambini пре 6 година
родитељ
комит
87ccab0e8e

+ 5 - 0
src/compiler/gravity_parser.c

@@ -2506,6 +2506,11 @@ static void parser_register_optional_classes (gravity_parser_t *parser) {
     gnode_t *decl = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_MATH_NAME()), NULL, 0, NULL, LAST_DECLARATION());
     gnode_array_push(decls, decl);
     #endif
+    
+    #ifdef GRAVITY_INCLUDE_ENV
+    gnode_t *decl2 = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_ENV_NAME()), NULL, 0, NULL, LAST_DECLARATION());
+    gnode_array_push(decls, decl2);
+    #endif
 
     // check if optional classes callback is registered
     if (parser->delegate && parser->delegate->optional_classes) {

+ 78 - 58
src/optionals/gravity_env.c

@@ -21,26 +21,28 @@
 
 #define ENV_CLASS_NAME "ENV"
 
+static gravity_class_t              *gravity_class_env = NULL;
+static uint32_t                     refcount = 0;
+
 /**
  * Wraps `getenv()` to be used with Gravity.
- * 
- * @param 
+ *
  */
-bool gravity_env_get(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+static bool gravity_env_get(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(nargs)
+    
     if(!VALUE_ISA_STRING(args[1])) {
         RETURN_ERROR("Environment variable key must be a string.");
     }
 
-    char* key = VALUE_AS_CSTRING(args[1]);
-    char* value = getenv(key);
-    gravity_value_t rt;
+    char *key = VALUE_AS_CSTRING(args[1]);
+    char *value = getenv(key);
+    gravity_value_t rt = VALUE_FROM_UNDEFINED;
 
     GRAVITY_DEBUG_PRINT("[ENV::GET args : %i] %s => %s\n", nargs, key, value);
 
-    if (value == NULL) {
-        rt = VALUE_FROM_UNDEFINED;
-    } else {
-        rt = VALUE_FROM_STRING(vm, value, strlen(value));
+    if (value) {
+        rt = VALUE_FROM_STRING(vm, value, (uint32_t)strlen(value));
     }
 
     RETURN_VALUE(rt, rindex);
@@ -48,74 +50,92 @@ bool gravity_env_get(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint
 
 /**
  * @brief  Wraps putenv() into a Gravity callable function
- * @param  *vm: The Gravity Virtual Maschine this function is associated with.
- * @param  *args: List of arguments passed to this function
- * @param  nargs: Number of arguments passed to this function
- * @param  rindex: Slot-index for the return value to be stored in.
+ * @param  vm The Gravity Virtual Maschine this function is associated with.
+ * @param  args List of arguments passed to this function
+ * @param  nargs Number of arguments passed to this function
+ * @param  rindex Slot-index for the return value to be stored in.
  * @retval  Weather this function was successful or not.
  */
-bool gravity_env_set(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+static bool gravity_env_set(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(nargs)
+    
     if(!VALUE_ISA_STRING(args[1]) || !VALUE_ISA_STRING(args[2])) {
         RETURN_ERROR("Environment variable key and value must both be strings.");
     }
 
-    gravity_string_t *key_var, *value_var;
-    key_var = VALUE_AS_STRING(args[1]);
-    value_var = VALUE_AS_STRING(args[2]);
-
-    uint32_t len = key_var->alloc + value_var->alloc + 1;
-    char *buf = (char *)mem_alloc(vm, len);
-    snprintf(buf, len, "%s=%s", key_var->s, value_var->s);
+    gravity_string_t *key = VALUE_AS_STRING(args[1]);
+    gravity_string_t *value = VALUE_AS_STRING(args[2]);
 
-    GRAVITY_DEBUG_PRINT(
-        "[ENV::SET args : %i] (%.*s) \"%.*s\" => \"%.*s\"\n",
-        nargs, (int)len, buf,
-        key_var->len, key_var->s,
-        value_var->len, value_var->s
-    );
-
-    int rt = putenv(buf);
-    mem_free(buf);
+    GRAVITY_DEBUG_PRINT("[ENV::SET args : %i] %s => %s\n", nargs, key, value);
 
+    int rt = setenv(key->s, value->s, 1);
     RETURN_VALUE(VALUE_FROM_INT(rt), rindex);
 }
 
-bool gravity_env_keys(gravity_vm *vm, gravity_value_t *args, uint16_t nparams, uint32_t rindex) {
+static bool gravity_env_keys(gravity_vm *vm, gravity_value_t *args, uint16_t nparams, uint32_t rindex) {
+    #pragma unused(args, nparams)
+    
     extern char **environ;
-    char *evar = *environ;
-    gravity_list_t *keys = gravity_list_new(vm, 1);
-
-    for (int i = 1; evar != NULL; i++) {
-        char key[strlen(evar)];
-        uint16_t len;
-        for (len = 0; evar[len] != '='; len++) {
-            key[len] = evar[len];
+    gravity_list_t *keys = gravity_list_new(vm, 16);
+    
+    for (char **env = environ; *env; ++env) {
+        char *entry = *env;
+        
+        // env is in the form key=value
+        uint32_t len = 0;
+        for (uint32_t i=0; entry[len]; ++i, ++len) {
+            if (entry[i] == '=') break;
         }
-        marray_push(
-            gravity_value_t, keys->array,
-            VALUE_FROM_STRING(vm, key, len)
-        );
-        evar = *(environ + i);
+        gravity_value_t key = VALUE_FROM_STRING(vm, entry, len);
+        marray_push(gravity_value_t, keys->array, key);
     }
 
     RETURN_VALUE(VALUE_FROM_OBJECT(keys), rindex);
 }
 
-void gravity_env_register(gravity_vm *vm) {
-    gravity_class_t *c = gravity_class_new_pair(vm, ENV_CLASS_NAME, NULL, 0, 0);
-    gravity_class_t *m = gravity_class_get_meta(c);
+// MARK: - Internals -
 
+static void create_optional_class (void) {
+    gravity_class_env = gravity_class_new_pair(NULL, ENV_CLASS_NAME, NULL, 0, 0);
+    gravity_class_t *meta = gravity_class_get_meta(gravity_class_env);
+    
     // .get(key) and .set(key, value)
-    gravity_class_bind(m, "get", NEW_CLOSURE_VALUE(gravity_env_get));
-    gravity_class_bind(m, "set", NEW_CLOSURE_VALUE(gravity_env_set));
-    gravity_class_bind(m, "keys", NEW_CLOSURE_VALUE(gravity_env_keys));
-
+    gravity_class_bind(meta, "get", NEW_CLOSURE_VALUE(gravity_env_get));
+    gravity_class_bind(meta, "set", NEW_CLOSURE_VALUE(gravity_env_set));
+    gravity_class_bind(meta, "keys", NEW_CLOSURE_VALUE(gravity_env_keys));
+    
     // Allow map-access
-    gravity_class_bind(m, GRAVITY_INTERNAL_LOADAT_NAME, NEW_CLOSURE_VALUE(gravity_env_get));
-    gravity_class_bind(m, GRAVITY_INTERNAL_STOREAT_NAME, NEW_CLOSURE_VALUE(gravity_env_set));
+    gravity_class_bind(meta, GRAVITY_INTERNAL_LOADAT_NAME, NEW_CLOSURE_VALUE(gravity_env_get));
+    gravity_class_bind(meta, GRAVITY_INTERNAL_STOREAT_NAME, NEW_CLOSURE_VALUE(gravity_env_set));
+    
+    SETMETA_INITED(gravity_class_env);
+}
+
+// MARK: - Commons -
 
-    // @TODO: add iteration support
+void gravity_env_register(gravity_vm *vm) {
+    if (!gravity_class_env) create_optional_class();
+    ++refcount;
+    
+    if (!vm || gravity_vm_ismini(vm)) return;
+    gravity_vm_setvalue(vm, ENV_CLASS_NAME, VALUE_FROM_OBJECT(gravity_class_env));
+}
+
+void gravity_env_free (void) {
+    if (!gravity_class_env) return;
+    if (--refcount) return;
+    
+    gravity_class_t *meta = gravity_class_get_meta(gravity_class_env);
+    gravity_class_free_core(NULL, meta);
+    gravity_class_free_core(NULL, gravity_class_env);
+    
+    gravity_class_env = NULL;
+}
 
-    // Install
-    gravity_vm_setvalue(vm, ENV_CLASS_NAME, VALUE_FROM_OBJECT(c));
-}
+bool gravity_isenv_class (gravity_class_t *c) {
+    return (c == gravity_class_env);
+}
+
+const char *gravity_env_name (void) {
+    return ENV_CLASS_NAME;
+}

+ 5 - 3
src/optionals/gravity_env.h

@@ -1,9 +1,11 @@
 #ifndef GRAVITY_ENV_H
 #define GRAVITY_ENV_H
 
-#include "gravity_vm.h"
 #include "gravity_value.h"
 
-GRAVITY_API void gravity_env_register(gravity_vm *vm);
+void gravity_env_register (gravity_vm *vm);
+void gravity_env_free (void);
+bool gravity_isenv_class (gravity_class_t *c);
+const char *gravity_env_name (void);
 
-#endif
+#endif

+ 6 - 4
src/optionals/gravity_optionals.h

@@ -31,14 +31,16 @@
 #endif
 
 #ifdef GRAVITY_INCLUDE_ENV
+#define GRAVITY_ENV_REGISTER(_vm)           gravity_env_register(_vm)
+#define GRAVITY_ENV_FREE()                  gravity_env_free()
+#define GRAVITY_ENV_NAME()                  gravity_env_name()
+#define GRAVITY_ISENV_CLASS(_c)             gravity_isenv_class(_c)
 #include "gravity_env.h"
-#define GRAVITY_ENV_REGISTER(_vm) gravity_env_register(_vm)
-#define GRAVITY_ENV_FREE() gravity_math_free()
-#define GRAVITY_ENV_NAME() ENV_CLASS_NAME
 #else
 #define GRAVITY_ENV_REGISTER(_vm)
 #define GRAVITY_ENV_FREE()
-#define GRAVITY_ENV_NAME() NULL
+#define GRAVITY_ENV_NAME()                  NULL
+#define GRAVITY_ISENV_CLASS(_c)             false
 #endif
 
 #endif

+ 3 - 1
src/runtime/gravity_vm.c

@@ -356,14 +356,16 @@ static void gravity_vm_loadclass (gravity_vm *vm, gravity_class_t *c) {
 
 void gravity_opt_register (gravity_vm *vm) {
     GRAVITY_MATH_REGISTER(vm);
+    GRAVITY_ENV_REGISTER(vm);
 }
 
 void gravity_opt_free() {
     GRAVITY_MATH_FREE();
+    GRAVITY_ENV_FREE();
 }
 
 bool gravity_isopt_class (gravity_class_t *c) {
-    return GRAVITY_ISMATH_CLASS(c);
+    return (GRAVITY_ISMATH_CLASS(c)) || (GRAVITY_ISENV_CLASS(c));
 }
 
 // MARK: - MAIN EXECUTION -

+ 17 - 0
test/unittest/env/env.gravity

@@ -0,0 +1,17 @@
+#unittest {
+	name: "Test ENV class";
+	error: NONE;
+	result: "barbaz";
+};
+
+func main() {
+	// set
+	ENV["foo"] = "bar";
+	ENV.set("foo2", "baz");
+	
+	// get
+	var r1 = ENV["foo"];
+	var r2 = ENV.get("foo2");
+	
+	return r1+r2;
+}