1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
* The variadic stub slots, marshalled into a shape Rust can be handed.
*
* Seven slots of `TclStubs` are C-variadic. Stable rustc refuses to *define*
* such a function outright:
*
* error[E0658]: C-variadic functions are unstable
* = note: see issue #44930 <https://github.com/rust-lang/rust/issues/44930>
*
* and on AAPCS64 there is no way to cheat around that from Rust either, because
* a variadic argument is passed on the stack while a fixed one of the same
* position would have been in a register (Procedure Call Standard for the Arm
* 64-bit Architecture, §6.4.2: "the variadic arguments are laid out on the
* stack"), so no non-variadic declaration can name one. A C file compiled by
* `build.rs` is the fix: it is the only place in the tree that may write
* `va_arg`, and everything it reads is handed on as a counted array or a
* finished string.
*
* Only the slots whose variadic arguments carry a payload are here:
*
* Tcl_AppendStringsToObj (slot 15) — Tk builds a fully qualified command
* name out of them (tk9.0.4/generic/tkUtil.c:1222), so a body that
* ignored them would register every ensemble subcommand under the
* ensemble's own name.
* Tcl_Panic (slot 2) — the message is the whole content of
* the call. Tk calls it 227 times and it never returns, so an abort
* without the formatted text is an abort with no diagnosis.
* Tcl_ObjPrintf (slot 578) — the formatted text *is* the returned
* value. `wm geometry .` answers with Tcl_ObjPrintf("%dx%d+%d+%d", ...)
* (tk9.0.4/generic/tkWm.c), so a body that ignored the arguments would
* return an empty geometry rather than the window's.
* Tcl_AppendPrintfToObj (slot 579) — the same, appending instead of
* returning. `bind Button` rebuilds every pattern it answers with
* through GetPatternObj, whose modifier names and button numbers arrive
* only as variadic arguments (tk9.0.4/generic/tkBind.c:5190,5212), so a
* body that ignored them would answer `<->` for `<Button-1>`.
*
* The remaining three are argued about, not marshalled; see `tk::eval`.
*/
/* Implemented in Rust: `src/tk/eval.rs`. */
extern void ;
extern void ;
extern void *;
extern void ;
/*
* Tcl's own loop over the argument list ends at the first NULL and has no upper
* bound (generic/tclStringObj.c:1820-1828). A fixed ceiling here is a refusal
* to walk off the end of a list that was not terminated: the longest call in Tk
* passes two strings, so nothing legitimate comes near it.
*/
/* Length of the buffer a panic message is formatted into. Tcl's own
* `Tcl_Panic` writes through vfprintf with no limit; a truncated diagnostic is
* still a diagnostic, and this process is about to abort either way. */
/* As above, for a formatted value. Tk's longest is a Postscript prologue line
* from tkImgBmap.c; a window geometry and a bind pattern are far shorter. */
/*
* void Tcl_AppendStringsToObj(Tcl_Obj *objPtr, ...) — generic/tclDecls.h:92.
*
* A NULL-terminated list of `char *`, appended in order
* (generic/tclStringObj.c:1808-1829).
*/
void
/*
* TCL_NORETURN void Tcl_Panic(const char *format, ...) —
* generic/tclDecls.h:62. A printf format and its arguments; Tcl's own body
* formats them and then aborts (generic/tclPanic.c).
*/
void
/*
* The formatting both printf slots share, and the byte count they hand on.
*
* Tcl formats with its own printf subset (AppendPrintfToObjVA,
* generic/tclStringObj.c:2708-2900) rather than with the C library's, because
* it has to accept Tcl's own size modifiers. This uses vsnprintf, which agrees
* with it on every conversion Tk actually passes — "%dx%d+%d+%d" for a window
* geometry, "%s-" and "-%u" for a bind pattern — and would disagree only on a
* Tcl-specific modifier, which would be a format string no C library could be
* handed at all.
*
* vsnprintf answers with the length it *would* have written, so the return is
* clamped to what the buffer holds: a truncated message is still the message,
* and a length past the end would have the caller read uninitialised bytes.
*/
static size_t
/*
* Tcl_Obj *Tcl_ObjPrintf(const char *format, ...) —
* generic/tclStringObj.c:2931-2944, which formats into a fresh value.
*/
void *
/*
* void Tcl_AppendPrintfToObj(Tcl_Obj *objPtr, const char *format, ...) —
* generic/tclDecls.h:1548-1549, body at generic/tclStringObj.c:2904-2915.
*
* Tcl_ObjPrintf with an existing value instead of a fresh one: the two share
* AppendPrintfToObjVA and differ only in where the text goes
* (generic/tclStringObj.c:2939-2941 against :2913).
*/
void