Commit 3c1d4bc2 authored by Philipp A. Hartmann's avatar Philipp A. Hartmann

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.
parent b37bd853
...@@ -24,6 +24,16 @@ RAPIDJSON_DIAG_PUSH ...@@ -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 ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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: ...@@ -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);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment