/* ToPrimitive() on a value */ void jsV_toprimitive(js_State *J, js_Value *v, int preferred) { js_Object *obj; if (v->type != JS_TOBJECT) return; obj = v->u.object; if (preferred == JS_HNONE) preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER; if (preferred == JS_HSTRING) { if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) { *v = *js_tovalue(J, -1); js_pop(J, 1); return; } } else { if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) { *v = *js_tovalue(J, -1); js_pop(J, 1); return; } } v->type = JS_TLITSTR; v->u.litstr = "[object]"; return; }
void js_concat(js_State *J) { js_Value va = js_toprimitive(J, -2, JS_HNONE); js_Value vb = js_toprimitive(J, -1, JS_HNONE); if (va.type == JS_TSTRING || vb.type == JS_TSTRING) { const char *sa = jsV_tostring(J, &va); const char *sb = jsV_tostring(J, &vb); char *sab = js_malloc(J, strlen(sa) + strlen(sb) + 1); strcpy(sab, sa); strcat(sab, sb); if (js_try(J)) { js_free(J, sab); js_throw(J); } js_pop(J, 2); js_pushstring(J, sab); js_endtry(J); js_free(J, sab); } else { double x = jsV_tonumber(J, &va); double y = jsV_tonumber(J, &vb); js_pop(J, 2); js_pushnumber(J, x + y); } }
/* ToPrimitive() on a value */ js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred) { js_Value vv; js_Object *obj; if (v->type != JS_TOBJECT) return *v; obj = v->u.object; if (preferred == JS_HNONE) preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER; if (preferred == JS_HSTRING) { if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) { vv = js_tovalue(J, -1); js_pop(J, 1); return vv; } } else { if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) { vv = js_tovalue(J, -1); js_pop(J, 1); return vv; } } js_typeerror(J, "cannot convert object to primitive"); }
static void wrapgetter(js_State *J) { pdf_jsimp_obj *ret; pdf_jsimp_getter *get; const char *type; void *jsctx; void *obj; js_getregistry(J, "jsctx"); jsctx = js_touserdata(J, "jsctx", -1); js_pop(J, 1); js_currentfunction(J); { js_getproperty(J, -1, "__get"); get = js_touserdata(J, "getter", -1); js_pop(J, 1); js_getproperty(J, -1, "__type"); type = js_tostring(J, -1); js_pop(J, 1); } js_pop(J, 1); if (js_isuserdata(J, type, 0)) obj = js_touserdata(J, type, 0); else obj = NULL; ret = get(jsctx, obj); if (ret) js_copy(J, IDX(ret)); else js_pushundefined(J); }
static void wrapsetter(js_State *J) { pdf_jsimp_setter *set; const char *type; void *jsctx; void *obj; js_getregistry(J, "jsctx"); jsctx = js_touserdata(J, "jsctx", -1); js_pop(J, 1); js_currentfunction(J); { js_getproperty(J, -1, "__set"); set = js_touserdata(J, "setter", -1); js_pop(J, 1); js_getproperty(J, -1, "__type"); type = js_tostring(J, -1); js_pop(J, 1); } js_pop(J, 1); if (js_isuserdata(J, type, 0)) obj = js_touserdata(J, type, 0); else obj = NULL; set(jsctx, obj, OBJ(1)); js_pushundefined(J); }
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); } } } }
void js_concat(js_State *J) { js_toprimitive(J, -2, JS_HNONE); js_toprimitive(J, -1, JS_HNONE); if (js_isstring(J, -2) || js_isstring(J, -1)) { const char *sa = js_tostring(J, -2); const char *sb = js_tostring(J, -1); /* TODO: create js_String directly */ char *sab = js_malloc(J, strlen(sa) + strlen(sb) + 1); strcpy(sab, sa); strcat(sab, sb); if (js_try(J)) { js_free(J, sab); js_throw(J); } js_pop(J, 2); js_pushstring(J, sab); js_endtry(J); js_free(J, sab); } else { double x = js_tonumber(J, -2); double y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x + y); } }
static int sortcmp(const void *avoid, const void *bvoid) { const struct sortslot *aslot = avoid, *bslot = bvoid; const js_Value *a = &aslot->v, *b = &bslot->v; js_State *J = aslot->J; const char *sx, *sy; int c; int unx = (a->type == JS_TUNDEFINED); int uny = (b->type == JS_TUNDEFINED); if (unx) return !uny; if (uny) return -1; if (js_iscallable(J, 1)) { js_copy(J, 1); /* copy function */ js_pushundefined(J); js_pushvalue(J, *a); js_pushvalue(J, *b); js_call(J, 2); c = js_tonumber(J, -1); js_pop(J, 1); } else { js_pushvalue(J, *a); js_pushvalue(J, *b); sx = js_tostring(J, -2); sy = js_tostring(J, -1); c = strcmp(sx, sy); js_pop(J, 2); } return c; }
static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc) { int haswritable = 0; int hasvalue = 0; int enumerable = 0; int configurable = 0; int writable = 0; int atts = 0; js_pushobject(J, obj); js_pushobject(J, desc); if (js_hasproperty(J, -1, "writable")) { haswritable = 1; writable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "enumerable")) { enumerable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "configurable")) { configurable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "value")) { hasvalue = 1; js_setproperty(J, -3, name); } if (!writable) atts |= JS_READONLY; if (!enumerable) atts |= JS_DONTENUM; if (!configurable) atts |= JS_DONTCONF; if (js_hasproperty(J, -1, "get")) { if (haswritable || hasvalue) js_typeerror(J, "value/writable and get/set attributes are exclusive"); } else { js_pushundefined(J); } if (js_hasproperty(J, -2, "set")) { if (haswritable || hasvalue) js_typeerror(J, "value/writable and get/set attributes are exclusive"); } else { js_pushundefined(J); } js_defaccessor(J, -4, name, atts); js_pop(J, 2); }
void js_construct(js_State *J, int n) { js_Object *obj; js_Object *prototype; js_Object *newobj; if (!js_iscallable(J, -n-1)) js_typeerror(J, "called object is not a function"); obj = js_toobject(J, -n-1); /* built-in constructors create their own objects, give them a 'null' this */ if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) { int savebot = BOT; js_pushnull(J); if (n > 0) js_rot(J, n + 1); BOT = TOP - n - 1; jsR_pushtrace(J, obj->u.c.name, "[C]", 0); jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor); --J->tracetop; BOT = savebot; return; } /* extract the function object's prototype property */ js_getproperty(J, -n - 1, "prototype"); if (js_isobject(J, -1)) prototype = js_toobject(J, -1); else prototype = J->Object_prototype; js_pop(J, 1); /* create a new object with above prototype, and shift it into the 'this' slot */ newobj = jsV_newobject(J, JS_COBJECT, prototype); js_pushobject(J, newobj); if (n > 0) js_rot(J, n + 1); /* call the function */ js_call(J, n); /* if result is not an object, return the original object we created */ if (!js_isobject(J, -1)) { js_pop(J, 1); js_pushobject(J, newobj); } }
int js_dostring(js_State *J, const char *source) { if (js_try(J)) { fprintf(stderr, "%s\n", js_tostring(J, -1)); js_pop(J, 1); return 1; } js_loadstring(J, "[string]", source); js_pushglobal(J); js_call(J, 0); js_pop(J, 1); js_endtry(J); return 0; }
int js_dofile(js_State *J, const char *filename) { if (js_try(J)) { fprintf(stderr, "libjs: %s\n", js_tostring(J, -1)); js_pop(J, 1); return 1; } js_loadfile(J, filename); js_pushglobal(J); js_call(J, 0); js_pop(J, 1); js_endtry(J); return 0; }
/* obj.valueOf() */ static int jsV_valueOf(js_State *J, js_Object *obj) { js_pushobject(J, obj); js_getproperty(J, -1, "valueOf"); if (js_iscallable(J, -1)) { js_rot2(J); js_call(J, 0); if (js_isprimitive(J, -1)) return 1; js_pop(J, 1); return 0; } js_pop(J, 2); return 0; }
int js_dostring(js_State *J, const char *source, int report) { if (js_try(J)) { fprintf(stderr, "libjs: %s\n", js_tostring(J, -1)); js_pop(J, 1); return 1; } js_loadstring(J, "(string)", source); js_pushglobal(J); js_call(J, 0); if (report) printf("%s\n", js_tostring(J, -1)); js_pop(J, 1); js_endtry(J); return 0; }
void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set) { js_State *J = imp->J; js_getregistry(J, (const char *)type); { js_newcfunction(J, wrapgetter, 0); { js_pushnull(J); js_newuserdata(J, "getter", get); js_defproperty(J, -2, "__get", JS_READONLY | JS_DONTENUM | JS_DONTCONF); js_pushstring(J, (const char *)type); js_defproperty(J, -2, "__type", JS_READONLY | JS_DONTENUM | JS_DONTCONF); } js_newcfunction(J, wrapsetter, 0); { js_pushnull(J); js_newuserdata(J, "setter", set); js_defproperty(J, -2, "__set", JS_READONLY | JS_DONTENUM | JS_DONTCONF); js_pushstring(J, (const char *)type); js_defproperty(J, -2, "__type", JS_READONLY | JS_DONTENUM | JS_DONTCONF); } js_defaccessor(J, -3, name, JS_READONLY | JS_DONTENUM | JS_DONTCONF); } js_pop(J, 1); }
int js_instanceof(js_State *J) { js_Object *O, *V; if (!js_iscallable(J, -1)) js_typeerror(J, "instanceof: invalid operand"); if (!js_isobject(J, -2)) return 0; js_getproperty(J, -1, "prototype"); if (!js_isobject(J, -1)) js_typeerror(J, "instanceof: 'prototype' property is not an object"); O = js_toobject(J, -1); js_pop(J, 1); V = js_toobject(J, -2); while (V) { V = V->prototype; if (O == V) return 1; } return 0; }
static int compare(js_State *J, unsigned int x, unsigned int y, int *hasx, int *hasy, int hasfn) { const char *sx, *sy; int c; *hasx = js_hasindex(J, 0, x); *hasy = js_hasindex(J, 0, y); if (*hasx && *hasy) { int unx = js_isundefined(J, -2); int uny = js_isundefined(J, -1); if (unx && uny) return 0; if (unx) return 1; if (uny) return -1; if (hasfn) { js_copy(J, 1); /* copy function */ js_pushundefinedthis(J); /* set this object */ js_copy(J, -4); /* copy x */ js_copy(J, -4); /* copy y */ js_call(J, 2); c = js_tonumber(J, -1); js_pop(J, 1); return c; } sx = js_tostring(J, -2); sy = js_tostring(J, -1); return strcmp(sx, sy); } if (*hasx) return -1; if (*hasy) return 1; return 0; }
static void js_setvar(js_State *J, const char *name) { js_Environment *E = J->E; do { js_Property *ref = jsV_getproperty(J, E->variables, name); if (ref) { if (ref->setter) { js_pushobject(J, ref->setter); js_pushobject(J, E->variables); js_copy(J, -3); js_call(J, 1); js_pop(J, 1); return; } if (!(ref->atts & JS_READONLY)) ref->value = *stackidx(J, -1); else if (J->strict) js_typeerror(J, "'%s' is read-only", name); return; } E = E->outer; } while (E); if (J->strict) js_referenceerror(J, "assignment to undeclared variable '%s'", name); jsR_setproperty(J, J->G, name, stackidx(J, -1)); }
static void Ap_forEach(js_State *J) { int hasthis = js_gettop(J) >= 3; int k, len; if (!js_iscallable(J, 1)) js_typeerror(J, "callback is not a function"); 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); js_pop(J, 2); } } js_pushundefined(J); }
void js_newobjectx(js_State *J) { js_Object *prototype = NULL; if (js_isobject(J, -1)) prototype = js_toobject(J, -1); js_pop(J, 1); js_pushobject(J, jsV_newobject(J, JS_COBJECT, prototype)); }
int js_getlength(js_State *J, int idx) { int len; js_getproperty(J, idx, "length"); len = js_tointeger(J, -1); js_pop(J, 1); return len; }
unsigned int js_getlength(js_State *J, int idx) { unsigned int len; js_getproperty(J, idx, "length"); len = js_touint32(J, -1); js_pop(J, 1); return len; }
static void Sp_replace_string(js_State *J) { const char *source, *needle, *s, *r; js_Buffer *sb = NULL; int n; source = js_tostring(J, 0); needle = js_tostring(J, 1); s = strstr(source, needle); if (!s) { js_copy(J, 0); return; } n = strlen(needle); if (js_iscallable(J, 2)) { js_copy(J, 2); js_pushglobal(J); js_pushlstring(J, s, n); /* arg 1: substring that matched */ js_pushnumber(J, s - source); /* arg 2: offset within search string */ js_copy(J, 0); /* arg 3: search string */ js_call(J, 3); r = js_tostring(J, -1); js_putm(J, &sb, source, s); js_puts(J, &sb, r); js_puts(J, &sb, s + n); js_putc(J, &sb, 0); js_pop(J, 1); } else { r = js_tostring(J, 2); js_putm(J, &sb, source, s); while (*r) { if (*r == '$') { switch (*(++r)) { case '$': js_putc(J, &sb, '$'); break; case '&': js_putm(J, &sb, s, s + n); break; case '`': js_putm(J, &sb, source, s); break; case '\'': js_puts(J, &sb, s + n); break; default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break; } ++r; } else { js_putc(J, &sb, *r++); } } js_puts(J, &sb, s + n); js_putc(J, &sb, 0); } if (js_try(J)) { js_free(J, sb); js_throw(J); } js_pushstring(J, sb ? sb->s : ""); js_endtry(J); js_free(J, sb); }
int eval_print(js_State *J, const char *source) { if (js_ploadstring(J, "[string]", source)) { fprintf(stderr, "%s\n", js_tostring(J, -1)); js_pop(J, 1); return 1; } js_pushglobal(J); if (js_pcall(J, 0)) { fprintf(stderr, "%s\n", js_tostring(J, -1)); js_pop(J, 1); return 1; } if (js_isdefined(J, -1)) printf("%s\n", js_tostring(J, -1)); js_pop(J, 1); return 0; }
static void wrapmethod(js_State *J) { pdf_jsimp_obj *args[MAXARGS]; pdf_jsimp_obj *ret; pdf_jsimp_method *meth; const char *type; void *jsctx; void *obj; int i; int argc = js_gettop(J) - 1; js_getregistry(J, "jsctx"); jsctx = js_touserdata(J, "jsctx", -1); js_pop(J, 1); js_currentfunction(J); { js_getproperty(J, -1, "__call"); meth = js_touserdata(J, "method", -1); js_pop(J, 1); js_getproperty(J, -1, "__type"); type = js_tostring(J, -1); js_pop(J, 1); } js_pop(J, 1); if (js_isuserdata(J, type, 0)) obj = js_touserdata(J, type, 0); else obj = NULL; if (argc > MAXARGS) js_rangeerror(J, "too many arguments"); for (i = 0; i < argc; ++i) args[i] = OBJ(i+1); ret = meth(jsctx, obj, argc, args); if (ret) js_copy(J, IDX(ret)); else js_pushundefined(J); }
static void jsR_callfunction(js_State *J, unsigned int n, js_Function *F, js_Environment *scope) { js_Value v; unsigned int i; scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope); jsR_savescope(J, scope); if (F->arguments) { js_newobject(J); if (!J->strict) { js_currentfunction(J); js_defproperty(J, -2, "callee", JS_DONTENUM); } js_pushnumber(J, n); js_defproperty(J, -2, "length", JS_DONTENUM); for (i = 0; i < n; ++i) { js_copy(J, i + 1); js_setindex(J, -2, i); } js_initvar(J, "arguments", -1); js_pop(J, 1); } for (i = 0; i < F->numparams; ++i) { if (i < n) js_initvar(J, F->vartab[i], i + 1); else { js_pushundefined(J); js_initvar(J, F->vartab[i], -1); js_pop(J, 1); } } js_pop(J, n); jsR_run(J, F); v = *stackidx(J, -1); TOP = --BOT; /* clear stack */ js_pushvalue(J, v); jsR_restorescope(J); }
static int eval_print(js_State *J, const char *source) { if (js_ploadstring(J, "[stdin]", source)) { fprintf(stderr, "%s\n", js_trystring(J, -1, "Error")); js_pop(J, 1); return 1; } js_pushundefined(J); if (js_pcall(J, 0)) { fprintf(stderr, "%s\n", js_trystring(J, -1, "Error")); js_pop(J, 1); return 1; } if (js_isdefined(J, -1)) { printf("%s\n", js_tryrepr(J, -1, "can't convert to string")); } js_pop(J, 1); return 0; }
static void Ap_sort(js_State *J) { struct sortslot *array = NULL; int i, n, len; len = js_getlength(J, 0); if (len <= 0) { js_copy(J, 0); return; } if (len >= INT_MAX / (int)sizeof(*array)) js_rangeerror(J, "array is too large to sort"); array = js_malloc(J, len * sizeof *array); /* Holding objects where the GC cannot see them is illegal, but if we * don't allow the GC to run we can use qsort() on a temporary array of * js_Values for fast sorting. */ ++J->gcpause; if (js_try(J)) { --J->gcpause; js_free(J, array); js_throw(J); } n = 0; for (i = 0; i < len; ++i) { if (js_hasindex(J, 0, i)) { array[n].v = *js_tovalue(J, -1); array[n].J = J; js_pop(J, 1); ++n; } } qsort(array, n, sizeof *array, sortcmp); for (i = 0; i < n; ++i) { js_pushvalue(J, array[i].v); js_setindex(J, 0, i); } for (i = n; i < len; ++i) { js_delindex(J, 0, i); } --J->gcpause; js_endtry(J); js_free(J, array); js_copy(J, 0); }
static void jsB_load(js_State *J) { int i, n = js_gettop(J); for (i = 1; i < n; ++i) { js_loadfile(J, js_tostring(J, i)); js_pushundefined(J); js_call(J, 0); js_pop(J, 1); } js_pushundefined(J); }
static void Ap_join(js_State *J) { char * volatile out = NULL; const char *sep; const char *r; unsigned int seplen; unsigned int k, n, len; len = js_getlength(J, 0); if (js_isdefined(J, 1)) { sep = js_tostring(J, 1); seplen = strlen(sep); } else { sep = ","; seplen = 1; } if (len == 0) { js_pushliteral(J, ""); return; } if (js_try(J)) { js_free(J, out); js_throw(J); } n = 1; for (k = 0; k < len; ++k) { js_getindex(J, 0, k); if (js_isundefined(J, -1) || js_isnull(J, -1)) r = ""; else r = js_tostring(J, -1); n += strlen(r); if (k == 0) { out = js_malloc(J, n); strcpy(out, r); } else { n += seplen; out = realloc(out, n); strcat(out, sep); strcat(out, r); } js_pop(J, 1); } js_pushstring(J, out); js_endtry(J); js_free(J, out); }