88#include < sstream>
99#include < cmath>
1010#include < functional>
11+ #include < set>
12+ #include < unordered_set>
1113#include < unordered_map>
1214#include < cstdint>
1315#include < cstdio>
@@ -881,7 +883,27 @@ void register_stdlib_core(VM *vm) {
881883 Value v = vm->peek (arg_count - 1 );
882884 int indent_size = (arg_count >= 2 ) ? (int )vm->peek (arg_count - 2 ).as_number () : 0 ;
883885
886+ std::unordered_set<const void *> seen;
887+ auto json_escape = [](const std::string& s) -> std::string {
888+ std::string res = " \" " ;
889+ for (char c : s) {
890+ switch (c) {
891+ case ' "' : res += " \\\" " ; break ;
892+ case ' \\ ' : res += " \\\\ " ; break ;
893+ case ' \b ' : res += " \\ b" ; break ;
894+ case ' \f ' : res += " \\ f" ; break ;
895+ case ' \n ' : res += " \\ n" ; break ;
896+ case ' \r ' : res += " \\ r" ; break ;
897+ case ' \t ' : res += " \\ t" ; break ;
898+ default : res += c; break ;
899+ }
900+ }
901+ res += " \" " ;
902+ return res;
903+ };
884904 std::function<std::string (Value, int )> stringify = [&](Value val, int depth) -> std::string {
905+ if (depth > 1000 )
906+ throw std::runtime_error (" json_stringify: nesting too deep" );
885907 if (val.is_null ()) return " null" ;
886908 if (val.is_bool ()) return val.as_bool () ? " true" : " false" ;
887909 if (val.is_number ()) {
@@ -893,80 +915,75 @@ void register_stdlib_core(VM *vm) {
893915 ss << num;
894916 return ss.str ();
895917 }
896- if (val.is_string ()) {
897- std::string s = val.as_string ();
898- std::string res = " \" " ;
899- for (char c : s) {
900- switch (c) {
901- case ' "' : res += " \\\" " ; break ;
902- case ' \\ ' : res += " \\\\ " ; break ;
903- case ' \b ' : res += " \\ b" ; break ;
904- case ' \f ' : res += " \\ f" ; break ;
905- case ' \n ' : res += " \\ n" ; break ;
906- case ' \r ' : res += " \\ r" ; break ;
907- case ' \t ' : res += " \\ t" ; break ;
908- default : res += c; break ;
909- }
910- }
911- res += " \" " ;
912- return res;
913- }
918+ if (val.is_string ()) return json_escape (val.as_string ());
914919 if (val.is_list ()) {
915920 auto *list = static_cast <ObjList*>(val.get_obj ());
916- if (list->elements .empty ()) return " []" ;
917- if (indent_size > 0 ) {
921+ if (!seen.insert (list).second )
922+ throw std::runtime_error (" json_stringify: cyclic structure" );
923+ std::string res;
924+ if (list->elements .empty ()) res = " []" ;
925+ else if (indent_size > 0 ) {
918926 std::string indent_str (depth * indent_size, ' ' );
919927 std::string inner_indent ((depth + 1 ) * indent_size, ' ' );
920- std::string res = " [\n " ;
928+ res = " [\n " ;
921929 for (size_t i = 0 ; i < list->elements .size (); ++i) {
922930 if (i > 0 ) res += " ,\n " ;
923931 res += inner_indent + stringify (list->elements [i], depth + 1 );
924932 }
925933 res += " \n " + indent_str + " ]" ;
926- return res;
927934 } else {
928- std::string res = " [" ;
935+ res = " [" ;
929936 for (size_t i = 0 ; i < list->elements .size (); ++i) {
930937 if (i > 0 ) res += " , " ;
931- res += stringify (list->elements [i], 0 );
938+ res += stringify (list->elements [i], depth + 1 );
932939 }
933940 res += " ]" ;
934- return res;
935941 }
942+ seen.erase (list);
943+ return res;
936944 }
937945 if (val.is_dict ()) {
938946 auto *dict = static_cast <ObjDict*>(val.get_obj ());
939- if (dict->elements .empty ()) return " {}" ;
940- if (indent_size > 0 ) {
947+ if (!seen.insert (dict).second )
948+ throw std::runtime_error (" json_stringify: cyclic structure" );
949+ std::string res;
950+ if (dict->elements .empty ()) res = " {}" ;
951+ else if (indent_size > 0 ) {
941952 std::string indent_str (depth * indent_size, ' ' );
942953 std::string inner_indent ((depth + 1 ) * indent_size, ' ' );
943- std::string res = " {\n " ;
954+ res = " {\n " ;
944955 bool first = true ;
945956 for (auto &pair : dict->elements ) {
946957 if (!first) res += " ,\n " ;
947958 first = false ;
948- res += inner_indent + stringify ( Value (vm-> arena (). allocate_string ( pair.first )), 0 );
959+ res += inner_indent + json_escape ( pair.first );
949960 res += " : " + stringify (pair.second , depth + 1 );
950961 }
951962 res += " \n " + indent_str + " }" ;
952- return res;
953963 } else {
954- std::string res = " {" ;
964+ res = " {" ;
955965 bool first = true ;
956966 for (auto &pair : dict->elements ) {
957967 if (!first) res += " , " ;
958968 first = false ;
959- res += stringify ( Value (vm-> arena (). allocate_string ( pair.first )), 0 );
969+ res += json_escape ( pair.first );
960970 res += " : " ;
961- res += stringify (pair.second , 0 );
971+ res += stringify (pair.second , depth + 1 );
962972 }
963973 res += " }" ;
964- return res;
965974 }
975+ seen.erase (dict);
976+ return res;
966977 }
967978 return " \" " + val.to_string () + " \" " ;
968979 };
969- return Value (vm->arena ().allocate_string (stringify (v, 0 )));
980+ try {
981+ return Value (vm->arena ().allocate_string (stringify (v, 0 )));
982+ } catch (const std::exception& e) {
983+ vm->has_error = true ;
984+ vm->error_value = Value (vm->arena ().allocate_string (e.what ()));
985+ return Value ();
986+ }
970987 };
971988
972989 NativeRegistry::register_builtin (vm, " json_stringify" , -1 , json_stringify_fn);
@@ -1020,9 +1037,10 @@ void register_stdlib_core(VM *vm) {
10201037 return out;
10211038 };
10221039
1023- std::function<Value ()> parse_val = [&]() -> Value {
1040+ std::function<Value (int )> parse_val = [&](int depth ) -> Value {
10241041 skip_ws ();
10251042 if (pos >= s.size ()) { error_out = true ; return Value (); }
1043+ if (depth > 1000 ) { error_out = true ; return Value (); }
10261044 char c = s[pos];
10271045 if (c == ' n' && s.substr (pos, 4 ) == " null" ) {
10281046 pos += 4 ;
@@ -1046,7 +1064,8 @@ void register_stdlib_core(VM *vm) {
10461064 skip_ws ();
10471065 if (match_c (' ]' )) return Value (list);
10481066 while (true ) {
1049- Value elem = parse_val ();
1067+ Value elem = parse_val (depth + 1 );
1068+ if (error_out) break ;
10501069 list->elements .push_back (elem);
10511070 skip_ws ();
10521071 if (match_c (' ]' )) break ;
@@ -1065,7 +1084,8 @@ void register_stdlib_core(VM *vm) {
10651084 std::string key = parse_str ();
10661085 skip_ws ();
10671086 if (!match_c (' :' )) { error_out = true ; break ; }
1068- Value v = parse_val ();
1087+ Value v = parse_val (depth + 1 );
1088+ if (error_out) break ;
10691089 dict->elements [key] = v;
10701090 skip_ws ();
10711091 if (match_c (' }' )) break ;
@@ -1098,7 +1118,7 @@ void register_stdlib_core(VM *vm) {
10981118 return Value ();
10991119 };
11001120
1101- Value result = parse_val ();
1121+ Value result = parse_val (0 );
11021122 skip_ws ();
11031123 if (pos < s.size ()) error_out = true ;
11041124 return result;
0 commit comments