Exemple #1
0
/* 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");
}
Exemple #4
0
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);
}
Exemple #5
0
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);
}
Exemple #6
0
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);
			}
		}
	}
}
Exemple #7
0
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);
	}
}
Exemple #8
0
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;
}
Exemple #9
0
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);
}
Exemple #10
0
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);
	}
}
Exemple #11
0
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;
}
Exemple #12
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;
}
Exemple #13
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;
}
Exemple #14
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;
}
Exemple #15
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);
}
Exemple #16
0
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;
}
Exemple #17
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;
}
Exemple #18
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));
}
Exemple #19
0
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);
}
Exemple #20
0
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));
}
Exemple #21
0
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;
}
Exemple #22
0
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;
}
Exemple #23
0
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);
}
Exemple #24
0
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;
}
Exemple #25
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);
}
Exemple #26
0
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);
}
Exemple #27
0
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;
}
Exemple #28
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);
}
Exemple #29
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);
}
Exemple #30
0
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);
}