Эх сурвалжийг харах

ncd: Implement object reference infrastructure and objref module.

Ambroz Bizjak 11 жил өмнө
parent
commit
462ed77f14

+ 1 - 0
blog_channels.txt

@@ -144,3 +144,4 @@ BThreadSignal 4
 BLockReactor 4
 ncd_load_module 4
 ncd_basic_functions 4
+ncd_objref 4

+ 4 - 0
generated/blog_channel_ncd_objref.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_objref

+ 2 - 1
generated/blog_channels_defines.h

@@ -144,4 +144,5 @@
 #define BLOG_CHANNEL_BLockReactor 143
 #define BLOG_CHANNEL_ncd_load_module 144
 #define BLOG_CHANNEL_ncd_basic_functions 145
-#define BLOG_NUM_CHANNELS 146
+#define BLOG_CHANNEL_ncd_objref 146
+#define BLOG_NUM_CHANNELS 147

+ 1 - 0
generated/blog_channels_list.h

@@ -144,3 +144,4 @@
 {"BLockReactor", 4},
 {"ncd_load_module", 4},
 {"ncd_basic_functions", 4},
+{"ncd_objref", 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -148,6 +148,7 @@ set(NCDINTERPRETER_SOURCES
     modules/log.c
     modules/getenv.c
     modules/basic_functions.c
+    modules/objref.c
     ${NCD_ADDITIONAL_SOURCES}
 )
 set(NCDINTERPRETER_LIBS

+ 19 - 2
ncd/NCDModule.c

@@ -33,6 +33,7 @@
 #include <inttypes.h>
 #include <limits.h>
 
+#include <misc/offset.h>
 #include <ncd/NCDModule.h>
 #include <ncd/static_strings.h>
 
@@ -73,6 +74,13 @@ static void set_process_state (NCDModuleProcess *p, int state)
 #endif
 }
 
+static NCDObject persistent_obj_retobj (NCDPersistentObj const *pobj)
+{
+    NCDModuleInst *n = UPPER_OBJECT(pobj, NCDModuleInst, pobj);
+    
+    return NCDModuleInst_Object(n);
+}
+
 void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void *method_context, NCDValRef args, const struct NCDModuleInst_params *params)
 {
     ASSERT(m)
@@ -102,6 +110,9 @@ void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void
     // give NCDModuleInst to methods, not mem
     n->pass_mem_to_methods = 0;
     
+    // init persistent object interface
+    NCDPersistentObj_Init(&n->pobj, persistent_obj_retobj);
+    
     DebugObject_Init(&n->d_obj);
     
     struct NCDModuleInst_new_params new_params;
@@ -115,6 +126,9 @@ void NCDModuleInst_Free (NCDModuleInst *n)
 {
     DebugObject_Free(&n->d_obj);
     ASSERT(n->state == STATE_DEAD)
+    
+    // free persistent object interface
+    NCDPersistentObj_Free(&n->pobj);
 }
 
 void NCDModuleInst_Die (NCDModuleInst *n)
@@ -144,6 +158,9 @@ int NCDModuleInst_TryFree (NCDModuleInst *n)
     
     DebugObject_Free(&n->d_obj);
     
+    // free persistent object interface
+    NCDPersistentObj_Free(&n->pobj);
+    
     return 1;
 }
 
@@ -169,7 +186,7 @@ NCDObject NCDModuleInst_Object (NCDModuleInst *n)
     
     void *method_user = (n->pass_mem_to_methods ? n->mem : n);
     
-    return NCDObject_BuildFull(n->m->base_type_id, n, 0, method_user, object_func_getvar, object_func_getobj);
+    return NCDObject_BuildFull(n->m->base_type_id, n, 0, method_user, object_func_getvar, object_func_getobj, &n->pobj);
 }
 
 void NCDModuleInst_Backend_PassMemToMethods (NCDModuleInst *n)
@@ -565,7 +582,7 @@ int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t
         if (name >= NCD_STRING_ARG0 && name <= NCD_STRING_ARG19) {
             int num = name - NCD_STRING_ARG0;
             if (num < NCDVal_ListCount(o->args)) {
-                *out_object = NCDObject_BuildFull(-1, o, num, NULL, process_arg_object_func_getvar, NCDObject_no_getobj);
+                *out_object = NCDObject_BuildFull(-1, o, num, NULL, process_arg_object_func_getvar, NCDObject_no_getobj, NULL);
                 return 1;
             }
         }

+ 1 - 0
ncd/NCDModule.h

