static void Ap_filter(js_State *J) { int hasthis = js_gettop(J) >= 3; int k, to, len; if (!js_iscallable(J, 1)) js_typeerror(J, "callback is not a function"); js_newarray(J); to = 0; len = js_getlength(J, 0); for (k = 0; k < len; ++k) { if (js_hasindex(J, 0, k)) { js_copy(J, 1); if (hasthis) js_copy(J, 2); else js_pushundefined(J); js_copy(J, -3); js_pushnumber(J, k); js_copy(J, 0); js_call(J, 3); if (js_toboolean(J, -1)) { js_pop(J, 1); js_setindex(J, -2, to++); } else { js_pop(J, 2); } } } }
static void O_keys(js_State *J) { js_Object *obj; js_Property *ref; int k; int i; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); js_newarray(J); i = 0; for (ref = obj->head; ref; ref = ref->next) { if (!(ref->atts & JS_DONTENUM)) { js_pushliteral(J, ref->name); js_setindex(J, -2, i++); } } if (obj->type == JS_CSTRING) { for (k = 0; k < obj->u.s.length; ++k) { js_pushnumber(J, k); js_setindex(J, -2, i++); } } }
static void Sp_split_regexp(js_State *J) { js_Regexp *re; const char *text; unsigned int limit, len, k; const char *p, *a, *b, *c, *e; Resub m; text = js_tostring(J, 0); re = js_toregexp(J, 1); limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30; js_newarray(J); len = 0; e = text + strlen(text); /* splitting the empty string */ if (e == 0) { if (js_regexec(re->prog, text, &m, 0)) { if (len == limit) return; js_pushliteral(J, ""); js_setindex(J, -2, 0); } return; } p = a = text; while (a < e) { if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0)) break; /* no match */ b = m.sub[0].sp; c = m.sub[0].ep; /* empty string at end of last match */ if (b == p) { ++a; continue; } if (len == limit) return; js_pushlstring(J, p, b - p); js_setindex(J, -2, len++); for (k = 1; k < m.nsub; ++k) { if (len == limit) return; js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp); js_setindex(J, -2, len++); } a = p = c; } if (len == limit) return; js_pushstring(J, p); js_setindex(J, -2, len); }
static void Sp_split(js_State *J) { if (js_isundefined(J, 1)) { js_newarray(J); js_copy(J, 0); js_setindex(J, -2, 0); } else if (js_isregexp(J, 1)) { Sp_split_regexp(J); } else { Sp_split_string(J); } }
static void Ap_splice(js_State *J) { unsigned int top = js_gettop(J); unsigned int len, start, del, add, k; double f; js_newarray(J); len = js_getlength(J, 0); f = js_tointeger(J, 1); if (f < 0) f = f + len; start = f < 0 ? 0 : f > len ? len : f; f = js_tointeger(J, 2); del = f < 0 ? 0 : f > len - start ? len - start : f; /* copy deleted items to return array */ for (k = 0; k < del; ++k) if (js_hasindex(J, 0, start + k)) js_setindex(J, -2, k); js_setlength(J, -1, del); /* shift the tail to resize the hole left by deleted items */ add = top - 3; if (add < del) { for (k = start; k < len - del; ++k) { if (js_hasindex(J, 0, k + del)) js_setindex(J, 0, k + add); else js_delindex(J, 0, k + add); } for (k = len; k > len - del + add; --k) js_delindex(J, 0, k - 1); } else if (add > del) { for (k = len - del; k > start; --k) { if (js_hasindex(J, 0, k + del - 1)) js_setindex(J, 0, k + add - 1); else js_delindex(J, 0, k + add - 1); } } /* copy new items into the hole */ for (k = 0; k < add; ++k) { js_copy(J, 3 + k); js_setindex(J, 0, start + k); } js_setlength(J, 0, len - del + add); }
static void O_getOwnPropertyNames(js_State *J) { js_Object *obj; js_Property *ref; int k; int i; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); js_newarray(J); i = 0; for (ref = obj->head; ref; ref = ref->next) { js_pushliteral(J, ref->name); js_setindex(J, -2, i++); } if (obj->type == JS_CARRAY) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); } if (obj->type == JS_CSTRING) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); for (k = 0; k < obj->u.s.length; ++k) { js_pushnumber(J, k); js_setindex(J, -2, i++); } } if (obj->type == JS_CREGEXP) { js_pushliteral(J, "source"); js_setindex(J, -2, i++); js_pushliteral(J, "global"); js_setindex(J, -2, i++); js_pushliteral(J, "ignoreCase"); js_setindex(J, -2, i++); js_pushliteral(J, "multiline"); js_setindex(J, -2, i++); js_pushliteral(J, "lastIndex"); js_setindex(J, -2, i++); } }
static void Sp_match(js_State *J) { js_Regexp *re; const char *text; unsigned int len; const char *a, *b, *c, *e; Resub m; text = js_tostring(J, 0); if (js_isregexp(J, 1)) js_copy(J, 1); else if (js_isundefined(J, 1)) js_newregexp(J, "", 0); else js_newregexp(J, js_tostring(J, 1), 0); re = js_toregexp(J, -1); if (!(re->flags & JS_REGEXP_G)) { js_RegExp_prototype_exec(J, re, text); return; } re->last = 0; js_newarray(J); len = 0; a = text; e = text + strlen(text); while (a <= e) { if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0)) break; b = m.sub[0].sp; c = m.sub[0].ep; js_pushlstring(J, b, c - b); js_setindex(J, -2, len++); a = c; if (c - b == 0) ++a; } }
static void Ap_concat(js_State *J) { unsigned int i, top = js_gettop(J); unsigned int n, k, len; js_newarray(J); n = 0; for (i = 0; i < top; ++i) { js_copy(J, i); if (js_isarray(J, -1)) { len = js_getlength(J, -1); for (k = 0; k < len; ++k) if (js_hasindex(J, -1, k)) js_setindex(J, -3, n++); js_pop(J, 1); } else { js_setindex(J, -2, n++); } } }
static void jsB_new_Array(js_State *J) { unsigned int i, top = js_gettop(J); js_newarray(J); if (top == 2) { if (js_isnumber(J, 1)) { js_copy(J, 1); js_setproperty(J, -2, "length"); } else { js_copy(J, 1); js_setindex(J, -2, 0); } } else { for (i = 1; i < top; ++i) { js_copy(J, i); js_setindex(J, -2, i - 1); } } }
static void Ap_slice(js_State *J) { unsigned int len, s, e, n; double sv, ev; js_newarray(J); len = js_getlength(J, 0); sv = js_tointeger(J, 1); ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len; if (sv < 0) sv = sv + len; if (ev < 0) ev = ev + len; s = sv < 0 ? 0 : sv > len ? len : sv; e = ev < 0 ? 0 : ev > len ? len : ev; for (n = 0; s < e; ++s, ++n) if (js_hasindex(J, 0, s)) js_setindex(J, -2, n); }
static void Sp_split_string(js_State *J) { const char *str = js_tostring(J, 0); const char *sep = js_tostring(J, 1); unsigned int limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30; unsigned int i, n; js_newarray(J); n = strlen(sep); /* empty string */ if (n == 0) { Rune rune; for (i = 0; *str && i < limit; ++i) { n = chartorune(&rune, str); js_pushlstring(J, str, n); js_setindex(J, -2, i); str += n; } return; } for (i = 0; str && i < limit; ++i) { const char *s = strstr(str, sep); if (s) { js_pushlstring(J, str, s-str); js_setindex(J, -2, i); str = s + n; } else { js_pushstring(J, str); js_setindex(J, -2, i); str = NULL; } } }
static void Fp_bind(js_State *J) { unsigned int i, top = js_gettop(J); unsigned int n; if (!js_iscallable(J, 0)) js_typeerror(J, "not a function"); n = js_getlength(J, 0); if (n > top - 2) n -= top - 2; else n = 0; js_newcconstructor(J, callbound, constructbound, "[bind]", n); /* Reuse target function's prototype for HasInstance check. */ js_getproperty(J, 0, "prototype"); js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF); /* target function */ js_copy(J, 0); js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); /* bound this */ js_copy(J, 1); js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); /* bound arguments */ js_newarray(J); for (i = 2; i < top; ++i) { js_copy(J, i); js_setindex(J, -2, i - 2); } js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF); }
static void jsR_run(js_State *J, js_Function *F) { js_Function **FT = F->funtab; double *NT = F->numtab; const char **ST = F->strtab; js_Instruction *pcstart = F->code; js_Instruction *pc = F->code; enum js_OpCode opcode; int offset; const char *str; js_Object *obj; double x, y; unsigned int ux, uy; int ix, iy, okay; int b; while (1) { if (J->gccounter > JS_GCLIMIT) { J->gccounter = 0; js_gc(J, 0); } opcode = *pc++; switch (opcode) { case OP_POP: js_pop(J, 1); break; case OP_DUP: js_dup(J); break; case OP_DUP2: js_dup2(J); break; case OP_ROT2: js_rot2(J); break; case OP_ROT3: js_rot3(J); break; case OP_ROT4: js_rot4(J); break; case OP_NUMBER_0: js_pushnumber(J, 0); break; case OP_NUMBER_1: js_pushnumber(J, 1); break; case OP_NUMBER_POS: js_pushnumber(J, *pc++); break; case OP_NUMBER_NEG: js_pushnumber(J, -(*pc++)); break; case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break; case OP_STRING: js_pushliteral(J, ST[*pc++]); break; case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break; case OP_NEWOBJECT: js_newobject(J); break; case OP_NEWARRAY: js_newarray(J); break; case OP_NEWREGEXP: js_newregexp(J, ST[pc[0]], pc[1]); pc += 2; break; case OP_UNDEF: js_pushundefined(J); break; case OP_NULL: js_pushnull(J); break; case OP_TRUE: js_pushboolean(J, 1); break; case OP_FALSE: js_pushboolean(J, 0); break; case OP_THIS: js_copy(J, 0); break; case OP_GLOBAL: js_pushobject(J, J->G); break; case OP_CURRENT: js_currentfunction(J); break; case OP_INITLOCAL: STACK[BOT + *pc++] = STACK[--TOP]; break; case OP_GETLOCAL: CHECKSTACK(1); STACK[TOP++] = STACK[BOT + *pc++]; break; case OP_SETLOCAL: STACK[BOT + *pc++] = STACK[TOP-1]; break; case OP_DELLOCAL: ++pc; js_pushboolean(J, 0); break; case OP_INITVAR: js_initvar(J, ST[*pc++], -1); js_pop(J, 1); break; case OP_DEFVAR: js_defvar(J, ST[*pc++]); break; case OP_GETVAR: str = ST[*pc++]; if (!js_hasvar(J, str)) js_referenceerror(J, "'%s' is not defined", str); break; case OP_HASVAR: if (!js_hasvar(J, ST[*pc++])) js_pushundefined(J); break; case OP_SETVAR: js_setvar(J, ST[*pc++]); break; case OP_DELVAR: b = js_delvar(J, ST[*pc++]); js_pushboolean(J, b); break; case OP_IN: str = js_tostring(J, -2); if (!js_isobject(J, -1)) js_typeerror(J, "operand to 'in' is not an object"); b = js_hasproperty(J, -1, str); js_pop(J, 2 + b); js_pushboolean(J, b); break; case OP_INITPROP: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_pop(J, 2); break; case OP_INITGETTER: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL); js_pop(J, 2); break; case OP_INITSETTER: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1)); js_pop(J, 2); break; case OP_GETPROP: str = js_tostring(J, -1); obj = js_toobject(J, -2); jsR_getproperty(J, obj, str); js_rot3pop2(J); break; case OP_GETPROP_S: str = ST[*pc++]; obj = js_toobject(J, -1); jsR_getproperty(J, obj, str); js_rot2pop1(J); break; case OP_SETPROP: str = js_tostring(J, -2); obj = js_toobject(J, -3); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot3pop2(J); break; case OP_SETPROP_S: str = ST[*pc++]; obj = js_toobject(J, -2); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot2pop1(J); break; case OP_DELPROP: str = js_tostring(J, -1); obj = js_toobject(J, -2); b = jsR_delproperty(J, obj, str); js_pop(J, 2); js_pushboolean(J, b); break; case OP_DELPROP_S: str = ST[*pc++]; obj = js_toobject(J, -1); b = jsR_delproperty(J, obj, str); js_pop(J, 1); js_pushboolean(J, b); break; case OP_ITERATOR: if (!js_isundefined(J, -1) && !js_isnull(J, -1)) { obj = jsV_newiterator(J, js_toobject(J, -1), 0); js_pop(J, 1); js_pushobject(J, obj); } break; case OP_NEXTITER: obj = js_toobject(J, -1); str = jsV_nextiterator(J, obj); if (str) { js_pushliteral(J, str); js_pushboolean(J, 1); } else { js_pop(J, 1); js_pushboolean(J, 0); } break; /* Function calls */ case OP_EVAL: js_eval(J); break; case OP_CALL: js_call(J, *pc++); break; case OP_NEW: js_construct(J, *pc++); break; /* Unary operators */ case OP_TYPEOF: str = js_typeof(J, -1); js_pop(J, 1); js_pushliteral(J, str); break; case OP_POS: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x); break; case OP_NEG: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, -x); break; case OP_BITNOT: ix = js_toint32(J, -1); js_pop(J, 1); js_pushnumber(J, ~ix); break; case OP_LOGNOT: b = js_toboolean(J, -1); js_pop(J, 1); js_pushboolean(J, !b); break; case OP_INC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x + 1); break; case OP_DEC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x - 1); break; case OP_POSTINC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x + 1); js_pushnumber(J, x); break; case OP_POSTDEC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x - 1); js_pushnumber(J, x); break; /* Multiplicative operators */ case OP_MUL: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x * y); break; case OP_DIV: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x / y); break; case OP_MOD: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, fmod(x, y)); break; /* Additive operators */ case OP_ADD: js_concat(J); break; case OP_SUB: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x - y); break; /* Shift operators */ case OP_SHL: ix = js_toint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix << (uy & 0x1F)); break; case OP_SHR: ix = js_toint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix >> (uy & 0x1F)); break; case OP_USHR: ux = js_touint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ux >> (uy & 0x1F)); break; /* Relational operators */ case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break; case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break; case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break; case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break; case OP_INSTANCEOF: b = js_instanceof(J); js_pop(J, 2); js_pushboolean(J, b); break; /* Equality */ case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break; case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break; case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break; case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break; case OP_JCASE: offset = *pc++; b = js_strictequal(J); if (b) { js_pop(J, 2); pc = pcstart + offset; } else { js_pop(J, 1); } break; /* Binary bitwise operators */ case OP_BITAND: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix & iy); break; case OP_BITXOR: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix ^ iy); break; case OP_BITOR: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix | iy); break; /* Try and Catch */ case OP_THROW: js_throw(J); case OP_TRY: offset = *pc++; if (js_trypc(J, pc)) { pc = J->trybuf[J->trytop].pc; } else { pc = pcstart + offset; } break; case OP_ENDTRY: js_endtry(J); break; case OP_CATCH: str = ST[*pc++]; obj = jsV_newobject(J, JS_COBJECT, NULL); js_pushobject(J, obj); js_rot2(J); js_setproperty(J, -2, str); J->E = jsR_newenvironment(J, obj, J->E); js_pop(J, 1); break; case OP_ENDCATCH: J->E = J->E->outer; break; /* With */ case OP_WITH: obj = js_toobject(J, -1); J->E = jsR_newenvironment(J, obj, J->E); js_pop(J, 1); break; case OP_ENDWITH: J->E = J->E->outer; break; /* Branching */ case OP_DEBUGGER: js_trap(J, (int)(pc - pcstart) - 1); break; case OP_JUMP: pc = pcstart + *pc; break; case OP_JTRUE: offset = *pc++; b = js_toboolean(J, -1); js_pop(J, 1); if (b) pc = pcstart + offset; break; case OP_JFALSE: offset = *pc++; b = js_toboolean(J, -1); js_pop(J, 1); if (!b) pc = pcstart + offset; break; case OP_RETURN: return; case OP_LINE: J->trace[J->tracetop].line = *pc++; break; } } }
int main(int argc, char **argv) { char *input; js_State *J; int status = 0; int strict = 0; int interactive = 0; int i, c; while ((c = xgetopt(argc, argv, "is")) != -1) { switch (c) { default: usage(); break; case 'i': interactive = 1; break; case 's': strict = 1; break; } } J = js_newstate(NULL, NULL, strict ? JS_STRICT : 0); js_newcfunction(J, jsB_gc, "gc", 0); js_setglobal(J, "gc"); js_newcfunction(J, jsB_load, "load", 1); js_setglobal(J, "load"); js_newcfunction(J, jsB_compile, "compile", 2); js_setglobal(J, "compile"); js_newcfunction(J, jsB_print, "print", 0); js_setglobal(J, "print"); js_newcfunction(J, jsB_write, "write", 0); js_setglobal(J, "write"); js_newcfunction(J, jsB_read, "read", 1); js_setglobal(J, "read"); js_newcfunction(J, jsB_readline, "readline", 0); js_setglobal(J, "readline"); js_newcfunction(J, jsB_repr, "repr", 0); js_setglobal(J, "repr"); js_newcfunction(J, jsB_quit, "quit", 1); js_setglobal(J, "quit"); js_dostring(J, require_js); js_dostring(J, stacktrace_js); if (xoptind == argc) { interactive = 1; } else { c = xoptind++; js_newarray(J); i = 0; while (xoptind < argc) { js_pushstring(J, argv[xoptind++]); js_setindex(J, -2, i++); } js_setglobal(J, "scriptArgs"); if (js_dofile(J, argv[c])) status = 1; } if (interactive) { if (isatty(0)) { using_history(); rl_bind_key('\t', rl_insert); input = readline(PS1); while (input) { eval_print(J, input); if (*input) add_history(input); free(input); input = readline(PS1); } putchar('\n'); } else { input = read_stdin(); if (!input || !js_dostring(J, input)) status = 1; free(input); } } js_gc(J, 0); js_freestate(J); return status; }
static void jsonvalue(js_State *J) { int i; const char *name; switch (J->lookahead) { case TK_STRING: js_pushliteral(J, J->text); jsonnext(J); break; case TK_NUMBER: js_pushnumber(J, J->number); jsonnext(J); break; case '{': js_newobject(J); jsonnext(J); if (jsonaccept(J, '}')) return; do { if (J->lookahead != TK_STRING) js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead)); name = J->text; jsonnext(J); jsonexpect(J, ':'); jsonvalue(J); js_setproperty(J, -2, name); } while (jsonaccept(J, ',')); jsonexpect(J, '}'); break; case '[': js_newarray(J); jsonnext(J); i = 0; if (jsonaccept(J, ']')) return; do { jsonvalue(J); js_setindex(J, -2, i++); } while (jsonaccept(J, ',')); jsonexpect(J, ']'); break; case TK_TRUE: js_pushboolean(J, 1); jsonnext(J); break; case TK_FALSE: js_pushboolean(J, 0); jsonnext(J); break; case TK_NULL: js_pushnull(J); jsonnext(J); break; default: js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead)); } }