Просмотр исходного кода

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 10 лет назад
Родитель
Сommit
f9095c5258
6 измененных файлов: 120 добавлений и 15 удалений
  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 Просмотреть файл

@ -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 Просмотреть файл

@ -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 Просмотреть файл

@ -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 Просмотреть файл

@ -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 Просмотреть файл

@ -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 Просмотреть файл

@ -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))).

Загрузка…
Отмена
Сохранить