Skip to content

Commit af17060

Browse files
authored
Adds Runtime::runSource method (#911)
Adds `Runtime::runSource` method to support running Luau source code directly. Resolves #285. To avoid duplication and unnecessary dependencies between the `Runtime` and `CLI`, I decided to move main logic of `runBytecode` to the `Runtime`, which feels like its natural place. If that's not the case and main logic of `runBytecode` should stay in `climain`, I can revert the changes. But the solution will be more verbose. The `CLI` layer's `runBytecode` now delegates to `Runtime::runBytecode` with an `onLoaded` callback for codegen and profiling, eliminating duplication within the `Runtime` implementation. ~~Additionally, I moved the `profiler options` from the `CLI` directly into the `profiler`. Currently, the sampling `frequency` in Hz duplicates its default value in two places: [here](https://github.com/luau-lang/lute/blob/6ed3142c96d109a7ac94ec7c9a7d8dc86a56ac69/lute/cli/src/profiler.cpp#L53) and [here](https://github.com/luau-lang/lute/blob/6ed3142c96d109a7ac94ec7c9a7d8dc86a56ac69/lute/cli/include/lute/climain.h#L13). It has no impact at the moment, but having a single source will help avoid potential bugs in the future.~~ Moved to PR #918.
1 parent db111d6 commit af17060

File tree

9 files changed

+160
-94
lines changed

9 files changed

+160
-94
lines changed

lute/cli/include/lute/climain.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
#pragma once
2-
#include "lute/profiler.h"
32

4-
#include <functional>
5-
#include <optional>
63
#include <string>
74

8-
struct lua_State;
9-
struct Runtime;
105
class LuteReporter;
116

127
int cliMain(int argc, char** argv, LuteReporter& reporter);
13-
bool runBytecode(
14-
Runtime& runtime,
15-
const std::string& bytecode,
16-
const std::string& chunkname,
17-
lua_State* GL,
18-
int program_argc,
19-
char** program_argv,
20-
LuteReporter& reporter,
21-
std::optional<ProfileOptions> profileOptions = std::nullopt
22-
);

lute/cli/src/climain.cpp

Lines changed: 27 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "lute/packagerun.h"
1010
#include "lute/process.h"
1111
#include "lute/profiler.h"
12-
#include "lute/ref.h"
1312
#include "lute/reporter.h"
1413
#include "lute/requiresetup.h"
1514
#include "lute/runtime.h"
@@ -116,84 +115,45 @@ Compile Options:
116115
-h, --help Display this usage message.
117116
)";
118117

