reader.h: prepare "early return path" for exception support
In case of a user-defined RAPIDJSON_PARSE_ERROR_NORETURN that throws an exception instead of using the Rapidjson ParseError API, the early return paths performing the stack unwinding manually can be omitted as well. This patch provides a customizable RAPIDJSON_PARSE_ERROR_EARLY_RETURN macro to remove these (then unneeded) control paths from the parsing implementation (with and without a return value). Secondly, clearing the parse stack is moved to a small helper struct that calls stack_.Clear() from its destructor. This avoids the need for the 'goto' in the ParseStream function and ensures proper cleanup even if e.g. a user-defined Allocator throws an exception.
This commit is contained in:
parent
b37bd85318
commit
3c1d4bc21d
@ -24,6 +24,16 @@ RAPIDJSON_DIAG_PUSH
|
|||||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define RAPIDJSON_NOTHING /* deliberately empty */
|
||||||
|
#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
|
||||||
|
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
|
||||||
|
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||||
|
if (HasParseError()) { return value; } \
|
||||||
|
RAPIDJSON_MULTILINEMACRO_END
|
||||||
|
#endif
|
||||||
|
#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
|
||||||
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING)
|
||||||
|
|
||||||
#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
|
#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
|
||||||
#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
|
#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
|
||||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||||
@ -37,7 +47,7 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
|||||||
#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
|
#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
|
||||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||||
RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
|
RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
|
||||||
return; \
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
|
||||||
RAPIDJSON_MULTILINEMACRO_END
|
RAPIDJSON_MULTILINEMACRO_END
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -278,28 +288,30 @@ public:
|
|||||||
parseErrorCode_ = kParseErrorNone;
|
parseErrorCode_ = kParseErrorNone;
|
||||||
errorOffset_ = 0;
|
errorOffset_ = 0;
|
||||||
|
|
||||||
|
ClearStackOnExit scope(*this);
|
||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
|
|
||||||
if (is.Peek() == '\0')
|
if (is.Peek() == '\0') {
|
||||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
|
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
|
||||||
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
switch (is.Peek()) {
|
switch (is.Peek()) {
|
||||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||||
case '[': ParseArray<parseFlags>(is, handler); break;
|
case '[': ParseArray<parseFlags>(is, handler); break;
|
||||||
default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell());
|
default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell());
|
||||||
}
|
}
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false);
|
||||||
goto out;
|
|
||||||
|
|
||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
|
|
||||||
if (is.Peek() != '\0')
|
if (is.Peek() != '\0') {
|
||||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
|
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
|
||||||
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
return true;
|
||||||
stack_.Clear();
|
|
||||||
return !HasParseError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Parse JSON text (with \ref kParseDefaultFlags)
|
//! Parse JSON text (with \ref kParseDefaultFlags)
|
||||||
@ -325,6 +337,16 @@ private:
|
|||||||
GenericReader(const GenericReader&);
|
GenericReader(const GenericReader&);
|
||||||
GenericReader& operator=(const GenericReader&);
|
GenericReader& operator=(const GenericReader&);
|
||||||
|
|
||||||
|
void ClearStack() { stack_.Clear(); }
|
||||||
|
|
||||||
|
// clear stack on any exit from ParseStream, e.g. due to exception
|
||||||
|
struct ClearStackOnExit {
|
||||||
|
explicit ClearStackOnExit(GenericReader& r) : r_(r) {}
|
||||||
|
~ClearStackOnExit() { r_.ClearStack(); }
|
||||||
|
private:
|
||||||
|
GenericReader& r_;
|
||||||
|
};
|
||||||
|
|
||||||
// Parse object: { string : value, ... }
|
// Parse object: { string : value, ... }
|
||||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||||
void ParseObject(InputStream& is, Handler& handler) {
|
void ParseObject(InputStream& is, Handler& handler) {
|
||||||
@ -344,8 +366,7 @@ private:
|
|||||||
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
|
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
|
||||||
|
|
||||||
ParseString<parseFlags>(is, handler);
|
ParseString<parseFlags>(is, handler);
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||||
return;
|
|
||||||
|
|
||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
|
|
||||||
@ -355,8 +376,7 @@ private:
|
|||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
|
|
||||||
ParseValue<parseFlags>(is, handler);
|
ParseValue<parseFlags>(is, handler);
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||||
return;
|
|
||||||
|
|
||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
|
|
||||||
@ -386,8 +406,7 @@ private:
|
|||||||
|
|
||||||
for (SizeType elementCount = 0;;) {
|
for (SizeType elementCount = 0;;) {
|
||||||
ParseValue<parseFlags>(is, handler);
|
ParseValue<parseFlags>(is, handler);
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||||
return;
|
|
||||||
|
|
||||||
++elementCount;
|
++elementCount;
|
||||||
SkipWhitespace(is);
|
SkipWhitespace(is);
|
||||||
@ -449,7 +468,7 @@ private:
|
|||||||
codepoint -= 'a' - 10;
|
codepoint -= 'a' - 10;
|
||||||
else {
|
else {
|
||||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1);
|
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1);
|
||||||
return 0;
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return codepoint;
|
return codepoint;
|
||||||
@ -481,8 +500,7 @@ private:
|
|||||||
if (parseFlags & kParseInsituFlag) {
|
if (parseFlags & kParseInsituFlag) {
|
||||||
typename InputStream::Ch *head = s.PutBegin();
|
typename InputStream::Ch *head = s.PutBegin();
|
||||||
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
|
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||||
return;
|
|
||||||
size_t length = s.PutEnd(head) - 1;
|
size_t length = s.PutEnd(head) - 1;
|
||||||
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
|
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
|
||||||
handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false);
|
handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false);
|
||||||
@ -490,8 +508,7 @@ private:
|
|||||||
else {
|
else {
|
||||||
StackStream stackStream(stack_);
|
StackStream stackStream(stack_);
|
||||||
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
|
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
|
||||||
if (HasParseError())
|
RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
|
||||||
return;
|
|
||||||
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true);
|
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user