@@ -378,6 +378,7 @@ typedef struct NCDModuleInst_s {
     unsigned int state:3;
     unsigned int pass_mem_to_methods:1;
     unsigned int istate:3; // untouched by NCDModuleInst
+    NCDPersistentObj pobj;
     DebugObject d_obj;
 } NCDModuleInst;
 

+ 53 - 1
ncd/NCDObject.c

@@ -27,6 +27,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <misc/offset.h>
+#include <misc/debug.h>
 #include <ncd/NCDVal.h>
 
 #include "NCDObject.h"
@@ -54,11 +56,12 @@ NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_
     obj.method_user = data_ptr;
     obj.func_getvar = func_getvar;
     obj.func_getobj = func_getobj;
+    obj.pobj = NULL;
     
     return obj;
 }
 
-NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj)
+NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj, NCDPersistentObj *pobj)
 {
     ASSERT(type >= -1)
     ASSERT(func_getvar)
@@ -71,6 +74,7 @@ NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_in
     obj.method_user = method_user;
     obj.func_getvar = func_getvar;
     obj.func_getobj = func_getobj;
+    obj.pobj = pobj;
     
     return obj;
 }
@@ -121,6 +125,11 @@ int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_o
     return res;
 }
 
+NCDPersistentObj * NCDObject_Pobj (const NCDObject *o)
+{
+    return o->pobj;
+}
+
 static NCDObject NCDObject__dig_into_object (NCDObject object)
 {
     NCDObject obj2;
@@ -180,3 +189,46 @@ int NCDObject_ResolveObjExprCompact (const NCDObject *o, const NCD_string_id_t *
     *out_object = object;
     return 1;
 }
+
+void NCDPersistentObj_Init (NCDPersistentObj *o, NCDPersistentObj_func_retobj func_retobj)
+{
+    ASSERT(func_retobj)
+    
+    o->func_retobj = func_retobj;
+    LinkedList0_Init(&o->refs_list);
+}
+
+void NCDPersistentObj_Free (NCDPersistentObj *o)
+{
+    for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->refs_list); ln; ln = LinkedList0Node_Next(ln)) {
+        NCDObjRef *ref = UPPER_OBJECT(ln, NCDObjRef, refs_list_node);
+        ASSERT(ref->pobj == o)
+        ref->pobj = NULL;
+    }
+}
+
+void NCDObjRef_Init (NCDObjRef *o, NCDPersistentObj *pobj)
+{
+    o->pobj = pobj;
+    if (o->pobj) {
+        LinkedList0_Prepend(&o->pobj->refs_list, &o->refs_list_node);
+    }
+}
+
+void NCDObjRef_Free (NCDObjRef *o)
+{
+    if (o->pobj) {
+        LinkedList0_Remove(&o->pobj->refs_list, &o->refs_list_node);
+    }
+}
+
+int NCDObjRef_Deref (NCDObjRef *o, NCDObject *res)
+{
+    ASSERT(res)
+    
+    if (!o->pobj) {
+        return 0;
+    }
+    *res = o->pobj->func_retobj(o->pobj);
+    return 1;
+}

+ 83 - 3
ncd/NCDObject.h

@@ -33,6 +33,7 @@
 #include <stddef.h>
 
 #include <misc/debug.h>
+#include <structure/LinkedList0.h>
 #include <ncd/NCDVal_types.h>
 #include <ncd/NCDStringIndex.h>
 #include <ncd/static_strings.h>
@@ -52,6 +53,21 @@
  */
 typedef struct NCDObject_s NCDObject;
 
