summaryrefslogtreecommitdiff
path: root/ext/json/generator/generator.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json/generator/generator.c')
-rw-r--r--ext/json/generator/generator.c149
1 files changed, 122 insertions, 27 deletions
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index e3a83472e1..6d78284bc4 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -16,7 +16,7 @@ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
i_pack, i_unpack, i_create_id, i_extend, i_key_p,
i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
- i_buffer_initial_length, i_dup, i_escape_slash;
+ i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash, i_strict;
/*
* Copyright 2001-2004 Unicode, Inc.
@@ -124,7 +124,7 @@ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
* and control characters are JSON escaped. */
-static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash)
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char script_safe)
{
const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
const UTF8 *sourceEnd = source + RSTRING_LEN(string);
@@ -175,7 +175,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escap
fbuffer_append(buffer, "\\\"", 2);
break;
case '/':
- if(escape_slash) {
+ if(script_safe) {
fbuffer_append(buffer, "\\/", 2);
break;
}
@@ -228,7 +228,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escap
* characters required by the JSON standard are JSON escaped. The remaining
* characters (should be UTF8) are just passed through and appended to the
* result. */
-static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash)
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char script_safe)
{
const char *ptr = RSTRING_PTR(string), *p;
unsigned long len = RSTRING_LEN(string), start = 0, end = 0;
@@ -280,7 +280,7 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slas
escape_len = 2;
break;
case '/':
- if(escape_slash) {
+ if(script_safe) {
escape = "\\/";
escape_len = 2;
break;
@@ -294,6 +294,22 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slas
rb_raise(rb_path2class("JSON::GeneratorError"),
"partial character in source, but hit end");
}
+
+ if (script_safe && c == 0xE2) {
+ unsigned char c2 = (unsigned char) *(p+1);
+ unsigned char c3 = (unsigned char) *(p+2);
+ if (c2 == 0x80 && (c3 == 0xA8 || c3 == 0xA9)) {
+ fbuffer_append(buffer, ptr + start, end - start);
+ start = end = (end + clen);
+ if (c3 == 0xA8) {
+ fbuffer_append(buffer, "\\u2028", 6);
+ } else {
+ fbuffer_append(buffer, "\\u2029", 6);
+ }
+ continue;
+ }
+ }
+
if (!isLegalUTF8((UTF8 *) p, clen)) {
rb_raise(rb_path2class("JSON::GeneratorError"),
"source sequence is illegal/malformed utf-8");
@@ -478,6 +494,7 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
*/
static VALUE mString_included_s(VALUE self, VALUE modul) {
VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
+ rb_call_super(1, &modul);
return result;
}
@@ -726,8 +743,14 @@ static VALUE cState_configure(VALUE self, VALUE opts)
state->allow_nan = RTEST(tmp);
tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
state->ascii_only = RTEST(tmp);
- tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
- state->escape_slash = RTEST(tmp);
+ tmp = rb_hash_aref(opts, ID2SYM(i_script_safe));
+ state->script_safe = RTEST(tmp);
+ if (!state->script_safe) {
+ tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
+ state->script_safe = RTEST(tmp);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_strict));
+ state->strict = RTEST(tmp);
return self;
}
@@ -762,7 +785,8 @@ static VALUE cState_to_h(VALUE self)
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
- rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_script_safe), state->script_safe ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse);
rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
return result;
@@ -843,7 +867,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
if (klass == rb_cString) {
key_to_s = key;
} else if (klass == rb_cSymbol) {
- key_to_s = rb_id2str(SYM2ID(key));
+ key_to_s = rb_sym2str(key);
} else {
key_to_s = rb_funcall(key, i_to_s, 0);
}
@@ -868,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
struct hash_foreach_arg arg;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '{');
@@ -903,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
long depth = ++state->depth;
int i, j;
if (max_nesting != 0 && depth > max_nesting) {
- fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '[');
@@ -947,9 +969,9 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
}
#endif
if (state->ascii_only) {
- convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash);
+ convert_UTF8_to_JSON_ASCII(buffer, obj, state->script_safe);
} else {
- convert_UTF8_to_JSON(buffer, obj, state->escape_slash);
+ convert_UTF8_to_JSON(buffer, obj, state->script_safe);
}
fbuffer_append_char(buffer, '"');
}
@@ -996,11 +1018,9 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
VALUE tmp = rb_funcall(obj, i_to_s, 0);
if (!allow_nan) {
if (isinf(value)) {
- fbuffer_free(buffer);
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} else if (isnan(value)) {
- fbuffer_free(buffer);
- rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
}
}
fbuffer_append_str(buffer, tmp);
@@ -1028,6 +1048,8 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s
generate_json_bignum(buffer, Vstate, state, obj);
} else if (klass == rb_cFloat) {
generate_json_float(buffer, Vstate, state, obj);
+ } else if (state->strict) {
+ rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj)));
} else if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, Vstate);
Check_Type(tmp, T_STRING);
@@ -1070,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self)
return buffer;
}
+struct generate_json_data {
+ FBuffer *buffer;
+ VALUE vstate;
+ JSON_Generator_State *state;
+ VALUE obj;
+};
+
+static VALUE generate_json_try(VALUE d)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+
+ generate_json(data->buffer, data->vstate, data->state, data->obj);
+
+ return Qnil;
+}
+
+static VALUE generate_json_rescue(VALUE d, VALUE exc)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+ fbuffer_free(data->buffer);
+
+ rb_exc_raise(exc);
+
+ return Qundef;
+}
+
static VALUE cState_partial_generate(VALUE self, VALUE obj)
{
FBuffer *buffer = cState_prepare_buffer(self);
GET_STATE(self);
- generate_json(buffer, self, state, obj);
+
+ struct generate_json_data data = {
+ .buffer = buffer,
+ .vstate = self,
+ .state = state,
+ .obj = obj
+ };
+ rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
+
return fbuffer_to_s(buffer);
}
@@ -1390,27 +1446,58 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
}
/*
- * call-seq: escape_slash
+ * call-seq: script_safe
*
* If this boolean is true, the forward slashes will be escaped in
* the json output.
*/
-static VALUE cState_escape_slash(VALUE self)
+static VALUE cState_script_safe(VALUE self)
{
GET_STATE(self);
- return state->escape_slash ? Qtrue : Qfalse;
+ return state->script_safe ? Qtrue : Qfalse;
}
/*
- * call-seq: escape_slash=(depth)
+ * call-seq: script_safe=(enable)
*
* This sets whether or not the forward slashes will be escaped in
* the json output.
*/
-static VALUE cState_escape_slash_set(VALUE self, VALUE enable)
+static VALUE cState_script_safe_set(VALUE self, VALUE enable)
+{
+ GET_STATE(self);
+ state->script_safe = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: strict
+ *
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict(VALUE self)
+{
+ GET_STATE(self);
+ return state->strict ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: strict=(enable)
+ *
+ * This sets whether or not to serialize types unsupported by the
+ * JSON format as strings.
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict_set(VALUE self, VALUE enable)
{
GET_STATE(self);
- state->escape_slash = RTEST(enable);
+ state->strict = RTEST(enable);
return Qnil;
}
@@ -1530,9 +1617,15 @@ void Init_generator(void)
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
- rb_define_method(cState, "escape_slash", cState_escape_slash, 0);
- rb_define_method(cState, "escape_slash?", cState_escape_slash, 0);
- rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1);
+ rb_define_method(cState, "script_safe", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe?", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
+ rb_define_alias(cState, "escape_slash", "script_safe");
+ rb_define_alias(cState, "escape_slash?", "script_safe?");
+ rb_define_alias(cState, "escape_slash=", "script_safe=");
+ rb_define_method(cState, "strict", cState_strict, 0);
+ rb_define_method(cState, "strict?", cState_strict, 0);
+ rb_define_method(cState, "strict=", cState_strict_set, 1);
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
@@ -1589,7 +1682,9 @@ void Init_generator(void)
i_object_nl = rb_intern("object_nl");
i_array_nl = rb_intern("array_nl");
i_max_nesting = rb_intern("max_nesting");
+ i_script_safe = rb_intern("script_safe");
i_escape_slash = rb_intern("escape_slash");
+ i_strict = rb_intern("strict");
i_allow_nan = rb_intern("allow_nan");
i_ascii_only = rb_intern("ascii_only");
i_depth = rb_intern("depth");