Переглянути джерело

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 роки тому
джерело
коміт
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))).

Завантаження…
Відмінити
Зберегти