A message longer than its buffer is cut at a character and ends in an ellipsis, and the break loop and the unhandled message use the full text
This commit is contained in:
parent
9d6694fd92
commit
a4e45d9a78
@ -106,19 +106,36 @@ typedef struct {
|
|||||||
* was cut rather than passing for the whole of it. */
|
* was cut rather than passing for the whole of it. */
|
||||||
#define FLAN_MESSAGE_MAX 512
|
#define FLAN_MESSAGE_MAX 512
|
||||||
|
|
||||||
|
/* The most the break loop's sentence takes, and the unhandled message: the
|
||||||
|
* full text, not a message's cut copy. */
|
||||||
|
#define FLAN_SENTENCE_MAX 2048
|
||||||
|
|
||||||
|
/* How many of [p]'s [len] bytes fit in [max] without splitting a UTF-8
|
||||||
|
* character: a cut that lands on a continuation byte backs up to the start
|
||||||
|
* of that character, so a shortened text is still text. */
|
||||||
|
static int64_t utf8_fit(const uint8_t *p, int64_t len, int64_t max) {
|
||||||
|
int64_t m;
|
||||||
|
if (len <= max) return len;
|
||||||
|
m = max < 0 ? 0 : max;
|
||||||
|
while (m > 0 && (p[m] & 0xC0) == 0x80) m--;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char ellipsis[] = "\xe2\x80\xa6";
|
||||||
|
|
||||||
/* Where [render] prints: the buffer of the signal asking, set around the one
|
/* Where [render] prints: the buffer of the signal asking, set around the one
|
||||||
* synchronous call. A printer only formats, so nothing nests inside it. */
|
* synchronous call. A printer only formats, so nothing nests inside it. */
|
||||||
static char *msg_out;
|
static char *msg_out;
|
||||||
|
static int64_t msg_cap;
|
||||||
static int64_t msg_len;
|
static int64_t msg_len;
|
||||||
static int msg_cut;
|
static int msg_cut;
|
||||||
|
|
||||||
static void msg_append(const uint8_t *p, int64_t n) {
|
static void msg_append(const uint8_t *p, int64_t n) {
|
||||||
static const char ell[] = "\xe2\x80\xa6";
|
|
||||||
if (msg_out == NULL || msg_cut || n <= 0) return;
|
if (msg_out == NULL || msg_cut || n <= 0) return;
|
||||||
if (msg_len + n > FLAN_MESSAGE_MAX - 4) {
|
if (msg_len + n > msg_cap - 4) {
|
||||||
int64_t room = FLAN_MESSAGE_MAX - 4 - msg_len;
|
int64_t room = utf8_fit(p, n, msg_cap - 4 - msg_len);
|
||||||
if (room > 0) { memcpy(msg_out + msg_len, p, (size_t)room); msg_len += room; }
|
if (room > 0) { memcpy(msg_out + msg_len, p, (size_t)room); msg_len += room; }
|
||||||
memcpy(msg_out + msg_len, ell, 3);
|
memcpy(msg_out + msg_len, ellipsis, 3);
|
||||||
msg_len += 3;
|
msg_len += 3;
|
||||||
msg_cut = 1;
|
msg_cut = 1;
|
||||||
return;
|
return;
|
||||||
@ -132,7 +149,7 @@ void flan_msg_emit(const uint8_t *p, int64_t n) { msg_append(p, n); }
|
|||||||
/* The view of [condition] for a parent's handler, its message in [buf],
|
/* The view of [condition] for a parent's handler, its message in [buf],
|
||||||
* which the caller owns and which lives as long as the handler runs. */
|
* which the caller owns and which lives as long as the handler runs. */
|
||||||
static void rt_view(const flan_condesc *d, void *condition, char *buf,
|
static void rt_view(const flan_condesc *d, void *condition, char *buf,
|
||||||
flan_view *v) {
|
int64_t cap, flan_view *v) {
|
||||||
if (d->flags & FLAN_CONDESC_SELF) {
|
if (d->flags & FLAN_CONDESC_SELF) {
|
||||||
*v = *(const flan_view *)condition;
|
*v = *(const flan_view *)condition;
|
||||||
return;
|
return;
|
||||||
@ -141,6 +158,7 @@ static void rt_view(const flan_condesc *d, void *condition, char *buf,
|
|||||||
v->namelen = d->namelen;
|
v->namelen = d->namelen;
|
||||||
v->message = (const uint8_t *)buf;
|
v->message = (const uint8_t *)buf;
|
||||||
msg_out = buf;
|
msg_out = buf;
|
||||||
|
msg_cap = cap;
|
||||||
msg_len = 0;
|
msg_len = 0;
|
||||||
msg_cut = 0;
|
msg_cut = 0;
|
||||||
if (d->messagelen > 0)
|
if (d->messagelen > 0)
|
||||||
@ -193,7 +211,10 @@ void flan_signal(const flan_condesc *d, void *condition, void *xfer) {
|
|||||||
for (flan_handler *h = saved; h != NULL; h = h->prev) {
|
for (flan_handler *h = saved; h != NULL; h = h->prev) {
|
||||||
int how = flan_handles(h->type_id, d);
|
int how = flan_handles(h->type_id, d);
|
||||||
if (how) {
|
if (how) {
|
||||||
if (how == 2 && !viewed) { rt_view(d, condition, buf, &v); viewed = 1; }
|
if (how == 2 && !viewed) {
|
||||||
|
rt_view(d, condition, buf, FLAN_MESSAGE_MAX, &v);
|
||||||
|
viewed = 1;
|
||||||
|
}
|
||||||
handlers = h->prev;
|
handlers = h->prev;
|
||||||
h->fn(how == 1 ? condition : (void *)&v, xfer, h->env);
|
h->fn(how == 1 ? condition : (void *)&v, xfer, h->env);
|
||||||
handlers = saved;
|
handlers = saved;
|
||||||
@ -884,14 +905,20 @@ _Noreturn void flan_trap(const uint8_t *name, int64_t namelen) {
|
|||||||
* nothing to point at. */
|
* nothing to point at. */
|
||||||
const uint8_t *flan_break_site;
|
const uint8_t *flan_break_site;
|
||||||
int64_t flan_break_site_len;
|
int64_t flan_break_site_len;
|
||||||
char flan_break_sentence[512];
|
char flan_break_sentence[FLAN_SENTENCE_MAX];
|
||||||
int64_t flan_break_sentence_len;
|
int64_t flan_break_sentence_len;
|
||||||
|
|
||||||
static void rt_sentencev(const char *fmt, va_list ap) {
|
static void rt_sentencev(const char *fmt, va_list ap) {
|
||||||
int n = vsnprintf(flan_break_sentence, sizeof flan_break_sentence, fmt, ap);
|
int n = vsnprintf(flan_break_sentence, sizeof flan_break_sentence, fmt, ap);
|
||||||
if (n < 0) n = 0;
|
if (n < 0) n = 0;
|
||||||
if (n >= (int)sizeof flan_break_sentence)
|
if (n >= (int)sizeof flan_break_sentence) {
|
||||||
n = (int)sizeof flan_break_sentence - 1;
|
/* Cut at a character and said to be cut. */
|
||||||
|
int64_t m = utf8_fit((const uint8_t *)flan_break_sentence,
|
||||||
|
(int64_t)sizeof flan_break_sentence - 1,
|
||||||
|
(int64_t)sizeof flan_break_sentence - 4);
|
||||||
|
memcpy(flan_break_sentence + m, ellipsis, 3);
|
||||||
|
n = (int)m + 3;
|
||||||
|
}
|
||||||
flan_break_sentence_len = n;
|
flan_break_sentence_len = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,8 +932,13 @@ static void rt_sentence(const char *fmt, ...) {
|
|||||||
/* The sentence just formatted, NUL-terminated into a caller's buffer. */
|
/* The sentence just formatted, NUL-terminated into a caller's buffer. */
|
||||||
static void rt_sentence_copy(char *out) {
|
static void rt_sentence_copy(char *out) {
|
||||||
int64_t n = flan_break_sentence_len;
|
int64_t n = flan_break_sentence_len;
|
||||||
if (n > FLAN_MESSAGE_MAX - 1) n = FLAN_MESSAGE_MAX - 1;
|
if (n > FLAN_MESSAGE_MAX - 1) {
|
||||||
memcpy(out, flan_break_sentence, (size_t)n);
|
n = utf8_fit((const uint8_t *)flan_break_sentence, n, FLAN_MESSAGE_MAX - 4);
|
||||||
|
memcpy(out, flan_break_sentence, (size_t)n);
|
||||||
|
memcpy(out + n, ellipsis, 3);
|
||||||
|
n += 3;
|
||||||
|
} else
|
||||||
|
memcpy(out, flan_break_sentence, (size_t)n);
|
||||||
out[n] = 0;
|
out[n] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,9 +1064,9 @@ void flan_error(const flan_condesc *d, void *condition, void *xfer) {
|
|||||||
/* What the condition is, as a parent's handler would read it: the
|
/* What the condition is, as a parent's handler would read it: the
|
||||||
* sentence, the printed condition, or an Error-shaped condition's own
|
* sentence, the printed condition, or an Error-shaped condition's own
|
||||||
* message. A condition with no parent and no sentence has none. */
|
* message. A condition with no parent and no sentence has none. */
|
||||||
char buf[FLAN_MESSAGE_MAX];
|
char buf[FLAN_SENTENCE_MAX];
|
||||||
flan_view v;
|
flan_view v;
|
||||||
rt_view(d, condition, buf, &v);
|
rt_view(d, condition, buf, FLAN_SENTENCE_MAX, &v);
|
||||||
if (flan_break_hook != NULL) {
|
if (flan_break_hook != NULL) {
|
||||||
flan_break_site = d->loclen > 0 ? d->loc : NULL;
|
flan_break_site = d->loclen > 0 ? d->loc : NULL;
|
||||||
flan_break_site_len = d->loclen;
|
flan_break_site_len = d->loclen;
|
||||||
@ -1197,7 +1229,6 @@ static void rt_condesc(flan_condesc *d, uint32_t chain[2], const uint8_t *name,
|
|||||||
* which case the caller returns and its caller's guard carries the transfer
|
* which case the caller returns and its caller's guard carries the transfer
|
||||||
* out. */
|
* out. */
|
||||||
static int rt_error_break(const flan_condesc *d, void *condition, void *xfer) {
|
static int rt_error_break(const flan_condesc *d, void *condition, void *xfer) {
|
||||||
rt_sentence("%.*s", (int)d->messagelen, (const char *)d->message);
|
|
||||||
if (flan_break_hook != NULL) {
|
if (flan_break_hook != NULL) {
|
||||||
flan_break_site = d->loc;
|
flan_break_site = d->loc;
|
||||||
flan_break_site_len = d->loclen;
|
flan_break_site_len = d->loclen;
|
||||||
@ -1229,6 +1260,11 @@ static int flan_bounds_signal(const uint8_t *loc, int64_t loclen, void *xfer,
|
|||||||
loclen);
|
loclen);
|
||||||
flan_signal(&d, &c, xfer);
|
flan_signal(&d, &c, xfer);
|
||||||
if (*(void **)xfer != NULL) return 1;
|
if (*(void **)xfer != NULL) return 1;
|
||||||
|
/* Formatted again, in full: [said] may be cut, and a handler the walk ran
|
||||||
|
* may have written a sentence of its own over the shared one. */
|
||||||
|
if (kind == BOUNDS_AT) bounds_sentence(low, len);
|
||||||
|
else if (kind == BOUNDS_SLICE) slice_sentence(low, high, len);
|
||||||
|
else promise_sentence(high);
|
||||||
return rt_error_break(&d, &c, xfer);
|
return rt_error_break(&d, &c, xfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1399,6 +1435,7 @@ void flan_arith_error(const uint8_t *loc, int64_t loclen, int32_t op,
|
|||||||
loclen);
|
loclen);
|
||||||
flan_signal(&d, &c, xfer);
|
flan_signal(&d, &c, xfer);
|
||||||
if (*(void **)xfer != NULL) return;
|
if (*(void **)xfer != NULL) return;
|
||||||
|
arith_sentence(op, lhs, rhs); /* in full; see flan_bounds_signal */
|
||||||
if (rt_error_break(&d, &c, xfer)) return;
|
if (rt_error_break(&d, &c, xfer)) return;
|
||||||
rt_print_sentence(loc, loclen);
|
rt_print_sentence(loc, loclen);
|
||||||
rt_die();
|
rt_die();
|
||||||
@ -1449,6 +1486,16 @@ static flan_slice flan_stale_copy(const char *s) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void stale_sentence(const char *callee, const char *want,
|
||||||
|
const char *now) {
|
||||||
|
rt_sentence("this call to %s was compiled for %s, and %s is defined as %s. "
|
||||||
|
"Evaluating the function this call is in again fixes its next "
|
||||||
|
"call. A function that is still running, such as main's loop, "
|
||||||
|
"is never called again: define %s with %s again, or run the "
|
||||||
|
"program again.",
|
||||||
|
callee, want, callee, now, callee, want);
|
||||||
|
}
|
||||||
|
|
||||||
void flan_stale_call(const char *site, const char *callee, const char *want,
|
void flan_stale_call(const char *site, const char *callee, const char *want,
|
||||||
void *const *cell, void *xfer) {
|
void *const *cell, void *xfer) {
|
||||||
/* A registry cell nothing has published into yet has no text. */
|
/* A registry cell nothing has published into yet has no text. */
|
||||||
@ -1461,17 +1508,13 @@ void flan_stale_call(const char *site, const char *callee, const char *want,
|
|||||||
flan_condesc d;
|
flan_condesc d;
|
||||||
uint32_t chain[2];
|
uint32_t chain[2];
|
||||||
char said[FLAN_MESSAGE_MAX];
|
char said[FLAN_MESSAGE_MAX];
|
||||||
rt_sentence("this call to %s was compiled for %s, and %s is defined as %s. "
|
stale_sentence(callee, want, now);
|
||||||
"Evaluating the function this call is in again fixes its next "
|
|
||||||
"call. A function that is still running, such as main's loop, "
|
|
||||||
"is never called again: define %s with %s again, or run the "
|
|
||||||
"program again.",
|
|
||||||
callee, want, callee, now, callee, want);
|
|
||||||
rt_sentence_copy(said);
|
rt_sentence_copy(said);
|
||||||
rt_condesc(&d, chain, flan_stale_name, FLAN_STALE_NAMELEN, said, where.ptr,
|
rt_condesc(&d, chain, flan_stale_name, FLAN_STALE_NAMELEN, said, where.ptr,
|
||||||
where.len);
|
where.len);
|
||||||
flan_signal(&d, &c, xfer);
|
flan_signal(&d, &c, xfer);
|
||||||
if (*(void **)xfer != NULL) return;
|
if (*(void **)xfer != NULL) return;
|
||||||
|
stale_sentence(callee, want, now); /* in full; see flan_bounds_signal */
|
||||||
if (rt_error_break(&d, &c, xfer)) return;
|
if (rt_error_break(&d, &c, xfer)) return;
|
||||||
rt_print_sentence(where.ptr, where.len);
|
rt_print_sentence(where.ptr, where.len);
|
||||||
rt_die();
|
rt_die();
|
||||||
|
|||||||
14
test/programs/condition-longmessage.flan
Normal file
14
test/programs/condition-longmessage.flan
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
;;;; A message longer than the buffer that holds it is cut at a character,
|
||||||
|
;;;; never inside one, and says it was cut. The printed condition below starts
|
||||||
|
;;;; with an odd number of bytes before 400 two-byte characters, so a cut at a
|
||||||
|
;;;; fixed byte count lands inside one. The unhandled message at the end is
|
||||||
|
;;;; printed in full: it is not made from the handler's shortened copy.
|
||||||
|
|
||||||
|
(defstruct Wide :parent Error [code i32 why string])
|
||||||
|
(defstruct Long :parent Error)
|
||||||
|
|
||||||
|
(defn main [] i32
|
||||||
|
(handler-case (do (error (Wide {.code 123 .why "éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"})) 0)
|
||||||
|
[(Error [e] (println (.message e)) 0)])
|
||||||
|
(error (Long {.name "long" .message "éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"}))
|
||||||
|
0)
|
||||||
@ -2929,6 +2929,24 @@ let () =
|
|||||||
"programs/condition-messages.flan" messages_out;
|
"programs/condition-messages.flan" messages_out;
|
||||||
outputs ~x86:true "a parent's message outlives the unwind, --x86"
|
outputs ~x86:true "a parent's message outlives the unwind, --x86"
|
||||||
"programs/condition-messages.flan" messages_out;
|
"programs/condition-messages.flan" messages_out;
|
||||||
|
(* A message longer than its buffer is cut at a character boundary and
|
||||||
|
ends in an ellipsis; the unhandled message is the full text. *)
|
||||||
|
let e n = String.concat "" (List.init n (fun _ -> "\xc3\xa9")) in
|
||||||
|
let cut = "(Wide {.code 123 .why \"" ^ e 242 ^ "\xe2\x80\xa6\n" in
|
||||||
|
let full = "unhandled Long: " ^ e 400 ^ "\n" in
|
||||||
|
List.iter
|
||||||
|
(fun x86 ->
|
||||||
|
let exe = compile ~x86 "programs/condition-longmessage.flan" in
|
||||||
|
let code, text = run exe None in
|
||||||
|
if code <> 134 || not (contains text cut) || not (contains text full)
|
||||||
|
then begin
|
||||||
|
incr failures;
|
||||||
|
Printf.printf
|
||||||
|
"FAIL a long message is cut at a character%s\n got: %S (exit %d)\n"
|
||||||
|
(if x86 then ", --x86" else "") text code
|
||||||
|
end;
|
||||||
|
(try Sys.remove exe with Sys_error _ -> ()))
|
||||||
|
[ false; true ];
|
||||||
|
|
||||||
(* And the half that finishes that thought. bounds-condition.flan's last
|
(* And the half that finishes that thought. bounds-condition.flan's last
|
||||||
line is `10 99 12 13` — an abandoned frame's leftovers — and a restart
|
line is `10 99 12 13` — an abandoned frame's leftovers — and a restart
|
||||||
|
|||||||
8
vendor/agent/flan_agent.c
vendored
8
vendor/agent/flan_agent.c
vendored
@ -570,7 +570,7 @@ typedef struct {
|
|||||||
/* The runtime's sentence about the stop, copied and consumed with the
|
/* The runtime's sentence about the stop, copied and consumed with the
|
||||||
* site. Empty for a stop whose condition says what it is in its fields. */
|
* site. Empty for a stop whose condition says what it is in its fields. */
|
||||||
int32_t sentencelen;
|
int32_t sentencelen;
|
||||||
char sentence[512];
|
char sentence[2048]; /* FLAN_SENTENCE_MAX */
|
||||||
} snapshot;
|
} snapshot;
|
||||||
|
|
||||||
/* One per nested break loop, because an inner break must not answer with the
|
/* One per nested break loop, because an inner break must not answer with the
|
||||||
@ -718,7 +718,11 @@ static int snap_push(int resumable, void *cond) {
|
|||||||
s->sentencelen = 0;
|
s->sentencelen = 0;
|
||||||
if (flan_break_sentence_len > 0) {
|
if (flan_break_sentence_len > 0) {
|
||||||
int64_t k = flan_break_sentence_len;
|
int64_t k = flan_break_sentence_len;
|
||||||
if (k > (int64_t)sizeof s->sentence) k = (int64_t)sizeof s->sentence;
|
if (k > (int64_t)sizeof s->sentence) {
|
||||||
|
/* Never splitting a UTF-8 character. */
|
||||||
|
k = (int64_t)sizeof s->sentence;
|
||||||
|
while (k > 0 && ((uint8_t)flan_break_sentence[k] & 0xC0) == 0x80) k--;
|
||||||
|
}
|
||||||
memcpy(s->sentence, flan_break_sentence, (size_t)k);
|
memcpy(s->sentence, flan_break_sentence, (size_t)k);
|
||||||
/* One line on the wire: a newline in it would end the reply early. */
|
/* One line on the wire: a newline in it would end the reply early. */
|
||||||
for (int64_t i = 0; i < k; i++)
|
for (int64_t i = 0; i < k; i++)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user