浏览代码

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

正在加载...
取消
保存