forked from weaselab/weaseljson
Fix stack overflow in no-musttail fallback for direct continuations
The no-musttail trampoline (063872e) only rerouted keepGoing dispatch.
Continuations that tail-call each other directly still recursed on the C
stack when MUSTTAIL is a no-op, so deep input overflowed it -- nested
arrays via n_value <-> n_array2, and runs of string escapes via
n_string2 -> n_string2 (the latter isn't even bounded by stackSize).
Add a TAILCALL(fn) macro: a direct musttail tail call when available,
otherwise a bounce to the parse() trampoline (which re-dispatches
continuations[top()] == fn, valid because fn's symbol is already on top
of the stack at every site). Route the 10 direct continuation tail calls
through it. The musttail build is unchanged; the fallback no longer
recurses without bound.
This commit is contained in:
+24
-10
@@ -161,6 +161,18 @@ struct Parser3 {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Transfer control to continuation `fn`, whose Symbol must already be on top of
|
||||||
|
// the stack. With musttail this is a direct tail call (fast path). Without it,
|
||||||
|
// a direct call would recurse on the C stack (e.g. n_value -> n_array2 ->
|
||||||
|
// n_value for nested arrays, or n_string2 -> n_string2 for runs of escapes) and
|
||||||
|
// overflow on deep input; instead we bounce to the parse() trampoline, which
|
||||||
|
// re-dispatches continuations[top()] == fn without growing the stack.
|
||||||
|
#ifdef HAS_MUSTTAIL
|
||||||
|
#define TAILCALL(fn) MUSTTAIL return fn(self, buf, bufEnd)
|
||||||
|
#else
|
||||||
|
#define TAILCALL(fn) return Parser3::keepGoing(self, buf, bufEnd)
|
||||||
|
#endif
|
||||||
|
|
||||||
inline PRESERVE_NONE ContinuationStatus skipWhitespace(char *&buf,
|
inline PRESERVE_NONE ContinuationStatus skipWhitespace(char *&buf,
|
||||||
char *bufEnd) {
|
char *bufEnd) {
|
||||||
constexpr int kStride = 4;
|
constexpr int kStride = 4;
|
||||||
@@ -306,7 +318,7 @@ inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_object2(self, buf, bufEnd);
|
TAILCALL(n_object2);
|
||||||
case '[':
|
case '[':
|
||||||
self->callbacks->on_begin_array(self->userdata);
|
self->callbacks->on_begin_array(self->userdata);
|
||||||
++buf;
|
++buf;
|
||||||
@@ -315,7 +327,7 @@ inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_array2(self, buf, bufEnd);
|
TAILCALL(n_array2);
|
||||||
case '"':
|
case '"':
|
||||||
++buf;
|
++buf;
|
||||||
self->dataBegin = self->writeBuf = buf;
|
self->dataBegin = self->writeBuf = buf;
|
||||||
@@ -325,7 +337,7 @@ inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_string2(self, buf, bufEnd);
|
TAILCALL(n_string2);
|
||||||
case '0':
|
case '0':
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
@@ -344,7 +356,7 @@ inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_number(self, buf, bufEnd);
|
TAILCALL(n_number);
|
||||||
case 't':
|
case 't':
|
||||||
++buf;
|
++buf;
|
||||||
self->pop();
|
self->pop();
|
||||||
@@ -432,7 +444,7 @@ inline PRESERVE_NONE ContinuationStatus n_object2(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_string2(self, buf, bufEnd);
|
TAILCALL(n_string2);
|
||||||
default:
|
default:
|
||||||
[[unlikely]] return WeaselJson_REJECT;
|
[[unlikely]] return WeaselJson_REJECT;
|
||||||
}
|
}
|
||||||
@@ -478,7 +490,7 @@ inline PRESERVE_NONE ContinuationStatus n_object3(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_string2(self, buf, bufEnd);
|
TAILCALL(n_string2);
|
||||||
default:
|
default:
|
||||||
[[unlikely]] return WeaselJson_REJECT;
|
[[unlikely]] return WeaselJson_REJECT;
|
||||||
}
|
}
|
||||||
@@ -506,7 +518,7 @@ inline PRESERVE_NONE ContinuationStatus n_array2(Parser3 *self, char *buf,
|
|||||||
if (auto s = self->push({N_VALUE, N_ARRAY3})) {
|
if (auto s = self->push({N_VALUE, N_ARRAY3})) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_value(self, buf, bufEnd);
|
TAILCALL(n_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +548,7 @@ inline PRESERVE_NONE ContinuationStatus n_array3(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_value(self, buf, bufEnd);
|
TAILCALL(n_value);
|
||||||
default:
|
default:
|
||||||
[[unlikely]] return WeaselJson_REJECT;
|
[[unlikely]] return WeaselJson_REJECT;
|
||||||
}
|
}
|
||||||
@@ -561,7 +573,7 @@ inline PRESERVE_NONE ContinuationStatus n_string(Parser3 *self, char *buf,
|
|||||||
if (buf == bufEnd) {
|
if (buf == bufEnd) {
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_string2(self, buf, bufEnd);
|
TAILCALL(n_string2);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int32_t read4_hex(const char *buf) {
|
inline int32_t read4_hex(const char *buf) {
|
||||||
@@ -664,7 +676,7 @@ inline PRESERVE_NONE ContinuationStatus n_string2(Parser3 *self, char *buf,
|
|||||||
self->flushString(false, buf);
|
self->flushString(false, buf);
|
||||||
return WeaselJson_AGAIN;
|
return WeaselJson_AGAIN;
|
||||||
}
|
}
|
||||||
MUSTTAIL return n_string2(self, buf, bufEnd);
|
TAILCALL(n_string2);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
[[unlikely]] return WeaselJson_REJECT;
|
[[unlikely]] return WeaselJson_REJECT;
|
||||||
@@ -1072,4 +1084,6 @@ inline PRESERVE_NONE ContinuationStatus Parser3::keepGoing(Parser3 *self,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef TAILCALL
|
||||||
|
|
||||||
} // namespace parser3
|
} // namespace parser3
|
||||||
|
|||||||
Reference in New Issue
Block a user