Преглед на файлове

ncd: rework object/variable resolution. Rather than working with string object expressions, work with individual
components. This allows modules that create template processes to predefine special objects and allow method
invocations on them.

ambrop7 преди 14 години
родител
ревизия
1de3b66b54

+ 1 - 0
blog_channels.txt

@@ -99,3 +99,4 @@ BDatagram 4
 PeerChat 4
 BArpProbe 4
 NCDModuleIndex 4
+NCDModuleProcess 4

+ 4 - 0
generated/blog_channel_NCDModuleProcess.h

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

+ 2 - 1
generated/blog_channels_defines.h

@@ -99,4 +99,5 @@
 #define BLOG_CHANNEL_PeerChat 98
 #define BLOG_CHANNEL_BArpProbe 99
 #define BLOG_CHANNEL_NCDModuleIndex 100
-#define BLOG_NUM_CHANNELS 101
+#define BLOG_CHANNEL_NCDModuleProcess 101
+#define BLOG_NUM_CHANNELS 102

+ 1 - 0
generated/blog_channels_list.h

@@ -99,3 +99,4 @@
 {.name = "PeerChat", .loglevel = 4},
 {.name = "BArpProbe", .loglevel = 4},
 {.name = "NCDModuleIndex", .loglevel = 4},
+{.name = "NCDModuleProcess", .loglevel = 4},

+ 97 - 64
ncd/NCDModule.c

@@ -28,6 +28,11 @@
  */
 
 #include <stdarg.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <misc/string_begins_with.h>
+#include <misc/parse_number.h>
 
 #include <ncd/NCDModule.h>
 
@@ -54,6 +59,11 @@
 #define PROCESS_STATE_TERMINATED_PENDING 9
 #define PROCESS_STATE_TERMINATED 10
 
+static int object_func_getvar (NCDModuleInst *n, const char *name, NCDValue *out_value);
+static int object_func_getobj (NCDModuleInst *n, const char *name, NCDObject *out_object);
+static int process_args_object_func_getvar (NCDModuleProcess *o, const char *name, NCDValue *out_value);
+static int process_arg_object_func_getvar2 (NCDModuleProcess *o, NCDValue *arg, const char *name, NCDValue *out_value);
+
 static void frontend_event (NCDModuleInst *n, int event)
 {
     n->func_event(n->user, event);
@@ -141,9 +151,8 @@ static void process_event_job_handler (NCDModuleProcess *o)
     }
 }
 
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, BReactor *reactor, BProcessManager *manager, NCDUdevManager *umanager, void *user,
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, const NCDObject *method_object, NCDValue *args, BReactor *reactor, BProcessManager *manager, NCDUdevManager *umanager, void *user,
                          NCDModuleInst_func_event func_event,
-                         NCDModuleInst_func_getvar func_getvar,
                          NCDModuleInst_func_getobj func_getobj,
                          NCDModuleInst_func_initprocess func_initprocess,
                          BLog_logfunc logfunc)
@@ -151,21 +160,19 @@ void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleI
     ASSERT(args)
     ASSERT(NCDValue_Type(args) == NCDVALUE_LIST)
     ASSERT(func_event)
-    ASSERT(func_getvar)
     ASSERT(func_getobj)
     ASSERT(func_initprocess)
     ASSERT(logfunc)
     
     // init arguments
     n->m = m;
-    n->method_object = method_object;
+    n->method_user = (method_object ? method_object->user : NULL);
     n->args = args;
     n->reactor = reactor;
     n->manager = manager;
     n->umanager = umanager;
     n->user = user;
     n->func_event = func_event;
-    n->func_getvar = func_getvar;
     n->func_getobj = func_getobj;
     n->func_initprocess = func_initprocess;
     n->logfunc = logfunc;
@@ -252,6 +259,15 @@ void NCDModuleInst_Clean (NCDModuleInst *n)
     }
 }
 
+NCDObject NCDModuleInst_Object (NCDModuleInst *n)
+{
+    DebugObject_Access(&n->d_obj);
+    
+    const char *type = (n->m->base_type ? n->m->base_type : n->m->type);
+    
+    return NCDObject_Build(type, n, (NCDObject_func_getvar)object_func_getvar, (NCDObject_func_getobj)object_func_getobj);
+}
+
 static int can_resolve (NCDModuleInst *n)
 {
     switch (n->state) {
@@ -268,28 +284,32 @@ static int can_resolve (NCDModuleInst *n)
     }
 }
 
-int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out)
+static int object_func_getvar (NCDModuleInst *n, const char *name, NCDValue *out_value)
 {
     DebugObject_Access(&n->d_obj);
-    ASSERT(name)
     
     if (!n->m->func_getvar || !can_resolve(n)) {
         return 0;
     }
     
-    return n->m->func_getvar(n->inst_user, name, out);
+    int res = n->m->func_getvar(n->inst_user, name, out_value);
+    ASSERT(res == 0 || res == 1)
+    
+    return res;
 }
 
-NCDModuleInst * NCDModuleInst_GetObj (NCDModuleInst *n, const char *name)
+static int object_func_getobj (NCDModuleInst *n, const char *name, NCDObject *out_object)
 {
     DebugObject_Access(&n->d_obj);
-    ASSERT(name)
     
     if (!n->m->func_getobj || !can_resolve(n)) {
-        return NULL;
+        return 0;
     }
     
-    return n->m->func_getobj(n->inst_user, name);
+    int res = n->m->func_getobj(n->inst_user, name, out_object);
+    ASSERT(res == 0 || res == 1)
+    
+    return res;
 }
 
 int NCDModuleInst_HaveError (NCDModuleInst *n)
@@ -383,31 +403,21 @@ void NCDModuleInst_Backend_Dead (NCDModuleInst *n)
     return;
 }
 
-int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *name, NCDValue *out)
+int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name, NCDObject *out_object)
 {
     DebugObject_Access(&n->d_obj);
     ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
            n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
            n->state == STATE_DYING)
     ASSERT(name)
+    ASSERT(out_object)
     
-    int res = n->func_getvar(n->user, name, out);
+    int res = n->func_getobj(n->user, name, out_object);
     ASSERT(res == 0 || res == 1)
     
     return res;
 }
 
-NCDModuleInst * NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name)
-{
-    DebugObject_Access(&n->d_obj);
-    ASSERT(n->state == STATE_DOWN_PCLEAN || n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
-           n->state == STATE_UP || n->state == STATE_DOWN_DIE || n->state == STATE_UP_DIE ||
-           n->state == STATE_DYING)
-    ASSERT(name)
-    
-    return n->func_getobj(n->user, name);
-}
-
 void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...)
 {
     DebugObject_Access(&n->d_obj);
@@ -441,11 +451,11 @@ int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *te
     
     // init arguments
     o->n = n;
+    o->args = args;
     o->user = user;
     o->handler_event = handler_event;
     
     // set no special functions
-    o->func_getspecialvar = NULL;
     o->func_getspecialobj = NULL;
     
     // init event job
@@ -454,15 +464,17 @@ int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *te
     // set state
     o->state = PROCESS_STATE_INIT;
     
-    // clear event func so we can assert it was set
+    // clear interp functions so we can assert they were set
     o->interp_func_event = NULL;
+    o->interp_func_getobj = NULL;
     
     // init interpreter part
-    if (!(n->func_initprocess(n->user, o, template_name, args))) {
+    if (!(n->func_initprocess(n->user, o, template_name))) {
         goto fail1;
     }
     
     ASSERT(o->interp_func_event)
+    ASSERT(o->interp_func_getobj)
     
     // set state
     o->state = PROCESS_STATE_DOWN;
@@ -482,13 +494,15 @@ void NCDModuleProcess_Free (NCDModuleProcess *o)
     
     // free event job
     BPending_Free(&o->event_job);
+    
+    // free arguments
+    NCDValue_Free(&o->args);
 }
 
-void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialvar func_getspecialvar, NCDModuleProcess_func_getspecialobj func_getspecialobj)
+void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj)
 {
     DebugObject_Access(&o->d_obj);
     
-    o->func_getspecialvar = func_getspecialvar;
     o->func_getspecialobj = func_getspecialobj;
 }
 
@@ -515,56 +529,50 @@ void NCDModuleProcess_Terminate (NCDModuleProcess *o)
     o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_TERMINATE);
 }
 
-int NCDModuleProcess_GetVar (NCDModuleProcess *o, const char *name, NCDValue *out)
+int NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name, NCDObject *out_object)
 {
     DebugObject_Access(&o->d_obj);
     ASSERT(o->state != PROCESS_STATE_INIT)
     ASSERT(name)
-    ASSERT(out)
+    ASSERT(out_object)
     
     // interpreter gone?
     if (o->state == PROCESS_STATE_TERMINATED_PENDING || o->state == PROCESS_STATE_TERMINATED) {
         return 0;
     }
     
-    int res = o->interp_func_getvar(o->interp_user, name, out);
+    int res = o->interp_func_getobj(o->interp_user, name, out_object);
     ASSERT(res == 0 || res == 1)
     
     return res;
 }
 
-NCDModuleInst * NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name)
+static void process_assert_interp (NCDModuleProcess *o)
 {
-    DebugObject_Access(&o->d_obj);
-    ASSERT(o->state != PROCESS_STATE_INIT)
-    ASSERT(name)
-    
-    // interpreter gone?
-    if (o->state == PROCESS_STATE_TERMINATED_PENDING || o->state == PROCESS_STATE_TERMINATED) {
-        return NULL;
-    }
-    
-    return o->interp_func_getobj(o->interp_user, name);
+    // assert that the interpreter knows about the object, and we're not in init
+    ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP_PENDING ||
+           o->state == PROCESS_STATE_DOWN_CONTINUE_PENDING || o->state == PROCESS_STATE_UP ||
+           o->state == PROCESS_STATE_DOWN_PENDING || o->state == PROCESS_STATE_DOWN_WAITING ||
+           o->state == PROCESS_STATE_TERMINATING)
 }
 
 void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user,
                                           NCDModuleProcess_interp_func_event interp_func_event,
