Quellcode durchsuchen

Improved encoder errors

This updates encoder errors to report the actual Erlang value that
caused the error. This should make it easier to debug errors when
generating JSON.
pull/72/head
Paul J. Davis vor 10 Jahren
Ursprung
Commit
f9095c5258
6 geänderte Dateien mit 120 neuen und 15 gelöschten Zeilen
  1. +18
    -12
      c_src/encoder.c
  2. +2
    -0
      c_src/jiffy.h
  3. +8
    -0
      c_src/util.c
  4. +4
    -2
      src/jiffy.erl
  5. +1
    -1
      test/jiffy_04_string_tests.erl
  6. +87
    -0
      test/jiffy_12_error_tests.erl

+ 18
- 12
c_src/encoder.c Datei anzeigen

@ -122,6 +122,12 @@ enc_error(Encoder* e, const char* msg)
return make_error(e->atoms, e->env, msg);
}
ERL_NIF_TERM
enc_obj_error(Encoder* e, const char* msg, ERL_NIF_TERM obj)
{
return make_obj_error(e->atoms, e->env, msg, obj);
}
static inline int
enc_ensure(Encoder* e, size_t req)
{
@ -667,11 +673,11 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
if(!enif_get_tuple(env, item, &arity, &tuple)) {
ret = enc_error(e, "invalid_object_pair");
ret = enc_obj_error(e, "invalid_object_member", item);
goto done;
}
if(arity != 2) {
ret = enc_error(e, "invalid_object_pair");
ret = enc_obj_error(e, "invalid_object_member_arity", item);
goto done;
}
if(!enc_comma(e)) {
@ -679,7 +685,7 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
if(!enc_string(e, tuple[0])) {
ret = enc_error(e, "invalid_object_key");
ret = enc_obj_error(e, "invalid_object_member_key", tuple[0]);
goto done;
}
if(!enc_colon(e)) {
@ -712,12 +718,12 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
stack = enif_make_list_cell(env, curr, stack);
stack = enif_make_list_cell(env, e->atoms->ref_array, stack);
stack = enif_make_list_cell(env, item, stack);
} else if(enif_compare(curr, e->atoms->atom_null) == 0
} else if(enif_compare(curr, e->atoms->atom_null) == 0 class="p">) {
if(!enc_literal(e, "null", 4)) {
ret = enc_error(e, "null");
goto done;
}
} else if(e->use_nil && enif_compare(curr, e->atoms->atom_nil) == 0)) {
} else if(e->use_nil && enif_compare(curr, e->atoms->atom_nil) == 0) {
if(!enc_literal(e, "null", 4)) {
ret = enc_error(e, "null");
goto done;
@ -734,12 +740,12 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
} else if(enif_is_binary(env, curr)) {
if(!enc_string(e, curr)) {
ret = enc_error(e, "invalid_string");
ret = enc_obj_error(e, "invalid_string", curr);
goto done;
}
} else if(enif_is_atom(env, curr)) {
if(!enc_string(e, curr)) {
ret = enc_error(e, "invalid_string");
ret = enc_obj_error(e, "invalid_string", curr);
goto done;
}
} else if(enif_get_int64(env, curr, &lval)) {
@ -754,11 +760,11 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
} else if(enif_get_tuple(env, curr, &arity, &tuple)) {
if(arity != 1) {
ret = enc_error(e, "invalid_ejson");
ret = enc_obj_error(e, "invalid_ejson", curr);
goto done;
}
if(!enif_is_list(env, tuple[0])) {
ret = enc_error(e, "invalid_object");
ret = enc_obj_error(e, "invalid_object", curr);
goto done;
}
if(!enc_start_object(e)) {
@ -777,15 +783,15 @@ encode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
if(!enif_get_tuple(env, item, &arity, &tuple)) {
ret = enc_error(e, "invalid_object_member");
ret = enc_obj_error(e, "invalid_object_member", item);
goto done;
}
if(arity != 2) {
ret = enc_error(e, "invalid_object_member_arity");
ret = enc_obj_error(e, "invalid_object_member_arity", item);
goto done;
}
if(!enc_string(e, tuple[0])) {
ret = enc_error(e, "invalid_object_member_key");
ret = enc_obj_error(e, "invalid_object_member_key", tuple[0]);
goto done;
}
if(!enc_colon(e)) {

+ 2
- 0
c_src/jiffy.h Datei anzeigen

@ -41,6 +41,8 @@ typedef struct {
ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name);
ERL_NIF_TERM make_ok(jiffy_st* st, ErlNifEnv* env, ERL_NIF_TERM data);
ERL_NIF_TERM make_error(jiffy_st* st, ErlNifEnv* env, const char* error);
ERL_NIF_TERM make_obj_error(jiffy_st* st, ErlNifEnv* env, const char* error,
ERL_NIF_TERM obj);
int get_bytes_per_iter(ErlNifEnv* env, ERL_NIF_TERM val, size_t* bpi);
int should_yield(size_t used, size_t limit);
int consume_timeslice(ErlNifEnv* env, size_t used, size_t limit);

+ 8
- 0
c_src/util.c Datei anzeigen

@ -25,6 +25,14 @@ make_error(jiffy_st* st, ErlNifEnv* env, const char* error)
return enif_make_tuple2(env, st->atom_error, make_atom(env, error));
}
ERL_NIF_TERM
make_obj_error(jiffy_st* st, ErlNifEnv* env,
const char* error, ERL_NIF_TERM obj)
{
ERL_NIF_TERM reason = enif_make_tuple2(env, make_atom(env, error), obj);
return enif_make_tuple2(env, st->atom_error, reason);
}
int
get_bytes_per_iter(ErlNifEnv* env, ERL_NIF_TERM val, size_t* bpi)
{

+ 4
- 2
src/jiffy.erl Datei anzeigen

@ -34,7 +34,7 @@ encode(Data) ->
encode(Data, Options) ->
ForceUTF8 = lists:member(force_utf8, Options),
case nif_encode_init(Data, Options) of
{error, invalid_string} when ForceUTF8 == true ->
{error, {invalid_string, _}} when ForceUTF8 == true ->
FixedData = jiffy_utf8:fix(Data),
encode(FixedData, Options -- [force_utf8]);
{error, _} = Error ->
@ -102,6 +102,8 @@ finish_encode([<<_/binary>>=B | Rest], Acc) ->
finish_encode([Val | Rest], Acc) when is_integer(Val) ->
Bin = list_to_binary(integer_to_list(Val)),
finish_encode(Rest, [Bin | Acc]);
finish_encode([InvalidEjson | _], _) ->
throw({error, {invalid_ejson, InvalidEjson}});
finish_encode(_, _) ->
throw({error, invalid_ejson}).
@ -134,7 +136,7 @@ decode_loop(Data, Decoder, Objs, Curr) ->
encode_loop(Data, Options, Encoder, Stack, IOBuf) ->
ForceUTF8 = lists:member(force_utf8, Options),
case nif_encode_iter(Encoder, Stack, IOBuf) of
{error, invalid_string} when ForceUTF8 == true ->
{error, {invalid_string, _}} when ForceUTF8 == true ->
FixedData = jiffy_utf8:fix(Data),
encode(FixedData, Options -- [force_utf8]);
{error, _} = Error ->

+ 1
- 1
test/jiffy_04_string_tests.erl Datei anzeigen

@ -48,7 +48,7 @@ gen(utf8, {Case, Fixed}) ->
Case2 = <<34, Case/binary, 34>>,
Fixed2 = <<34, Fixed/binary, 34>>,
{msg("UTF-8: ~s", [hex(Case)]), [
?_assertThrow({error, invalid_string}, jiffy:encode(Case)),
?_assertThrow({error, {invalid_string, _}}, jiffy:encode(Case)),
?_assertEqual(Fixed2, jiffy:encode(Case, [force_utf8])),
?_assertThrow({error, {_, invalid_string}}, jiffy:decode(Case2))
]}.

+ 87
- 0
test/jiffy_12_error_tests.erl Datei anzeigen

@ -0,0 +1,87 @@
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_12_error_tests).
-include_lib("eunit/include/eunit.hrl").
enc_invalid_ejson_test_() ->
Type = invalid_ejson,
Ref = make_ref(),
{"invalid_ejson", [
{"Basic", enc_error(Type, Ref, Ref)},
{"Nested", enc_error(Type, {Ref, Ref}, {Ref, Ref})}
]}.
enc_invalid_string_test_() ->
Type = invalid_string,
{"invalid_string", [
{"Bare strign", enc_error(Type, <<143>>, <<143>>)},
{"List element", enc_error(Type, <<143>>, [<<143>>])},
{"Bad obj value", enc_error(Type, <<143>>, {[{foo, <<143>>}]})}
]}.
enc_invalid_object_test_() ->
Type = invalid_object,
Ref = make_ref(),
{"invalid_object", [
{"Number", enc_error(Type, {1}, {1})},
{"Ref", enc_error(Type, {Ref}, {Ref})},
{"Tuple", enc_error(Type, {{[]}}, {{[]}})},
{"Atom", enc_error(Type, {foo}, {foo})}
]}.
enc_invalid_object_member_test_() ->
Type = invalid_object_member,
{"invalid_object_member", [
{"Basic", enc_error(Type, foo, {[foo]})},
{"Basic", enc_error(Type, foo, {[{bar, baz}, foo]})},
{"Nested", enc_error(Type, foo, {[{bar,{[foo]}}]})},
{"Nested", enc_error(Type, foo, {[{bar,{[{baz, 1}, foo]}}]})},
{"In List", enc_error(Type, foo, [{[foo]}])},
{"In List", enc_error(Type, foo, [{[{bang, true}, foo]}])}
]}.
enc_invalid_object_member_arity_test_() ->
Type = invalid_object_member_arity,
E1 = {foo},
E2 = {x, y, z},
{"invalid_object_member", [
{"Basic", enc_error(Type, E1, {[E1]})},
{"Basic", enc_error(Type, E2, {[E2]})},
{"Basic", enc_error(Type, E1, {[{bar, baz}, E1]})},
{"Basic", enc_error(Type, E2, {[{bar, baz}, E2]})},
{"Nested", enc_error(Type, E1, {[{bar,{[E1]}}]})},
{"Nested", enc_error(Type, E2, {[{bar,{[E2]}}]})},
{"Nested", enc_error(Type, E1, {[{bar,{[{baz, 1}, E1]}}]})},
{"Nested", enc_error(Type, E2, {[{bar,{[{baz, 1}, E2]}}]})},
{"In List", enc_error(Type, E1, [{[E1]}])},
{"In List", enc_error(Type, E2, [{[E2]}])},
{"In List", enc_error(Type, E1, [{[{bang, true}, E1]}])},
{"In List", enc_error(Type, E2, [{[{bang, true}, E2]}])}
]}.
enc_invalid_object_member_key_test_() ->
Type = invalid_object_member_key,
E1 = {1, true},
{"invalid_object_member_key", [
{"Bad string", enc_error(Type, <<143>>, {[{<<143>>, true}]})},
{"Basic", enc_error(Type, 1, {[{1, true}]})},
{"Basic", enc_error(Type, [1], {[{[1], true}]})},
{"Basic", enc_error(Type, {[{foo,bar}]}, {[{{[{foo,bar}]}, true}]})},
{"Second", enc_error(Type, 1, {[{bar, baz}, E1]})},
{"Nested", enc_error(Type, 1, {[{bar,{[E1]}}]})},
{"Nested", enc_error(Type, 1, {[{bar,{[{baz, 1}, E1]}}]})},
{"In List", enc_error(Type, 1, [{[E1]}])},
{"In List", enc_error(Type, 1, [{[{bang, true}, E1]}])}
]}.
enc_error(Type, Obj, Case) ->
?_assertEqual({error, {Type, Obj}}, (catch jiffy:encode(Case))).

Laden…
Abbrechen
Speichern