V7_PRIVATE void v7_freeval(struct v7 *v7, struct v7_val *v) { assert(v->ref_count > 0); if (--v->ref_count > 0) return; if (v->type == V7_TYPE_OBJ) { struct v7_prop *p, *tmp; for (p = v->props; p != NULL; p = tmp) { tmp = p->next; free_prop(v7, p); } v->props = NULL; } if (v7_is_class(v, V7_CLASS_ARRAY)) { struct v7_prop *p, *tmp; for (p = v->v.array; p != NULL; p = tmp) { tmp = p->next; free_prop(v7, p); } v->v.array = NULL; } else if (v->type == V7_TYPE_STR && (v->flags & V7_STR_ALLOCATED)) { free(v->v.str.buf); } else if (v7_is_class(v, V7_CLASS_REGEXP) && (v->flags & V7_STR_ALLOCATED)) { free(v->v.regex); } else if (v7_is_class(v, V7_CLASS_FUNCTION)) { if ((v->flags & V7_STR_ALLOCATED) && (v->flags & V7_JS_FUNC)) { free(v->v.func.source_code); v7_freeval(v7, v->v.func.var_obj); } } if (v->flags & V7_VAL_ALLOCATED) { v->flags &= ~V7_VAL_ALLOCATED; v->flags |= ~V7_VAL_DEALLOCATED; memset(v, 0, sizeof(*v)); #ifdef V7_CACHE_OBJS v->next = v7->free_values; v7->free_values = v; #else free(v); #endif } }
V7_PRIVATE struct v7_prop *v7_get2(struct v7_val *obj, const struct v7_val *key, int own_prop) { struct v7_prop *m; for (; obj != NULL; obj = obj->proto) { if (v7_is_class(obj, V7_CLASS_ARRAY) && key->type == V7_TYPE_NUM) { int i = (int) key->v.num; for (m = obj->v.array; m != NULL; m = m->next) { if (i-- == 0) return m; } } else if (obj->type == V7_TYPE_OBJ) { for (m = obj->props; m != NULL; m = m->next) { if (cmp(m->key, key) == 0) return m; } } if (own_prop) break; if (obj->proto == obj) break; } return NULL; }
static void Str_match(struct v7_c_func_arg *cfa) { struct slre_cap caps[100]; const struct v7_string *s = &cfa->this_obj->v.str; int i, n; cfa->result->type = V7_TYPE_NULL; memset(caps, 0, sizeof(caps)); if (cfa->num_args == 1 && v7_is_class(cfa->args[0], V7_CLASS_REGEXP) && (n = slre_match(cfa->args[0]->v.regex, s->buf, s->len, caps, ARRAY_SIZE(caps) - 1, 0)) > 0) { v7_set_class(cfa->result, V7_CLASS_ARRAY); v7_append(cfa->v7, cfa->result, v7_mkv(cfa->v7, V7_TYPE_STR, s->buf, (long) n, 1)); for (i = 0; i < (int) ARRAY_SIZE(caps); i++) { if (caps[i].len == 0) break; v7_append(cfa->v7, cfa->result, v7_mkv(cfa->v7, V7_TYPE_STR, caps[i].ptr, (long) caps[i].len, 1)); } } }
V7_PRIVATE int is_bool(const struct v7_val *v) { obj_sanity_check(v); return v->type == V7_TYPE_BOOL || v7_is_class(v, V7_CLASS_BOOLEAN); }
V7_PRIVATE int is_num(const struct v7_val *v) { obj_sanity_check(v); return v->type == V7_TYPE_NUM || v7_is_class(v, V7_CLASS_NUMBER); }
V7_PRIVATE int is_string(const struct v7_val *v) { obj_sanity_check(v); return v->type == V7_TYPE_STR || v7_is_class(v, V7_CLASS_STRING); }
V7_PRIVATE enum v7_err Str_replace(struct v7_c_func_arg *cfa) { #define v7 (cfa->v7) /* Needed for TRY() macro below */ struct v7_val *result = v7_push_new_object(v7); const char *out_str = cfa->this_obj->v.str.buf; uint8_t own = 1; size_t out_len = cfa->this_obj->v.str.len; int old_sp = v7->sp; if (cfa->num_args > 1) { const char *const str_end = cfa->this_obj->v.str.buf + cfa->this_obj->v.str.len; char *p = cfa->this_obj->v.str.buf; uint32_t out_sub_num = 0; struct v7_val *re = cfa->args[0], *str_func = cfa->args[1], *arr = NULL; struct re_tok out_sub[V7_RE_MAX_REPL_SUB], *ptok = out_sub; struct Resub loot; TRY(check_str_re_conv(v7, &re, 1)); TRY(regex_check_prog(re)); if (v7_is_class(str_func, V7_CLASS_FUNCTION)) { arr = v7_push_new_object(v7); v7_set_class(arr, V7_CLASS_ARRAY); TRY(v7_push(v7, str_func)); } else TRY(check_str_re_conv(v7, &str_func, 0)); out_len = 0; do { int i; if (re_exec(re->v.str.prog, re->fl.fl, p, &loot)) break; if (p != loot.sub->start) { ptok->start = p; ptok->end = loot.sub->start; ptok++; out_len += loot.sub->start - p; out_sub_num++; } if (NULL != arr) { /* replace function */ Rune rune; int old_sp = v7->sp, utf_shift = 0; struct v7_val *rez_str; for (i = 0; i < loot.subexpr_num; i++) v7_push_string(v7, loot.sub[i].start, loot.sub[i].end - loot.sub[i].start, 1); for (i = 0; p + i < loot.sub[0].start; i += chartorune(&rune, p + i), utf_shift++) ; TRY(push_number(v7, utf_shift)); TRY(v7_push(v7, cfa->this_obj)); rez_str = v7_call(v7, cfa->this_obj, loot.subexpr_num + 2); TRY(check_str_re_conv(v7, &rez_str, 0)); if (rez_str->v.str.len) { ptok->start = rez_str->v.str.buf; ptok->end = rez_str->v.str.buf + rez_str->v.str.len; ptok++; out_len += rez_str->v.str.len; out_sub_num++; v7_append(v7, arr, rez_str); } TRY(inc_stack(v7, old_sp - v7->sp)); } else { /* replace string */ struct Resub newsub; re_rplc(&loot, p, str_func->v.str.buf, &newsub); for (i = 0; i < newsub.subexpr_num; i++) { ptok->start = newsub.sub[i].start; ptok->end = newsub.sub[i].end; ptok++; out_len += newsub.sub[i].end - newsub.sub[i].start; out_sub_num++; } } p = (char *)loot.sub->end; } while (re->fl.fl.re_g && p < str_end); if (p < str_end) { ptok->start = p; ptok->end = str_end; ptok++; out_len += str_end - p; out_sub_num++; } out_str = malloc(out_len + 1); CHECK(out_str, V7_OUT_OF_MEMORY); ptok = out_sub; p = (char *)out_str; do { size_t ln = ptok->end - ptok->start; memcpy(p, ptok->start, ln); p += ln; ptok++; } while (--out_sub_num); *p = '\0'; own = 0; } TRY(inc_stack(v7, old_sp - v7->sp)); v7_init_str(result, out_str, out_len, own); result->fl.fl.str_alloc = 1; return V7_OK; #undef v7 }
static const char *test_v7_exec(void) { struct v7 *v7 = v7_create(); ASSERT(v7_exec(v7, "") == V7_OK); ASSERT(v7_exec(v7, "-2;") == V7_OK); ASSERT(check_num(v7, -2.0)); ASSERT(v7_exec(v7, "3 + 4") == V7_OK); ASSERT(check_num(v7, 7.0)); ASSERT(v7_exec(v7, "123.456") == V7_OK); ASSERT(check_num(v7, 123.456)); ASSERT(v7_exec(v7, "NaN") == V7_OK); ASSERT(check_num(v7, NAN)); // TODO: fix infinity handling under MSVC6 #ifndef _WIN32 ASSERT(v7_exec(v7, "Infinity") == V7_OK); ASSERT(check_num(v7, INFINITY)); ASSERT(v7_exec(v7, "-Infinity") == V7_OK); ASSERT(check_num(v7, -INFINITY)); #endif ASSERT(v7_exec(v7, "2()") == V7_CALLED_NON_FUNCTION); ASSERT(v7_exec(v7, " 15 + 2 \r\n * 2 / 1 - 3 * 4 ; ") == V7_OK); ASSERT(v7_exec(v7, "( (5 ) );") == V7_OK); ASSERT(check_num(v7, 5.0)); ASSERT(v7_exec(v7, "(2 + (12 / 4));") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_NUM); ASSERT(v7_top(v7)[-1]->v.num == 5.0); ASSERT(v7_exec(v7, "1;2 7") == V7_OK); ASSERT(check_num(v7, 7.0)); ASSERT(v7_exec(v7, "a + 5") == V7_TYPE_ERROR); ASSERT(v7_exec(v7, "print();") == V7_OK); ASSERT(v7_exec(v7, "print(this);") == V7_OK); ASSERT(v7_exec(v7, "a = 7;") == V7_OK); ASSERT(check_num(v7, 7.0)); ASSERT(v7_exec(v7, "print(this);") == V7_OK); ASSERT(v7_exec(v7, "b = a + 3;") == V7_OK); ASSERT(check_num(v7, 10.0)); ASSERT(v7_exec(v7, "print(this);") == V7_OK); ASSERT(v7_exec(v7, "c = b * (a + 3) / 2;") == V7_OK); ASSERT(check_num(v7, 50.0)); ASSERT(v7_exec(v7, "print(this);") == V7_OK); ASSERT(v7_exec(v7, "var x = 12 + 2 - a + b+ 3 / 4 * a;") == V7_OK); ASSERT(v7_exec(v7, "b + 2; x + 3 + 1 z = x -2;") == V7_OK); ASSERT(v7_exec(v7, "x; var y, z;") == V7_OK); ASSERT(v7_exec(v7, "1 2 3") == V7_OK); ASSERT(v7_exec(v7, "var k = true;") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_BOOL); ASSERT(v7_top(v7)[-1]->v.num != 0.0); ASSERT(v7_exec(v7, "var blah = 'kuku';") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_STR); // Test that k.y does exist ASSERT(v7_exec(v7, "k = { y: 17 };") == V7_OK); ASSERT(v7_exec(v7, "k.y") == V7_OK); ASSERT(check_num(v7, 17.0)); v7_exec(v7, "print(this);"); // Delete k.y and make sure it's gone ASSERT(v7_exec(v7, "delete k.y;") == V7_OK); ASSERT(v7_exec(v7, "k.y;") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_UNDEF); ASSERT(v7_sp(v7) == 1); ASSERT(v7_exec(v7, "delete b; b;") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_UNDEF); ASSERT(v7_sp(v7) == 1); ASSERT(v7_exec(v7, "k = { key1: {x:3}, key2: ':-)', y: 5 };") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_OBJ); ASSERT(v7_sp(v7) == 1); ASSERT(v7_exec(v7, "k.x = 47;") == V7_OK); ASSERT(v7_exec(v7, "k.qwe = { foo: 5 };") == V7_OK); v7_exec(v7, "print(k);"); ASSERT(v7_exec(v7, "k.qwe.foo = 15;") == V7_OK); v7_exec(v7, "print(k);"); ASSERT(v7_exec(v7, "k.key1.x + 4") == V7_OK); ASSERT(check_num(v7, 7.0)); ASSERT(v7_exec(v7, "k.foo") == V7_OK); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_UNDEF); ASSERT(v7_exec(v7, "var z = 'key1'; k[z]['x']") == V7_OK); ASSERT(check_num(v7, 3.0)); ASSERT(v7_exec(v7, "var stk = 1;") == V7_OK); ASSERT(check_num(v7, 1.0)); ASSERT(v7_exec(v7, "var f1 = function(x, y) { } ; ") == V7_OK); ASSERT(v7_sp(v7) > 0); ASSERT(v7_top(v7)[-1]->type == V7_TYPE_OBJ); ASSERT(memcmp(v7_top(v7)[-1]->v.func.source_code, "(x, y) { } ", 10) == 0); ASSERT(v7_exec(v7, "var f1 = function(x, y) { return x * y; };") == V7_OK); ASSERT(v7_is_class(v7_top(v7)[-1], V7_CLASS_FUNCTION)); ASSERT(v7_exec(v7, "f1(2, 3)") == V7_OK); ASSERT(check_num(v7, 6.0)); ASSERT(v7_exec(v7, "f1(12, 4) + 1;") == V7_OK); ASSERT(check_num(v7, 49.0)); ASSERT(v7_exec(v7, "f1(12, 4) * 2;") == V7_OK); ASSERT(check_num(v7, 96.0)); ASSERT(v7_exec(v7, "f = function(x,y,z) {print(this);};") == V7_OK); ASSERT(v7_sp(v7) == 1); ASSERT(v7_exec(v7, "f();") == V7_OK); ASSERT(v7_exec(v7, "f({});") == V7_OK); ASSERT(v7_exec(v7, "f(1, 2);") == V7_OK); ASSERT(v7_exec(v7, "f(123, {});") == V7_OK); ASSERT(v7_exec(v7, "if (0) f1 = 2; ") == V7_OK); ASSERT(v7_exec(v7, "if (5) { f1 = 3; f2 = function(){}; } ") == V7_OK); ASSERT(v7_exec(v7, "0 ? 1 : 2;") == V7_OK); ASSERT(check_num(v7, 2.0)); ASSERT(v7_exec(v7, "k = true ? 1 : 2;") == V7_OK); ASSERT(check_num(v7, 1.0)); ASSERT(v7_exec(v7, "var f = function(){var x=12; return x + 1;};") == V7_OK); ASSERT(v7_sp(v7) > 0); ASSERT(v7_exec(v7, "k = f(1,2,3);") == V7_OK); ASSERT(check_num(v7, 13.0)); ASSERT(v7_exec(v7, "(function() { return f() + 7; })()") == V7_OK); ASSERT(check_num(v7, 20.0)); ASSERT(v7_exec(v7, "var a = 1; if (a == 1) { a = 2; }; a;") == V7_OK); ASSERT(check_num(v7, 2.0)); ASSERT(v7_exec(v7, "var a = 'foo'; a == 'foo';") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "a = { x: function(p) { print(this); } }") == V7_OK); ASSERT(v7_exec(v7, "a.x(2);") == V7_OK); ASSERT(v7_exec(v7, "74.toString()") == V7_OK); ASSERT(check_str(v7, "74")); ASSERT(v7_exec(v7, "'hello'.length") == V7_OK); ASSERT(check_num(v7, 5.0)); ASSERT(v7_exec(v7, "k = { x : function() { if (1) 2; } }") == V7_OK); ASSERT(v7_exec(v7, "'foo' + 'bar'") == V7_OK); ASSERT(check_str(v7, "foobar")); ASSERT(v7_exec(v7, "var x = [1, 'foo', true, 7];") == V7_OK); ASSERT(v7_exec(v7, "x.length") == V7_OK); ASSERT(check_num(v7, 4.0)); ASSERT(v7_exec(v7, "x[1]") == V7_OK); ASSERT(check_str(v7, "foo")); ASSERT(v7_exec(v7, "var f1 = function() { 1; };") == V7_OK); ASSERT(v7_exec(v7, "var f2 = function(x) { if (x) return x; };") == V7_OK); ASSERT(v7_exec(v7, "f1()") == V7_OK); ASSERT(v7->sp == 1 && v7->stack[0]->type == V7_TYPE_UNDEF); ASSERT(v7_exec(v7, "f2(false)") == V7_OK); ASSERT(v7->sp == 1 && v7->stack[0]->type == V7_TYPE_UNDEF); ASSERT(v7_exec(v7, "f2(17)") == V7_OK); ASSERT(check_num(v7, 17.0)); ASSERT(v7_exec(v7, "f2(true)") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "1 <= 2 ? 7 : 8") == V7_OK); ASSERT(check_num(v7, 7.0)); ASSERT(v7_exec(v7, "function a (t) { return t * t }; ") == V7_OK); ASSERT(v7_exec(v7, "a(2)") == V7_OK); ASSERT(check_num(v7, 4.0)); ASSERT(v7_exec(v7, "a(0)") == V7_OK); ASSERT(check_num(v7, 0.0)); ASSERT(v7_exec(v7, "function fac(x) { " "return x <= 1 ? 1 : x * fac(x - 1); }") == V7_OK); ASSERT(v7_exec(v7, "fac(1)") == V7_OK); ASSERT(check_num(v7, 1.0)); ASSERT(v7_exec(v7, "fac(5)") == V7_OK); ASSERT(check_num(v7, 120.0)); ASSERT(v7_exec(v7, "fac(20)") == V7_OK); ASSERT(v7_exec(v7, "function qq(a,b) { return a + b; }") == V7_OK); ASSERT(v7_exec(v7, "qq(1,2)") == V7_OK); ASSERT(check_num(v7, 3.0)); ASSERT(v7_exec(v7, "1 < 2 == 2 < 3") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "5 % 3 * 3") == V7_OK); ASSERT(check_num(v7, 6.0)); ASSERT(v7_exec(v7, "76 & 13") == V7_OK); ASSERT(check_num(v7, 12.0)); ASSERT(v7_exec(v7, "34325 ^ 190992 & 74832") == V7_OK); ASSERT(check_num(v7, 42501.0)); ASSERT(v7_exec(v7, "a = 12;") == V7_OK); ASSERT(v7_exec(v7, "a += 44; a;") == V7_OK); ASSERT(check_num(v7, 56.0)); ASSERT(v7_exec(v7, "a -= a / 2; a;") == V7_OK); ASSERT(check_num(v7, 28.0)); ASSERT(v7_exec(v7, "a *= 0.5; a;") == V7_OK); ASSERT(check_num(v7, 14.0)); #ifndef _WIN32 ASSERT(v7_exec(v7, "a /= 0; a;") == V7_OK); ASSERT(check_num(v7, INFINITY)); #endif ASSERT(v7_exec(v7, "!5") == V7_OK); ASSERT(check_bool(v7, 0.0)); ASSERT(v7_exec(v7, "!''") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "1 != 2") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "7 >= 0") == V7_OK); ASSERT(check_bool(v7, 1.0)); ASSERT(v7_exec(v7, "if (false) 3; ") == V7_OK); ASSERT(v7_exec(v7, "if (true) { if (1) {2;} 5; } ") == V7_OK); ASSERT(check_num(v7, 5.0)); ASSERT(v7_exec(v7, "if ('') 3; ") == V7_OK); ASSERT(v7_exec(v7, "if ('0') 9; ") == V7_OK); ASSERT(check_num(v7, 9.0)); ASSERT(v7_exec(v7, "if (false) 1; else 3;") == V7_OK); ASSERT(check_num(v7, 3.0)); ASSERT(v7_exec(v7, "if (false) 1; else if (0) { 3 } else { 2 }") == V7_OK); ASSERT(check_num(v7, 2.0)); ASSERT(v7_exec(v7, "if (false) 1; else if (1) { 3 } else { 2 }") == V7_OK); ASSERT(check_num(v7, 3.0)); ASSERT(v7_exec(v7, "a = 32; 2 + a++;") == V7_OK); ASSERT(check_num(v7, 35.0)); ASSERT(v7_exec(v7, "print(['hi', 1, true, null, /\\s+/])") == V7_OK); ASSERT(v7_exec(v7, "a = {};") == V7_OK); ASSERT(v7_exec(v7, "a.foo = function(x) { var y = " "x.substr(1).split() }") == V7_OK); ASSERT(v7_exec(v7, "typeof 2") == V7_OK); ASSERT(check_str(v7, "number")); v7_destroy(&v7); return NULL; }