+/**
+ * Serves as an anchor point for NCDObjRef weak references.
+ * A callback produces the temporary NCDObject values on demand.
+ */
+typedef struct NCDPersistentObj_s NCDPersistentObj;
+
+/**
+ * A weak reference to an NCDPersistentObj.
+ * This means that the existence of a reference does not prevent
+ * the NCDPersistentObj from going away, rather when it goes away
+ * the NCDObjRef becomes a broken reference - any further
+ * dereference operations will fail "gracefully".
+ */
+typedef struct NCDObjRef_s NCDObjRef;
+
 /**
  * Callback function for variable resolution requests.
  * 
@@ -87,6 +103,12 @@ typedef int (*NCDObject_func_getvar) (const NCDObject *obj, NCD_string_id_t name
  */
 typedef int (*NCDObject_func_getobj) (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
 
+/**
+ * A callback of NCDPersistentObj which produces the corresponding temporary
+ * NCDObject value.
+ */
+typedef NCDObject (*NCDPersistentObj_func_retobj) (NCDPersistentObj const *pobj);
+
 struct NCDObject_s {
     NCD_string_id_t type;
     int data_int;
@@ -94,12 +116,23 @@ struct NCDObject_s {
     void *method_user;
     NCDObject_func_getvar func_getvar;
     NCDObject_func_getobj func_getobj;
+    NCDPersistentObj *pobj;
+};
+
+struct NCDPersistentObj_s {
+    NCDPersistentObj_func_retobj func_retobj;
+    LinkedList0 refs_list;
+};
+
+struct NCDObjRef_s {
+    NCDPersistentObj *pobj;
+    LinkedList0Node refs_list_node;
 };
 
 /**
  * Basic object construction function.
- * This is equivalent to calling {@link NCDObject_BuildFull} with data_int=0
- * and method_user=data_ptr. See that function for detailed documentation.
+ * This is equivalent to calling {@link NCDObject_BuildFull} with data_int=0,
+ * method_user=data_ptr and pobj=NULL. See that function for detailed documentation.
  */
 NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj);
 
@@ -123,9 +156,11 @@ NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_
  *                    be NULL; if the object exposes no variables, pass {@link NCDObject_no_getvar}.
  * @param func_getobj callback for resolving objects within the object. This must not
  *                    be NULL; if the object exposes no objects, pass {@link NCDObject_no_getobj}.
+ * @param pobj pointer to an NCDPersistentObj, serving as a reference anchor for this
+ *             object. NULL if references are not supported by the object.
  * @return an NCDObject structure encapsulating the information given
  */
-NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj);
+NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj, NCDPersistentObj *pobj);
 
 /**
  * Returns the 'type' attribute; see {@link NCDObject_BuildFull}.
@@ -161,6 +196,12 @@ int NCDObject_GetVar (const NCDObject *o, NCD_string_id_t name, NCDValMem *mem,
  */
 int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED;
 
+/**
+ * Returns a pointer to the NCDPersistentObj pointer for this
+ * object (if any).
+ */
+NCDPersistentObj * NCDObject_Pobj (const NCDObject *o);
+
 /**
  * Resolves a variable expression starting with this object.
  * A variable expression is usually represented in dotted form,
@@ -211,4 +252,43 @@ int NCDObject_no_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *
  */
 int NCDObject_no_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
 
+/**
+ * Initializes an NCDPersistentObj, an anchor point for NCDObjRef
+ * refrences.
+ * 
+ * @param func_retobj callback for producing NCDObject temporary objects
+ */
+void NCDPersistentObj_Init (NCDPersistentObj *o, NCDPersistentObj_func_retobj func_retobj);
+
+/**
+ * Frees the NCDPersistentObj.
+ * This breaks any NCDObjRef references referencing this object.
+ * This means that any further NCDObjRef_Deref calls on those
+ * references will fail.
+ */
+void NCDPersistentObj_Free (NCDPersistentObj *o);
+
+/**
+ * Initializes an object reference.
+ * 
+ * @param pobj the NCDPersistentObj for the reference, or NULL to make
+ *             a broken reference
+ */
+void NCDObjRef_Init (NCDObjRef *o, NCDPersistentObj *pobj);
+
+/**
+ * Frees the object reference.
+ */
+void NCDObjRef_Free (NCDObjRef *o);
+
+/**
+ * Dereferences the object reference.
+ * Note that the refernce will be broken if the originally
+ * reference NCDPersistentObj was freed.
+ * 
+ * @param res on success, *res will be set to the resulting NCDObject
+ * @return 1 on success, 0 on failure (broken reference)
+ */
+int NCDObjRef_Deref (NCDObjRef *o, NCDObject *res) WARN_UNUSED;
+
 #endif

+ 2 - 0
ncd/modules/modules.h

@@ -80,6 +80,7 @@ extern const struct NCDModuleGroup ncdmodule_substr;
 extern const struct NCDModuleGroup ncdmodule_log;
 extern const struct NCDModuleGroup ncdmodule_getenv;
 extern const struct NCDModuleGroup ncdmodule_basic_functions;
+extern const struct NCDModuleGroup ncdmodule_objref;
 #ifndef BADVPN_EMSCRIPTEN
 extern const struct NCDModuleGroup ncdmodule_regex_match;
 extern const struct NCDModuleGroup ncdmodule_run;
@@ -166,6 +167,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_log,
     &ncdmodule_getenv,
     &ncdmodule_basic_functions,
+    &ncdmodule_objref,
 #ifndef BADVPN_EMSCRIPTEN
     &ncdmodule_regex_match,
     &ncdmodule_run,

