Skip to content

Commit

Permalink
changed parse errors to raise a StandardError child
Browse files Browse the repository at this point in the history
  • Loading branch information
ohler55 committed Oct 12, 2012
1 parent 1a05dc9 commit e3a04e8
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 41 deletions.
10 changes: 2 additions & 8 deletions README.md
Expand Up @@ -32,15 +32,9 @@ A fast JSON parser and Object marshaller as a Ruby gem.


## <a name="release">Release Notes</a> ## <a name="release">Release Notes</a>


### Release 1.3.7 ### Release 1.4.0


- Added header file for linux builds. - Parse errors now raise an Exception that inherites form Oj::Error which inherits from StandardError. Some other Exceptions were changed as well to make it easier to rescue errors.

### Release 1.3.6

- Oj.load() now raises a SystemStackError if a JSON is too deeply nested. The loading is allowed to use on 75% of the stack.

- Oj::Doc.open() now raises a SystemStackError if a JSON is too deeply nested. The loading is allowed to use on 75% of the stack. Oj::Doc.open will allow much deeper nesting than Oj.load().


## <a name="description">Description</a> ## <a name="description">Description</a>


Expand Down
11 changes: 6 additions & 5 deletions ext/oj/fast.c
Expand Up @@ -276,7 +276,7 @@ leaf_value(Doc doc, Leaf leaf) {
return leaf_hash_value(doc, leaf); return leaf_hash_value(doc, leaf);
break; break;
default: default:
rb_raise(rb_eTypeError, "Unexpected type %02x.", leaf->type); rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->type);
break; break;
} }
} }
Expand Down Expand Up @@ -901,7 +901,7 @@ get_leaf(Leaf *stack, Leaf *lp, const char *path) {
Leaf leaf = *lp; Leaf leaf = *lp;


if (MAX_STACK <= lp - stack) { if (MAX_STACK <= lp - stack) {
rb_raise(rb_eIOError, "Path too deep. limit is %d levels.\n", MAX_STACK); rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
} }
if ('\0' != *path) { if ('\0' != *path) {
if ('.' == *path && '.' == *(path + 1)) { if ('.' == *path && '.' == *(path + 1)) {
Expand Down Expand Up @@ -988,7 +988,7 @@ each_leaf(Doc doc, VALUE self) {
static int static int
move_step(Doc doc, const char *path, int loc) { move_step(Doc doc, const char *path, int loc) {
if (MAX_STACK <= doc->where - doc->where_path) { if (MAX_STACK <= doc->where - doc->where_path) {
rb_raise(rb_eIOError, "Path too deep. limit is %d levels.\n", MAX_STACK); rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
} }
if ('\0' == *path) { if ('\0' == *path) {
loc = 0; loc = 0;
Expand Down Expand Up @@ -1167,7 +1167,7 @@ doc_open_file(VALUE clas, VALUE filename) {
Check_Type(filename, T_STRING); Check_Type(filename, T_STRING);
path = StringValuePtr(filename); path = StringValuePtr(filename);
if (0 == (f = fopen(path, "r"))) { if (0 == (f = fopen(path, "r"))) {
rb_raise(rb_eIOError, "%s\n", strerror(errno)); rb_raise(rb_eIOError, "%s", strerror(errno));
} }
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
len = ftell(f); len = ftell(f);
Expand All @@ -1180,7 +1180,8 @@ doc_open_file(VALUE clas, VALUE filename) {
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
if (len != fread(json, 1, len, f)) { if (len != fread(json, 1, len, f)) {
fclose(f); fclose(f);
rb_raise(rb_eLoadError, "Failed to read %lu bytes from %s.\n", (unsigned long)len, path); rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
"Failed to read %lu bytes from %s.", (unsigned long)len, path);
} }
fclose(f); fclose(f);
json[len] = '\0'; json[len] = '\0';
Expand Down
57 changes: 30 additions & 27 deletions ext/oj/oj.c
Expand Up @@ -67,6 +67,7 @@ ID oj_utc_offset_id;
ID oj_write_id; ID oj_write_id;


VALUE oj_bag_class; VALUE oj_bag_class;
VALUE oj_parse_error_class;
VALUE oj_bigdecimal_class; VALUE oj_bigdecimal_class;
VALUE oj_stringio_class; VALUE oj_stringio_class;
VALUE oj_struct_class; VALUE oj_struct_class;
Expand Down Expand Up @@ -249,7 +250,7 @@ set_def_opts(VALUE self, VALUE opts) {
} else if (null_sym == v) { } else if (null_sym == v) {
oj_default_options.mode = NullMode; oj_default_options.mode = NullMode;
} else { } else {
rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.\n"); rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
} }


v = rb_hash_lookup(opts, time_format_sym); v = rb_hash_lookup(opts, time_format_sym);
Expand All @@ -262,7 +263,7 @@ set_def_opts(VALUE self, VALUE opts) {
} else if (ruby_sym == v) { } else if (ruby_sym == v) {
oj_default_options.time_format = RubyTime; oj_default_options.time_format = RubyTime;
} else { } else {
rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.\n"); rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
} }


if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) { if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) {
Expand Down Expand Up @@ -291,7 +292,7 @@ set_def_opts(VALUE self, VALUE opts) {
} else if (Qfalse == v) { } else if (Qfalse == v) {
*o->attr = No; *o->attr = No;
} else { } else {
rb_raise(rb_eArgError, "%s must be true, false, or nil.\n", rb_id2name(SYM2ID(o->sym))); rb_raise(rb_eArgError, "%s must be true, false, or nil.", rb_id2name(SYM2ID(o->sym)));
} }
} }
} }
Expand All @@ -314,7 +315,7 @@ parse_options(VALUE ropts, Options copts) {


if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) { if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
if (rb_cFixnum != rb_obj_class(v)) { if (rb_cFixnum != rb_obj_class(v)) {
rb_raise(rb_eArgError, ":indent must be a Fixnum.\n"); rb_raise(rb_eArgError, ":indent must be a Fixnum.");
} }
copts->indent = NUM2INT(v); copts->indent = NUM2INT(v);
} }
Expand All @@ -328,7 +329,7 @@ parse_options(VALUE ropts, Options copts) {
} else if (null_sym == v) { } else if (null_sym == v) {
copts->mode = NullMode; copts->mode = NullMode;
} else { } else {
rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.\n"); rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
} }
} }
if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) { if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
Expand All @@ -339,7 +340,7 @@ parse_options(VALUE ropts, Options copts) {
} else if (ruby_sym == v) { } else if (ruby_sym == v) {
copts->time_format = RubyTime; copts->time_format = RubyTime;
} else { } else {
rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.\n"); rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
} }
} }
for (o = ynos; 0 != o->attr; o++) { for (o = ynos; 0 != o->attr; o++) {
Expand All @@ -349,7 +350,7 @@ parse_options(VALUE ropts, Options copts) {
} else if (Qfalse == v) { } else if (Qfalse == v) {
*o->attr = No; *o->attr = No;
} else { } else {
rb_raise(rb_eArgError, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym))); rb_raise(rb_eArgError, "%s must be true or false.", rb_id2name(SYM2ID(o->sym)));
} }
} }
} }
Expand Down Expand Up @@ -398,7 +399,7 @@ load_with_opts(VALUE input, Options copts) {
json = ALLOCA_N(char, len + 1); json = ALLOCA_N(char, len + 1);
} }
if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) { if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
rb_raise(rb_eIOError, "failed to read from IO Object.\n"); rb_raise(rb_eIOError, "failed to read from IO Object.");
} }
json[len] = '\0'; json[len] = '\0';
#endif #endif
Expand All @@ -412,7 +413,7 @@ load_with_opts(VALUE input, Options copts) {
} }
strcpy(json, StringValuePtr(s)); strcpy(json, StringValuePtr(s));
} else { } else {
rb_raise(rb_eArgError, "load() expected a String or IO Object.\n"); rb_raise(rb_eArgError, "load() expected a String or IO Object.");
} }
} }
obj = oj_parse(json, copts); obj = oj_parse(json, copts);
Expand All @@ -435,7 +436,7 @@ load(int argc, VALUE *argv, VALUE self) {
struct _Options options = oj_default_options; struct _Options options = oj_default_options;


if (1 > argc) { if (1 > argc) {
rb_raise(rb_eArgError, "Wrong number of arguments to load().\n"); rb_raise(rb_eArgError, "Wrong number of arguments to load().");
} }
if (2 <= argc) { if (2 <= argc) {
parse_options(argv[1], &options); parse_options(argv[1], &options);
Expand Down Expand Up @@ -466,7 +467,7 @@ load_file(int argc, VALUE *argv, VALUE self) {
Check_Type(*argv, T_STRING); Check_Type(*argv, T_STRING);
path = StringValuePtr(*argv); path = StringValuePtr(*argv);
if (0 == (f = fopen(path, "r"))) { if (0 == (f = fopen(path, "r"))) {
rb_raise(rb_eIOError, "%s\n", strerror(errno)); rb_raise(rb_eIOError, "%s", strerror(errno));
} }
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
len = ftell(f); len = ftell(f);
Expand All @@ -478,7 +479,7 @@ load_file(int argc, VALUE *argv, VALUE self) {
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
if (len != fread(json, 1, len, f)) { if (len != fread(json, 1, len, f)) {
fclose(f); fclose(f);
rb_raise(rb_eLoadError, "Failed to read %ld bytes from %s.\n", len, path); rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")), "Failed to read %ld bytes from %s.", len, path);
} }
fclose(f); fclose(f);
json[len] = '\0'; json[len] = '\0';
Expand Down Expand Up @@ -508,7 +509,7 @@ dump(int argc, VALUE *argv, VALUE self) {
parse_options(argv[1], &copts); parse_options(argv[1], &copts);
} }
if (0 == (json = oj_write_obj_to_str(*argv, &copts))) { if (0 == (json = oj_write_obj_to_str(*argv, &copts))) {
rb_raise(rb_eNoMemError, "Not enough memory.\n"); rb_raise(rb_eNoMemError, "Not enough memory.");
} }
rstr = rb_str_new2(json); rstr = rb_str_new2(json);
#if HAS_ENCODING_SUPPORT #if HAS_ENCODING_SUPPORT
Expand Down Expand Up @@ -551,7 +552,7 @@ mimic_dump(int argc, VALUE *argv, VALUE self) {
VALUE rstr; VALUE rstr;


if (0 == (json = oj_write_obj_to_str(*argv, &copts))) { if (0 == (json = oj_write_obj_to_str(*argv, &copts))) {
rb_raise(rb_eNoMemError, "Not enough memory.\n"); rb_raise(rb_eNoMemError, "Not enough memory.");
} }
rstr = rb_str_new2(json); rstr = rb_str_new2(json);
#if HAS_ENCODING_SUPPORT #if HAS_ENCODING_SUPPORT
Expand Down Expand Up @@ -601,7 +602,7 @@ mimic_walk(VALUE key, VALUE obj, VALUE proc) {
*args = obj; *args = obj;
rb_proc_call_with_block(proc, 1, args, Qnil); rb_proc_call_with_block(proc, 1, args, Qnil);
#else #else
rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.\n"); rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
#endif #endif
} }
return ST_CONTINUE; return ST_CONTINUE;
Expand All @@ -623,7 +624,7 @@ mimic_load(int argc, VALUE *argv, VALUE self) {
static VALUE static VALUE
mimic_dump_load(int argc, VALUE *argv, VALUE self) { mimic_dump_load(int argc, VALUE *argv, VALUE self) {
if (1 > argc) { if (1 > argc) {
rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)\n"); rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
} else if (T_STRING == rb_type(*argv)) { } else if (T_STRING == rb_type(*argv)) {
return mimic_load(argc, argv, self); return mimic_load(argc, argv, self);
} else { } else {
Expand All @@ -644,7 +645,7 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {


memset(&dump_opts, 0, sizeof(dump_opts)); // may not be needed memset(&dump_opts, 0, sizeof(dump_opts)); // may not be needed
if (T_HASH != rb_type(ropts)) { if (T_HASH != rb_type(ropts)) {
rb_raise(rb_eArgError, "options must be a hash.\n"); rb_raise(rb_eArgError, "options must be a hash.");
} }
if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) { if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
rb_check_type(v, T_STRING); rb_check_type(v, T_STRING);
Expand Down Expand Up @@ -690,7 +691,7 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {
// :max_nesting is always set to 100 // :max_nesting is always set to 100
} }
if (0 == (json = oj_write_obj_to_str(*argv, copts))) { if (0 == (json = oj_write_obj_to_str(*argv, copts))) {
rb_raise(rb_eNoMemError, "Not enough memory.\n"); rb_raise(rb_eNoMemError, "Not enough memory.");
} }
rstr = rb_str_new2(json); rstr = rb_str_new2(json);
#if HAS_ENCODING_SUPPORT #if HAS_ENCODING_SUPPORT
Expand Down Expand Up @@ -733,14 +734,14 @@ mimic_parse(int argc, VALUE *argv, VALUE self) {
struct _Options options = oj_default_options; struct _Options options = oj_default_options;


if (1 > argc) { if (1 > argc) {
rb_raise(rb_eArgError, "Wrong number of arguments to parse().\n"); rb_raise(rb_eArgError, "Wrong number of arguments to parse().");
} }
if (2 <= argc && Qnil != argv[1]) { if (2 <= argc && Qnil != argv[1]) {
VALUE ropts = argv[1]; VALUE ropts = argv[1];
VALUE v; VALUE v;


if (T_HASH != rb_type(ropts)) { if (T_HASH != rb_type(ropts)) {
rb_raise(rb_eArgError, "options must be a hash.\n"); rb_raise(rb_eArgError, "options must be a hash.");
} }
if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) { if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
options.sym_key = (Qtrue == v) ? Yes : No; options.sym_key = (Qtrue == v) ? Yes : No;
Expand Down Expand Up @@ -804,7 +805,8 @@ define_mimic_json(VALUE self) {
VALUE dummy; VALUE dummy;


if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) { if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
rb_raise(rb_eTypeError, "JSON module already exists. Can not mimic. Do not require 'json' before calling mimic_JSON."); rb_raise(rb_const_get_at(Oj, rb_intern("MimicError")),
"JSON module already exists. Can not mimic. Do not require 'json' before calling mimic_JSON.");
} }
mimic = rb_define_module("JSON"); mimic = rb_define_module("JSON");
ext = rb_define_module_under(mimic, "Ext"); ext = rb_define_module_under(mimic, "Ext");
Expand Down Expand Up @@ -884,6 +886,7 @@ void Init_oj() {
oj_write_id = rb_intern("write"); oj_write_id = rb_intern("write");


oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag")); oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct")); oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
oj_time_class = rb_const_get(rb_cObject, rb_intern("Time")); oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal")); oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
Expand Down Expand Up @@ -965,19 +968,19 @@ void Init_oj() {
} }


void void
_oj_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) { _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line) {
int xline = 1; int jline = 1;
int col = 1; int col = 1;


for (; xml < current && '\n' != *current; current--) { for (; json < current && '\n' != *current; current--) {
col++; col++;
} }
for (; xml < current; current--) { for (; json < current; current--) {

This comment has been minimized.

Copy link
@sferik

sferik Oct 12, 2012

Contributor

:wink2:

if ('\n' == *current) { if ('\n' == *current) {
xline++; jline++;
} }
} }
rb_raise(rb_eSyntaxError, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line); rb_raise(oj_parse_error_class, "%s at line %d, column %d [%s:%d]", msg, jline, col, file, line);
} }


// mimic JSON documentation // mimic JSON documentation
Expand Down
1 change: 1 addition & 0 deletions lib/oj.rb
Expand Up @@ -26,6 +26,7 @@ module Oj


require 'oj/version' require 'oj/version'
require 'oj/bag' require 'oj/bag'
require 'oj/error'
require 'oj/mimic' require 'oj/mimic'


require 'oj/oj' # C extension require 'oj/oj' # C extension
23 changes: 23 additions & 0 deletions lib/oj/error.rb
@@ -0,0 +1,23 @@

module Oj

class Error < StandardError
end # Error

# An Exception that is raised as a result of a parse error while parsing a JSON document.
class ParseError < Error
end # ParseError

# An Exception that is raised as a result of a path being too deep.
class DepthError < Error
end # DepthError

# An Exception that is raised if a file fails to load.
class LoadError < Error
end # LoadError

# An Exception that is raised if there is a conflict with mimicing JSON
class MimicError < Error
end # MimicError

end # Oj
2 changes: 1 addition & 1 deletion lib/oj/version.rb
@@ -1,5 +1,5 @@


module Oj module Oj
# Current version of the module. # Current version of the module.
VERSION = '1.3.7' VERSION = '1.4.0'
end end

0 comments on commit e3a04e8

Please sign in to comment.