Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/VM/src/ldebug.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
3
#include "ldebug.h"
4
5
#include "lapi.h"
6
#include "lfunc.h"
7
#include "lmem.h"
8
#include "lgc.h"
9
#include "ldo.h"
10
#include "lbytecode.h"
11
12
#include <string.h>
13
#include <stdio.h>
14
15
static const char* getfuncname(Closure* f);
16
17
static int currentpc(lua_State* L, CallInfo* ci)
18
{
19
return pcRel(ci->savedpc, ci_func(ci)->l.p);
20
}
21
22
static int currentline(lua_State* L, CallInfo* ci)
23
{
24
return luaG_getline(ci_func(ci)->l.p, currentpc(L, ci));
25
}
26
27
static Proto* getluaproto(CallInfo* ci)
28
{
29
return (isLua(ci) ? cast_to(Proto*, ci_func(ci)->l.p) : NULL);
30
}
31
32
int lua_getargument(lua_State* L, int level, int n)
33
{
34
if (unsigned(level) >= unsigned(L->ci - L->base_ci))
35
return 0;
36
37
CallInfo* ci = L->ci - level;
38
// changing tables in native functions externally may invalidate safety contracts wrt table state (metatable/size/readonly)
39
if (ci->flags & LUA_CALLINFO_NATIVE)
40
return 0;
41
42
Proto* fp = getluaproto(ci);
43
int res = 0;
44
45
if (fp && n > 0)
46
{
47
if (n <= fp->numparams)
48
{
49
luaC_threadbarrier(L);
50
luaA_pushobject(L, ci->base + (n - 1));
51
res = 1;
52
}
53
else if (fp->is_vararg && n < ci->base - ci->func)
54
{
55
luaC_threadbarrier(L);
56
luaA_pushobject(L, ci->func + n);
57
res = 1;
58
}
59
}
60
61
return res;
62
}
63
64
const char* lua_getlocal(lua_State* L, int level, int n)
65
{
66
if (unsigned(level) >= unsigned(L->ci - L->base_ci))
67
return NULL;
68
69
CallInfo* ci = L->ci - level;
70
// changing tables in native functions externally may invalidate safety contracts wrt table state (metatable/size/readonly)
71
if (ci->flags & LUA_CALLINFO_NATIVE)
72
return NULL;
73
74
Proto* fp = getluaproto(ci);
75
const LocVar* var = fp ? luaF_getlocal(fp, n, currentpc(L, ci)) : NULL;
76
if (var)
77
{
78
luaC_threadbarrier(L);
79
luaA_pushobject(L, ci->base + var->reg);
80
}
81
const char* name = var ? getstr(var->varname) : NULL;
82
return name;
83
}
84
85
const char* lua_setlocal(lua_State* L, int level, int n)
86
{
87
if (unsigned(level) >= unsigned(L->ci - L->base_ci))
88
return NULL;
89
90
CallInfo* ci = L->ci - level;
91
// changing registers in native functions externally may invalidate safety contracts wrt register type tags
92
if (ci->flags & LUA_CALLINFO_NATIVE)
93
return NULL;
94
95
Proto* fp = getluaproto(ci);
96
const LocVar* var = fp ? luaF_getlocal(fp, n, currentpc(L, ci)) : NULL;
97
if (var)
98
setobj2s(L, ci->base + var->reg, L->top - 1);
99
L->top--; // pop value
100
const char* name = var ? getstr(var->varname) : NULL;
101
return name;
102
}
103
104
static Closure* auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci)
105
{
106
Closure* cl = NULL;
107
for (; *what; what++)
108
{
109
switch (*what)
110
{
111
case 's':
112
{
113
if (f->isC)
114
{
115
ar->source = "=[C]";
116
ar->what = "C";
117
ar->linedefined = -1;
118
ar->short_src = "[C]";
119
}
120
else
121
{
122
TString* source = f->l.p->source;
123
ar->source = getstr(source);
124
ar->what = "Lua";
125
ar->linedefined = f->l.p->linedefined;
126
ar->short_src = luaO_chunkid(ar->ssbuf, sizeof(ar->ssbuf), getstr(source), source->len);
127
}
128
break;
129
}
130
case 'l':
131
{
132
if (ci)
133
{
134
ar->currentline = isLua(ci) ? currentline(L, ci) : -1;
135
}
136
else
137
{
138
ar->currentline = f->isC ? -1 : f->l.p->linedefined;
139
}
140
141
break;
142
}
143
case 'u':
144
{
145
ar->nupvals = f->nupvalues;
146
break;
147
}
148
case 'a':
149
{
150
if (f->isC)
151
{
152
ar->isvararg = 1;
153
ar->nparams = 0;
154
}
155
else
156
{
157
ar->isvararg = f->l.p->is_vararg;
158
ar->nparams = f->l.p->numparams;
159
}
160
break;
161
}
162
case 'n':
163
{
164
ar->name = ci ? getfuncname(ci_func(ci)) : getfuncname(f);
165
break;
166
}
167
case 'f':
168
{
169
cl = f;
170
break;
171
}
172
default:;
173
}
174
}
175
return cl;
176
}
177
178
int lua_stackdepth(lua_State* L)
179
{
180
return int(L->ci - L->base_ci);
181
}
182
183
int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
184
{
185
Closure* f = NULL;
186
CallInfo* ci = NULL;
187
if (level < 0)
188
{
189
// element has to be within stack
190
if (-level > L->top - L->base)
191
return 0;
192
193
StkId func = L->top + level;
194
195
// and it has to be a function
196
if (!ttisfunction(func))
197
return 0;
198
199
f = clvalue(func);
200
}
201
else if (unsigned(level) < unsigned(L->ci - L->base_ci))
202
{
203
ci = L->ci - level;
204
LUAU_ASSERT(ttisfunction(ci->func));
205
f = clvalue(ci->func);
206
}
207
if (f)
208
{
209
// auxgetinfo fills ar and optionally requests to put closure on stack
210
if (Closure* fcl = auxgetinfo(L, what, ar, f, ci))
211
{
212
luaC_threadbarrier(L);
213
setclvalue(L, L->top, fcl);
214
incr_top(L);
215
}
216
}
217
return f ? 1 : 0;
218
}
219
220
static const char* getfuncname(Closure* cl)
221
{
222
if (cl->isC)
223
{
224
if (cl->c.debugname)
225
{
226
return cl->c.debugname;
227
}
228
}
229
else
230
{
231
Proto* p = cl->l.p;
232
233
if (p->debugname)
234
{
235
return getstr(p->debugname);
236
}
237
}
238
return nullptr;
239
}
240
241
l_noret luaG_typeerrorL(lua_State* L, const TValue* o, const char* op)
242
{
243
const char* t = luaT_objtypename(L, o);
244
245
luaG_runerror(L, "attempt to %s a %s value", op, t);
246
}
247
248
l_noret luaG_forerrorL(lua_State* L, const TValue* o, const char* what)
249
{
250
const char* t = luaT_objtypename(L, o);
251
252
luaG_runerror(L, "invalid 'for' %s (number expected, got %s)", what, t);
253
}
254
255
l_noret luaG_concaterror(lua_State* L, StkId p1, StkId p2)
256
{
257
const char* t1 = luaT_objtypename(L, p1);
258
const char* t2 = luaT_objtypename(L, p2);
259
260
luaG_runerror(L, "attempt to concatenate %s with %s", t1, t2);
261
}
262
263
l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op)
264
{
265
const char* t1 = luaT_objtypename(L, p1);
266
const char* t2 = luaT_objtypename(L, p2);
267
const char* opname = luaT_eventname[op] + 2; // skip __ from metamethod name
268
269
if (t1 == t2)
270
luaG_runerror(L, "attempt to perform arithmetic (%s) on %s", opname, t1);
271
else
272
luaG_runerror(L, "attempt to perform arithmetic (%s) on %s and %s", opname, t1, t2);
273
}
274
275
l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op)
276
{
277
const char* t1 = luaT_objtypename(L, p1);
278
const char* t2 = luaT_objtypename(L, p2);
279
const char* opname = (op == TM_LT) ? "<" : (op == TM_LE) ? "<=" : "==";
280
281
luaG_runerror(L, "attempt to compare %s %s %s", t1, opname, t2);
282
}
283
284
l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2)
285
{
286
const char* t1 = luaT_objtypename(L, p1);
287
const char* t2 = luaT_objtypename(L, p2);
288
const TString* key = ttisstring(p2) ? tsvalue(p2) : 0;
289
290
if (key && key->len <= 64) // limit length to make sure we don't generate very long error messages for very long keys
291
luaG_runerror(L, "attempt to index %s with '%s'", t1, getstr(key));
292
else
293
luaG_runerror(L, "attempt to index %s with %s", t1, t2);
294
}
295
296
l_noret luaG_methoderror(lua_State* L, const TValue* p1, const TString* p2)
297
{
298
const char* t1 = luaT_objtypename(L, p1);
299
300
luaG_runerror(L, "attempt to call missing method '%s' of %s", getstr(p2), t1);
301
}
302
303
l_noret luaG_readonlyerror(lua_State* L)
304
{
305
luaG_runerror(L, "attempt to modify a readonly table");
306
}
307
308
static void pusherror(lua_State* L, const char* msg)
309
{
310
CallInfo* ci = L->ci;
311
if (isLua(ci))
312
{
313
TString* source = getluaproto(ci)->source;
314
char chunkbuf[LUA_IDSIZE]; // add file:line information
315
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), getstr(source), source->len);
316
int line = currentline(L, ci);
317
luaO_pushfstring(L, "%s:%d: %s", chunkid, line, msg);
318
}
319
else
320
{
321
lua_pushstring(L, msg);
322
}
323
}
324
325
l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...)
326
{
327
va_list argp;
328
va_start(argp, fmt);
329
char result[LUA_BUFFERSIZE];
330
vsnprintf(result, sizeof(result), fmt, argp);
331
va_end(argp);
332
333
lua_rawcheckstack(L, 1);
334
335
pusherror(L, result);
336
luaD_throw(L, LUA_ERRRUN);
337
}
338
339
void luaG_pusherror(lua_State* L, const char* error)
340
{
341
lua_rawcheckstack(L, 1);
342
343
pusherror(L, error);
344
}
345
346
void luaG_breakpoint(lua_State* L, Proto* p, int line, bool enable)
347
{
348
void (*ondisable)(lua_State*, Proto*) = L->global->ecb.disable;
349
350
// since native code doesn't support breakpoints, we would need to update all call frames with LUAU_CALLINFO_NATIVE that refer to p
351
if (p->lineinfo && (ondisable || !p->execdata))
352
{
353
for (int i = 0; i < p->sizecode; ++i)
354
{
355
// note: we keep prologue as is, instead opting to break at the first meaningful instruction
356
if (LUAU_INSN_OP(p->code[i]) == LOP_PREPVARARGS)
357
continue;
358
359
if (luaG_getline(p, i) != line)
360
continue;
361
362
// lazy copy of the original opcode array; done when the first breakpoint is set
363
if (!p->debuginsn)
364
{
365
p->debuginsn = luaM_newarray(L, p->sizecode, uint8_t, p->memcat);
366
for (int j = 0; j < p->sizecode; ++j)
367
p->debuginsn[j] = LUAU_INSN_OP(p->code[j]);
368
}
369
370
uint8_t op = enable ? LOP_BREAK : LUAU_INSN_OP(p->debuginsn[i]);
371
372
// patch just the opcode byte, leave arguments alone
373
p->code[i] &= ~0xff;
374
p->code[i] |= op;
375
LUAU_ASSERT(LUAU_INSN_OP(p->code[i]) == op);
376
377
// currently we don't restore native code when breakpoint is disabled.
378
// this will be addressed in the future.
379
if (enable && p->execdata && ondisable)
380
ondisable(L, p);
381
382
// note: this is important!
383
// we only patch the *first* instruction in each proto that's attributed to a given line
384
// this can be changed, but if requires making patching a bit more nuanced so that we don't patch AUX words
385
break;
386
}
387
}
388
389
for (int i = 0; i < p->sizep; ++i)
390
{
391
luaG_breakpoint(L, p->p[i], line, enable);
392
}
393
}
394
395
bool luaG_onbreak(lua_State* L)
396
{
397
if (L->ci == L->base_ci)
398
return false;
399
400
if (!isLua(L->ci))
401
return false;
402
403
return LUAU_INSN_OP(*L->ci->savedpc) == LOP_BREAK;
404
}
405
406
int luaG_getline(Proto* p, int pc)
407
{
408
LUAU_ASSERT(pc >= 0 && pc < p->sizecode);
409
410
if (!p->lineinfo)
411
return 0;
412
413
return p->abslineinfo[pc >> p->linegaplog2] + p->lineinfo[pc];
414
}
415
416
int luaG_isnative(lua_State* L, int level)
417
{
418
if (unsigned(level) >= unsigned(L->ci - L->base_ci))
419
return 0;
420
421
CallInfo* ci = L->ci - level;
422
return (ci->flags & LUA_CALLINFO_NATIVE) != 0 ? 1 : 0;
423
}
424
425
int luaG_hasnative(lua_State* L, int level)
426
{
427
if (unsigned(level) >= unsigned(L->ci - L->base_ci))
428
return 0;
429
430
CallInfo* ci = L->ci - level;
431
432
Proto* proto = getluaproto(ci);
433
if (proto == nullptr)
434
return 0;
435
436
return (proto->execdata != nullptr);
437
}
438
439
void lua_singlestep(lua_State* L, int enabled)
440
{
441
L->singlestep = bool(enabled);
442
}
443
444
static int getmaxline(Proto* p)
445
{
446
int result = -1;
447
448
for (int i = 0; i < p->sizecode; ++i)
449
{
450
int line = luaG_getline(p, i);
451
result = result < line ? line : result;
452
}
453
454
for (int i = 0; i < p->sizep; ++i)
455
{
456
int psize = getmaxline(p->p[i]);
457
result = result < psize ? psize : result;
458
}
459
460
return result;
461
}
462
463
// Find the line number with instructions. If the provided line doesn't have any instruction, it should return the next valid line number.
464
static int getnextline(Proto* p, int line)
465
{
466
int closest = -1;
467
468
if (p->lineinfo)
469
{
470
for (int i = 0; i < p->sizecode; ++i)
471
{
472
// note: we keep prologue as is, instead opting to break at the first meaningful instruction
473
if (LUAU_INSN_OP(p->code[i]) == LOP_PREPVARARGS)
474
continue;
475
476
int candidate = luaG_getline(p, i);
477
478
if (candidate == line)
479
return line;
480
481
if (candidate > line && (closest == -1 || candidate < closest))
482
closest = candidate;
483
}
484
}
485
486
for (int i = 0; i < p->sizep; ++i)
487
{
488
int candidate = getnextline(p->p[i], line);
489
490
if (candidate == line)
491
return line;
492
493
if (candidate > line && (closest == -1 || candidate < closest))
494
closest = candidate;
495
}
496
497
return closest;
498
}
499
500
int lua_breakpoint(lua_State* L, int funcindex, int line, int enabled)
501
{
502
const TValue* func = luaA_toobject(L, funcindex);
503
api_check(L, ttisfunction(func) && !clvalue(func)->isC);
504
505
Proto* p = clvalue(func)->l.p;
506
507
// set the breakpoint to the next closest line with valid instructions
508
int target = getnextline(p, line);
509
510
if (target != -1)
511
luaG_breakpoint(L, p, target, bool(enabled));
512
513
return target;
514
}
515
516
static void getcoverage(Proto* p, int depth, int* buffer, size_t size, void* context, lua_Coverage callback)
517
{
518
memset(buffer, -1, size * sizeof(int));
519
520
for (int i = 0; i < p->sizecode; ++i)
521
{
522
Instruction insn = p->code[i];
523
if (LUAU_INSN_OP(insn) != LOP_COVERAGE)
524
continue;
525
526
int line = luaG_getline(p, i);
527
int hits = LUAU_INSN_E(insn);
528
529
LUAU_ASSERT(size_t(line) < size);
530
buffer[line] = buffer[line] < hits ? hits : buffer[line];
531
}
532
533
const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
534
int linedefined = p->linedefined;
535
536
callback(context, debugname, linedefined, depth, buffer, size);
537
538
for (int i = 0; i < p->sizep; ++i)
539
getcoverage(p->p[i], depth + 1, buffer, size, context, callback);
540
}
541
542
void lua_getcoverage(lua_State* L, int funcindex, void* context, lua_Coverage callback)
543
{
544
const TValue* func = luaA_toobject(L, funcindex);
545
api_check(L, ttisfunction(func) && !clvalue(func)->isC);
546
547
Proto* p = clvalue(func)->l.p;
548
549
size_t size = getmaxline(p) + 1;
550
if (size == 0)
551
return;
552
553
int* buffer = luaM_newarray(L, size, int, 0);
554
555
getcoverage(p, 0, buffer, size, context, callback);
556
557
luaM_freearray(L, buffer, size, int, 0);
558
}
559
560
static void getcounters(lua_State* L, Proto* p, void* context, lua_CounterFunction functionvisit, lua_CounterValue countervisit)
561
{
562
if (p->execdata != nullptr && L->global->ecb.getcounterdata != nullptr)
563
{
564
size_t count = 0;
565
char* data = L->global->ecb.getcounterdata(L, p, &count);
566
567
if (data != nullptr && count != 0)
568
{
569
const char* debugname = p->debugname ? getstr(p->debugname) : nullptr;
570
int linedefined = p->linedefined;
571
572
functionvisit(context, debugname, linedefined);
573
574
for (size_t i = 0; i < count; i++)
575
{
576
uint32_t kind = 0;
577
memcpy(&kind, data + 0, sizeof(kind));
578
data += sizeof(kind);
579
580
uint32_t pcpos = 0;
581
memcpy(&pcpos, data + 0, sizeof(pcpos));
582
data += sizeof(pcpos);
583
584
uint64_t hits = 0;
585
memcpy(&hits, data + 0, sizeof(hits));
586
data += sizeof(hits);
587
588
int line = pcpos == ~0u ? p->linedefined : luaG_getline(p, pcpos);
589
590
countervisit(context, kind, line, hits);
591
}
592
}
593
}
594
595
for (int i = 0; i < p->sizep; ++i)
596
getcounters(L, p->p[i], context, functionvisit, countervisit);
597
}
598
599
void lua_getcounters(lua_State* L, int funcindex, void* context, lua_CounterFunction functionvisit, lua_CounterValue countervisit)
600
{
601
const TValue* func = luaA_toobject(L, funcindex);
602
api_check(L, ttisfunction(func) && !clvalue(func)->isC);
603
604
if (L->global->ecb.getcounterdata == nullptr)
605
return;
606
607
Proto* p = clvalue(func)->l.p;
608
609
getcounters(L, p, context, functionvisit, countervisit);
610
}
611
612
static size_t append(char* buf, size_t bufsize, size_t offset, const char* data)
613
{
614
size_t size = strlen(data);
615
size_t copy = offset + size >= bufsize ? bufsize - offset - 1 : size;
616
memcpy(buf + offset, data, copy);
617
return offset + copy;
618
}
619
620
const char* lua_debugtrace(lua_State* L)
621
{
622
static char buf[4096];
623
624
const int limit1 = 10;
625
const int limit2 = 10;
626
627
int depth = int(L->ci - L->base_ci);
628
size_t offset = 0;
629
630
lua_Debug ar;
631
for (int level = 0; lua_getinfo(L, level, "sln", &ar); ++level)
632
{
633
if (ar.source)
634
offset = append(buf, sizeof(buf), offset, ar.short_src);
635
636
if (ar.currentline > 0)
637
{
638
char line[32];
639
snprintf(line, sizeof(line), ":%d", ar.currentline);
640
641
offset = append(buf, sizeof(buf), offset, line);
642
}
643
644
if (ar.name)
645
{
646
offset = append(buf, sizeof(buf), offset, " function ");
647
offset = append(buf, sizeof(buf), offset, ar.name);
648
}
649
650
offset = append(buf, sizeof(buf), offset, "\n");
651
652
if (depth > limit1 + limit2 && level == limit1 - 1)
653
{
654
char skip[32];
655
snprintf(skip, sizeof(skip), "... (+%d frames)\n", int(depth - limit1 - limit2));
656
657
offset = append(buf, sizeof(buf), offset, skip);
658
659
level = depth - limit2 - 1;
660
}
661
}
662
663
LUAU_ASSERT(offset < sizeof(buf));
664
buf[offset] = '\0';
665
666
return buf;
667
}
668
669