+ 189 - 0
ncd/modules/objref.c

@@ -0,0 +1,189 @@
+/**
+ * @file objref.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Synopsis:
+ *   objref(list(string) target)
+ *   objref_arg(list(string) target)
+ */
+
+#include <misc/debug.h>
+#include <misc/balloc.h>
+
+#include <ncd/module_common.h>
+
+#include <generated/blog_channel_ncd_objref.h>
+
+struct instance {
+    NCDModuleInst *i;
+    NCDObjRef ref;
+};
+
+static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_arg)
+{
+    ASSERT(is_arg == 0 || is_arg == 1)
+    
+    struct instance *o = vo;
+    o->i = i;
+    
+    NCDValRef target_arg;
+    if (!NCDVal_ListRead(params->args, 1, &target_arg)) {
+        ModuleLog(i, BLOG_ERROR, "wrong arity");
+        goto fail0;
+    }
+    
+    if (!NCDVal_IsList(target_arg)) {
+        ModuleLog(i, BLOG_ERROR, "argument is not a list");
+        goto fail0;
+    }
+    
+    size_t num_names = NCDVal_ListCount(target_arg);
+    if (num_names == 0) {
+        ModuleLog(i, BLOG_ERROR, "target list is empty");
+        goto fail0;
+    }
+    
+    size_t total_names = is_arg + num_names;
+    
+    NCD_string_id_t *names = BAllocArray(total_names, sizeof(*names));
+    if (!names) {
+        ModuleLog(i, BLOG_ERROR, "BAllocArray failed");
+        goto fail0;
+    }
+    
+    if (is_arg) {
+        names[0] = NCD_STRING_CALLER;
+    }
+    
+    for (size_t j = 0; j < num_names; j++) {
+        NCDValRef name_val = NCDVal_ListGet(target_arg, j);
+        
+        if (!NCDVal_IsString(name_val)) {
+            ModuleLog(i, BLOG_ERROR, "target component is not a string");
+            goto fail1;
+        }
+        
+        NCD_string_id_t name_id = ncd_get_string_id(name_val, i->params->iparams->string_index);
+        if (name_id < 0) {
+            ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed");
+            goto fail1;
+        }
+        
+        names[is_arg + j] = name_id;
+    }
+    
+    NCDObject first_obj;
+    if (!NCDModuleInst_Backend_GetObj(i, names[0], &first_obj)) {
+        ModuleLog(i, BLOG_ERROR, "first target object not found");
+        goto fail1;
+    }
+    
+    NCDObject obj;
+    if (!NCDObject_ResolveObjExprCompact(&first_obj, names + 1, total_names - 1, &obj)) {
+        ModuleLog(i, BLOG_ERROR, "non-first target object not found");
+        goto fail1;
+    }
+    
+    NCDPersistentObj *pobj = NCDObject_Pobj(&obj);
+    if (!pobj) {
+        ModuleLog(i, BLOG_ERROR, "object does not support references");
+        goto fail1;
+    }
+    
+    NCDObjRef_Init(&o->ref, pobj);
+    
+    BFree(names);
+    
+    NCDModuleInst_Backend_Up(i);
+    return;
+    
+fail1:
+    BFree(names);
+fail0:
+    NCDModuleInst_Backend_DeadError(i);
+}
+
+static void func_new_objref (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
+{
+    func_new_common(vo, i, params, 0);
+}
+
+static void func_new_objref_arg (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
+{
+    func_new_common(vo, i, params, 1);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    
+    NCDObjRef_Free(&o->ref);
+    
+    NCDModuleInst_Backend_Dead(o->i);
+}
+
+static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object)
+{
+    struct instance *o = vo;
+    
+    NCDObject obj;
+    if (!NCDObjRef_Deref(&o->ref, &obj)) {
+        ModuleLog(o->i, BLOG_ERROR, "object reference has been broken");
+        return 0;
+    }
+    
+    if (name == NCD_STRING_EMPTY) {
+        *out_object = obj;
+        return 1;
+    }
+    
+    return NCDObject_GetObj(&obj, name, out_object);
+}
+
+static struct NCDModule modules[] = {
+    {
+        .type = "objref",
+        .func_new2 = func_new_objref,
+        .func_die = func_die,
+        .func_getobj = func_getobj,
+        .alloc_size = sizeof(struct instance)
+    }, {
+        .type = "objref_arg",
+        .func_new2 = func_new_objref_arg,
+        .func_die = func_die,
+        .func_getobj = func_getobj,
+        .alloc_size = sizeof(struct instance)
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_objref = {
+    .modules = modules
+};