-                                          NCDModuleProcess_interp_func_getvar interp_func_getvar,
                                           NCDModuleProcess_interp_func_getobj interp_func_getobj)
 {
+    ASSERT(o->state == PROCESS_STATE_INIT)
     ASSERT(interp_func_event)
-    ASSERT(interp_func_getvar)
     ASSERT(interp_func_getobj)
     
     o->interp_user = interp_user;
     o->interp_func_event = interp_func_event;
-    o->interp_func_getvar = interp_func_getvar;
     o->interp_func_getobj = interp_func_getobj;
 }
 
 void NCDModuleProcess_Interp_Up (NCDModuleProcess *o)
 {
     DebugObject_Access(&o->d_obj);
+    process_assert_interp(o);
     ASSERT(o->state == PROCESS_STATE_DOWN)
     
     BPending_Set(&o->event_job);
@@ -574,6 +582,7 @@ void NCDModuleProcess_Interp_Up (NCDModuleProcess *o)
 void NCDModuleProcess_Interp_Down (NCDModuleProcess *o)
 {
     DebugObject_Access(&o->d_obj);
+    process_assert_interp(o);
     
     switch (o->state) {
         case PROCESS_STATE_UP_PENDING: {
@@ -594,44 +603,68 @@ void NCDModuleProcess_Interp_Down (NCDModuleProcess *o)
 void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o)
 {
     DebugObject_Access(&o->d_obj);
+    process_assert_interp(o);
     ASSERT(o->state == PROCESS_STATE_TERMINATING)
     
     BPending_Set(&o->event_job);
     o->state = PROCESS_STATE_TERMINATED_PENDING;
 }
 
-int NCDModuleProcess_Interp_GetSpecialVar (NCDModuleProcess *o, const char *name, NCDValue *out)
+int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name, NCDObject *out_object)
 {
     DebugObject_Access(&o->d_obj);
-    ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP_PENDING ||
-           o->state == PROCESS_STATE_DOWN_CONTINUE_PENDING || o->state == PROCESS_STATE_UP ||
-           o->state == PROCESS_STATE_DOWN_PENDING || o->state == PROCESS_STATE_DOWN_WAITING ||
-           o->state == PROCESS_STATE_TERMINATING)
+    process_assert_interp(o);
     ASSERT(name)
-    ASSERT(out)
+    ASSERT(out_object)
     
-    if (!o->func_getspecialvar) {
+    if (!strcmp(name, "_args")) {
+        *out_object = NCDObject_Build(NULL, o, (NCDObject_func_getvar)process_args_object_func_getvar, NULL);
+        return 1;
+    }
+    
+    size_t len;
+    uintmax_t n;
+    if ((len = string_begins_with(name, "_arg")) && parse_unsigned_integer(name + len, &n) && n < NCDValue_ListCount(&o->args)) {
+        *out_object = NCDObject_Build2(NULL, o, NCDValue_ListGet(&o->args, n), (NCDObject_func_getvar2)process_arg_object_func_getvar2, NULL);
+        return 1;
+    }
+    
+    if (!o->func_getspecialobj) {
         return 0;
     }
     
-    int res = o->func_getspecialvar(o->user, name, out);
+    int res = o->func_getspecialobj(o->user, name, out_object);
     ASSERT(res == 0 || res == 1)
     
     return res;
 }
 
-NCDModuleInst * NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name)
+static int process_args_object_func_getvar (NCDModuleProcess *o, const char *name, NCDValue *out_value)
 {
     DebugObject_Access(&o->d_obj);
-    ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP_PENDING ||
-           o->state == PROCESS_STATE_DOWN_CONTINUE_PENDING || o->state == PROCESS_STATE_UP ||
-           o->state == PROCESS_STATE_DOWN_PENDING || o->state == PROCESS_STATE_DOWN_WAITING ||
-           o->state == PROCESS_STATE_TERMINATING)
-    ASSERT(name)
+    process_assert_interp(o);
     
-    if (!o->func_getspecialobj) {
-        return NULL;
+    if (strcmp(name, "")) {
+        return 0;
     }
     
-    return o->func_getspecialobj(o->user, name);
+    if (!NCDValue_InitCopy(out_value, &o->args)) {
+        BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDValue_InitCopy failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int process_arg_object_func_getvar2 (NCDModuleProcess *o, NCDValue *arg, const char *name, NCDValue *out_value)
+{
+    DebugObject_Access(&o->d_obj);
+    process_assert_interp(o);
+    
+    if (!NCDValue_InitCopy(out_value, arg)) {
+        BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDValue_InitCopy failed");
+        return 0;
+    }
+    
+    return 1;
 }

+ 35 - 119
ncd/NCDModule.h

@@ -37,6 +37,7 @@
 #include <system/BProcess.h>
 #include <udevmonitor/NCDUdevManager.h>
 #include <ncd/NCDValue.h>
+#include <ncd/NCDObject.h>
 
 #define NCDMODULE_EVENT_UP 1
 #define NCDMODULE_EVENT_DOWN 2
@@ -75,19 +76,6 @@ struct NCDModuleProcess_s;
  */
 typedef void (*NCDModuleInst_func_event) (void *user, int event);
 
-/**
- * Function called when the module instance wants the interpreter to
- * resolve a variable from the point of view of its statement.
- * The instance will not be in dead state.
- * This function must not have any side effects.
- * 
- * @param user as in {@link NCDModuleInst_Init}
- * @param name name of the variable
- * @param out on success, the interpreter should initialize the value here
- * @return 1 on success, 0 on failure
- */
-typedef int (*NCDModuleInst_func_getvar) (void *user, const char *name, NCDValue *out);
-
 /**
  * Function called when the module instance wants the interpreter to
  * resolve an object from the point of view of its statement.
@@ -96,9 +84,10 @@ typedef int (*NCDModuleInst_func_getvar) (void *user, const char *name, NCDValue
  * 
  * @param user as in {@link NCDModuleInst_Init}
  * @param name name of the object
- * @return object, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-typedef struct NCDModuleInst_s * (*NCDModuleInst_func_getobj) (void *user, const char *name);
+typedef int (*NCDModuleInst_func_getobj) (void *user, const char *name, NCDObject *out_object);
 
 /**
  * Function called when the module instance wants the interpreter to
@@ -116,12 +105,9 @@ typedef struct NCDModuleInst_s * (*NCDModuleInst_func_getobj) (void *user, const
  * @param user as in {@link NCDModuleInst_Init}
  * @param p handle for the new process backend
  * @param template_name name of the template to create the process from
- * @param args process arguments. On success, the arguments become owned
- *             by the interpreter. On failure, the interpreter must leave
- *             them unchanged.
  * @return 1 on success, 0 on failure
  */
-typedef int (*NCDModuleInst_func_initprocess) (void *user, struct NCDModuleProcess_s *p, const char *template_name, NCDValue args);
+typedef int (*NCDModuleInst_func_initprocess) (void *user, struct NCDModuleProcess_s *p, const char *template_name);
 
 #define NCDMODULEPROCESS_EVENT_UP 1
 #define NCDMODULEPROCESS_EVENT_DOWN 2
@@ -151,18 +137,6 @@ typedef int (*NCDModuleInst_func_initprocess) (void *user, struct NCDModuleProce
  */
 typedef void (*NCDModuleProcess_handler_event) (void *user, int event);
 
-/**
- * Function called when the interpreter wants to resolve a special
- * variable in the process.
- * This function must have no side effects.
- * 
- * @param user as in {@link NCDModuleProcess_Init}
- * @param name name of the variable
- * @param out on success, should initialize the value here
- * @return 1 on success, 0 on failure
- */
-typedef int (*NCDModuleProcess_func_getspecialvar) (void *user, const char *name, NCDValue *out);
-
 /**
  * Function called when the interpreter wants to resolve a special
  * object in the process.
@@ -170,9 +144,10 @@ typedef int (*NCDModuleProcess_func_getspecialvar) (void *user, const char *name
  * 
  * @param user as in {@link NCDModuleProcess_Init}
  * @param name name of the object
- * @return object, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-typedef struct NCDModuleInst_s * (*NCDModuleProcess_func_getspecialobj) (void *user, const char *name);
+typedef int (*NCDModuleProcess_func_getspecialobj) (void *user, const char *name, NCDObject *out_object);
 
 #define NCDMODULEPROCESS_INTERP_EVENT_CONTINUE 1
 #define NCDMODULEPROCESS_INTERP_EVENT_TERMINATE 2
@@ -201,18 +176,6 @@ typedef struct NCDModuleInst_s * (*NCDModuleProcess_func_getspecialobj) (void *u
  */
 typedef void (*NCDModuleProcess_interp_func_event) (void *user, int event);
 
-/**
- * Function called to have the interpreter resolve a variable within the process
- * of a process backend.
- * This function must not have any side effects.
- * 
- * @param user as in {@link NCDModuleProcess_Interp_SetHandlers}
- * @param name name of the variable
- * @param out on success, the interpreter should initialize the value here
- * @return 1 on success, 0 on failure
- */
-typedef int (*NCDModuleProcess_interp_func_getvar) (void *user, const char *name, NCDValue *out);
-
 /**
  * Function called to have the interpreter resolve an object within the process
  * of a process backend.
@@ -220,9 +183,10 @@ typedef int (*NCDModuleProcess_interp_func_getvar) (void *user, const char *name
  * 
  * @param user as in {@link NCDModuleProcess_Interp_SetHandlers}
  * @param name name of the object
- * @return object, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 in failure
  */
-typedef struct NCDModuleInst_s * (*NCDModuleProcess_interp_func_getobj) (void *user, const char *name);
+typedef int (*NCDModuleProcess_interp_func_getobj) (void *user, const char *name, NCDObject *out_object);
 
 struct NCDModule;
 
@@ -240,14 +204,13 @@ struct NCDModuleInitParams {
  */
 typedef struct NCDModuleInst_s {
     const struct NCDModule *m;
-    struct NCDModuleInst_s *method_object;
+    void *method_user;
     NCDValue *args;
     BReactor *reactor;
     BProcessManager *manager;
     NCDUdevManager *umanager;
     void *user;
     NCDModuleInst_func_event func_event;
-    NCDModuleInst_func_getvar func_getvar;
     NCDModuleInst_func_getobj func_getobj;
     NCDModuleInst_func_initprocess func_initprocess;
     BLog_logfunc logfunc;
@@ -267,15 +230,14 @@ typedef struct NCDModuleInst_s {
  */
 typedef struct NCDModuleProcess_s {
     NCDModuleInst *n;
+    NCDValue args;
     void *user;
     NCDModuleProcess_handler_event handler_event;
-    NCDModuleProcess_func_getspecialvar func_getspecialvar;
     NCDModuleProcess_func_getspecialobj func_getspecialobj;
     BPending event_job;
     int state;
     void *interp_user;
     NCDModuleProcess_interp_func_event interp_func_event;
-    NCDModuleProcess_interp_func_getvar interp_func_getvar;
     NCDModuleProcess_interp_func_getobj interp_func_getobj;
     DebugObject d_obj;
 } NCDModuleProcess;
@@ -290,8 +252,8 @@ typedef struct NCDModuleProcess_s {
  * 
  * @param n the instance
  * @param m structure of module functions implementing the module backend
- * @param method_object the base module if the module being initialized is a method, otherwise NULL.
- *                      The caller must ensure that this module is of the type expected by the module
+ * @param method_object the base object if the module being initialized is a method, otherwise NULL.
+ *                      The caller must ensure that this object is of the type expected by the module
  *                      being initialized.
  * @param args arguments to the module. Must be a NCDVALUE_LIST value. Must be available as long as
  *             the instance is freed.
@@ -300,13 +262,11 @@ typedef struct NCDModuleProcess_s {
  * @param umanager udev manager
  * @param user argument to callback functions
  * @param func_event callback to report state changes
- * @param func_getvar callback to resolve variables from the viewpoint of the instance
  * @param func_getobj callback to resolve objects from the viewpoint of the instance
  * @param logfunc log function which appends a log prefix with {@link BLog_Append}
  */
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, NCDModuleInst *method_object, NCDValue *args, BReactor *reactor, BProcessManager *manager, NCDUdevManager *umanager, void *user,
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, const NCDObject *method_object, NCDValue *args, BReactor *reactor, BProcessManager *manager, NCDUdevManager *umanager, void *user,
                          NCDModuleInst_func_event func_event,
-                         NCDModuleInst_func_getvar func_getvar,
                          NCDModuleInst_func_getobj func_getobj,
                          NCDModuleInst_func_initprocess func_initprocess,
                          BLog_logfunc logfunc);
@@ -337,25 +297,14 @@ void NCDModuleInst_Die (NCDModuleInst *n);
 void NCDModuleInst_Clean (NCDModuleInst *n);
 
 /**
- * Resolves a variable within the instance.
- * This function does not have any side effects.
- * 
- * @param n the instance
- * @param name name of the variable to resolve
- * @param out the value will be initialized here if successful
- * @return 1 on success, 0 on failure
- */
-int NCDModuleInst_GetVar (NCDModuleInst *n, const char *name, NCDValue *out) WARN_UNUSED;
-
-/**
- * Resolves an object within the instance.
- * This function does not have any side effects.
+ * Returns an {@link NCDObject} which can be used to resolve variables and objects
+ * within this instance, as well as call its methods. The resulting object may only
+ * be used immediately, and becomes invalid when the instance is freed.
  * 
  * @param n the instance
- * @param name name of the object to resolve
- * @return module instance, or NULL on failure
+ * @return an NCDObject for this instance
  */
-NCDModuleInst * NCDModuleInst_GetObj (NCDModuleInst *n, const char *name) WARN_UNUSED;
+NCDObject NCDModuleInst_Object (NCDModuleInst *n);
 
 /**
  * Checks whether the module terminated unsuccessfully.
@@ -401,26 +350,16 @@ void NCDModuleInst_Backend_Down (NCDModuleInst *n);
  */
 void NCDModuleInst_Backend_Dead (NCDModuleInst *n);
 
-/**
- * Resolves a variable for a backend instance, from the point of the instance's
- * statement in the containing process.
- * 
- * @param n backend instance handle
- * @param name name of the variable to resolve
- * @param out the value will be initialized here if successful
- * @return 1 on success, 0 on failure
- */
-int NCDModuleInst_Backend_GetVar (NCDModuleInst *n, const char *name, NCDValue *out) WARN_UNUSED;
-
 /**
  * Resolves an object for a backend instance, from the point of the instance's
  * statement in the containing process.
  * 
  * @param n backend instance handle
  * @param name name of the object to resolve
- * @return module instance, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-NCDModuleInst * NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name) WARN_UNUSED;
+int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Logs a backend instance message.
@@ -467,14 +406,12 @@ int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *te
 void NCDModuleProcess_Free (NCDModuleProcess *o);
 
 /**
- * Sets callback functions for providing special variables and objects within
- * the process.
+ * Sets callback functions for providing special objects within the process.
  * 
  * @param o the process
- * @param func_getspecialvar function for resolving special variables, or NULL
  * @param func_getspecialobj function for resolving special objects, or NULL
  */
-void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialvar func_getspecialvar, NCDModuleProcess_func_getspecialobj func_getspecialobj);
+void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj);
 
 /**
  * Continues the process after the process went down.
@@ -494,18 +431,6 @@ void NCDModuleProcess_Continue (NCDModuleProcess *o);
  */
 void NCDModuleProcess_Terminate (NCDModuleProcess *o);
 
-/**
- * Resolves a variable within the process from the point
- * at the end of the process.
- * This function has no side effects.
- * 
- * @param o the process
- * @param name name of the variable to resolve
- * @param out the value will be initialized here if successful
- * @return 1 on success, 0 on failure
- */
-int NCDModuleProcess_GetVar (NCDModuleProcess *o, const char *name, NCDValue *out) WARN_UNUSED;
-
 /**
  * Resolves an object within the process from the point
  * at the end of the process.
@@ -513,9 +438,10 @@ int NCDModuleProcess_GetVar (NCDModuleProcess *o, const char *name, NCDValue *ou
  * 
  * @param o the process
  * @param name name of the object to resolve
- * @return module instance, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-NCDModuleInst * NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name) WARN_UNUSED;
+int NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Sets callback functions for the interpreter to implement the
@@ -526,12 +452,10 @@ NCDModuleInst * NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name)
  * @param o process backend handle, as in {@link NCDModuleInst_func_initprocess}
  * @param interp_user argument to callback functions
  * @param interp_func_event function for reporting continue/terminate requests
- * @param interp_func_getvar function for resolving variables within the process
  * @param interp_func_getobj function for resolving objects within the process
  */
 void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user,
                                           NCDModuleProcess_interp_func_event interp_func_event,
-                                          NCDModuleProcess_interp_func_getvar interp_func_getvar,
                                           NCDModuleProcess_interp_func_getobj interp_func_getobj);
 
 /**
@@ -567,24 +491,15 @@ void NCDModuleProcess_Interp_Down (NCDModuleProcess *o);
  */
 void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o);
 
-/**
- * Resolves a special process variable for the process backend.
- * 
- * @param o process backend handle
- * @param name name of the variable
- * @param out the value will be initialized here if successful
- * @return 1 on success, 0 on failure
- */
-int NCDModuleProcess_Interp_GetSpecialVar (NCDModuleProcess *o, const char *name, NCDValue *out) WARN_UNUSED;
-
 /**
  * Resolves a special process object for the process backend.
  * 
  * @param o process backend handle
  * @param name name of the object
- * @return object, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-NCDModuleInst * NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name) WARN_UNUSED;
+int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Function called before any instance of any backend in a module
@@ -647,9 +562,10 @@ typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValue *out);
  * 
  * @param o as in {@link NCDModuleInst_Backend_SetUser}, or NULL by default
  * @param name name of the object to resolve
- * @return object, or NULL on failure
+ * @param out_object the object will be returned here
+ * @return 1 on success, 0 on failure
  */
-typedef NCDModuleInst * (*NCDModule_func_getobj) (void *o, const char *name);
+typedef int (*NCDModule_func_getobj) (void *o, const char *name, NCDObject *out_object);
 
 /**
  * Handler called when the module instance is in a clean state.

+ 155 - 0
ncd/NCDObject.c

@@ -0,0 +1,155 @@
+/**
+ * @file NCDObject.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.
+ */
+
+#include <stddef.h>
+
+#include "NCDObject.h"
+
+NCDObject NCDObject_Build (const char *type, void *user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj)
+{
+    NCDObject obj;
+    obj.type = type;
+    obj.user = user;
+    obj.user2 = NULL;
+    obj.uv.func_getvar = func_getvar;
+    obj.uo.func_getobj = func_getobj;
+    
+    return obj;
+}
+
+NCDObject NCDObject_Build2 (const char *type, void *user, void *user2, NCDObject_func_getvar2 func_getvar2, NCDObject_func_getobj2 func_getobj2)
+{
+    ASSERT(user2)
+    
+    NCDObject obj;
+    obj.type = type;
+    obj.user = user;
+    obj.user2 = user2;
+    obj.uv.func_getvar2 = func_getvar2;
+    obj.uo.func_getobj2 = func_getobj2;
+    
+    return obj;
+}
+
+const char * NCDObject_Type (NCDObject *o)
+{
+    return o->type;
+}
+
+int NCDObject_GetObj (NCDObject *o, const char *name, NCDObject *out_object)
+{
+    ASSERT(name)
+    ASSERT(out_object)
+    
+    int res;
+    if (o->user2) {
+        res = (!o->uo.func_getobj2 ? 0 : o->uo.func_getobj2(o->user, o->user2, name, out_object));
+    } else {
+        res = (!o->uo.func_getobj ? 0 : o->uo.func_getobj(o->user, name, out_object));
+    }
+    
+    ASSERT(res == 0 || res == 1)
+    
+    return res;
+}
+
+int NCDObject_GetVar (NCDObject *o, const char *name, NCDValue *out_value)
+{
+    ASSERT(name)
+    ASSERT(out_value)
+    
+    int res;
+    if (o->user2) {
+        res = (!o->uv.func_getvar2 ? 0 : o->uv.func_getvar2(o->user, o->user2, name, out_value));
+    } else {
+        res = (!o->uv.func_getvar ? 0 : o->uv.func_getvar(o->user, name, out_value));
+    }
+    
+    ASSERT(res == 0 || res == 1)
+#ifndef NDEBUG
+    if (res) {
+        NCDValue_Type(out_value);
+    }
+#endif
+    
+    return res;
+}
+
+static NCDObject dig_into_object (NCDObject object)
+{
+    NCDObject obj2;
+    while (NCDObject_GetObj(&object, "", &obj2)) {
+        object = obj2;
+    }
+    
+    return object;
+}
+
+int NCDObject_ResolveObjExpr (NCDObject *o, char **names, NCDObject *out_object)
+{
+    ASSERT(names)
+    ASSERT(out_object)
+    
+    NCDObject object = dig_into_object(*o);
+    
+    for (size_t i = 0; names[i]; i++) {
+        NCDObject obj2;
+        if (!NCDObject_GetObj(&object, names[i], &obj2)) {
+            return 0;
+        }
+        
+        object = dig_into_object(obj2);
+    }
+    
+    *out_object = object;
+    return 1;
+}
+
+int NCDObject_ResolveVarExpr (NCDObject *o, char **names, NCDValue *out_value)
+{
+    ASSERT(names)
+    ASSERT(out_value)
+    
+    NCDObject object = dig_into_object(*o);
+    
+    for (size_t i = 0; names[i]; i++) {
+        NCDObject obj2;
+        if (!NCDObject_GetObj(&object, names[i], &obj2)) {
+            if (!names[i + 1] && NCDObject_GetVar(&object, names[i], out_value)) {
+                return 1;
+            }
+            
+            return 0;
+        }
+        
+        object = dig_into_object(obj2);
+    }
+    
+    return NCDObject_GetVar(&object, "", out_value);
+}

+ 65 - 0
ncd/NCDObject.h

@@ -0,0 +1,65 @@
+/**
+ * @file NCDObject.h
+ * @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.
+ */
+
+#ifndef BADVPN_NCDOBJECT_H
+#define BADVPN_NCDOBJECT_H
+
+#include <misc/debug.h>
+#include <ncd/NCDValue.h>
+
+typedef struct NCDObject_s NCDObject;
+
+typedef int (*NCDObject_func_getvar) (void *user, const char *name, NCDValue *out_value);
+typedef int (*NCDObject_func_getobj) (void *user, const char *name, NCDObject *out_object);
+typedef int (*NCDObject_func_getvar2) (void *user, void *user2, const char *name, NCDValue *out_value);
+typedef int (*NCDObject_func_getobj2) (void *user, void *user2, const char *name, NCDObject *out_object);
+
+struct NCDObject_s {
+    const char *type;
+    void *user;
+    void *user2;
+    union {
+        NCDObject_func_getvar func_getvar;
+        NCDObject_func_getvar2 func_getvar2;
+    } uv;
+    union {
+        NCDObject_func_getobj func_getobj;
+        NCDObject_func_getobj2 func_getobj2;
+    } uo;
+};
+
+NCDObject NCDObject_Build (const char *type, void *user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj);
+NCDObject NCDObject_Build2 (const char *type, void *user, void *user2, NCDObject_func_getvar2 func_getvar2, NCDObject_func_getobj2 func_getobj2);
+const char * NCDObject_Type (NCDObject *o);
+int NCDObject_GetObj (NCDObject *o, const char *name, NCDObject *out_object) WARN_UNUSED;
+int NCDObject_GetVar (NCDObject *o, const char *name, NCDValue *out_value) WARN_UNUSED;
+int NCDObject_ResolveObjExpr (NCDObject *o, char **names, NCDObject *out_object) WARN_UNUSED;
+int NCDObject_ResolveVarExpr (NCDObject *o, char **names, NCDValue *out_value) WARN_UNUSED;
+
+#endif

+ 89 - 36
ncd/modules/alias.c

@@ -40,6 +40,7 @@
 #include <string.h>
 
 #include <misc/concat_strings.h>
+#include <misc/balloc.h>
 #include <ncd/NCDModule.h>
 
 #include <generated/blog_channel_ncd_alias.h>
@@ -48,9 +49,71 @@
 
 struct instance {
     NCDModuleInst *i;
-    char *target;
+    char **target;
 };
 
+static char ** split_string (const char *str, char del)
+{
+    size_t len = strlen(str);
+    
+    // count parts
+    size_t num_parts = 0;
+    size_t i = 0;
+    while (1) {
+        size_t j = i;
+        while (j < len && str[j] != del) j++;
+        if (j == i) {
+            goto fail0;
+        }
+        
+        num_parts++;
+        if (j == len) {
+            break;
+        }
+        i = j + 1;
+    }
+    
+    // allocate array for part pointers
+    char **result = BAllocArray(num_parts + 1, sizeof(*result));
+    if (!result) {
+        goto fail0;
+    }
+    
+    num_parts = 0;
+    i = 0;
+    while (1) {
+        size_t j = i;
+        while (j < len && str[j] != del) j++;
+        if (j == i) {
+            goto fail1;
+        }
+        
+        if (!(result[num_parts] = malloc(j - i + 1))) {
+            goto fail1;
+        }
+        memcpy(result[num_parts], str + i, j - i);
+        result[num_parts][j - i] = '\0';
+        
+        num_parts++;
+        if (j == len) {
+            break;
+        }
+        i = j + 1;
+    }
+    
+    result[num_parts] = NULL;
+    
+    return result;
+    
+fail1:
+    while (num_parts-- > 0) {
+        free(result[num_parts]);
+    }
+    BFree(result);
+fail0:
+    return NULL;
+}
+
 static void func_new (NCDModuleInst *i)
 {
     // allocate instance
@@ -59,14 +122,12 @@ static void func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
-    NCDModuleInst_Backend_SetUser(i, o);
-    
-    // init arguments
     o->i = i;
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // read arguments
     NCDValue *target_arg;
-    if (!NCDValue_ListRead(o->i->args, 1, &target_arg)) {
+    if (!NCDValue_ListRead(i->args, 1, &target_arg)) {
         ModuleLog(o->i, BLOG_ERROR, "wrong arity");
         goto fail1;
     }
@@ -74,7 +135,12 @@ static void func_new (NCDModuleInst *i)
         ModuleLog(o->i, BLOG_ERROR, "wrong type");
         goto fail1;
     }
-    o->target = NCDValue_StringValue(target_arg);
+    
+    // split target into components
+    if (!(o->target = split_string(NCDValue_StringValue(target_arg), '.'))) {
+        ModuleLog(o->i, BLOG_ERROR, "bad target");
+        goto fail1;
+    }
     
     // signal up
     NCDModuleInst_Backend_Up(o->i);
@@ -92,52 +158,40 @@ static void func_die (void *vo)
     struct instance *o = vo;
     NCDModuleInst *i = o->i;
     
+    // free target
+    char **str = o->target;
+    while (*str) {
+        free(*str);
+        str++;
+    }
+    free(o->target);
+    
     // free instance
     free(o);
     
     NCDModuleInst_Backend_Dead(i);
 }
 
-static int func_getvar (void *vo, const char *name, NCDValue *out)
+static int func_getobj (void *vo, const char *name, NCDObject *out_object)
 {
     struct instance *o = vo;
     
-    if (!strcmp(name, "")) {
-        return NCDModuleInst_Backend_GetVar(o->i, o->target, out);
+    NCDObject object;
+    if (!NCDModuleInst_Backend_GetObj(o->i, o->target[0], &object)) {
+        return 0;
     }
     
-    char *target_name = concat_strings(3, o->target, ".", name);
-    if (!target_name) {
-        ModuleLog(o->i, BLOG_ERROR, "concat_strings failed");
+    NCDObject obj2;
+    if (!NCDObject_ResolveObjExpr(&object, o->target + 1, &obj2)) {
         return 0;
     }
     
-    int res = NCDModuleInst_Backend_GetVar(o->i, target_name, out);
-    
-    free(target_name);
-    
-    return res;
-}
-
-static NCDModuleInst * func_getobj (void *vo, const char *name)
-{
-    struct instance *o = vo;
-    
     if (!strcmp(name, "")) {
-        return NCDModuleInst_Backend_GetObj(o->i, o->target);
+        *out_object = obj2;
+        return 1;
     }
     
-    char *target_name = concat_strings(3, o->target, ".", name);
-    if (!target_name) {
-        ModuleLog(o->i, BLOG_ERROR, "concat_strings failed");
-        return NULL;
-    }
-    
-    NCDModuleInst *res = NCDModuleInst_Backend_GetObj(o->i, target_name);
-    
-    free(target_name);
-    
-    return res;
+    return NCDObject_GetObj(&obj2, name, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -145,7 +199,6 @@ static const struct NCDModule modules[] = {
         .type = "alias",
         .func_new = func_new,
         .func_die = func_die,
-        .func_getvar = func_getvar,
         .func_getobj = func_getobj
     }, {
         .type = NULL

+ 2 - 2
ncd/modules/blocker.c

@@ -163,7 +163,7 @@ static void updown_func_new_templ (NCDModuleInst *i, int up)
     NCDModuleInst_Backend_Up(o->i);
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     if (mo->up != up) {
         // change up state
@@ -234,7 +234,7 @@ static void use_func_new (NCDModuleInst *i)
     }
     
     // set blocker
-    o->blocker = i->method_object->inst_user;
+    o->blocker = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // add to blocker's list
     LinkedList2_Append(&o->blocker->users, &o->blocker_node);

+ 22 - 35
ncd/modules/call.c

@@ -95,6 +95,8 @@ struct instance {
 };
 
 static void instance_free (struct instance *o);
+static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int ref_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
 
 static void process_handler_event (struct instance *o, int event)
 {
@@ -131,36 +133,21 @@ static void process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialvar (struct instance *o, const char *name, NCDValue *out)
-{
-    size_t p;
-    
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetVar(o->i, name + p, out);
+static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+{     
+    if (!strcmp(name, "_caller")) {
+        *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)caller_obj_func_getobj);
+        return 1;
     }
     
-    if (o->crh && (p = string_begins_with(name, "_ref."))) {
-        return NCDModuleInst_Backend_GetVar(o->crh->i, name + p, out);
+    if (!strcmp(name, "_ref")) {
+        *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)ref_obj_func_getobj);
+        return 1;
     }
     
     return 0;
 }
 
-static NCDModuleInst * process_func_getspecialobj (struct instance *o, const char *name)
-{
-    size_t p;
-    
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetObj(o->i, name + p);
-    }
-    
-    if (o->crh && (p = string_begins_with(name, "_ref."))) {
-        return NCDModuleInst_Backend_GetObj(o->crh->i, name + p);
-    }
-    
-    return NULL;
-}
-
 static void callrefhere_func_new (NCDModuleInst *i)
 {
     // allocate instance
@@ -255,11 +242,10 @@ static void func_new (NCDModuleInst *i)
         
         // set special functions
         NCDModuleProcess_SetSpecialFuncs(&o->process,
-                                        (NCDModuleProcess_func_getspecialvar)process_func_getspecialvar,
                                         (NCDModuleProcess_func_getspecialobj)process_func_getspecialobj);
         
         // set callrefhere
-        o->crh = (o->i->method_object ? o->i->method_object->inst_user : NULL);
+        o->crh = (o->i->method_user ? ((NCDModuleInst *)i->method_user)->inst_user : NULL);
         
         // add to callrefhere's calls list
         if (o->crh) {
@@ -330,7 +316,7 @@ static void func_clean (void *vo)
     o->state = STATE_WORKING;
 }
 
-static int func_getvar (void *vo, const char *name, NCDValue *out)
+static int func_getobj (void *vo, const char *name, NCDObject *out_object)
 {
     struct instance *o = vo;
     
@@ -338,18 +324,21 @@ static int func_getvar (void *vo, const char *name, NCDValue *out)
         return 0;
     }
     
-    return NCDModuleProcess_GetVar(&o->process, name, out);
+    return NCDModuleProcess_GetObj(&o->process, name, out_object);
 }
 
-static NCDModuleInst * func_getobj (void *vo, const char *name)
+static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
 {
-    struct instance *o = vo;
-    
-    if (o->state == STATE_NONE) {
-        return NULL;
+    return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
+}
+
+static int ref_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+{
+    if (!o->crh) {
+        return 0;
     }
     
-    return NCDModuleProcess_GetObj(&o->process, name);
+    return NCDModuleInst_Backend_GetObj(o->crh->i, name, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -362,7 +351,6 @@ static const struct NCDModule modules[] = {
         .func_new = func_new,
         .func_die = func_die,
         .func_clean = func_clean,
-        .func_getvar = func_getvar,
         .func_getobj = func_getobj,
         .can_resolve_when_down = 1
     }, {
@@ -370,7 +358,6 @@ static const struct NCDModule modules[] = {
         .func_new = func_new,
         .func_die = func_die,
         .func_clean = func_clean,
-        .func_getvar = func_getvar,
         .func_getobj = func_getobj,
         .can_resolve_when_down = 1
     }, {

+ 2 - 15
ncd/modules/depend.c

@@ -410,7 +410,7 @@ static void depend_func_clean (void *vo)
     }
 }
 
-static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
+static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     ASSERT(!o->p || !o->p->is_queued)
@@ -419,19 +419,7 @@ static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
         return 0;
     }
     
-    return NCDModuleInst_Backend_GetVar(o->p->i, varname, out);
-}
-
-static NCDModuleInst * depend_func_getobj (void *vo, const char *objname)
-{
-    struct depend *o = vo;
-    ASSERT(!o->p || !o->p->is_queued)
-    
-    if (!o->p) {
-        return NULL;
-    }
-    
-    return NCDModuleInst_Backend_GetObj(o->p->i, objname);
+    return NCDModuleInst_Backend_GetObj(o->p->i, objname, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -448,7 +436,6 @@ static const struct NCDModule modules[] = {
         .func_new = depend_func_new,
         .func_die = depend_func_die,
         .func_clean = depend_func_clean,
-        .func_getvar = depend_func_getvar,
         .func_getobj = depend_func_getobj,
         .can_resolve_when_down = 1
     }, {

+ 2 - 16
ncd/modules/dynamic_depend.c

@@ -496,7 +496,7 @@ static void depend_func_clean (void *vo)
     name_continue_resetting(n);
 }
 
-static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
+static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     struct name *n = o->n;
@@ -506,20 +506,7 @@ static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
         return 0;
     }
     
-    return NCDModuleInst_Backend_GetVar(n->cur_p->i, varname, out);
-}
-
-static NCDModuleInst * depend_func_getobj (void *vo, const char *objname)
-{
-    struct depend *o = vo;
-    struct name *n = o->n;
-    ASSERT(!o->is_bound || n->cur_p)
-    
-    if (!o->is_bound) {
-        return NULL;
-    }
-    
-    return NCDModuleInst_Backend_GetObj(n->cur_p->i, objname);
+    return NCDModuleInst_Backend_GetObj(n->cur_p->i, objname, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -532,7 +519,6 @@ static const struct NCDModule modules[] = {
         .func_new = depend_func_new,
         .func_die = depend_func_die,
         .func_clean = depend_func_clean,
-        .func_getvar = depend_func_getvar,
         .func_getobj = depend_func_getobj,
         .can_resolve_when_down = 1
     }, {

+ 45 - 34
ncd/modules/foreach.c

@@ -99,8 +99,10 @@ static void work (struct instance *o);
 static void advance (struct instance *o);
 static void timer_handler (struct instance *o);
 static void element_process_handler_event (struct element *e, int event);
-static int element_process_func_getspecialvar (struct element *e, const char *name, NCDValue *out);
-static NCDModuleInst * element_process_func_getspecialobj (struct element *e, const char *name);
+static int element_process_func_getspecialobj (struct element *e, const char *name, NCDObject *out_object);
+static int element_caller_object_func_getobj (struct element *e, const char *name, NCDObject *out_object);
+static int element_index_object_func_getvar (struct element *e, const char *name, NCDValue *out_value);
+static int element_elem_object_func_getvar (struct element *e, const char *name, NCDValue *out_value);
 static void instance_free (struct instance *o);
 
 static void assert_state (struct instance *o)
@@ -243,9 +245,7 @@ static void advance (struct instance *o)
     }
     
     // set special functions
-    NCDModuleProcess_SetSpecialFuncs(&e->process,
-                                     (NCDModuleProcess_func_getspecialvar)element_process_func_getspecialvar,
-                                     (NCDModuleProcess_func_getspecialobj)element_process_func_getspecialobj);
+    NCDModuleProcess_SetSpecialFuncs(&e->process, (NCDModuleProcess_func_getspecialobj)element_process_func_getspecialobj);
     
     // set element state down
     e->state = ESTATE_DOWN;
@@ -323,61 +323,72 @@ static void element_process_handler_event (struct element *e, int event)
     return;
 }
 
-static int element_process_func_getspecialvar (struct element *e, const char *name, NCDValue *out)
+static int element_process_func_getspecialobj (struct element *e, const char *name, NCDObject *out_object)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     
-    if (e->i >= o->gp) {
-        BLog(BLOG_ERROR, "tried to resolve variable %s but it's dirty", name);
-        return 0;
+    if (!strcmp(name, "_caller")) {
+        *out_object = NCDObject_Build(NULL, e, NULL, (NCDObject_func_getobj)element_caller_object_func_getobj);
+        return 1;
     }
     
     if (!strcmp(name, "_index")) {
-        char str[64];
-        snprintf(str, sizeof(str), "%zu", e->i);
-        
-        if (!NCDValue_InitString(out, str)) {
-            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
-            return 0;
-        }
-        
+        *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_index_object_func_getvar, NULL);
         return 1;
     }
     
     if (!strcmp(name, "_elem")) {
-        if (!NCDValue_InitCopy(out, e->value)) {
-            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
-            return 0;
-        }
-        
+        *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_elem_object_func_getvar, NULL);
         return 1;
     }
     
-    size_t p;
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetVar(o->i, name + p, out);
+    return 0;
+}
+
+static int element_caller_object_func_getobj (struct element *e, const char *name, NCDObject *out_object)
+{
+    struct instance *o = e->inst;
+    ASSERT(e->state != ESTATE_FORGOTTEN)
+    
+    return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
+}
+
+static int element_index_object_func_getvar (struct element *e, const char *name, NCDValue *out_value)
+{
+    struct instance *o = e->inst;
+    ASSERT(e->state != ESTATE_FORGOTTEN)
+    
+    if (strcmp(name, "")) {
+        return 0;
     }
     
-    return 0;
+    char str[64];
+    snprintf(str, sizeof(str), "%zu", e->i);
+    
+    if (!NCDValue_InitString(out_value, str)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+        return 0;
+    }
+    
+    return 1;
 }
 
-static NCDModuleInst * element_process_func_getspecialobj (struct element *e, const char *name)
+static int element_elem_object_func_getvar (struct element *e, const char *name, NCDValue *out_value)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     
-    if (e->i >= o->gp) {
-        BLog(BLOG_ERROR, "tried to resolve object %s but it's dirty", name);
-        return NULL;
+    if (strcmp(name, "")) {
+        return 0;
     }
     
-    size_t p;
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetObj(o->i, name + p);
+    if (!NCDValue_InitCopy(out_value, e->value)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
+        return 0;
     }
     
-    return NULL;
+    return 1;
 }
 
 static void func_new (NCDModuleInst *i)

+ 9 - 16
ncd/modules/imperative.c

@@ -80,8 +80,8 @@ static int start_process (struct instance *o, const char *templ, NCDValue *args,
 static void go_deinit (struct instance *o);
 static void init_process_handler_event (struct instance *o, int event);
 static void deinit_process_handler_event (struct instance *o, int event);
-static int process_func_getspecialvar (struct instance *o, const char *name, NCDValue *out);
-static NCDModuleInst * process_func_getspecialobj (struct instance *o, const char *name);
+static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object);
+static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
 static void deinit_timer_handler (struct instance *o);
 static void instance_free (struct instance *o);
 
@@ -102,9 +102,7 @@ static int start_process (struct instance *o, const char *templ, NCDValue *args,
     }
     
     // set special functions
-    NCDModuleProcess_SetSpecialFuncs(&o->process,
-                                    (NCDModuleProcess_func_getspecialvar)process_func_getspecialvar,
-                                    (NCDModuleProcess_func_getspecialobj)process_func_getspecialobj);
+    NCDModuleProcess_SetSpecialFuncs(&o->process, (NCDModuleProcess_func_getspecialobj)process_func_getspecialobj);
     return 1;
     
 fail:
@@ -203,28 +201,23 @@ static void deinit_process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialvar (struct instance *o, const char *name, NCDValue *out)
+static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
 {
     ASSERT(o->state != STATE_UP)
     
-    size_t p;
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetVar(o->i, name + p, out);
+    if (!strcmp(name, "_caller")) {
+        *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)process_caller_object_func_getobj);
+        return 1;
     }
     
     return 0;
 }
 
-static NCDModuleInst * process_func_getspecialobj (struct instance *o, const char *name)
+static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
 {
     ASSERT(o->state != STATE_UP)
     
-    size_t p;
-    if (p = string_begins_with(name, "_caller.")) {
-        return NCDModuleInst_Backend_GetObj(o->i, name + p);
-    }
-    
-    return NULL;
+    return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
 
 static void deinit_timer_handler (struct instance *o)

+ 1 - 1
ncd/modules/index.c

@@ -119,7 +119,7 @@ fail0:
 
 static void func_new_from_index (NCDModuleInst *i)
 {
-    struct instance *index = i->method_object->inst_user;
+    struct instance *index = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // check overflow
     if (index->value == SIZE_MAX) {

+ 10 - 10
ncd/modules/list.c

@@ -320,7 +320,7 @@ static void append_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // append
     NCDValue v;
@@ -382,7 +382,7 @@ static void appendv_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // append
     NCDValue l;
@@ -439,7 +439,7 @@ static void length_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // remember length
     o->length = NCDValue_ListCount(&mo->list);
@@ -516,7 +516,7 @@ static void get_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // check index
     if (index >= NCDValue_ListCount(&mo->list)) {
@@ -592,7 +592,7 @@ static void shift_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // shift
     if (!NCDValue_ListFirst(&mo->list)) {
@@ -646,7 +646,7 @@ static void contains_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // search
     o->contains = 0;
@@ -730,7 +730,7 @@ static void find_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // search
     o->is_found = 0;
@@ -833,7 +833,7 @@ static void removeat_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // check position
     if (remove_pos >= NCDValue_ListCount(&mo->list)) {
@@ -895,7 +895,7 @@ static void remove_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // find value
     NCDValue *e = find_in_list(&mo->list, value_arg);
@@ -953,7 +953,7 @@ static void set_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // replace list
     NCDValue_Free(&mo->list);

+ 2 - 14
ncd/modules/multidepend.c

@@ -364,7 +364,7 @@ static void depend_func_clean (void *vo)
     depend_update(o);
 }
 
-static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
+static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     
@@ -372,18 +372,7 @@ static int depend_func_getvar (void *vo, const char *varname, NCDValue *out)
         return 0;
     }
     
-    return NCDModuleInst_Backend_GetVar(o->provide->i, varname, out);
-}
-
-static NCDModuleInst * depend_func_getobj (void *vo, const char *objname)
-{
-    struct depend *o = vo;
-    
-    if (!o->provide) {
-        return NULL;
-    }
-    
-    return NCDModuleInst_Backend_GetObj(o->provide->i, objname);
+    return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -396,7 +385,6 @@ static const struct NCDModule modules[] = {
         .func_new = depend_func_new,
         .func_die = depend_func_die,
         .func_clean = depend_func_clean,
-        .func_getvar = depend_func_getvar,
         .func_getobj = depend_func_getobj,
         .can_resolve_when_down = 1
     }, {

+ 1 - 1
ncd/modules/net_iptables.c

@@ -490,7 +490,7 @@ static void unlock_func_new (NCDModuleInst *i)
     o->i = i;
     
     // get lock lock
-    struct lock_instance *lock = i->method_object->inst_user;
+    struct lock_instance *lock = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure lock doesn't already have an unlock
     if (lock->unlock) {

+ 1 - 1
ncd/modules/net_watch_interfaces.c

@@ -460,7 +460,7 @@ static void nextevent_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure we are currently reporting an event
     if (!event_template_is_enabled(&mo->templ)) {

+ 3 - 13
ncd/modules/ondemand.c

@@ -321,7 +321,7 @@ static void demand_func_new (NCDModuleInst *i)
     }
     
     // set ondemand
-    o->od = i->method_object->inst_user;
+    o->od = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // add to ondemand's demands list
     LinkedList1_Append(&o->od->demands_list, &o->demands_list_node);
@@ -376,22 +376,13 @@ static void demand_func_die (void *vo)
     demand_free(o);
 }
 
-static int demand_func_getvar (void *vo, const char *varname, NCDValue *out)
+static int demand_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct demand *o = vo;
     ASSERT(o->od->have_process)
     ASSERT(o->od->process_up)
     
-    return NCDModuleProcess_GetVar(&o->od->process, varname, out);
-}
-
-static NCDModuleInst * demand_func_getobj (void *vo, const char *objname)
-{
-    struct demand *o = vo;
-    ASSERT(o->od->have_process)
-    ASSERT(o->od->process_up)
-    
-    return NCDModuleProcess_GetObj(&o->od->process, objname);
+    return NCDModuleProcess_GetObj(&o->od->process, objname, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -403,7 +394,6 @@ static const struct NCDModule modules[] = {
         .type = "ondemand::demand",
         .func_new = demand_func_new,
         .func_die = demand_func_die,
-        .func_getvar = demand_func_getvar,
         .func_getobj = demand_func_getobj
     }, {
         .type = NULL

+ 2 - 2
ncd/modules/process_manager.c

@@ -478,7 +478,7 @@ static void start_func_new (NCDModuleInst *i)
     NCDModuleInst_Backend_Up(o->i);
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     if (mo->dying) {
         ModuleLog(o->i, BLOG_INFO, "manager is dying, not creating process %s", name);
@@ -540,7 +540,7 @@ static void stop_func_new (NCDModuleInst *i)
     NCDModuleInst_Backend_Up(o->i);
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     if (mo->dying) {
         ModuleLog(o->i, BLOG_INFO, "manager is dying, not stopping process %s", name);

+ 8 - 25
ncd/modules/ref.c

@@ -118,24 +118,17 @@ static void refhere_func_die (void *vo)
     NCDModuleInst_Backend_Dead(i);
 }
 
-static int refhere_func_getvar (void *vo, const char *varname, NCDValue *out)
-{
-    struct refhere_instance *o = vo;
-    
-    return NCDModuleInst_Backend_GetVar(o->i, varname, out);
-}
-
-static NCDModuleInst * refhere_func_getobj (void *vo, const char *objname)
+static int refhere_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct refhere_instance *o = vo;
     
     // We don't redirect methods, and there will never be an object
     // with empty name. Fail here so we don't report non-errors.
     if (!strcmp(objname, "")) {
-        return NULL;
+        return 0;
     }
     
-    return NCDModuleInst_Backend_GetObj(o->i, objname);
+    return NCDModuleInst_Backend_GetObj(o->i, objname, out_object);
 }
 
 static void ref_func_new_templ (NCDModuleInst *i, struct refhere_instance *rh)
@@ -176,14 +169,14 @@ fail0:
 
 static void ref_func_new_from_refhere (NCDModuleInst *i)
 {
-    struct refhere_instance *rh = i->method_object->inst_user;
+    struct refhere_instance *rh = ((NCDModuleInst *)i->method_user)->inst_user;
     
     return ref_func_new_templ(i, rh);
 }
 
 static void ref_func_new_from_ref (NCDModuleInst *i)
 {
-    struct ref_instance *ref = i->method_object->inst_user;
+    struct ref_instance *ref = ((NCDModuleInst *)i->method_user)->inst_user;
     
     return ref_func_new_templ(i, ref->rh);
 }
@@ -208,24 +201,17 @@ static void ref_func_die (void *vo)
     ref_instance_free(o);
 }
 
-static int ref_func_getvar (void *vo, const char *varname, NCDValue *out)
-{
-    struct ref_instance *o = vo;
-    
-    return NCDModuleInst_Backend_GetVar(o->rh->i, varname, out);
-}
-
-static NCDModuleInst * ref_func_getobj (void *vo, const char *objname)
+static int ref_func_getobj (void *vo, const char *objname, NCDObject *out_object)
 {
     struct ref_instance *o = vo;
     
     // We don't redirect methods, and there will never be an object
     // with empty name. Fail here so we don't report non-errors.
     if (!strcmp(objname, "")) {
-        return NULL;
+        return 0;
     }
     
-    return NCDModuleInst_Backend_GetObj(o->rh->i, objname);
+    return NCDModuleInst_Backend_GetObj(o->rh->i, objname, out_object);
 }
 
 static const struct NCDModule modules[] = {
@@ -233,21 +219,18 @@ static const struct NCDModule modules[] = {
         .type = "refhere",
         .func_new = refhere_func_new,
         .func_die = refhere_func_die,
-        .func_getvar = refhere_func_getvar,
         .func_getobj = refhere_func_getobj
     }, {
         .type = "refhere::ref",
         .base_type = "ref",
         .func_new = ref_func_new_from_refhere,
         .func_die = ref_func_die,
-        .func_getvar = ref_func_getvar,
         .func_getobj = ref_func_getobj
     }, {
         .type = "ref::ref",
         .base_type = "ref",
         .func_new = ref_func_new_from_ref,
         .func_die = ref_func_die,
-        .func_getvar = ref_func_getvar,
         .func_getobj = ref_func_getobj
     }, {
         .type = NULL

+ 1 - 1
ncd/modules/sys_evdev.c

@@ -333,7 +333,7 @@ static void nextevent_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure we are currently reporting an event
     if (!mo->processing) {

+ 1 - 1
ncd/modules/sys_watch_directory.c

@@ -405,7 +405,7 @@ static void nextevent_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure we are currently reporting an event
     if (!mo->processing) {

+ 1 - 1
ncd/modules/sys_watch_input.c

@@ -439,7 +439,7 @@ static void nextevent_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure we are currently reporting an event
     if (!event_template_is_enabled(&mo->templ)) {

+ 1 - 1
ncd/modules/sys_watch_usb.c

@@ -407,7 +407,7 @@ static void nextevent_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
     
     // make sure we are currently reporting an event
     if (!event_template_is_enabled(&mo->templ)) {

+ 1 - 1
ncd/modules/var.c

@@ -144,7 +144,7 @@ static void set_func_new (NCDModuleInst *i)
     }
     
     // get method object
-    struct instance *mo = i->method_object->inst_user;
+    struct instance *mo = ((NCDModuleInst * )i->method_user)->inst_user;
     
     // set
     NCDValue v;

+ 215 - 243
ncd/ncd.c

@@ -42,6 +42,7 @@
 #include <misc/string_begins_with.h>
 #include <misc/parse_number.h>
 #include <misc/open_standard_streams.h>
+#include <misc/expstring.h>
 #include <structure/LinkedList1.h>
 #include <structure/LinkedList2.h>
 #include <base/BLog.h>
@@ -84,7 +85,7 @@ struct arg_value {
     int type;
     union {
         char *string;
-        char *variable;
+        char **variable_names;
         LinkedList1 list;
     };
 };
@@ -95,7 +96,7 @@ struct arg_list_elem {
 };
 
 struct statement {
-    char *object_name;
+    char **object_names;
     char *method_name;
     struct arg_value args;
     char *name;
@@ -103,7 +104,6 @@ struct statement {
 
 struct process {
     NCDModuleProcess *module_process;
-    NCDValue args;
     char *name;
     size_t num_statements;
     struct process_statement *statements;
@@ -170,14 +170,18 @@ static void print_version (void);
 static int parse_arguments (int argc, char *argv[]);
 static void signal_handler (void *unused);
 static int arg_value_init_string (struct arg_value *o, const char *string);
-static int arg_value_init_variable (struct arg_value *o, const char *variable);
+static int arg_value_init_variable (struct arg_value *o, struct NCDConfig_strings *ast_names);
 static void arg_value_init_list (struct arg_value *o);
 static int arg_value_list_append (struct arg_value *o, struct arg_value v);
 static void arg_value_free (struct arg_value *o);
 static int build_arg_list_from_ast_list (struct arg_value *o, struct NCDConfig_list *list);
+static char ** names_new (struct NCDConfig_strings *ast_names);
+static size_t names_count (char **names);
+static char * names_tostring (char **names);
+static void names_free (char **names);
 static int statement_init (struct statement *s, struct NCDConfig_statements *conf);
 static void statement_free (struct statement *s);
-static int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process, NCDValue args);
+static int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process);
 static void process_free (struct process *p);
 static void process_start_terminating (struct process *p);
 static void process_free_statements (struct process *p);
@@ -189,22 +193,19 @@ static void process_schedule_work (struct process *p);
 static void process_work_job_handler (struct process *p);
 static void process_advance_job_handler (struct process *p);
 static void process_wait_timer_handler (struct process *p);
-static struct process_statement * process_find_statement (struct process *p, size_t pos, const char *name);
-static int process_resolve_name (struct process *p, size_t pos, const char *name, struct process_statement **first_ps, const char **rest);
-static int process_resolve_variable (struct process *p, size_t pos, const char *varname, NCDValue *out);
-static struct process_statement * process_resolve_object (struct process *p, size_t pos, const char *objname);
+static int process_find_object (struct process *p, size_t pos, const char *name, NCDObject *out_object);
+static int process_resolve_object_expr (struct process *p, size_t pos, char **names, NCDObject *out_object);
+static int process_resolve_variable_expr (struct process *p, size_t pos, char **names, NCDValue *out_value);
 static void process_statement_logfunc (struct process_statement *ps);
 static void process_statement_log (struct process_statement *ps, int level, const char *fmt, ...);
 static void process_statement_set_error (struct process_statement *ps);
 static int process_statement_resolve_argument (struct process_statement *ps, struct arg_value *arg, NCDValue *out);
 static void process_statement_instance_func_event (struct process_statement *ps, int event);
-static int process_statement_instance_func_getvar (struct process_statement *ps, const char *varname, NCDValue *out);
-static NCDModuleInst * process_statement_instance_func_getobj (struct process_statement *ps, const char *objname);
-static int process_statement_instance_func_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name, NCDValue args);
+static int process_statement_instance_func_getobj (struct process_statement *ps, const char *objname, NCDObject *out_object);
+static int process_statement_instance_func_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name);
 static void process_statement_instance_logfunc (struct process_statement *ps);
 static void process_moduleprocess_func_event (struct process *p, int event);
-static int process_moduleprocess_func_getvar (struct process *p, const char *name, NCDValue *out);
-static NCDModuleInst * process_moduleprocess_func_getobj (struct process *p, const char *name);
+static int process_moduleprocess_func_getobj (struct process *p, const char *name, NCDObject *out_object);
 
 int main (int argc, char **argv)
 {
@@ -347,11 +348,7 @@ int main (int argc, char **argv)
     struct NCDConfig_processes *conf = config_ast;
     while (conf) {
         if (!conf->is_template) {
-            NCDValue args;
-            NCDValue_InitList(&args);
-            if (!process_new(conf, NULL, args)) {
-                NCDValue_Free(&args);
-            }
+            process_new(conf, NULL);
         }
         conf = conf->next;
     }
@@ -604,11 +601,12 @@ int arg_value_init_string (struct arg_value *o, const char *string)
     return 1;
 }
 
-int arg_value_init_variable (struct arg_value *o, const char *variable)
+int arg_value_init_variable (struct arg_value *o, struct NCDConfig_strings *ast_names)
 {
+    ASSERT(ast_names)
+    
     o->type = ARG_VALUE_TYPE_VARIABLE;
-    if (!(o->variable = strdup(variable))) {
-        BLog(BLOG_ERROR, "strdup failed");
+    if (!(o->variable_names = names_new(ast_names))) {
         return 0;
     }
     
@@ -644,7 +642,7 @@ void arg_value_free (struct arg_value *o)
         } break;
         
         case ARG_VALUE_TYPE_VARIABLE: {
-            free(o->variable);
+            names_free(o->variable_names);
         } break;
         
         case ARG_VALUE_TYPE_LIST: {
@@ -675,18 +673,9 @@ int build_arg_list_from_ast_list (struct arg_value *o, struct NCDConfig_list *li
             } break;
             
             case NCDCONFIG_ARG_VAR: {
-                char *variable = NCDConfig_concat_strings(c->var);
-                if (!variable) {
-                    BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
-                    goto fail;
-                }
-                
-                if (!arg_value_init_variable(&e, variable)) {
-                    free(variable);
+                if (!arg_value_init_variable(&e, c->var)) {
                     goto fail;
                 }
-                
-                free(variable);
             } break;
             
             case NCDCONFIG_ARG_LIST: {
@@ -711,20 +700,103 @@ fail:
     return 0;
 }
 
-int statement_init (struct statement *s, struct NCDConfig_statements *conf)
+static char ** names_new (struct NCDConfig_strings *ast_names)
 {
-    s->object_name = NULL;
-    s->method_name = NULL;
-    s->name = NULL;
+    ASSERT(ast_names)
     
-    // set object name
-    if (conf->objname) {
-        if (!(s->object_name = NCDConfig_concat_strings(conf->objname))) {
-            BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
+    bsize_t size = bsize_fromsize(1);
+    for (struct NCDConfig_strings *n = ast_names; n; n = n->next) {
+        size = bsize_add(size, bsize_fromsize(1));
+    }
+    
+    char **names;
+    if (size.is_overflow || !(names = BAllocArray(size.value, sizeof(names[0])))) {
+        BLog(BLOG_ERROR, "BAllocArray failed");
+        goto fail0;
+    }
+    
+    size_t i = 0;
+    for (struct NCDConfig_strings *n = ast_names; n; n = n->next) {
+        if (!(names[i] = strdup(n->value))) {
+            BLog(BLOG_ERROR, "strdup failed");
+            goto fail1;
+        }
+        i++;
+    }
+    
+    names[i] = NULL;
+    
+    return names;
+    
+fail1:
+    while (i-- > 0) {
+        free(names[i]);
+    }
+    BFree(names);
+fail0:
+    return NULL;
+}
+
+static size_t names_count (char **names)
+{
+    ASSERT(names)
+    
+    size_t i;
+    for (i = 0; names[i]; i++);
+    
+    return i;
+}
+
+static char * names_tostring (char **names)
+{
+    ASSERT(names)
+    
+    ExpString str;
+    if (!ExpString_Init(&str)) {
+        goto fail0;
+    }
+    
+    for (size_t i = 0; names[i]; i++) {
+        if (i > 0 && !ExpString_AppendChar(&str, '.')) {
+            goto fail1;
+        }
+        if (!ExpString_Append(&str, names[i])) {
             goto fail1;
         }
     }
     
+    return ExpString_Get(&str);
+    
+fail1:
+    ExpString_Free(&str);
+fail0:
+    return NULL;
+}
+
+static void names_free (char **names)
+{
+    ASSERT(names)
+    
+    size_t i = names_count(names);
+    
+    while (i-- > 0) {
+        free(names[i]);
+    }
+    
+    BFree(names);
+}
+
+int statement_init (struct statement *s, struct NCDConfig_statements *conf)
+{
+    // set object names
+    if (conf->objname) {
+        if (!(s->object_names = names_new(conf->objname))) {
+            goto fail0;
+        }
+    } else {
+        s->object_names = NULL;
+    }
+    
     // set method name
     if (!(s->method_name = NCDConfig_concat_strings(conf->names))) {
         BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
@@ -735,22 +807,29 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
     if (conf->name) {
         if (!(s->name = strdup(conf->name))) {
             BLog(BLOG_ERROR, "strdup failed");
-            goto fail1;
+            goto fail2;
         }
+    } else {
+        s->name = NULL;
     }
     
     // init arguments
     if (!build_arg_list_from_ast_list(&s->args, conf->args)) {
         BLog(BLOG_ERROR, "build_arg_list_from_ast_list failed");
-        goto fail1;
+        goto fail3;
     }
     
     return 1;
     
-fail1:
+fail3:
     free(s->name);
+fail2:
     free(s->method_name);
-    free(s->object_name);
+fail1:
+    if (s->object_names) {
+        names_free(s->object_names);
+    }
+fail0:
     return 0;
 }
 
@@ -759,16 +838,20 @@ void statement_free (struct statement *s)
     // free arguments
     arg_value_free(&s->args);
     
-    // free names
+    // free name
     free(s->name);
+    
+    // free method name
     free(s->method_name);
-    free(s->object_name);
+    
+    // free object names
+    if (s->object_names) {
+        names_free(s->object_names);
+    }
 }
 
-int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process, NCDValue args)
+int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process)
 {
-    ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
-    
     // allocate strucure
     struct process *p = malloc(sizeof(*p));
     if (!p) {
@@ -783,13 +866,9 @@ int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_proc
     if (p->module_process) {
         NCDModuleProcess_Interp_SetHandlers(p->module_process, p,
                                             (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event,
-                                            (NCDModuleProcess_interp_func_getvar)process_moduleprocess_func_getvar,
                                             (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj);
     }
     
-    // set arguments
-    p->args = args;
-    
     // init name
     if (!(p->name = strdup(conf->name))) {
         BLog(BLOG_ERROR, "strdup failed");
@@ -901,9 +980,6 @@ void process_free (struct process *p)
     // free name
     free(p->name);
     
-    // free arguments
-    NCDValue_Free(&p->args);
-    
     // free strucure
     free(p);
 }
@@ -1134,56 +1210,54 @@ void process_advance_job_handler (struct process *p)
     
     process_statement_log(ps, BLOG_INFO, "initializing");
     
-    NCDModuleInst *method_object = NULL;
+    NCDObject object;
+    NCDObject *object_ptr = NULL;
     char *type;
+    int free_type = 0;
     
-    // construct type
-    if (!ps->s.object_name) {
+    if (!ps->s.object_names) {
         // this is a function_call(); type is "function_call"
-        if (!(type = strdup(ps->s.method_name))) {
-            process_statement_log(ps, BLOG_ERROR, "strdup failed");
-            goto fail0;
-        }
+        type = ps->s.method_name;
     } else {
         // this is a some.object.somewhere->method_call(); type is "base_type(some.object.somewhere)::method_call"
         
-        // resolve object
-        struct process_statement *obj_ps = process_resolve_object(p, p->ap, ps->s.object_name);
-        if (!obj_ps) {
-            process_statement_log(ps, BLOG_ERROR, "failed to resolve object %s for method call", ps->s.object_name);
-            goto fail0;
+        // get object
+        if (!process_resolve_object_expr(p, p->ap, ps->s.object_names, &object)) {
+            goto fail;
         }
-        ASSERT(obj_ps->state == SSTATE_ADULT)
+        object_ptr = &object;
         
-        // base type defaults to type
-        const char *base_type = (obj_ps->module->base_type ? obj_ps->module->base_type : obj_ps->module->type);
+        // get object type
+        const char *object_type = NCDObject_Type(&object);
+        if (!object_type) {
+            process_statement_log(ps, BLOG_ERROR, "cannot call method on object with no type");
+            goto fail;
+        }
         
         // build type string
-        if (!(type = concat_strings(3, base_type, "::", ps->s.method_name))) {
+        if (!(type = concat_strings(3, object_type, "::", ps->s.method_name))) {
             process_statement_log(ps, BLOG_ERROR, "concat_strings failed");
-            goto fail0;
+            goto fail;
         }
-        
-        method_object = &obj_ps->inst;
+        free_type = 1;
     }
     
     // find module to instantiate
     if (!(ps->module = NCDModuleIndex_FindModule(&mindex, type))) {
         process_statement_log(ps, BLOG_ERROR, "failed to find module: %s", type);
-        goto fail1;
+        goto fail;
     }
     
     // resolve arguments
     if (!process_statement_resolve_argument(ps, &ps->s.args, &ps->inst_args)) {
         process_statement_log(ps, BLOG_ERROR, "failed to resolve arguments");
-        goto fail1;
+        goto fail;
     }
     
     // initialize module instance
     NCDModuleInst_Init(
-        &ps->inst, ps->module, method_object, &ps->inst_args, &ss, &manager, &umanager, ps,
+        &ps->inst, ps->module, object_ptr, &ps->inst_args, &ss, &manager, &umanager, ps,
         (NCDModuleInst_func_event)process_statement_instance_func_event,
-        (NCDModuleInst_func_getvar)process_statement_instance_func_getvar,
         (NCDModuleInst_func_getobj)process_statement_instance_func_getobj,
         (NCDModuleInst_func_initprocess)process_statement_instance_func_initprocess,
         (BLog_logfunc)process_statement_instance_logfunc
@@ -1198,14 +1272,18 @@ void process_advance_job_handler (struct process *p)
     // increment FP
     p->fp++;
     
-    free(type);
+    if (free_type) {
+        free(type);
+    }
     
     process_assert_pointers(p);
     return;
     
-fail1:
-    free(type);
-fail0:
+fail:
+    if (free_type) {
+        free(type);
+    }
+    
     // mark error
     process_statement_set_error(ps);
     
@@ -1233,163 +1311,81 @@ void process_wait_timer_handler (struct process *p)
     BPending_Set(&p->work_job);
 }
 
-struct process_statement * process_find_statement (struct process *p, size_t pos, const char *name)
+int process_find_object (struct process *p, size_t pos, const char *name, NCDObject *out_object)
 {
-    process_assert_pointers(p);
     ASSERT(pos <= p->num_statements)
+    ASSERT(name)
+    ASSERT(out_object)
     
     for (size_t i = pos; i > 0; i--) {
         struct process_statement *ps = &p->statements[i - 1];
         if (ps->s.name && !strcmp(ps->s.name, name)) {
-            return ps;
+            if (ps->state == SSTATE_FORGOTTEN) {
+                process_log(p, BLOG_ERROR, "statement (%zu) is uninitialized", i - 1);
+                goto fail;
+            }
+            
+            *out_object = NCDModuleInst_Object(&ps->inst);
+            return 1;
         }
     }
     
-    return NULL;
-}
-
-int process_resolve_name (struct process *p, size_t pos, const char *name, struct process_statement **first_ps, const char **rest)
-{
-    process_assert_pointers(p);
-    ASSERT(pos <= p->num_statements)
-    ASSERT(name)
-    
-    char *dot = strstr(name, ".");
-    if (!dot) {
-        *first_ps = process_find_statement(p, pos, name);
-        *rest = NULL;
-    } else {
-        // copy modname and terminate
-        char *modname = malloc((dot - name) + 1);
-        if (!modname) {
-            process_log(p, BLOG_ERROR, "malloc failed");
-            return 0;
-        }
-        memcpy(modname, name, dot - name);
-        modname[dot - name] = '\0';
-        
-        *first_ps = process_find_statement(p, pos, modname);
-        *rest = dot + 1;
-        
-        free(modname);
+    if (p->module_process && NCDModuleProcess_Interp_GetSpecialObj(p->module_process, name, out_object)) {
+        return 1;
     }
     
-    return 1;
+fail:
+    return 0;
 }
 
-int process_resolve_variable (struct process *p, size_t pos, const char *varname, NCDValue *out)
+int process_resolve_object_expr (struct process *p, size_t pos, char **names, NCDObject *out_object)
 {
-    process_assert_pointers(p);
     ASSERT(pos <= p->num_statements)
-    ASSERT(varname)
-    
-    // find referred statement and remaining name
-    struct process_statement *rps;
-    const char *rest;
-    if (!process_resolve_name(p, pos, varname, &rps, &rest)) {
-        return 0;
-    }
-    
-    if (!rps) {
-        // handle _args
-        if (!strcmp(varname, "_args")) {
-            if (!NCDValue_InitCopy(out, &p->args)) {
-                process_log(p, BLOG_ERROR, "NCDValue_InitCopy failed");
-                return 0;
-            }
-            
-            return 1;
-        }
-        
-        // handle _argN
-        size_t len;
-        uintmax_t n;
-        if ((len = string_begins_with(varname, "_arg")) && parse_unsigned_integer(varname + len, &n) && n < NCDValue_ListCount(&p->args)) {
-            if (!NCDValue_InitCopy(out, NCDValue_ListGet(&p->args, n))) {
-                process_log(p, BLOG_ERROR, "NCDValue_InitCopy failed");
-                return 0;
-            }
-            
-            return 1;
-        }
-        
-        // handle special variables
-        if (p->module_process) {
-            if (NCDModuleProcess_Interp_GetSpecialVar(p->module_process, varname, out)) {
-                return 1;
-            }
-        }
-        
-        process_log(p, BLOG_ERROR, "unknown statement name in variable: %s", varname);
-        return 0;
-    }
+    ASSERT(names)
+    ASSERT(names_count(names) > 0)
+    ASSERT(out_object)
     
-    // must not be forgotten
-    if (rps->state == SSTATE_FORGOTTEN) {
-        process_log(p, BLOG_ERROR, "referred module is uninitialized and cannot resolve variable: %s", varname);
-        return 0;
+    NCDObject object;
+    if (!process_find_object(p, pos, names[0], &object)) {
+        goto fail;
     }
     
-    // resolve variable in referred statement
-    if (!NCDModuleInst_GetVar(&rps->inst, (rest ? rest : ""), out)) {
-        process_log(p, BLOG_ERROR, "referred module failed to resolve variable: %s", varname);
-        return 0;
+    if (!NCDObject_ResolveObjExpr(&object, names + 1, out_object)) {
+        goto fail;
     }
     
     return 1;
+    
+fail:;
+    char *name = names_tostring(names);
+    process_log(p, BLOG_ERROR, "failed to resolve object (%s) from position %zu", (name ? name : ""), pos);
+    free(name);
+    return 0;
 }
 
-struct process_statement * process_resolve_object (struct process *p, size_t pos, const char *objname)
+int process_resolve_variable_expr (struct process *p, size_t pos, char **names, NCDValue *out_value)
 {
-    process_assert_pointers(p);
     ASSERT(pos <= p->num_statements)
-    ASSERT(objname)
+    ASSERT(names)
+    ASSERT(names_count(names) > 0)
+    ASSERT(out_value)
     
-    // find referred statement and remaining name
-    struct process_statement *rps;
-    const char *rest;
-    if (!process_resolve_name(p, pos, objname, &rps, &rest)) {
-        return NULL;
+    NCDObject object;
+    if (!process_find_object(p, pos, names[0], &object)) {
+        goto fail;
     }
     
-    if (!rps) {
-        // handle special objects
-        if (p->module_process) {
-            NCDModuleInst *inst = NCDModuleProcess_Interp_GetSpecialObj(p->module_process, objname);
-            if (inst) {
-                struct process_statement *res_ps = UPPER_OBJECT(inst, struct process_statement, inst);
-                ASSERT(res_ps->state == SSTATE_ADULT)
-                return res_ps;
-            }
-        }
-        
-        process_log(p, BLOG_ERROR, "unknown statement name in object: %s", objname);
-        return NULL;
+    if (!NCDObject_ResolveVarExpr(&object, names + 1, out_value)) {
+        goto fail;
     }
     
-    // must not be forgotten
-    if (rps->state == SSTATE_FORGOTTEN) {
-        process_log(p, BLOG_ERROR, "referred module is uninitialized and cannot resolve object: %s", objname);
-        return NULL;
-    }
-    
-    // Resolve object in referred statement. If there is no rest, resolve empty string
-    // instead, or use this statement if it fails. This allows a statement to forward method
-    // calls elsewhere.
-    NCDModuleInst *inst = NCDModuleInst_GetObj(&rps->inst, (rest ? rest : ""));
-    if (!inst) {
-        if (!rest) {
-            return rps;
-        }
-        
-        process_log(p, BLOG_ERROR, "referred module failed to resolve object: %s", objname);
-        return NULL;
-    }
-    
-    struct process_statement *res_ps = UPPER_OBJECT(inst, struct process_statement, inst);
-    ASSERT(res_ps->state == SSTATE_ADULT)
+    return 1;
     
-    return res_ps;
+fail:;
+    char *name = names_tostring(names);
+    process_log(p, BLOG_ERROR, "failed to resolve variable (%s) from position %zu", (name ? name : ""), pos);
+    free(name);
+    return 0;
 }
 
 void process_statement_logfunc (struct process_statement *ps)
@@ -1417,6 +1413,8 @@ void process_statement_set_error (struct process_statement *ps)
 int process_statement_resolve_argument (struct process_statement *ps, struct arg_value *arg, NCDValue *out)
 {
     ASSERT(ps->i <= process_rap(ps->p))
+    ASSERT(arg)
+    ASSERT(out)
     
     switch (arg->type) {
         case ARG_VALUE_TYPE_STRING: {
@@ -1427,8 +1425,7 @@ int process_statement_resolve_argument (struct process_statement *ps, struct arg
         } break;
         
         case ARG_VALUE_TYPE_VARIABLE: {
-            if (!process_resolve_variable(ps->p, ps->i, arg->variable, out)) {
-                process_statement_log(ps, BLOG_ERROR, "failed to resolve variable");
+            if (!process_resolve_variable_expr(ps->p, ps->i, arg->variable_names, out)) {
                 return 0;
             }
         } break;
@@ -1534,30 +1531,17 @@ void process_statement_instance_func_event (struct process_statement *ps, int ev
     }
 }
 
-int process_statement_instance_func_getvar (struct process_statement *ps, const char *varname, NCDValue *out)
+int process_statement_instance_func_getobj (struct process_statement *ps, const char *objname, NCDObject *out_object)
 {
     ASSERT(ps->state != SSTATE_FORGOTTEN)
     
-    return process_resolve_variable(ps->p, ps->i, varname, out);
+    return process_find_object(ps->p, ps->i, objname, out_object);
 }
 
-NCDModuleInst * process_statement_instance_func_getobj (struct process_statement *ps, const char *objname)
+int process_statement_instance_func_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name)
 {
     ASSERT(ps->state != SSTATE_FORGOTTEN)
     
-    struct process_statement *rps = process_resolve_object(ps->p, ps->i, objname);
-    if (!rps) {
-        return NULL;
-    }
-    
-    return &rps->inst;
-}
-
-int process_statement_instance_func_initprocess (struct process_statement *ps, NCDModuleProcess *mp, const char *template_name, NCDValue args)
-{
-    ASSERT(ps->state != SSTATE_FORGOTTEN)
-    ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
-    
     // find template
     struct NCDConfig_processes *conf = config_ast;
     while (conf) {
@@ -1573,7 +1557,7 @@ int process_statement_instance_func_initprocess (struct process_statement *ps, N
     }
     
     // create process
-    if (!process_new(conf, mp, args)) {
+    if (!process_new(conf, mp)) {
         process_statement_log(ps, BLOG_ERROR, "failed to create process from template %s", template_name);
         return 0;
     }
@@ -1619,21 +1603,9 @@ void process_moduleprocess_func_event (struct process *p, int event)
     }
 }
 
-int process_moduleprocess_func_getvar (struct process *p, const char *name, NCDValue *out)
-{
-    ASSERT(p->module_process)
-    
-    return process_resolve_variable(p, p->num_statements, name, out);
-}
-
-NCDModuleInst * process_moduleprocess_func_getobj (struct process *p, const char *name)
+int process_moduleprocess_func_getobj (struct process *p, const char *name, NCDObject *out_object)
 {
     ASSERT(p->module_process)
     
-    struct process_statement *rps = process_resolve_object(p, p->num_statements, name);
-    if (!rps) {
-        return NULL;
-    }
-    
-    return &rps->inst;
+    return process_find_object(p, p->num_statements, name, out_object);
 }