119-
static bool setupArguments(lua_State* L, int argc, char** argv)
120-
{
121-
if (!lua_checkstack(L, argc))
122-
return false;
123-
124-
for (int i = 0; i < argc; ++i)
125-
lua_pushstring(L, argv[i]);
126-
127-
return true;
128-
}
129-
130-
bool runBytecode(
118+
static bool runBytecode(
131119
Runtime& runtime,
132120
const std::string& bytecode,
133121
const std::string& chunkname,
134-
lua_State* GL,
135122
int program_argc,
136123
char** program_argv,
137124
LuteReporter& reporter,
138-
std::optional<ProfileOptions> profileOptions
125+
std::optional<ProfileOptions> profileOptions = std::nullopt
139126
)
140127
{
141-
// module needs to run in a new thread, isolated from the rest
142-
lua_State* L = lua_newthread(GL);
143-
144-
if (profileOptions)
145-
profilerStart(L, profileOptions->frequency);
146-
147-
// new thread needs to have the globals sandboxed
148-
luaL_sandboxthread(L);
149-
150-
if (luau_load(L, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) != 0)
151-
{
152-
if (const char* str = lua_tostring(L, -1))
153-
reporter.reportError(str);
154-
else
155-
reporter.reportError("Failed to load bytecode");
156-
157-
lua_pop(GL, 1);
158-
return false;
159-
}
160-
161-
if (getCodegenEnabled())
162-
{
163-
Luau::CodeGen::CompilationOptions nativeOptions;
164-
Luau::CodeGen::compile(L, -1, nativeOptions);
165-
}
166-
167-
if (!setupArguments(L, program_argc, program_argv))
168-
{
169-
reporter.reportError("Failed to pass arguments to Luau");
170-
lua_pop(GL, 1);
171-
return false;
172-
}
173-
174-
runtime.args.clear();
175-
for (int i = 0; i < program_argc; ++i)
176-
runtime.args.emplace_back(program_argv[i]);
177-
178-
runtime.GL = GL;
179-
runtime.runningThreads.push_back({true, getRefForThread(L), program_argc});
180-
181-
lua_pop(GL, 1);
128+
bool success = runtime.runBytecode(
129+
bytecode,
130+
chunkname,
131+
program_argc,
132+
program_argv,
133+
[&](lua_State* L)
134+
{
135+
if (getCodegenEnabled())
136+
{
137+
Luau::CodeGen::CompilationOptions nativeOptions;
138+
Luau::CodeGen::compile(L, -1, nativeOptions);
139+
}
140+
if (profileOptions)
141+
profilerStart(L, profileOptions->frequency);
142+
}
143+
);
182144

183-
bool b = runtime.runToCompletion();
184145
if (profileOptions)
185146
{
186147
profilerStop();
187148
profilerDump(profileOptions->filename.c_str(), reporter);
188149
}
189150

190-
return b;
151+
return success;
191152
}
192153

193154
static bool runFile(
194155
Runtime& runtime,
195156
const char* name,
196-
lua_State* GL,
197157
int program_argc,
198158
char** program_argv,
199159
LuteReporter& reporter,
@@ -217,7 +177,7 @@ static bool runFile(
217177

218178
std::string bytecode = Luau::compile(*source, copts());
219179

220-
return runBytecode(runtime, bytecode, chunkname, GL, program_argc, program_argv, reporter, profileOptions);
180+
return runBytecode(runtime, bytecode, chunkname, program_argc, program_argv, reporter, profileOptions);
221181
}
222182

223183
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
@@ -396,7 +356,6 @@ int handleRunCommand(int argc, char** argv, int argOffset, bool packageAwareness
396356
}
397357

398358
Runtime runtime{reporter};
399-
lua_State* L;
400359

401360
if (packageAwareness)
402361
{
@@ -419,14 +378,14 @@ int handleRunCommand(int argc, char** argv, int argOffset, bool packageAwareness
419378
}
420379

421380
auto [directDependencies, allDependencies] = getDependenciesFromLockfile(*lockfile);
422-
L = setupPkgRunState(runtime, std::move(directDependencies), std::move(allDependencies));
381+
setupPkgRunState(runtime, std::move(directDependencies), std::move(allDependencies));
423382
}
424383
else
425384
{
426-
L = setupRunState(runtime);
385+
setupRunState(runtime);
427386
}
428387

429-
bool success = runFile(runtime, validPath.c_str(), L, program_argc, program_argv, reporter, profileOptions);
388+
bool success = runFile(runtime, validPath.c_str(), program_argc, program_argv, reporter, profileOptions);
430389
return success ? 0 : 1;
431390
}
432391

@@ -650,10 +609,9 @@ void setupVersionLibrary(lua_State* L)
650609
int handleCliCommand(CliCommandResult result, int program_argc, char** program_argv, LuteReporter& reporter)
651610
{
652611
Runtime runtime{reporter};
653-
lua_State* L = setupCliCommandState(runtime, setupVersionLibrary);
612+
setupCliCommandState(runtime, setupVersionLibrary);
654613

655-
std::string bytecode = Luau::compile(std::string(result.contents), copts());
656-
return runBytecode(runtime, bytecode, "@" + result.path, L, program_argc, program_argv, reporter) ? 0 : 1;
614+
return runtime.runSource(std::string(result.contents), copts(), "@" + result.path, program_argc, program_argv) ? 0 : 1;
657615
}
658616

659617
int cliMain(int argc, char** argv, LuteReporter& reporter)
@@ -674,12 +632,12 @@ int cliMain(int argc, char** argv, LuteReporter& reporter)
674632
{
675633
Runtime runtime{reporter};
676634

677-
lua_State* GL = setupBundleState(runtime, payload->luauConfigFiles, payload->filePathToBytecode);
635+
setupBundleState(runtime, payload->luauConfigFiles, payload->filePathToBytecode);
678636
std::string entryPoint = payload->entryPointPath;
679637
auto entryModule = payload->filePathToBytecode.find(entryPoint);
680638
if (entryModule != nullptr)
681639
{
682-
bool success = runBytecode(runtime, *entryModule, "@@bundle/" + entryPoint, GL, argc, argv, reporter);
640+
bool success = runBytecode(runtime, *entryModule, "@@bundle/" + entryPoint, argc, argv, reporter);
683641
return success ? 0 : 1;
684642
}
685643
}

lute/runtime/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ target_sources(Lute.Runtime PRIVATE
1616
target_compile_features(Lute.Runtime PUBLIC cxx_std_17)
1717
target_include_directories(Lute.Runtime PUBLIC "include" ${LIBUV_INCLUDE_DIR})
1818
target_link_libraries(Lute.Runtime PUBLIC Lute.Common)
19-
target_link_libraries(Lute.Runtime PRIVATE Luau.Require Luau.VM uv_a)
19+
target_link_libraries(Lute.Runtime PRIVATE Luau.Require Luau.Compiler Luau.VM uv_a)
2020
target_compile_options(Lute.Runtime PRIVATE ${LUTE_OPTIONS})

lute/runtime/include/lute/runtime.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919

2020
struct lua_State;
2121

22+
namespace Luau
23+
{
24+
struct CompileOptions;
25+
}
26+
2227
struct ThreadToContinue
2328
{
2429
bool success = false;
@@ -81,6 +86,26 @@ struct Runtime
8186

8287
uv_loop_t* getEventLoop();
8388

89+
// Load and run bytecode.
90+
// If provided, onLoaded is called after bytecode is loaded
91+
// with the loaded function at the top of the stack.
92+
bool runBytecode(
93+
const std::string& bytecode,
94+
const std::string& chunkname,
95+
int argc = 0,
96+
char** argv = nullptr,
97+
std::function<void(lua_State*)> onLoaded = nullptr
98+
);
99+
100+
// Compile Luau source and run it.
101+
bool runSource(
102+
const std::string& source,
103+
const Luau::CompileOptions& compileOptions,
104+
const std::string& chunkname = "=stdin",
105+
int argc = 0,
106+
char** argv = nullptr
107+
);
108+
84109
LuteReporter& reporter;
85110

86111
// VM for this runtime

lute/runtime/src/runtime.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#include "lute/runtime.h"
22

33
#include "lute/common.h"
4+
#include "lute/ref.h"
5+
6+
#include "Luau/Compiler.h"
47

58
#include "lua.h"
9+
#include "luacode.h"
610
#include "lualib.h"
711

812
#include "uv.h"
@@ -363,6 +367,52 @@ ResumeToken getResumeToken(lua_State* L)
363367
return token;
364368
}
365369

370+
bool Runtime::runBytecode(const std::string& bytecode, const std::string& chunkname, int argc, char** argv, std::function<void(lua_State*)> onLoaded)
371+
{
372+
lua_State* L = lua_newthread(GL);
373+
374+
luaL_sandboxthread(L);
375+
376+
if (luau_load(L, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) != 0)
377+
{
378+
reportError(L);
379+
lua_pop(GL, 1);
380+
return false;
381+
}
382+
383+
if (onLoaded)
384+
onLoaded(L);
385+
386+
if (argc > 0 && argv != nullptr)
387+
{
388+
if (!lua_checkstack(L, argc))
389+
{
390+
fprintf(stderr, "Failed to pass arguments to Luau\n");
391+
lua_pop(GL, 1);
392+
return false;
393+
}
394+
395+
for (int i = 0; i < argc; ++i)
396+
lua_pushstring(L, argv[i]);
397+
}
398+
399+
args.clear();
400+
for (int i = 0; i < argc; ++i)
401+
args.emplace_back(argv[i]);
402+
403+
runningThreads.push_back({true, getRefForThread(L), argc});
404+
405+
lua_pop(GL, 1);
406+
407+
return runToCompletion();
408+
}
409+
410+
bool Runtime::runSource(const std::string& source, const Luau::CompileOptions& compileOptions, const std::string& chunkname, int argc, char** argv)
411+
{
412+
std::string bytecode = Luau::compile(source, compileOptions);
413+
return runBytecode(bytecode, chunkname, argc, argv);
414+
}
415+
366416
lua_State* setupState(Runtime& runtime, std::function<void(lua_State*)> doBeforeSandbox)
367417
{
368418
// Separate VM for data copies

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ target_sources(Lute.Test PRIVATE
2222

2323
# Test files
2424
src/compile.test.cpp
25+
src/runsource.test.cpp
2526
src/configresolver.test.cpp
2627
src/lutefs.test.cpp
2728
src/modulepath.test.cpp

tests/src/cliruntimefixture.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
#include "cliruntimefixture.h"
22

3-
#include "lute/climain.h"
3+
#include "lute/options.h"
44
#include "lute/requiresetup.h"
55

6-
#include "Luau/Compiler.h"
7-
86
#include "lua.h"
97
#include "lualib.h"
108

@@ -34,6 +32,5 @@ CliRuntimeFixture::CliRuntimeFixture()
3432

3533
bool CliRuntimeFixture::runCode(const std::string& source)
3634
{
37-
std::string bytecode = Luau::compile(source, Luau::CompileOptions());
38-
return runBytecode(*runtime, bytecode, "=stdin", L, 0, nullptr, getReporter());
35+
return runtime->runSource(source, copts());
3936
}

tests/src/packagerequire.test.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "lute/runtime.h"
66
#include "lute/userlandvfs.h"
77

8-
#include "Luau/Compiler.h"
98
#include "Luau/FileUtils.h"
109
#include "Luau/Require.h"
1110

@@ -24,7 +23,7 @@ TEST_CASE_FIXTURE(LuteFixture, "package_aware_require")
2423
{
2524
Runtime runtime{getReporter()};
2625

27-
lua_State* L = setupState(
26+
setupState(
2827
runtime,
2928
[](lua_State* L)
3029
{
@@ -84,8 +83,7 @@ TEST_CASE_FIXTURE(LuteFixture, "package_aware_require")
8483
std::optional<std::string> contents = readFile(path);
8584
REQUIRE(contents);
8685

87-
std::string bytecode = Luau::compile(*contents, copts());
88-
bool success = runBytecode(runtime, bytecode, "@" + path, L, 0, nullptr, getReporter());
86+
bool success = runtime.runSource(*contents, copts(), "@" + path);
8987
CHECK(success);
9088
}
9189

0 commit comments

Comments
 (0)