Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CLI/src/ReplRequirer.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
#include "Luau/ReplRequirer.h"
3
4
#include "Luau/CodeGen.h"
5
#include "Luau/CodeGenOptions.h"
6
#include "Luau/FileUtils.h"
7
#include "Luau/Require.h"
8
#include "Luau/VfsNavigator.h"
9
10
#include "lua.h"
11
#include "lualib.h"
12
13
#include <string>
14
#include <string_view>
15
#include <utility>
16
17
static luarequire_WriteResult write(std::optional<std::string> contents, char* buffer, size_t bufferSize, size_t* sizeOut)
18
{
19
if (!contents)
20
return luarequire_WriteResult::WRITE_FAILURE;
21
22
size_t nullTerminatedSize = contents->size() + 1;
23
24
if (bufferSize < nullTerminatedSize)
25
{
26
*sizeOut = nullTerminatedSize;
27
return luarequire_WriteResult::WRITE_BUFFER_TOO_SMALL;
28
}
29
30
*sizeOut = nullTerminatedSize;
31
memcpy(buffer, contents->c_str(), nullTerminatedSize);
32
return luarequire_WriteResult::WRITE_SUCCESS;
33
}
34
35
static luarequire_NavigateResult convert(NavigationStatus status)
36
{
37
if (status == NavigationStatus::Success)
38
return NAVIGATE_SUCCESS;
39
else if (status == NavigationStatus::Ambiguous)
40
return NAVIGATE_AMBIGUOUS;
41
else
42
return NAVIGATE_NOT_FOUND;
43
}
44
45
static luarequire_ConfigStatus convert(VfsNavigator::ConfigStatus status)
46
{
47
if (status == VfsNavigator::ConfigStatus::Ambiguous)
48
return CONFIG_AMBIGUOUS;
49
else if (status == VfsNavigator::ConfigStatus::PresentJson)
50
return CONFIG_PRESENT_JSON;
51
else if (status == VfsNavigator::ConfigStatus::PresentLuau)
52
return CONFIG_PRESENT_LUAU;
53
else
54
return CONFIG_ABSENT;
55
}
56
57
static bool is_require_allowed(lua_State* L, void* ctx, const char* requirer_chunkname)
58
{
59
std::string_view chunkname = requirer_chunkname;
60
return chunkname == "=stdin" || (!chunkname.empty() && chunkname[0] == '@');
61
}
62
63
static luarequire_NavigateResult reset(lua_State* L, void* ctx, const char* requirer_chunkname)
64
{
65
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
66
67
std::string chunkname = requirer_chunkname;
68
if (chunkname == "=stdin")
69
return convert(req->vfs.resetToStdIn());
70
else if (!chunkname.empty() && chunkname[0] == '@')
71
return convert(req->vfs.resetToPath(chunkname.substr(1)));
72
73
return NAVIGATE_NOT_FOUND;
74
}
75
76
static luarequire_NavigateResult jump_to_alias(lua_State* L, void* ctx, const char* path)
77
{
78
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
79
80
if (!isAbsolutePath(path))
81
return NAVIGATE_NOT_FOUND;
82
83
return convert(req->vfs.resetToPath(path));
84
}
85
86
static luarequire_NavigateResult to_parent(lua_State* L, void* ctx)
87
{
88
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
89
return convert(req->vfs.toParent());
90
}
91
92
static luarequire_NavigateResult to_child(lua_State* L, void* ctx, const char* name)
93
{
94
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
95
return convert(req->vfs.toChild(name));
96
}
97
98
static bool is_module_present(lua_State* L, void* ctx)
99
{
100
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
101
return isFile(req->vfs.getFilePath());
102
}
103
104
static luarequire_WriteResult get_chunkname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
105
{
106
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
107
return write("@" + req->vfs.getFilePath(), buffer, buffer_size, size_out);
108
}
109
110
static luarequire_WriteResult get_loadname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
111
{
112
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
113
return write(req->vfs.getAbsoluteFilePath(), buffer, buffer_size, size_out);
114
}
115
116
static luarequire_WriteResult get_cache_key(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
117
{
118
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
119
return write(req->vfs.getAbsoluteFilePath(), buffer, buffer_size, size_out);
120
}
121
122
static luarequire_ConfigStatus get_config_status(lua_State* L, void* ctx)
123
{
124
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
125
return convert(req->vfs.getConfigStatus());
126
}
127
128
static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
129
{
130
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
131
return write(req->vfs.getConfig(), buffer, buffer_size, size_out);
132
}
133
134
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname)
135
{
136
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
137
138
// module needs to run in a new thread, isolated from the rest
139
// note: we create ML on main thread so that it doesn't inherit environment of L
140
lua_State* GL = lua_mainthread(L);
141
lua_State* ML = lua_newthread(GL);
142
lua_xmove(GL, L, 1);
143
144
// new thread needs to have the globals sandboxed
145
luaL_sandboxthread(ML);
146
147
bool hadContents = false;
148
int status = LUA_OK;
149
150
// Handle C++ RAII objects in a scope which doesn't cause a Luau error
151
{
152
std::optional<std::string> contents = readFile(loadname);
153
hadContents = contents.has_value();
154
155
if (contents)
156
{
157
// now we can compile & run module on the new thread
158
std::string bytecode = Luau::compile(*contents, req->copts());
159
status = luau_load(ML, chunkname, bytecode.data(), bytecode.size(), 0);
160
}
161
}
162
163
if (!hadContents)
164
luaL_error(L, "could not read file '%s'", loadname);
165
166
if (status == 0)
167
{
168
if (req->codegenEnabled())
169
{
170
Luau::CodeGen::CompilationOptions nativeOptions;
171
172
if (req->countersActive())
173
nativeOptions.recordCounters = true;
174
175
Luau::CodeGen::compile(ML, -1, nativeOptions);
176
}
177
178
if (req->coverageActive())
179
req->coverageTrack(ML, -1);
180
181
if (req->countersActive())
182
req->countersTrack(ML, -1);
183
184
int status = lua_resume(ML, L, 0);
185
186
if (status == 0)
187
{
188
if (lua_gettop(ML) != 1)
189
luaL_error(L, "module must return a single value");
190
}
191
else if (status == LUA_YIELD)
192
{
193
luaL_error(L, "module can not yield");
194
}
195
else if (!lua_isstring(ML, -1))
196
{
197
luaL_error(L, "unknown error while running module");
198
}
199
else
200
{
201
luaL_error(L, "error while running module: %s", lua_tostring(ML, -1));
202
}
203
}
204
205
// add ML result to L stack
206
lua_xmove(ML, L, 1);
207
208
// remove ML thread from L stack
209
lua_remove(L, -2);
210
211
// added one value to L stack: module result
212
return 1;
213
}
214
215
void requireConfigInit(luarequire_Configuration* config)
216
{
217
if (config == nullptr)
218
return;
219
220
config->is_require_allowed = is_require_allowed;
221
config->reset = reset;
222
config->jump_to_alias = jump_to_alias;
223
config->to_parent = to_parent;
224
config->to_child = to_child;
225
config->is_module_present = is_module_present;
226
config->get_config_status = get_config_status;
227
config->get_chunkname = get_chunkname;
228
config->get_loadname = get_loadname;
229
config->get_cache_key = get_cache_key;
230
config->get_config = get_config;
231
config->load = load;
232
}
233
234
ReplRequirer::ReplRequirer(
235
CompileOptions copts,
236
BoolCheck coverageActive,
237
BoolCheck codegenEnabled,
238
Coverage coverageTrack,
239
BoolCheck countersActive,
240
Coverage countersTrack
241
)
242
: copts(copts)
243
, coverageActive(coverageActive)
244
, codegenEnabled(codegenEnabled)
245
, coverageTrack(coverageTrack)
246
, countersActive(countersActive)
247
, countersTrack(countersTrack)
248
{
249
}
250
251