Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/VM/src/lbaselib.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 "lualib.h"
4
5
#include "lstate.h"
6
#include "lapi.h"
7
#include "ldo.h"
8
#include "ludata.h"
9
10
#include <ctype.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
14
LUAU_FASTFLAG(LuauStacklessPcall)
15
16
static void writestring(const char* s, size_t l)
17
{
18
fwrite(s, 1, l, stdout);
19
}
20
21
static int luaB_print(lua_State* L)
22
{
23
int n = lua_gettop(L); // number of arguments
24
for (int i = 1; i <= n; i++)
25
{
26
size_t l;
27
const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
28
if (i > 1)
29
writestring("\t", 1);
30
writestring(s, l);
31
lua_pop(L, 1); // pop result
32
}
33
writestring("\n", 1);
34
return 0;
35
}
36
37
static int luaB_tonumber(lua_State* L)
38
{
39
int base = luaL_optinteger(L, 2, 10);
40
if (base == 10)
41
{ // standard conversion
42
int isnum = 0;
43
double n = lua_tonumberx(L, 1, &isnum);
44
if (isnum)
45
{
46
lua_pushnumber(L, n);
47
return 1;
48
}
49
luaL_checkany(L, 1); // error if we don't have any argument
50
}
51
else
52
{
53
const char* s1 = luaL_checkstring(L, 1);
54
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
55
char* s2;
56
unsigned long long n;
57
n = strtoull(s1, &s2, base);
58
if (s1 != s2)
59
{ // at least one valid digit?
60
while (isspace((unsigned char)(*s2)))
61
s2++; // skip trailing spaces
62
if (*s2 == '\0')
63
{ // no invalid trailing characters?
64
lua_pushnumber(L, (double)n);
65
return 1;
66
}
67
}
68
}
69
lua_pushnil(L); // else not a number
70
return 1;
71
}
72
73
static int luaB_error(lua_State* L)
74
{
75
int level = luaL_optinteger(L, 2, 1);
76
lua_settop(L, 1);
77
if (lua_isstring(L, 1) && level > 0)
78
{ // add extra information?
79
luaL_where(L, level);
80
lua_pushvalue(L, 1);
81
lua_concat(L, 2);
82
}
83
lua_error(L);
84
}
85
86
static int luaB_getmetatable(lua_State* L)
87
{
88
luaL_checkany(L, 1);
89
if (!lua_getmetatable(L, 1))
90
{
91
lua_pushnil(L);
92
return 1; // no metatable
93
}
94
luaL_getmetafield(L, 1, "__metatable");
95
return 1; // returns either __metatable field (if present) or metatable
96
}
97
98
static int luaB_setmetatable(lua_State* L)
99
{
100
int t = lua_type(L, 2);
101
luaL_checktype(L, 1, LUA_TTABLE);
102
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
103
if (luaL_getmetafield(L, 1, "__metatable"))
104
luaL_error(L, "cannot change a protected metatable");
105
lua_settop(L, 2);
106
lua_setmetatable(L, 1);
107
return 1;
108
}
109
110
static void getfunc(lua_State* L, int opt)
111
{
112
if (lua_isfunction(L, 1))
113
lua_pushvalue(L, 1);
114
else
115
{
116
lua_Debug ar;
117
int level = opt ? luaL_optinteger(L, 1, 1) : luaL_checkinteger(L, 1);
118
luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
119
if (lua_getinfo(L, level, "f", &ar) == 0)
120
luaL_argerror(L, 1, "invalid level");
121
if (lua_isnil(L, -1))
122
luaL_error(L, "no function environment for tail call at level %d", level);
123
}
124
}
125
126
static int luaB_getfenv(lua_State* L)
127
{
128
getfunc(L, 1);
129
if (lua_iscfunction(L, -1)) // is a C function?
130
lua_pushvalue(L, LUA_GLOBALSINDEX); // return the thread's global env.
131
else
132
lua_getfenv(L, -1);
133
lua_setsafeenv(L, -1, false);
134
return 1;
135
}
136
137
static int luaB_setfenv(lua_State* L)
138
{
139
luaL_checktype(L, 2, LUA_TTABLE);
140
getfunc(L, 0);
141
lua_pushvalue(L, 2);
142
lua_setsafeenv(L, -1, false);
143
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)
144
{
145
// change environment of current thread
146
lua_pushthread(L);
147
lua_insert(L, -2);
148
lua_setfenv(L, -2);
149
return 0;
150
}
151
else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
152
luaL_error(L, "'setfenv' cannot change environment of given object");
153
return 1;
154
}
155
156
static int luaB_rawequal(lua_State* L)
157
{
158
luaL_checkany(L, 1);
159
luaL_checkany(L, 2);
160
lua_pushboolean(L, lua_rawequal(L, 1, 2));
161
return 1;
162
}
163
164
static int luaB_rawget(lua_State* L)
165
{
166
luaL_checktype(L, 1, LUA_TTABLE);
167
luaL_checkany(L, 2);
168
lua_settop(L, 2);
169
lua_rawget(L, 1);
170
return 1;
171
}
172
173
static int luaB_rawset(lua_State* L)
174
{
175
luaL_checktype(L, 1, LUA_TTABLE);
176
luaL_checkany(L, 2);
177
luaL_checkany(L, 3);
178
lua_settop(L, 3);
179
lua_rawset(L, 1);
180
return 1;
181
}
182
183
static int luaB_rawlen(lua_State* L)
184
{
185
int tt = lua_type(L, 1);
186
luaL_argcheck(L, tt == LUA_TTABLE || tt == LUA_TSTRING, 1, "table or string expected");
187
int len = lua_objlen(L, 1);
188
lua_pushinteger(L, len);
189
return 1;
190
}
191
192
static int luaB_gcinfo(lua_State* L)
193
{
194
lua_pushinteger(L, lua_gc(L, LUA_GCCOUNT, 0));
195
return 1;
196
}
197
198
static int luaB_type(lua_State* L)
199
{
200
luaL_checkany(L, 1);
201
// resulting name doesn't differentiate between userdata types
202
lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
203
return 1;
204
}
205
206
static int luaB_typeof(lua_State* L)
207
{
208
luaL_checkany(L, 1);
209
// resulting name returns __type if specified unless the input is a newproxy-created userdata
210
lua_pushstring(L, luaL_typename(L, 1));
211
return 1;
212
}
213
214
int luaB_next(lua_State* L)
215
{
216
luaL_checktype(L, 1, LUA_TTABLE);
217
lua_settop(L, 2); // create a 2nd argument if there isn't one
218
if (lua_next(L, 1))
219
return 2;
220
else
221
{
222
lua_pushnil(L);
223
return 1;
224
}
225
}
226
227
static int luaB_pairs(lua_State* L)
228
{
229
luaL_checktype(L, 1, LUA_TTABLE);
230
lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
231
lua_pushvalue(L, 1); // state,
232
lua_pushnil(L); // and initial value
233
return 3;
234
}
235
236
int luaB_inext(lua_State* L)
237
{
238
int i = luaL_checkinteger(L, 2);
239
luaL_checktype(L, 1, LUA_TTABLE);
240
i++; // next value
241
lua_pushinteger(L, i);
242
lua_rawgeti(L, 1, i);
243
return (lua_isnil(L, -1)) ? 0 : 2;
244
}
245
246
static int luaB_ipairs(lua_State* L)
247
{
248
luaL_checktype(L, 1, LUA_TTABLE);
249
lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
250
lua_pushvalue(L, 1); // state,
251
lua_pushinteger(L, 0); // and initial value
252
return 3;
253
}
254
255
static int luaB_assert(lua_State* L)
256
{
257
luaL_checkany(L, 1);
258
if (!lua_toboolean(L, 1))
259
luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
260
return lua_gettop(L);
261
}
262
263
static int luaB_select(lua_State* L)
264
{
265
int n = lua_gettop(L);
266
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#')
267
{
268
lua_pushinteger(L, n - 1);
269
return 1;
270
}
271
else
272
{
273
int i = luaL_checkinteger(L, 1);
274
if (i < 0)
275
i = n + i;
276
else if (i > n)
277
i = n;
278
luaL_argcheck(L, 1 <= i, 1, "index out of range");
279
return n - i;
280
}
281
}
282
283
static void luaB_pcallrun(lua_State* L, void* ud)
284
{
285
StkId func = (StkId)ud;
286
287
if (FFlag::LuauStacklessPcall)
288
{
289
// if we can yield, schedule a call setup with postponed reentry
290
luaD_callint(L, func, LUA_MULTRET, lua_isyieldable(L) != 0);
291
}
292
else
293
{
294
luaD_call(L, func, LUA_MULTRET);
295
}
296
}
297
298
static int luaB_pcally(lua_State* L)
299
{
300
luaL_checkany(L, 1);
301
302
StkId func = L->base;
303
304
// any errors from this point on are handled by continuation
305
L->ci->flags |= LUA_CALLINFO_HANDLE;
306
307
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), 0);
308
309
// necessary to accommodate functions that return lots of values
310
expandstacklimit(L, L->top);
311
312
if (FFlag::LuauStacklessPcall)
313
{
314
// yielding means we need to propagate yield; resume will call continuation function later
315
if (status == 0 && isyielded(L))
316
return C_CALL_YIELD;
317
}
318
else
319
{
320
// yielding means we need to propagate yield; resume will call continuation function later
321
if (status == 0 && (L->status == LUA_YIELD || L->status == LUA_BREAK))
322
return -1; // -1 is a marker for yielding from C
323
}
324
325
// immediate return (error or success)
326
lua_rawcheckstack(L, 1);
327
lua_pushboolean(L, status == 0);
328
lua_insert(L, 1);
329
return lua_gettop(L); // return status + all results
330
}
331
332
static int luaB_pcallcont(lua_State* L, int status)
333
{
334
if (status == 0)
335
{
336
lua_rawcheckstack(L, 1);
337
lua_pushboolean(L, true);
338
lua_insert(L, 1); // insert status before all results
339
return lua_gettop(L);
340
}
341
else
342
{
343
lua_rawcheckstack(L, 1);
344
lua_pushboolean(L, false);
345
lua_insert(L, -2); // insert status before error object
346
return 2;
347
}
348
}
349
350
static int luaB_xpcally(lua_State* L)
351
{
352
luaL_checktype(L, 2, LUA_TFUNCTION);
353
354
// swap function & error function
355
lua_pushvalue(L, 1);
356
lua_pushvalue(L, 2);
357
lua_replace(L, 1);
358
lua_replace(L, 2);
359
// at this point the stack looks like err, f, args
360
361
// any errors from this point on are handled by continuation
362
L->ci->flags |= LUA_CALLINFO_HANDLE;
363
364
StkId errf = L->base;
365
StkId func = L->base + 1;
366
367
int status = luaD_pcall(L, luaB_pcallrun, func, savestack(L, func), savestack(L, errf));
368
369
// necessary to accommodate functions that return lots of values
370
expandstacklimit(L, L->top);
371
372
if (FFlag::LuauStacklessPcall)
373
{
374
// yielding means we need to propagate yield; resume will call continuation function later
375
if (status == 0 && isyielded(L))
376
return C_CALL_YIELD;
377
}
378
else
379
{
380
// yielding means we need to propagate yield; resume will call continuation function later
381
if (status == 0 && (L->status == LUA_YIELD || L->status == LUA_BREAK))
382
return -1; // -1 is a marker for yielding from C
383
}
384
385
// immediate return (error or success)
386
lua_rawcheckstack(L, 1);
387
lua_pushboolean(L, status == 0);
388
lua_replace(L, 1); // replace error function with status
389
return lua_gettop(L); // return status + all results
390
}
391
392
static void luaB_xpcallerr(lua_State* L, void* ud)
393
{
394
StkId func = (StkId)ud;
395
396
luaD_callny(L, func, 1);
397
}
398
399
static int luaB_xpcallcont(lua_State* L, int status)
400
{
401
if (status == 0)
402
{
403
lua_rawcheckstack(L, 1);
404
lua_pushboolean(L, true);
405
lua_replace(L, 1); // replace error function with status
406
return lua_gettop(L); // return status + all results
407
}
408
else
409
{
410
lua_rawcheckstack(L, 3);
411
lua_pushboolean(L, false);
412
lua_pushvalue(L, 1); // push error function on top of the stack
413
lua_pushvalue(L, -3); // push error object (that was on top of the stack before)
414
415
StkId errf = L->top - 2;
416
ptrdiff_t oldtopoffset = savestack(L, errf);
417
418
int err = luaD_pcall(L, luaB_xpcallerr, errf, oldtopoffset, 0);
419
420
if (err != 0)
421
{
422
int errstatus = status;
423
424
// in general we preserve the status, except for cases when the error handler fails
425
// out of memory is treated specially because it's common for it to be cascading, in which case we preserve the code
426
if (status == LUA_ERRMEM && err == LUA_ERRMEM)
427
errstatus = LUA_ERRMEM;
428
else
429
errstatus = LUA_ERRERR;
430
431
StkId oldtop = restorestack(L, oldtopoffset);
432
luaD_seterrorobj(L, errstatus, oldtop);
433
}
434
435
return 2;
436
}
437
}
438
439
static int luaB_tostring(lua_State* L)
440
{
441
luaL_checkany(L, 1);
442
luaL_tolstring(L, 1, NULL);
443
return 1;
444
}
445
446
static int luaB_newproxy(lua_State* L)
447
{
448
int t = lua_type(L, 1);
449
luaL_argexpected(L, t == LUA_TNONE || t == LUA_TNIL || t == LUA_TBOOLEAN, 1, "nil or boolean");
450
451
bool needsmt = lua_toboolean(L, 1);
452
453
lua_newuserdatatagged(L, 0, UTAG_PROXY);
454
455
if (needsmt)
456
{
457
lua_newtable(L);
458
lua_setmetatable(L, -2);
459
}
460
461
return 1;
462
}
463
464
static const luaL_Reg base_funcs[] = {
465
{"assert", luaB_assert},
466
{"error", luaB_error},
467
{"gcinfo", luaB_gcinfo},
468
{"getfenv", luaB_getfenv},
469
{"getmetatable", luaB_getmetatable},
470
{"next", luaB_next},
471
{"newproxy", luaB_newproxy},
472
{"print", luaB_print},
473
{"rawequal", luaB_rawequal},
474
{"rawget", luaB_rawget},
475
{"rawset", luaB_rawset},
476
{"rawlen", luaB_rawlen},
477
{"select", luaB_select},
478
{"setfenv", luaB_setfenv},
479
{"setmetatable", luaB_setmetatable},
480
{"tonumber", luaB_tonumber},
481
{"tostring", luaB_tostring},
482
{"type", luaB_type},
483
{"typeof", luaB_typeof},
484
{NULL, NULL},
485
};
486
487
static void auxopen(lua_State* L, const char* name, lua_CFunction f, lua_CFunction u)
488
{
489
lua_pushcfunction(L, u, NULL);
490
lua_pushcclosure(L, f, name, 1);
491
lua_setfield(L, -2, name);
492
}
493
494
int luaopen_base(lua_State* L)
495
{
496
// set global _G
497
lua_pushvalue(L, LUA_GLOBALSINDEX);
498
lua_setglobal(L, "_G");
499
500
// open lib into global table
501
luaL_register(L, "_G", base_funcs);
502
lua_pushliteral(L, "Luau");
503
lua_setglobal(L, "_VERSION"); // set global _VERSION
504
505
// `ipairs' and `pairs' need auxiliary functions as upvalues
506
auxopen(L, "ipairs", luaB_ipairs, luaB_inext);
507
auxopen(L, "pairs", luaB_pairs, luaB_next);
508
509
lua_pushcclosurek(L, luaB_pcally, "pcall", 0, luaB_pcallcont);
510
lua_setfield(L, -2, "pcall");
511
512
lua_pushcclosurek(L, luaB_xpcally, "xpcall", 0, luaB_xpcallcont);
513
lua_setfield(L, -2, "xpcall");
514
515
return 1;
516
}
517
518