shithub: libmujs

Download patch

ref: 457f87b17bbf6e1654073c870a49e9f02ae63143
parent: d9f555652a9743d98143ccf3a0a5b6b903dfeaaa
author: Tor Andersson <tor.andersson@artifex.com>
date: Thu Jan 2 09:22:00 EST 2020

Issue 128: Support property list as replacer argument in JSON.stringify.

Also adds helper functions js_isstringobject and js_isnumberobject.

--- a/json.c
+++ b/json.c
@@ -5,6 +5,16 @@
 
 #include "utf.h"
 
+int js_isnumberobject(js_State *J, int idx)
+{
+	return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CNUMBER;
+}
+
+int js_isstringobject(js_State *J, int idx)
+{
+	return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CSTRING;
+}
+
 static void jsonnext(js_State *J)
 {
 	J->lookahead = jsY_lexjson(J);
@@ -206,6 +216,25 @@
 
 static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
 
+static int filterprop(js_State *J, const char *key)
+{
+	int i, n, found;
+	/* replacer/property-list is in stack slot 2 */
+	if (js_isarray(J, 2)) {
+		found = 0;
+		n = js_getlength(J, 2);
+		for (i = 0; i < n && !found; ++i) {
+			js_getindex(J, 2, i);
+			if (js_isstring(J, -1) || js_isnumber(J, -1) ||
+				js_isstringobject(J, -1) || js_isnumberobject(J, -1))
+				found = !strcmp(key, js_tostring(J, -1));
+			js_pop(J, 1);
+		}
+		return found;
+	}
+	return 1;
+}
+
 static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
 {
 	const char *key;
@@ -222,19 +251,21 @@
 	js_putc(J, sb, '{');
 	js_pushiterator(J, -1, 1);
 	while ((key = js_nextiterator(J, -1))) {
-		save = (*sb)->n;
-		if (n) js_putc(J, sb, ',');
-		if (gap) fmtindent(J, sb, gap, level + 1);
-		fmtstr(J, sb, key);
-		js_putc(J, sb, ':');
-		if (gap)
-			js_putc(J, sb, ' ');
-		js_rot2(J);
-		if (!fmtvalue(J, sb, key, gap, level + 1))
-			(*sb)->n = save;
-		else
-			++n;
-		js_rot2(J);
+		if (filterprop(J, key)) {
+			save = (*sb)->n;
+			if (n) js_putc(J, sb, ',');
+			if (gap) fmtindent(J, sb, gap, level + 1);
+			fmtstr(J, sb, key);
+			js_putc(J, sb, ':');
+			if (gap)
+				js_putc(J, sb, ' ');
+			js_rot2(J);
+			if (!fmtvalue(J, sb, key, gap, level + 1))
+				(*sb)->n = save;
+			else
+				++n;
+			js_rot2(J);
+		}
 	}
 	js_pop(J, 1);
 	if (gap && n) fmtindent(J, sb, gap, level);
@@ -266,7 +297,7 @@
 
 static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
 {
-	/* replacer is in 2 */
+	/* replacer/property-list is in 2 */
 	/* holder is in -1 */
 
 	js_getproperty(J, -1, key);
@@ -329,7 +360,7 @@
 
 	gap = NULL;
 
-	if (js_isnumber(J, 3) || (js_isobject(J, 3) && js_toobject(J, 3)->type == JS_CNUMBER)) {
+	if (js_isnumber(J, 3) || js_isnumberobject(J, 3)) {
 		n = js_tointeger(J, 3);
 		if (n < 0) n = 0;
 		if (n > 10) n = 10;
@@ -336,7 +367,7 @@
 		memset(buf, ' ', n);
 		buf[n] = 0;
 		if (n > 0) gap = buf;
-	} else if (js_isstring(J, 3) || (js_isobject(J, 3) && js_toobject(J, 3)->type == JS_CSTRING)) {
+	} else if (js_isstring(J, 3) || js_isstringobject(J, 3)) {
 		s = js_tostring(J, 3);
 		n = strlen(s);
 		if (n > 10) n = 10;
--- a/mujs.h
+++ b/mujs.h
@@ -176,6 +176,8 @@
 int js_iscallable(js_State *J, int idx);
 int js_isuserdata(js_State *J, int idx, const char *tag);
 int js_iserror(js_State *J, int idx);
+int js_isnumberobject(js_State *J, int idx);
+int js_isstringobject(js_State *J, int idx);
 
 int js_toboolean(js_State *J, int idx);
 double js_tonumber(js_State *J, int idx);