summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ast.c165
-rw-r--r--ast.h28
-rw-r--r--eval.c293
-rw-r--r--eval.h3
-rw-r--r--lib.c63
-rw-r--r--tests/02_runtime/05_closure_scope35
6 files changed, 344 insertions, 243 deletions
diff --git a/ast.c b/ast.c
index 2ac6714..0a04892 100644
--- a/ast.c
+++ b/ast.c
@@ -278,55 +278,154 @@ ut_new_regexp(const char *source, bool icase, bool newline, bool global, char **
return op->val;
}
+static void
+func_free(struct json_object *v, void *ud)
+{
+ struct ut_op *op = ud;
+ struct ut_function *fn = op->tag.data;
+
+ json_object_put(fn->args);
+ ut_release_scope(fn->parent_scope);
+
+ free(op);
+}
+
static int
func_to_string(struct json_object *v, struct printbuf *pb, int level, int flags)
{
bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT);
struct ut_op *op = json_object_get_userdata(v);
- struct ut_op *base, *decl, *name, *args, *arg;
-
- /* find start of operand array */
- for (base = op; base && !base->is_first; base--)
- ;
-
- decl = base + (op->tag.off ? op->tag.off - 1 : 0);
- name = base + (decl->tree.operand[0] ? decl->tree.operand[0] - 1 : 0);
- args = base + (decl->tree.operand[1] ? decl->tree.operand[1] - 1 : 0);
+ struct ut_function *fn = op->tag.data;
+ size_t i;
sprintbuf(pb, "%sfunction%s%s(",
strict ? "\"" : "",
- (name != base) ? " " : "",
- (name != base) ? json_object_get_string(name->val) : "");
-
- for (arg = args; arg != base; arg = base + (arg->tree.next ? arg->tree.next - 1 : 0))
- sprintbuf(pb, "%s%s",
- (arg != args) ? ", " : "",
- json_object_get_string(arg->val));
+ fn->name ? " " : "",
+ fn->name ? fn->name : "");
+
+ if (fn->args) {
+ for (i = 0; i < json_object_array_length(fn->args); i++) {
+ sprintbuf(pb, "%s%s",
+ i ? ", " : "",
+ json_object_get_string(json_object_array_get_idx(fn->args, i)));
+ }
+ }
return sprintbuf(pb, ") { ... }%s", strict ? "\"" : "");
}
+#define ALIGN(x) (((x) + sizeof(size_t) - 1) & -sizeof(size_t))
+
struct json_object *
-ut_new_func(struct ut_op *decl)
+ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope)
{
struct json_object *val = xjs_new_object();
- struct ut_op *op = xalloc(sizeof(*op));
- struct ut_op *base;
+ struct ut_op *op, *name, *args, *arg;
+ struct ut_function *fn;
+ size_t sz;
+
+ sz = ALIGN(sizeof(*op)) + ALIGN(sizeof(*fn));
- /* find start of operand array */
- for (base = decl; base && !base->is_first; base--)
- ;
+ name = ut_get_op(s, decl->tree.operand[0]);
+ args = ut_get_op(s, decl->tree.operand[1]);
+
+ if (name)
+ sz += ALIGN(json_object_get_string_len(name->val) + 1);
+
+ op = xalloc(sz);
+
+ fn = (void *)op + ALIGN(sizeof(*op));
+ fn->entry = decl->tree.operand[2];
+
+ if (name) {
+ fn->name = (char *)fn + ALIGN(sizeof(*fn));
+ strcpy(fn->name, json_object_get_string(name->val));
+ }
+
+ if (args) {
+ fn->args = xjs_new_array();
+
+ for (arg = args; arg; arg = ut_get_op(s, arg->tree.next))
+ json_object_array_add(fn->args, json_object_get(arg->val));
+ }
+
+ fn->parent_scope = ut_acquire_scope(scope);
op->val = val;
op->type = T_FUNC;
- op->tag.off = (decl - base) + 1;
+ op->tag.data = fn;
- json_object_set_serializer(val, func_to_string, op, obj_free);
+ json_object_set_serializer(val, func_to_string, op, func_free);
return op->val;
}
static void
+scope_free(struct json_object *v, void *ud)
+{
+ struct ut_scope *sc = ud;
+
+ if (sc->parent) {
+ ut_release_scope(json_object_get_userdata(sc->parent));
+ sc->parent = NULL;
+ }
+
+ sc->scope = NULL;
+}
+
+void
+ut_release_scope(struct ut_scope *sc)
+{
+ if (sc->refs == 0)
+ abort();
+
+ sc->refs--;
+
+ if (sc->refs == 0) {
+ json_object_put(sc->scope);
+ sc->scope = NULL;
+
+ if (sc->parent) {
+ ut_release_scope(json_object_get_userdata(sc->parent));
+ sc->parent = NULL;
+ }
+ }
+}
+
+struct ut_scope *
+ut_acquire_scope(struct ut_scope *sc)
+{
+ sc->refs++;
+
+ return sc;
+}
+
+struct ut_scope *
+ut_new_scope(struct ut_state *s, struct ut_scope *parent)
+{
+ struct ut_scope *sc;
+
+ sc = xalloc(sizeof(*sc));
+ sc->scope = xjs_new_object();
+
+ if (parent)
+ sc->parent = ut_acquire_scope(parent)->scope;
+
+ json_object_set_userdata(sc->scope, sc, scope_free);
+
+ sc->next = s->scopelist;
+ s->scopelist = sc;
+
+ return ut_acquire_scope(sc);
+}
+
+struct ut_scope *
+ut_parent_scope(struct ut_scope *scope)
+{
+ return json_object_get_userdata(scope->parent);
+}
+
+static void
ut_reset(struct ut_state *s)
{
s->semicolon_emitted = false;
@@ -343,16 +442,12 @@ ut_reset(struct ut_state *s)
void
ut_free(struct ut_state *s)
{
+ struct ut_scope *sc, *sc_next;
size_t n;
if (s) {
json_object_put(s->ctx);
- while (s->stack.off > 0)
- json_object_put(s->stack.scope[--s->stack.off]);
-
- free(s->stack.scope);
-
for (n = 0; n < s->poolsize; n++)
json_object_put(s->pool[n].val);
@@ -367,6 +462,18 @@ ut_free(struct ut_state *s)
while (ut_ext_types_count > 0)
json_object_put(ut_ext_types[--ut_ext_types_count].proto);
+ json_object_put(s->rval);
+
+ for (sc = s->scopelist; sc; sc = sc->next) {
+ json_object_put(sc->scope);
+ sc->scope = NULL;
+ }
+
+ for (sc = s->scopelist; sc; sc = sc_next) {
+ sc_next = sc->next;
+ free(sc);
+ }
+
free(ut_ext_types);
free(s->filename);
free(s);
diff --git a/ast.h b/ast.h
index 3cf0b4f..67330fb 100644
--- a/ast.h
+++ b/ast.h
@@ -81,6 +81,19 @@ struct ut_op {
};
};
+struct ut_scope {
+ struct ut_scope *next;
+ struct json_object *scope, *ctx, *parent;
+ size_t refs;
+};
+
+struct ut_function {
+ char *name;
+ struct json_object *args;
+ struct ut_scope *scope, *parent_scope;
+ uint32_t entry;
+};
+
struct ut_state {
struct ut_op *pool;
uint32_t poolsize;
@@ -103,12 +116,8 @@ struct ut_state {
char *regexp_error;
} info;
} error;
- struct {
- struct json_object **scope;
- uint8_t size;
- uint8_t off;
- } stack;
- struct json_object *ctx;
+ struct ut_scope *scopelist, *scope;
+ struct json_object *ctx, *rval;
char *filename;
};
@@ -137,12 +146,17 @@ uint32_t ut_append_op(struct ut_state *s, uint32_t a, uint32_t b);
enum ut_error_type ut_parse(struct ut_state *s, const char *expr);
void ut_free(struct ut_state *s);
-struct json_object *ut_new_func(struct ut_op *decl);
+struct json_object *ut_new_func(struct ut_state *s, struct ut_op *decl, struct ut_scope *scope);
struct json_object *ut_new_object(struct json_object *proto);
struct json_object *ut_new_double(double v);
struct json_object *ut_new_null(void);
struct json_object *ut_new_regexp(const char *source, bool icase, bool newline, bool global, char **err);
+struct ut_scope *ut_new_scope(struct ut_state *s, struct ut_scope *parent);
+struct ut_scope *ut_parent_scope(struct ut_scope *scope);
+struct ut_scope *ut_acquire_scope(struct ut_scope *scope);
+void ut_release_scope(struct ut_scope *scope);
+
bool ut_register_extended_type(const char *name, struct json_object *proto, void (*freefn)(void *));
struct json_object *ut_set_extended_type(struct json_object *v, const char *name, void *data);
void **ut_get_extended_type(struct json_object *val, const char *name);
diff --git a/eval.c b/eval.c
index 18fdf3c..7052749 100644
--- a/eval.c
+++ b/eval.c
@@ -166,45 +166,6 @@ ut_cast_number(struct json_object *v, int64_t *n, double *d)
}
static struct json_object *
-ut_getscope(struct ut_state *state, uint8_t depth)
-{
- if (depth >= state->stack.off)
- return NULL;
-
- return state->stack.scope[state->stack.off - depth - 1];
-}
-
-static struct json_object *
-ut_addscope(struct ut_state *state, uint32_t decl)
-{
- struct json_object *scope;
-
- if (state->stack.off >= 255)
- return ut_exception(state, decl, "Runtime error: Too much recursion");
-
- if (state->stack.off >= state->stack.size) {
- state->stack.scope = xrealloc(state->stack.scope, (state->stack.size + 1) * sizeof(*state->stack.scope));
- state->stack.size++;
- }
-
- scope = ut_new_object(NULL);
- state->stack.scope[state->stack.off++] = scope;
-
- return scope;
-}
-
-void
-ut_putval(struct json_object *val)
-{
- struct ut_op *tag = json_object_get_userdata(val);
-
- if (tag && tag->val != val)
- json_object_put(tag->val);
-
- json_object_put(val);
-}
-
-static struct json_object *
ut_execute_op(struct ut_state *state, uint32_t off);
static struct json_object *
@@ -288,8 +249,8 @@ ut_getref(struct ut_state *state, uint32_t off, struct json_object **key)
struct ut_op *op = ut_get_op(state, off);
uint32_t off1 = op ? op->tree.operand[0] : 0;
uint32_t off2 = op ? op->tree.operand[1] : 0;
- struct json_object *val, *scope, *next;
- uint8_t i;
+ struct ut_scope *sc, *next;
+ struct json_object *val;
if (key)
*key = NULL;
@@ -313,14 +274,13 @@ ut_getref(struct ut_state *state, uint32_t off, struct json_object **key)
return ut_execute_op(state, off1);
}
else if (op && op->type == T_LABEL) {
- i = 0;
- scope = ut_getscope(state, i);
+ sc = state->scope;
while (true) {
- if (json_object_object_get_ex(scope, json_object_get_string(op->val), NULL))
+ if (json_object_object_get_ex(sc->scope, json_object_get_string(op->val), NULL))
break;
- next = ut_getscope(state, ++i);
+ next = ut_parent_scope(sc);
if (!next) {
if (state->strict_declarations) {
@@ -332,13 +292,13 @@ ut_getref(struct ut_state *state, uint32_t off, struct json_object **key)
break;
}
- scope = next;
+ sc = next;
}
if (key)
*key = json_object_get(op->val);
- return json_object_get(scope);
+ return json_object_get(sc->scope);
}
else {
if (key)
@@ -484,8 +444,8 @@ ut_execute_assign(struct ut_state *state, uint32_t off)
if (!ut_is_type(val, T_EXCEPTION))
ut_setval(scope, key, val);
- ut_putval(scope);
- ut_putval(key);
+ json_object_put(scope);
+ json_object_put(key);
return val;
}
@@ -506,7 +466,7 @@ ut_execute_local(struct ut_state *state, uint32_t off)
if (ut_is_type(val, T_EXCEPTION))
return val;
- rv = ut_setval(ut_getscope(state, 0), label->val, val);
+ rv = ut_setval(state->scope->scope, label->val, val);
}
as = ut_get_op(state, as->tree.next);
@@ -524,7 +484,7 @@ ut_test_condition(struct ut_state *state, uint32_t off)
struct json_object *val = ut_execute_op_sequence(state, off);
bool istrue = ut_val_is_truish(val);
- ut_putval(val);
+ json_object_put(val);
return istrue;
}
@@ -580,10 +540,10 @@ ut_execute_for(struct ut_state *state, uint32_t off)
if (ut_is_type(val, T_EXCEPTION))
return val;
- scope = local ? ut_getscope(state, 0) : ut_getref(state, ut_get_off(state, ivar), NULL);
+ scope = local ? state->scope->scope : ut_getref(state, ut_get_off(state, ivar), NULL);
if (ut_is_type(scope, T_EXCEPTION)) {
- ut_putval(val);
+ json_object_put(val);
return scope;
}
@@ -594,7 +554,7 @@ ut_execute_for(struct ut_state *state, uint32_t off)
item = json_object_array_get_idx(val, arridx);
ut_setval(scope, ivar->val, item);
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_execute_op_sequence(state, ut_get_off(state, body));
tag = json_object_get_userdata(rv);
@@ -602,13 +562,13 @@ ut_execute_for(struct ut_state *state, uint32_t off)
switch (tag ? tag->type : 0) {
case T_RETURN:
case T_EXCEPTION:
- ut_putval(val);
+ json_object_put(val);
return rv;
case T_BREAK:
- ut_putval(val);
- ut_putval(rv);
+ json_object_put(val);
+ json_object_put(rv);
return NULL;
}
@@ -617,7 +577,7 @@ ut_execute_for(struct ut_state *state, uint32_t off)
else if (json_object_is_type(val, json_type_object)) {
json_object_object_foreach(val, key, item) {
ut_setval(scope, ivar->val, xjs_new_string(key));
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_execute_op_sequence(state, ut_get_off(state, body));
tag = json_object_get_userdata(rv);
@@ -625,21 +585,21 @@ ut_execute_for(struct ut_state *state, uint32_t off)
switch (tag ? tag->type : 0) {
case T_RETURN:
case T_EXCEPTION:
- ut_putval(val);
+ json_object_put(val);
return rv;
case T_BREAK:
- ut_putval(val);
- ut_putval(rv);
+ json_object_put(val);
+ json_object_put(rv);
return NULL;
}
}
}
- ut_putval(val);
- ut_putval(rv);
+ json_object_put(val);
+ json_object_put(rv);
return NULL;
}
@@ -650,11 +610,11 @@ ut_execute_for(struct ut_state *state, uint32_t off)
if (ut_is_type(val, T_EXCEPTION))
return val;
- ut_putval(val);
+ json_object_put(val);
}
while (test ? ut_test_condition(state, ut_get_off(state, test)) : true) {
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_execute_op_sequence(state, ut_get_off(state, body));
tag = json_object_get_userdata(rv);
@@ -665,7 +625,7 @@ ut_execute_for(struct ut_state *state, uint32_t off)
return rv;
case T_BREAK:
- ut_putval(rv);
+ json_object_put(rv);
return NULL;
}
@@ -674,16 +634,16 @@ ut_execute_for(struct ut_state *state, uint32_t off)
val = ut_execute_op_sequence(state, ut_get_off(state, incr));
if (ut_is_type(val, T_EXCEPTION)) {
- ut_putval(rv);
+ json_object_put(rv);
return val;
}
- ut_putval(val);
+ json_object_put(val);
}
}
- ut_putval(rv);
+ json_object_put(rv);
return NULL;
}
@@ -699,7 +659,7 @@ ut_execute_while(struct ut_state *state, uint32_t off)
bool cond;
while (1) {
- ut_putval(rv);
+ json_object_put(rv);
v = test ? ut_execute_op_sequence(state, test) : NULL;
cond = test ? ut_val_is_truish(v) : true;
@@ -707,7 +667,7 @@ ut_execute_while(struct ut_state *state, uint32_t off)
if (ut_is_type(v, T_EXCEPTION))
return v;
- ut_putval(v);
+ json_object_put(v);
if (!cond)
return NULL;
@@ -721,13 +681,13 @@ ut_execute_while(struct ut_state *state, uint32_t off)
return rv;
case T_BREAK:
- ut_putval(rv);
+ json_object_put(rv);
return NULL;
}
}
- ut_putval(rv);
+ json_object_put(rv);
return NULL;
}
@@ -740,7 +700,7 @@ ut_execute_and_or(struct ut_state *state, uint32_t off)
int i;
for (i = 0; i < ARRAY_SIZE(op->tree.operand) && op->tree.operand[i]; i++) {
- ut_putval(val);
+ json_object_put(val);
val = ut_execute_op(state, op->tree.operand[i]);
@@ -833,7 +793,7 @@ _ut_get_operands(struct ut_state *state, struct ut_op *op, size_t n, struct json
if (ut_is_type(v[i], T_EXCEPTION)) {
for (j = 0; j < i; j++)
- ut_putval(v[j]);
+ json_object_put(v[j]);
return v[i];
}
@@ -858,8 +818,8 @@ ut_execute_rel(struct ut_state *state, uint32_t off)
rv = xjs_new_boolean(ut_cmp(op->type, v[0], v[1]));
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
return rv;
}
@@ -917,8 +877,8 @@ ut_execute_equality(struct ut_state *state, uint32_t off)
equal = ut_eq(v[0], v[1]);
rv = xjs_new_boolean((op->type == T_EQS) ? equal : !equal);
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
return rv;
}
@@ -950,8 +910,8 @@ ut_execute_in(struct ut_state *state, uint32_t off)
found = json_object_object_get_ex(v[1], key, NULL);
}
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
return xjs_new_boolean(found);
}
@@ -972,21 +932,21 @@ ut_execute_inc_dec(struct ut_state *state, uint32_t off)
val = ut_getval(scope, key);
- ut_putval(scope);
- ut_putval(key);
+ json_object_put(scope);
+ json_object_put(key);
if (ut_cast_number(val, &n, &d) == json_type_double)
nval = ut_new_double(d + (op->type == T_INC ? 1.0 : -1.0));
else
nval = xjs_new_int64(n + (op->type == T_INC ? 1 : -1));
- ut_putval(ut_setval(scope, key, nval));
+ json_object_put(ut_setval(scope, key, nval));
/* postfix inc/dec, return old val */
if (op->is_postfix)
return val;
- ut_putval(val);
+ json_object_put(val);
return json_object_get(nval);
}
@@ -1037,12 +997,12 @@ ut_execute_object(struct ut_state *state, uint32_t off)
}
struct json_object *
-ut_invoke(struct ut_state *state, uint32_t off, struct json_object *scope,
+ut_invoke(struct ut_state *state, uint32_t off, struct json_object *this,
struct json_object *func, struct json_object *argvals)
{
struct ut_op *tag = json_object_get_userdata(func);
- struct ut_op *arg, *decl;
- struct json_object *s, *rv = NULL;
+ struct json_object *rv = NULL;
+ struct ut_function *fn;
size_t arridx;
ut_c_fn *cfn;
@@ -1056,59 +1016,47 @@ ut_invoke(struct ut_state *state, uint32_t off, struct json_object *scope,
return cfn ? cfn(state, off, argvals) : NULL;
}
- decl = ut_get_op(state, tag->tag.off);
- arg = ut_get_op(state, decl ? decl->tree.operand[1] : 0);
-
- s = scope ? scope : ut_addscope(state, ut_get_off(state, decl));
-
- if (!json_object_is_type(s, json_type_object))
- return s;
-
- for (arridx = 0; arg; arridx++, arg = ut_get_op(state, arg->tree.next))
- ut_setval(s, arg->val, argvals ? json_object_array_get_idx(argvals, arridx) : NULL);
+ fn = tag->tag.data;
+ fn->scope = ut_new_scope(state, fn->parent_scope);
+ fn->scope->ctx = json_object_get(this ? this : state->ctx);
- /* store the function "this" context in the proto member of the scope tag structure */
- tag = json_object_get_userdata(s);
+ ut_release_scope(state->scope);
+ state->scope = ut_acquire_scope(fn->scope);
- if (tag)
- tag->tag.proto = json_object_get(state->ctx);
+ if (fn->args)
+ for (arridx = 0; arridx < json_object_array_length(fn->args); arridx++)
+ ut_setval(fn->scope->scope, json_object_array_get_idx(fn->args, arridx),
+ argvals ? json_object_array_get_idx(argvals, arridx) : NULL);
- rv = ut_execute_op_sequence(state, decl->tree.operand[2]);
+ rv = ut_execute_op_sequence(state, fn->entry);
tag = json_object_get_userdata(rv);
switch (tag ? tag->type : 0) {
case T_BREAK:
case T_CONTINUE:
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_exception(state, ut_get_off(state, tag),
"Syntax error: %s statement must be inside loop",
tokennames[tag->type]);
break;
case T_RETURN:
- /* handle magic null */
- if (json_object_is_type(rv, json_type_boolean)) {
- if (!strcmp(json_object_get_string(rv), "null")) {
- ut_putval(rv);
- rv = NULL;
- }
- }
-
+ json_object_put(rv);
+ rv = json_object_get(state->rval);
break;
}
- /* we left the function, remove the "this" context from the scope tag structure */
- tag = json_object_get_userdata(s);
+ /* we left the function, pop the function scope... */
+ ut_release_scope(state->scope);
+ state->scope = ut_acquire_scope(ut_parent_scope(fn->scope));
- if (tag) {
- json_object_put(tag->tag.proto);
- tag->tag.proto = NULL;
- }
+ /* ... and remove the "this" context... */
+ json_object_put(fn->scope->ctx);
+ fn->scope->ctx = NULL;
- if (!scope) {
- state->stack.scope[--state->stack.off] = NULL;
- json_object_put(s);
- }
+ /* ... and reset the function scope */
+ ut_release_scope(fn->scope);
+ fn->scope = NULL;
return rv;
}
@@ -1141,8 +1089,8 @@ ut_execute_call(struct ut_state *state, uint32_t off)
rv = ut_invoke(state, off, NULL, v[0], v[1]);
}
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
return rv;
}
@@ -1195,7 +1143,7 @@ ut_execute_exp(struct ut_state *state, uint32_t off)
break;
}
- ut_putval(val);
+ json_object_put(val);
return NULL;
}
@@ -1214,7 +1162,7 @@ ut_execute_unary_plus_minus(struct ut_state *state, uint32_t off)
t = ut_cast_number(v[0], &n, &d);
- ut_putval(v[0]);
+ json_object_put(v[0]);
switch (t) {
case json_type_int:
@@ -1258,8 +1206,8 @@ ut_execute_arith(struct ut_state *state, uint32_t off)
rv = xjs_new_string(s);
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
free(s);
return rv;
@@ -1268,8 +1216,8 @@ ut_execute_arith(struct ut_state *state, uint32_t off)
t1 = ut_cast_number(v[0], &n1, &d1);
t2 = ut_cast_number(v[1], &n2, &d2);
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
if (t1 == json_type_double || t2 == json_type_double) {
d1 = (t1 == json_type_double) ? d1 : (double)n1;
@@ -1339,8 +1287,8 @@ ut_execute_bitop(struct ut_state *state, uint32_t off)
if (ut_cast_number(v[1], &n2, &d) == json_type_double)
n2 = isnan(d) ? 0 : (int64_t)d;
- ut_putval(v[0]);
- ut_putval(v[1]);
+ json_object_put(v[0]);
+ json_object_put(v[1]);
switch (op->type) {
case T_LSHIFT:
@@ -1384,7 +1332,7 @@ ut_execute_compl(struct ut_state *state, uint32_t off)
if (ut_cast_number(v[0], &n, &d) == json_type_double)
n = isnan(d) ? 0 : (int64_t)d;
- ut_putval(v[0]);
+ json_object_put(v[0]);
return xjs_new_int64(~n);
}
@@ -1393,16 +1341,18 @@ static struct json_object *
ut_execute_return(struct ut_state *state, uint32_t off)
{
struct ut_op *op = ut_get_op(state, off);
- struct json_object *v[1];
+ struct json_object *v[1], *rv;
ut_get_operands(state, op, v);
- if (!v[0])
- v[0] = ut_new_null();
+ json_object_put(state->rval);
+ state->rval = v[0];
+
+ rv = xjs_new_boolean(false);
- json_object_set_userdata(v[0], op, NULL);
+ json_object_set_userdata(rv, op, NULL);
- return v[0];
+ return rv;
}
static struct json_object *
@@ -1420,7 +1370,7 @@ static struct json_object *
ut_execute_function(struct ut_state *state, uint32_t off)
{
struct ut_op *op = ut_get_op(state, off);
- struct json_object *obj = ut_new_func(op);
+ struct json_object *obj = ut_new_func(state, op, state->scope);
return obj;
}
@@ -1428,7 +1378,7 @@ ut_execute_function(struct ut_state *state, uint32_t off)
static struct json_object *
ut_execute_this(struct ut_state *state, uint32_t off)
{
- return json_object_get(ut_getproto(ut_getscope(state, 0)));
+ return json_object_get(state->scope->ctx);
}
static struct json_object *
@@ -1441,12 +1391,12 @@ ut_execute_try_catch(struct ut_state *state, uint32_t off)
if (ut_is_type(rv, T_EXCEPTION)) {
if (op->tree.operand[1])
- ut_putval(ut_setval(ut_getscope(state, 0), ut_get_child(state, off, 1)->val,
+ json_object_put(ut_setval(state->scope->scope, ut_get_child(state, off, 1)->val,
xjs_new_string(json_object_get_string(rv))));
memset(&state->error, 0, sizeof(state->error));
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_execute_op_sequence(state, op->tree.operand[2]);
}
@@ -1459,7 +1409,7 @@ ut_match_case(struct ut_state *state, struct json_object *v, struct ut_op *Case)
struct json_object *caseval = ut_execute_op_sequence(state, Case->tree.operand[0]);
bool rv = ut_eq(v, caseval);
- ut_putval(caseval);
+ json_object_put(caseval);
return rv;
}
@@ -1480,7 +1430,7 @@ ut_execute_switch_case(struct ut_state *state, uint32_t off)
/* remember default case and throw on dupes */
if (Case->type == T_DEFAULT) {
if (Default) {
- ut_putval(v[0]);
+ json_object_put(v[0]);
return ut_exception(state, ut_get_off(state, Case),
"Syntax error: more than one switch default case");
@@ -1502,7 +1452,7 @@ ut_execute_switch_case(struct ut_state *state, uint32_t off)
Case != NULL;
Case = ut_get_op(state, Case->tree.next))
{
- ut_putval(rv);
+ json_object_put(rv);
if (Case == Default)
rv = ut_execute_op_sequence(state, Default->tree.operand[0]);
@@ -1510,13 +1460,13 @@ ut_execute_switch_case(struct ut_state *state, uint32_t off)
rv = ut_execute_op_sequence(state, Case->tree.operand[1]);
if (ut_is_type(rv, T_BREAK)) {
- ut_putval(rv);
+ json_object_put(rv);
rv = NULL;
break;
}
}
- ut_putval(v[0]);
+ json_object_put(v[0]);
return rv;
}
@@ -1544,7 +1494,7 @@ ut_execute_op(struct ut_state *state, uint32_t off)
val = ut_execute_function(state, off);
if (op1)
- ut_setval(ut_getscope(state, 0), op1->val, val);
+ ut_setval(state->scope->scope, op1->val, val);
return val;
@@ -1562,7 +1512,7 @@ ut_execute_op(struct ut_state *state, uint32_t off)
case T_LABEL:
scope = ut_getref(state, off, &key);
- ut_putval(state->ctx);
+ json_object_put(state->ctx);
state->ctx = NULL;
if (state->strict_declarations && scope == NULL) {
@@ -1572,23 +1522,23 @@ ut_execute_op(struct ut_state *state, uint32_t off)
}
val = ut_getval(scope, key);
- ut_putval(scope);
- ut_putval(key);
+ json_object_put(scope);
+ json_object_put(key);
return val;
case T_DOT:
scope = ut_getref_required(state, off, &key);
- ut_putval(state->ctx);
+ json_object_put(state->ctx);
state->ctx = json_object_get(scope);
if (!key)
return scope;
val = ut_getval(scope, key);
- ut_putval(scope);
- ut_putval(key);
+ json_object_put(scope);
+ json_object_put(key);
return val;
@@ -1597,15 +1547,15 @@ ut_execute_op(struct ut_state *state, uint32_t off)
if (op->is_postfix) {
scope = ut_getref_required(state, off, &key);
- ut_putval(state->ctx);
+ json_object_put(state->ctx);
state->ctx = json_object_get(scope);
if (!key)
return scope;
val = ut_getval(scope, key);
- ut_putval(scope);
- ut_putval(key);
+ json_object_put(scope);
+ json_object_put(key);
return val;
}
@@ -1700,7 +1650,7 @@ ut_execute_op_sequence(struct ut_state *state, uint32_t off)
struct ut_op *op = NULL;
while (off) {
- ut_putval(v);
+ json_object_put(v);
v = ut_execute_op(state, off);
tag = v ? json_object_get_userdata(v) : NULL;
@@ -1761,7 +1711,7 @@ enum ut_error_type
ut_run(struct ut_state *state, struct json_object *env, struct json_object *modules)
{
struct ut_op *op = ut_get_op(state, state->main);
- struct json_object *entry, *scope, *args, *rv;
+ struct json_object *entry, *args, *rv;
size_t i;
if (!op || op->type != T_FUNC) {
@@ -1770,25 +1720,18 @@ ut_run(struct ut_state *state, struct json_object *env, struct json_object *modu
return UT_ERROR_EXCEPTION;
}
- entry = ut_execute_function(state, state->main);
-
- if (!entry)
- return UT_ERROR_EXCEPTION;
-
- scope = ut_addscope(state, state->main);
-
- if (!json_object_is_type(scope, json_type_object))
- return UT_ERROR_EXCEPTION;
-
+ state->scope = ut_new_scope(state, NULL);
state->ctx = NULL;
+ entry = ut_execute_function(state, state->main);
+
if (env) {
json_object_object_foreach(env, key, val)
- ut_register_variable(scope, key, json_object_get(val));
+ ut_register_variable(state->scope->scope, key, json_object_get(val));
}
- ut_globals_init(state, scope);
- ut_lib_init(state, scope);
+ ut_globals_init(state, state->scope->scope);
+ ut_lib_init(state, state->scope->scope);
args = xjs_new_array();
@@ -1797,13 +1740,13 @@ ut_run(struct ut_state *state, struct json_object *env, struct json_object *modu
json_object_array_put_idx(args, 0, json_object_get(json_object_array_get_idx(modules, i)));
rv = ut_invoke(state, 0, NULL,
- json_object_object_get(scope, "require"),
+ json_object_object_get(state->scope->scope, "require"),
args);
if (ut_is_type(rv, T_EXCEPTION))
goto out;
- ut_register_variable(scope,
+ ut_register_variable(state->scope->scope,
json_object_get_string(json_object_array_get_idx(modules, i)),
rv);
}
@@ -1814,6 +1757,8 @@ ut_run(struct ut_state *state, struct json_object *env, struct json_object *modu
rv = ut_invoke(state, state->main, NULL, entry, args);
out:
+ ut_release_scope(state->scope);
+
json_object_put(entry);
json_object_put(args);
json_object_put(rv);
diff --git a/eval.h b/eval.h
index 26f932f..869839e 100644
--- a/eval.h
+++ b/eval.h
@@ -27,9 +27,6 @@
__attribute__((format(printf, 3, 0))) struct json_object *
ut_exception(struct ut_state *state, uint32_t op, const char *fmt, ...);
-void
-ut_putval(struct json_object *val);
-
bool
ut_cmp(int how, struct json_object *v1, struct json_object *v2);
diff --git a/lib.c b/lib.c
index 3cbe11e..603deb8 100644
--- a/lib.c
+++ b/lib.c
@@ -527,7 +527,7 @@ ut_delete(struct ut_state *s, uint32_t off, struct json_object *args)
return NULL;
for (arrlen = json_object_array_length(args), arridx = 1; arridx < arrlen; arridx++) {
- ut_putval(rv);
+ json_object_put(rv);
key = json_object_get_string(json_object_array_get_idx(args, arridx));
rv = json_object_get(json_object_object_get(obj, key ? key : "null"));
@@ -600,10 +600,10 @@ ut_filter(struct ut_state *s, uint32_t off, struct json_object *args)
if (ut_val_is_truish(rv))
json_object_array_add(arr, json_object_get(json_object_array_get_idx(obj, arridx)));
- ut_putval(rv);
+ json_object_put(rv);
}
- ut_putval(cmpargs);
+ json_object_put(cmpargs);
return arr;
}
@@ -758,7 +758,7 @@ ut_map(struct ut_state *s, uint32_t off, struct json_object *args)
json_object_array_add(arr, ut_invoke(s, off, NULL, func, cmpargs));
}
- ut_putval(cmpargs);
+ json_object_put(cmpargs);
return arr;
}
@@ -875,7 +875,7 @@ sort_fn(const void *k1, const void *k2)
rv = ut_invoke(sort_ctx.s, sort_ctx.off, NULL, sort_ctx.fn, sort_ctx.args);
ret = !ut_val_is_truish(rv);
- ut_putval(rv);
+ json_object_put(rv);
return ret;
}
@@ -897,7 +897,7 @@ ut_sort(struct ut_state *s, uint32_t off, struct json_object *args)
}
json_object_array_sort(arr, sort_fn);
- ut_putval(sort_ctx.args);
+ json_object_put(sort_ctx.args);
return json_object_get(arr);
}
@@ -1489,9 +1489,9 @@ ut_require_so(struct ut_state *s, uint32_t off, const char *path)
}
static struct json_object *
-ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct json_object *scope)
+ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct ut_scope *scope)
{
- struct json_object *ex, *sc, *entry, *rv;
+ struct json_object *ex, *entry, *rv;
char *source, *msg;
struct stat st;
FILE *sfile;
@@ -1519,15 +1519,13 @@ ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct json_
return ex;
}
- sc = scope ? scope : xjs_new_object();
-
- entry = ut_new_func(ut_get_op(s, s->main));
+ entry = ut_new_func(s, ut_get_op(s, s->main), scope ? scope : s->scope);
- rv = ut_invoke(s, off, sc, entry, NULL);
+ rv = ut_invoke(s, off, NULL, entry, NULL);
if (ut_is_type(rv, T_EXCEPTION)) {
msg = ut_format_error(s, source);
- ut_putval(rv);
+ json_object_put(rv);
rv = ut_exception(s, off, "%s", msg);
free(msg);
}
@@ -1536,9 +1534,6 @@ ut_require_utpl(struct ut_state *s, uint32_t off, const char *path, struct json_
json_object_put(entry);
- if (sc != scope)
- json_object_put(sc);
-
return rv;
}
@@ -1593,14 +1588,25 @@ ut_require(struct ut_state *s, uint32_t off, struct json_object *args)
{
struct json_object *val = json_object_array_get_idx(args, 0);
struct json_object *search, *se, *res;
+ struct ut_scope *sc, *scparent;
size_t arridx, arrlen;
const char *name;
if (!json_object_is_type(val, json_type_string))
return NULL;
+ /* find root scope */
+ for (sc = s->scope; sc; ) {
+ scparent = ut_parent_scope(sc);
+
+ if (!scparent)
+ break;
+
+ sc = scparent;
+ }
+
name = json_object_get_string(val);
- search = json_object_object_get(s->stack.scope[0], "REQUIRE_SEARCH_PATH");
+ search = sc ? json_object_object_get(sc->scope, "REQUIRE_SEARCH_PATH") : NULL;
if (!json_object_is_type(search, json_type_array))
return ut_exception(s, off, "Global require search path not set");
@@ -2028,8 +2034,7 @@ ut_include(struct ut_state *s, uint32_t off, struct json_object *args)
{
struct json_object *rv, *path = json_object_array_get_idx(args, 0);
struct json_object *scope = json_object_array_get_idx(args, 1);
- struct json_object *prev_scope;
- size_t prev_stack_offset;
+ struct ut_scope *sc;
char *p, *filename;
if (!json_object_is_type(path, json_type_string))
@@ -2044,28 +2049,26 @@ ut_include(struct ut_state *s, uint32_t off, struct json_object *args)
return ut_exception(s, off, "Include file not found");
if (scope) {
- prev_stack_offset = s->stack.off;
- prev_scope = s->stack.scope[0];
+ sc = ut_new_scope(s, NULL);
- s->stack.scope[0] = json_object_get(scope);
- s->stack.off = 1;
+ json_object_object_foreach(scope, key, val)
+ json_object_object_add(sc->scope, key, json_object_get(val));
+ }
+ else {
+ sc = s->scope;
}
filename = s->filename;
s->filename = p;
- rv = ut_require_utpl(s, off, p, s->stack.scope[s->stack.off - 1]);
+ rv = ut_require_utpl(s, off, p, sc);
s->filename = filename;
free(p);
- if (scope) {
- s->stack.scope[0] = prev_scope;
- s->stack.off = prev_stack_offset;
-
- json_object_put(scope);
- }
+ if (scope)
+ json_object_put(sc->scope);
if (ut_is_type(rv, T_EXCEPTION))
return rv;
diff --git a/tests/02_runtime/05_closure_scope b/tests/02_runtime/05_closure_scope
new file mode 100644
index 0000000..40f9245
--- /dev/null
+++ b/tests/02_runtime/05_closure_scope
@@ -0,0 +1,35 @@
+Testing closure scopes.
+
+
+1. Ensure that the declaring scope is retained in functions.
+
+-- Expect stdout --
+Make function with x=1
+Make function with x=2
+Make function with x=3
+x is 1
+x is 2
+x is 3
+-- End --
+
+-- Testcase --
+{%
+ local count=0;
+
+ function a() {
+ local x = ++count;
+ print("Make function with x=", x, "\n");
+ return function() {
+ print("x is ", x, "\n");
+ };
+ }
+
+ local fn1 = a();
+ local fn2 = a();
+ local fn3 = a();
+
+ fn1();
+ fn2();
+ fn3();
+%}
+-- End --