Add files via upload

This commit is contained in:
Babyhamsta 2022-07-30 22:28:28 -05:00 committed by GitHub
parent 681359d4d1
commit 05d9edddb6
Signed by: DevComp
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1906 additions and 1021 deletions

View file

@ -1,20 +1,19 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ModuleResolver.h"
#include "Luau/TypeInfer.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Frontend.h"
#include "Luau/TypeAttach.h"
#include "Luau/Transpiler.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/ModuleResolver.h"
#include "lluz/TypeInfer.h"
#include "lluz/BuiltinDefinitions.h"
#include "lluz/Frontend.h"
#include "lluz/TypeAttach.h"
#include "lluz/Transpiler.h"
#include "FileUtils.h"
#include "Flags.h"
#ifdef CALLGRIND
#include <valgrind/callgrind.h>
#endif
LUAU_FASTFLAG(DebugLuauTimeTracing)
LUAU_FASTFLAG(LuauTypeMismatchModuleNameResolution)
lluz_FASTFLAG(DebugLluTimeTracing)
lluz_FASTFLAG(LluTypeMismatchModuleNameResolution)
enum class ReportFormat
{
@ -23,7 +22,7 @@ enum class ReportFormat
Gnu,
};
static void report(ReportFormat format, const char* name, const Luau::Location& loc, const char* type, const char* message)
static void report(ReportFormat format, const char* name, const lluz::Location& loc, const char* type, const char* message)
{
switch (format)
{
@ -49,41 +48,41 @@ static void report(ReportFormat format, const char* name, const Luau::Location&
}
}
static void reportError(const Luau::Frontend& frontend, ReportFormat format, const Luau::TypeError& error)
static void reportError(const lluz::Frontend& frontend, ReportFormat format, const lluz::TypeError& error)
{
std::string humanReadableName = frontend.fileResolver->getHumanReadableModuleName(error.moduleName);
if (const Luau::SyntaxError* syntaxError = Luau::get_if<Luau::SyntaxError>(&error.data))
report(format, humanReadableName.c_str(), error.location, "SyntaxError", syntaxError->message.c_str());
else if (FFlag::LuauTypeMismatchModuleNameResolution)
report(format, humanReadableName.c_str(), error.location, "TypeError",
Luau::toString(error, Luau::TypeErrorToStringOptions{frontend.fileResolver}).c_str());
if (const lluz::SyntaxError* syntaxError = lluz::get_if<lluz::SyntaxError>(&error.data))
report(format, humanReadableName.c_str(), error.location, XorStr("SyntaxError"), syntaxError->message.c_str());
else if (FFlag::LluTypeMismatchModuleNameResolution)
report(format, humanReadableName.c_str(), error.location, XorStr("TypeError"),
lluz::toString(error, lluz::TypeErrorToStringOptions{frontend.fileResolver}).c_str());
else
report(format, humanReadableName.c_str(), error.location, "TypeError", Luau::toString(error).c_str());
report(format, humanReadableName.c_str(), error.location, XorStr("TypeError"), lluz::toString(error).c_str());
}
static void reportWarning(ReportFormat format, const char* name, const Luau::LintWarning& warning)
static void reportWarning(ReportFormat format, const char* name, const lluz::LintWarning& warning)
{
report(format, name, warning.location, Luau::LintWarning::getName(warning.code), warning.text.c_str());
report(format, name, warning.location, lluz::LintWarning::getName(warning.code), warning.text.c_str());
}
static bool analyzeFile(Luau::Frontend& frontend, const char* name, ReportFormat format, bool annotate)
static bool analyzeFile(lluz::Frontend& frontend, const char* name, ReportFormat format, bool annotate)
{
Luau::CheckResult cr;
lluz::CheckResult cr;
if (frontend.isDirty(name))
cr = frontend.check(name);
if (!frontend.getSourceModule(name))
{
fprintf(stderr, "Error opening %s\n", name);
fprintf(stderr, XorStr("Error opening %s\n"), name);
return false;
}
for (auto& error : cr.errors)
reportError(frontend, format, error);
Luau::LintResult lr = frontend.lint(name);
lluz::LintResult lr = frontend.lint(name);
std::string humanReadableName = frontend.fileResolver->getHumanReadableModuleName(name);
for (auto& error : lr.errors)
@ -93,12 +92,12 @@ static bool analyzeFile(Luau::Frontend& frontend, const char* name, ReportFormat
if (annotate)
{
Luau::SourceModule* sm = frontend.getSourceModule(name);
Luau::ModulePtr m = frontend.moduleResolver.getModule(name);
lluz::SourceModule* sm = frontend.getSourceModule(name);
lluz::ModulePtr m = frontend.moduleResolver.getModule(name);
Luau::attachTypeData(*sm, *m);
lluz::attachTypeData(*sm, *m);
std::string annotated = Luau::transpileWithTypes(*sm->root);
std::string annotated = lluz::transpileWithTypes(*sm->root);
printf("%s", annotated.c_str());
}
@ -108,58 +107,58 @@ static bool analyzeFile(Luau::Frontend& frontend, const char* name, ReportFormat
static void displayHelp(const char* argv0)
{
printf("Usage: %s [--mode] [options] [file list]\n", argv0);
printf("\n");
printf("Available modes:\n");
printf(" omitted: typecheck and lint input files\n");
printf(" --annotate: typecheck input files and output source with type annotations\n");
printf("\n");
printf("Available options:\n");
printf(" --formatter=plain: report analysis errors in Luacheck-compatible format\n");
printf(" --formatter=gnu: report analysis errors in GNU-compatible format\n");
printf(" --mode=strict: default to strict mode when typechecking\n");
printf(" --timetrace: record compiler time tracing information into trace.json\n");
printf(XorStr("Usage: %s [--mode] [options] [file list]\n"), argv0);
printf(XorStr("\n"));
printf(XorStr("Available modes:\n"));
printf(XorStr(" omitted: typecheck and lint input files\n"));
printf(XorStr(" --annotate: typecheck input files and output source with type annotations\n"));
printf(XorStr("\n"));
printf(XorStr("Available options:\n"));
printf(XorStr(" --formatter=plain: report analysis errors in Luacheck-compatible format\n"));
printf(XorStr(" --formatter=gnu: report analysis errors in GNU-compatible format\n"));
printf(XorStr(" --mode=strict: default to strict mode when typechecking\n"));
printf(XorStr(" --timetrace: record compiler time tracing information into trace.json\n"));
}
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
{
printf("%s(%d): ASSERTION FAILED: %s\n", file, line, expr);
printf(XorStr("%s(%d): ASSERTION FAILED: %s\n"), file, line, expr);
return 1;
}
struct CliFileResolver : Luau::FileResolver
struct CliFileResolver : lluz::FileResolver
{
std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override
std::optional<lluz::SourceCode> readSource(const lluz::ModuleName& name) override
{
Luau::SourceCode::Type sourceType;
lluz::SourceCode::Type sourceType;
std::optional<std::string> source = std::nullopt;
// If the module name is "-", then read source from stdin
if (name == "-")
if (name == XorStr("-"))
{
source = readStdin();
sourceType = Luau::SourceCode::Script;
sourceType = lluz::SourceCode::Script;
}
else
{
source = readFile(name);
sourceType = Luau::SourceCode::Module;
sourceType = lluz::SourceCode::Module;
}
if (!source)
return std::nullopt;
return Luau::SourceCode{*source, sourceType};
return lluz::SourceCode{*source, sourceType};
}
std::optional<Luau::ModuleInfo> resolveModule(const Luau::ModuleInfo* context, Luau::AstExpr* node) override
std::optional<lluz::ModuleInfo> resolveModule(const lluz::ModuleInfo* context, lluz::AstExpr* node) override
{
if (Luau::AstExprConstantString* expr = node->as<Luau::AstExprConstantString>())
if (lluz::AstExprConstantString* expr = node->as<lluz::AstExprConstantString>())
{
Luau::ModuleName name = std::string(expr->value.data, expr->value.size) + ".luau";
lluz::ModuleName name = std::string(expr->value.data, expr->value.size) + ".lluz";
if (!readFile(name))
{
// fall back to .lua if a module with .luau doesn't exist
// fall back to .lua if a module with .lluz doesn't exist
name = std::string(expr->value.data, expr->value.size) + ".lua";
}
@ -169,27 +168,27 @@ struct CliFileResolver : Luau::FileResolver
return std::nullopt;
}
std::string getHumanReadableModuleName(const Luau::ModuleName& name) const override
std::string getHumanReadableModuleName(const lluz::ModuleName& name) const override
{
if (name == "-")
return "stdin";
if (name == XorStr("-"))
return XorStr("stdin");
return name;
}
};
struct CliConfigResolver : Luau::ConfigResolver
struct CliConfigResolver : lluz::ConfigResolver
{
Luau::Config defaultConfig;
lluz::Config defaultConfig;
mutable std::unordered_map<std::string, Luau::Config> configCache;
mutable std::unordered_map<std::string, lluz::Config> configCache;
mutable std::vector<std::pair<std::string, std::string>> configErrors;
CliConfigResolver(Luau::Mode mode)
CliConfigResolver(lluz::Mode mode)
{
defaultConfig.mode = mode;
}
const Luau::Config& getConfig(const Luau::ModuleName& name) const override
const lluz::Config& getConfig(const lluz::ModuleName& name) const override
{
std::optional<std::string> path = getParentPath(name);
if (!path)
@ -198,20 +197,20 @@ struct CliConfigResolver : Luau::ConfigResolver
return readConfigRec(*path);
}
const Luau::Config& readConfigRec(const std::string& path) const
const lluz::Config& readConfigRec(const std::string& path) const
{
auto it = configCache.find(path);
if (it != configCache.end())
return it->second;
std::optional<std::string> parent = getParentPath(path);
Luau::Config result = parent ? readConfigRec(*parent) : defaultConfig;
lluz::Config result = parent ? readConfigRec(*parent) : defaultConfig;
std::string configPath = joinPaths(path, Luau::kConfigName);
std::string configPath = joinPaths(path, lluz::kConfigName);
if (std::optional<std::string> contents = readFile(configPath))
{
std::optional<std::string> error = Luau::parseConfig(*contents, result);
std::optional<std::string> error = lluz::parseConfig(*contents, result);
if (error)
configErrors.push_back({configPath, *error});
}
@ -222,18 +221,20 @@ struct CliConfigResolver : Luau::ConfigResolver
int main(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
lluz::assertHandler() = assertionHandler;
setLuauFlagsDefault();
for (lluz::FValue<bool>* flag = lluz::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, XorStr("lluz"), 4) == 0)
flag->value = true;
if (argc >= 2 && strcmp(argv[1], "--help") == 0)
if (argc >= 2 && strcmp(argv[1], XorStr("--help")) == 0)
{
displayHelp(argv[0]);
return 0;
}
ReportFormat format = ReportFormat::Default;
Luau::Mode mode = Luau::Mode::Nonstrict;
lluz::Mode mode = lluz::Mode::Nonstrict;
bool annotate = false;
for (int i = 1; i < argc; ++i)
@ -246,32 +247,30 @@ int main(int argc, char** argv)
else if (strcmp(argv[i], "--formatter=gnu") == 0)
format = ReportFormat::Gnu;
else if (strcmp(argv[i], "--mode=strict") == 0)
mode = Luau::Mode::Strict;
mode = lluz::Mode::Strict;
else if (strcmp(argv[i], "--annotate") == 0)
annotate = true;
else if (strcmp(argv[i], "--timetrace") == 0)
FFlag::DebugLuauTimeTracing.value = true;
else if (strncmp(argv[i], "--fflags=", 9) == 0)
setLuauFlags(argv[i] + 9);
FFlag::DebugLluTimeTracing.value = true;
}
#if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing)
#if !defined(lluz_ENABLE_TIME_TRACE)
if (FFlag::DebugLluTimeTracing)
{
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
printf(XorStr("To run with --timetrace, lluz has to be built with lluz_ENABLE_TIME_TRACE enabled\n"));
return 1;
}
#endif
Luau::FrontendOptions frontendOptions;
lluz::FrontendOptions frontendOptions;
frontendOptions.retainFullTypeGraphs = annotate;
CliFileResolver fileResolver;
CliConfigResolver configResolver(mode);
Luau::Frontend frontend(&fileResolver, &configResolver, frontendOptions);
lluz::Frontend frontend(&fileResolver, &configResolver, frontendOptions);
Luau::registerBuiltinTypes(frontend.typeChecker);
Luau::freeze(frontend.typeChecker.globalTypes);
lluz::registerBuiltinTypes(frontend.typeChecker);
lluz::freeze(frontend.typeChecker.globalTypes);
#ifdef CALLGRIND
CALLGRIND_ZERO_STATS;

View file

@ -1,11 +1,11 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include <optional>
#include "Luau/Common.h"
#include "Luau/Ast.h"
#include "Luau/JsonEncoder.h"
#include "Luau/Parser.h"
#include "Luau/ParseOptions.h"
#include "lluz/Common.h"
#include "lluz/Ast.h"
#include "lluz/JsonEncoder.h"
#include "lluz/Parser.h"
#include "lluz/ParseOptions.h"
#include "FileUtils.h"
@ -22,10 +22,10 @@ static int assertionHandler(const char* expr, const char* file, int line, const
int main(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
lluz::assertHandler() = assertionHandler;
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
for (lluz::FValue<bool>* flag = lluz::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "lluz", 4) == 0)
flag->value = true;
if (argc >= 2 && strcmp(argv[1], "--help") == 0)
@ -58,28 +58,27 @@ int main(int argc, char** argv)
std::string source = *maybeSource;
Luau::Allocator allocator;
Luau::AstNameTable names(allocator);
lluz::Allocator allocator;
lluz::AstNameTable names(allocator);
Luau::ParseOptions options;
options.captureComments = true;
lluz::ParseOptions options;
options.supportContinueStatement = true;
options.allowTypeAnnotations = true;
options.allowDeclarationSyntax = true;
Luau::ParseResult parseResult = Luau::Parser::parse(source.data(), source.size(), names, allocator, options);
lluz::ParseResult parseResult = lluz::Parser::parse(source.data(), source.size(), names, allocator, options);
if (parseResult.errors.size() > 0)
{
fprintf(stderr, "Parse errors were encountered:\n");
for (const Luau::ParseError& error : parseResult.errors)
fprintf(stderr, XorStr("Parse errors were encountered:\n"));
for (const lluz::ParseError& error : parseResult.errors)
{
fprintf(stderr, " %s - %s\n", toString(error.getLocation()).c_str(), error.getMessage().c_str());
}
fprintf(stderr, "\n");
fprintf(stderr, XorStr("\n"));
}
printf("%s", Luau::toJson(parseResult.root, parseResult.commentLocations).c_str());
printf("%s", lluz::toJson(parseResult.root).c_str());
return parseResult.errors.size() > 0 ? 1 : 0;
}

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Coverage.h"
#include "lua.h"
@ -59,14 +59,14 @@ void coverageDump(const char* path)
{
lua_State* L = gCoverage.L;
FILE* f = fopen(path, "w");
FILE* f = fopen(path, XorStr("w"));
if (!f)
{
fprintf(stderr, "Error opening coverage %s\n", path);
return;
}
fprintf(f, "TN:\n");
fprintf(f, XorStr("TN:\n"));
for (int fref : gCoverage.functions)
{
@ -77,7 +77,7 @@ void coverageDump(const char* path)
fprintf(f, "SF:%s\n", ar.short_src);
lua_getcoverage(L, -1, f, coverageCallback);
fprintf(f, "end_of_record\n");
fprintf(f, XorStr("end_of_record\n"));
lua_pop(L, 1);
}

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
struct lua_State;

View file

@ -1,7 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "FileUtils.h"
#include "Luau/Common.h"
#include "lluz/Common.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -24,7 +24,7 @@
static std::wstring fromUtf8(const std::string& path)
{
size_t result = MultiByteToWideChar(CP_UTF8, 0, path.data(), int(path.size()), nullptr, 0);
LUAU_ASSERT(result);
lluz_ASSERT(result);
std::wstring buf(result, L'\0');
MultiByteToWideChar(CP_UTF8, 0, path.data(), int(path.size()), &buf[0], int(buf.size()));
@ -35,7 +35,7 @@ static std::wstring fromUtf8(const std::string& path)
static std::string toUtf8(const std::wstring& path)
{
size_t result = WideCharToMultiByte(CP_UTF8, 0, path.data(), int(path.size()), nullptr, 0, nullptr, nullptr);
LUAU_ASSERT(result);
lluz_ASSERT(result);
std::string buf(result, '\0');
WideCharToMultiByte(CP_UTF8, 0, path.data(), int(path.size()), &buf[0], int(buf.size()), nullptr, nullptr);
@ -47,9 +47,9 @@ static std::string toUtf8(const std::wstring& path)
std::optional<std::string> readFile(const std::string& name)
{
#ifdef _WIN32
FILE* file = _wfopen(fromUtf8(name).c_str(), L"rb");
FILE* file = _wfopen(fromUtf8(name).c_str(), LXorStr("rb"));
#else
FILE* file = fopen(name.c_str(), "rb");
FILE* file = fopen(name.c_str(), XorStr("rb"));
#endif
if (!file)
@ -106,7 +106,7 @@ static void joinPaths(std::basic_string<Ch>& str, const Ch* lhs, const Ch* rhs)
#ifdef _WIN32
static bool traverseDirectoryRec(const std::wstring& path, const std::function<void(const std::string& name)>& callback)
{
std::wstring query = path + std::wstring(L"/*");
std::wstring query = path + std::wstring(LXorStr("/*"));
WIN32_FIND_DATAW data;
HANDLE h = FindFirstFileW(query.c_str(), &data);
@ -232,7 +232,7 @@ std::string joinPaths(const std::string& lhs, const std::string& rhs)
std::optional<std::string> getParentPath(const std::string& path)
{
if (path == "" || path == "." || path == "/")
if (path == XorStr("") || path == XorStr(".") || path == XorStr("/"))
return std::nullopt;
#ifdef _WIN32
@ -243,20 +243,20 @@ std::optional<std::string> getParentPath(const std::string& path)
size_t slash = path.find_last_of("\\/", path.size() - 1);
if (slash == 0)
return "/";
return XorStr("/");
if (slash != std::string::npos)
return path.substr(0, slash);
return "";
return XorStr("");
}
static std::string getExtension(const std::string& path)
{
size_t dot = path.find_last_of(".\\/");
size_t dot = path.find_last_of(XorStr(".\\/"));
if (dot == std::string::npos || path[dot] != '.')
return "";
return XorStr("");
return path.substr(dot);
}
@ -277,7 +277,7 @@ std::vector<std::string> getSourceFiles(int argc, char** argv)
traverseDirectory(argv[i], [&](const std::string& name) {
std::string ext = getExtension(name);
if (ext == ".lua" || ext == ".luau")
if (ext == XorStr(".lua") || ext == XorStr(".lluz"))
files.push_back(name);
});
}

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <optional>

View file

@ -1,7 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lua.h"
#include "Luau/DenseHash.h"
#include "lluz/DenseHash.h"
#include <thread>
#include <atomic>
@ -24,7 +24,7 @@ struct Profiler
std::string stackScratch;
// statistics, updated by trigger
Luau::DenseHashMap<std::string, uint64_t> data{""};
lluz::DenseHashMap<std::string, uint64_t> data{""};
uint64_t gc[16] = {};
} gProfiler;
@ -112,7 +112,7 @@ void profilerStop()
void profilerDump(const char* path)
{
FILE* f = fopen(path, "wb");
FILE* f = fopen(path, XorStr("wb"));
if (!f)
{
fprintf(stderr, "Error opening profile %s\n", path);
@ -150,6 +150,6 @@ void profilerDump(const char* path)
printf(", %s %.2f%%", luaC_statename(int(i)), double(p) / double(totalgc) * 100);
}
printf("\n");
printf(XorStr("\n"));
}
}

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
struct lua_State;

View file

@ -1,20 +1,21 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Repl.h"
#include "lua.h"
#include "lualib.h"
#include "Luau/Compiler.h"
#include "Luau/BytecodeBuilder.h"
#include "Luau/Parser.h"
#include "lluz/Compiler.h"
#include "lluz/BytecodeBuilder.h"
#include "lluz/Parser.h"
#include "Coverage.h"
#include "FileUtils.h"
#include "Flags.h"
#include "Profiler.h"
#include "Coverage.h"
#include "isocline.h"
#include "..\..\..\Security\XorString.h"
#include <memory>
#ifdef _WIN32
@ -28,7 +29,7 @@
#include <locale.h>
LUAU_FASTFLAG(DebugLuauTimeTracing)
lluz_FASTFLAG(DebugLluTimeTracing)
enum class CliMode
{
@ -53,9 +54,9 @@ struct GlobalOptions
int debugLevel = 1;
} globalOptions;
static Luau::CompileOptions copts()
static lluz::CompileOptions copts()
{
Luau::CompileOptions result = {};
lluz::CompileOptions result = {};
result.optimizationLevel = globalOptions.optimizationLevel;
result.debugLevel = globalOptions.debugLevel;
result.coverageLevel = coverageActive() ? 2 : 0;
@ -71,8 +72,8 @@ static int lua_loadstring(lua_State* L)
lua_setsafeenv(L, LUA_ENVIRONINDEX, false);
std::string bytecode = Luau::compile(std::string(s, l), copts());
if (luau_load(L, chunkname, bytecode.data(), bytecode.size(), 0) == 0)
std::string bytecode = lluz::compile(std::string(s, l), copts());
if (lluz_load(L, chunkname, bytecode.data(), bytecode.size(), 0) == 0)
return 1;
lua_pushnil(L);
@ -98,23 +99,18 @@ static int lua_require(lua_State* L)
// return the module from the cache
lua_getfield(L, -1, name.c_str());
if (!lua_isnil(L, -1))
{
// L stack: _MODULES result
return finishrequire(L);
}
lua_pop(L, 1);
std::optional<std::string> source = readFile(name + ".luau");
std::optional<std::string> source = readFile(name + XorStr(".lluz"));
if (!source)
{
source = readFile(name + ".lua"); // try .lua if .luau doesn't exist
source = readFile(name + XorStr(".lua")); // try .lua if .lluz doesn't exist
if (!source)
luaL_argerrorL(L, 1, ("error loading " + name).c_str()); // if neither .luau nor .lua exist, we have an error
luaL_argerrorL(L, 1, ("error loading " + name).c_str()); // if neither .lluz nor .lua exist, we have an error
}
// module needs to run in a new thread, isolated from the rest
// note: we create ML on main thread so that it doesn't inherit environment of L
lua_State* GL = lua_mainthread(L);
lua_State* ML = lua_newthread(GL);
lua_xmove(GL, L, 1);
@ -123,8 +119,8 @@ static int lua_require(lua_State* L)
luaL_sandboxthread(ML);
// now we can compile & run module on the new thread
std::string bytecode = Luau::compile(*source, copts());
if (luau_load(ML, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) == 0)
std::string bytecode = lluz::compile(*source, copts());
if (lluz_load(ML, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) == 0)
{
if (coverageActive())
coverageTrack(ML, -1);
@ -134,32 +130,31 @@ static int lua_require(lua_State* L)
if (status == 0)
{
if (lua_gettop(ML) == 0)
lua_pushstring(ML, "module must return a value");
lua_pushstring(ML, XorStr("module must return a value"));
else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
lua_pushstring(ML, "module must return a table or function");
lua_pushstring(ML, XorStr("module must return a table or function"));
}
else if (status == LUA_YIELD)
{
lua_pushstring(ML, "module can not yield");
lua_pushstring(ML, XorStr("module can not yield"));
}
else if (!lua_isstring(ML, -1))
{
lua_pushstring(ML, "unknown error while running module");
lua_pushstring(ML, XorStr("unknown error while running module"));
}
}
// there's now a return value on top of ML; L stack: _MODULES ML
// there's now a return value on top of ML; stack of L is MODULES thread
lua_xmove(ML, L, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -4, name.c_str());
// L stack: _MODULES ML result
return finishrequire(L);
}
static int lua_collectgarbage(lua_State* L)
{
const char* option = luaL_optstring(L, 1, "collect");
const char* option = luaL_optstring(L, 1, XorStr("collect"));
if (strcmp(option, "collect") == 0)
{
@ -174,7 +169,7 @@ static int lua_collectgarbage(lua_State* L)
return 1;
}
luaL_error(L, "collectgarbage must be called with 'count' or 'collect'");
luaL_error(L, XorStr("collectgarbage must be called with 'count' or 'collect'"));
}
#ifdef CALLGRIND
@ -203,7 +198,7 @@ static int lua_callgrind(lua_State* L)
return 0;
}
luaL_error(L, "callgrind must be called with one of 'running', 'zero', 'dump'");
luaL_error(L, XorStr("callgrind must be called with one of 'running', 'zero', 'dump'"));
}
#endif
@ -212,11 +207,11 @@ void setupState(lua_State* L)
luaL_openlibs(L);
static const luaL_Reg funcs[] = {
{"loadstring", lua_loadstring},
{"require", lua_require},
{"collectgarbage", lua_collectgarbage},
{XorStr("loadstring"), lua_loadstring},
{XorStr("require"), lua_require},
{XorStr("collectgarbage"), lua_collectgarbage},
#ifdef CALLGRIND
{"callgrind", lua_callgrind},
{XorStr("callgrind"), lua_callgrind},
#endif
{NULL, NULL},
};
@ -230,9 +225,9 @@ void setupState(lua_State* L)
std::string runCode(lua_State* L, const std::string& source)
{
std::string bytecode = Luau::compile(source, copts());
std::string bytecode = lluz::compile(source, copts());
if (luau_load(L, "=stdin", bytecode.data(), bytecode.size(), 0) != 0)
if (lluz_load(L, XorStr("=stdin"), bytecode.data(), bytecode.size(), 0) != 0)
{
size_t len;
const char* msg = lua_tolstring(L, -1, &len);
@ -257,13 +252,13 @@ std::string runCode(lua_State* L, const std::string& source)
if (n)
{
luaL_checkstack(T, LUA_MINSTACK, "too many results to print");
lua_getglobal(T, "_PRETTYPRINT");
luaL_checkstack(T, LUA_MINSTACK, XorStr("too many results to print"));
lua_getglobal(T, XorStr("_PRETTYPRINT"));
// If _PRETTYPRINT is nil, then use the standard print function instead
if (lua_isnil(T, -1))
{
lua_pop(T, 1);
lua_getglobal(T, "print");
lua_getglobal(T, XorStr("print"));
}
lua_insert(T, 1);
lua_pcall(T, n, 0, 0);
@ -275,14 +270,14 @@ std::string runCode(lua_State* L, const std::string& source)
if (status == LUA_YIELD)
{
error = "thread yielded unexpectedly";
error = XorStr("thread yielded unexpectedly");
}
else if (const char* str = lua_tostring(T, -1))
{
error = str;
}
error += "\nstack backtrace:\n";
error += XorStr("\nstack backtrace:\n");
error += lua_debugtrace(T);
fprintf(stdout, "%s", error.c_str());
@ -296,7 +291,7 @@ std::string runCode(lua_State* L, const std::string& source)
// if it exists. Returns true iff __index exists.
static bool tryReplaceTopWithIndex(lua_State* L)
{
if (luaL_getmetafield(L, -1, "__index"))
if (luaL_getmetafield(L, -1, XorStr("__index")))
{
// Remove the table leaving __index on the top of stack
lua_remove(L, -2);
@ -326,7 +321,7 @@ static void safeGetTable(lua_State* L, int tableIndex)
else
{
lua_pop(L, 1); // Pop the nil result
if (!luaL_getmetafield(L, -1, "__index"))
if (!luaL_getmetafield(L, -1, XorStr("__index")))
{
lua_pushnil(L);
break;
@ -371,7 +366,7 @@ static void completePartialMatches(lua_State* L, bool completeOnlyFunctions, con
// If the last separator was a ':' (i.e. a method call) then only functions should be completed.
bool requiredValueType = (!completeOnlyFunctions || valueType == LUA_TFUNCTION);
if (!key.empty() && requiredValueType && Luau::startsWith(key, prefix))
if (!key.empty() && requiredValueType && lluz::startsWith(key, prefix))
{
std::string completedComponent(key.substr(prefix.size()));
std::string completion(editBuffer + completedComponent);
@ -404,7 +399,7 @@ static void completeIndexer(lua_State* L, const std::string& editBuffer, const A
for (;;)
{
size_t sep = lookup.find_first_of(".:");
size_t sep = lookup.find_first_of(XorStr(".:"));
std::string_view prefix = lookup.substr(0, sep);
if (sep == std::string_view::npos)
@ -464,11 +459,11 @@ static void loadHistory(const char* name)
{
std::string path;
if (const char* home = getenv("HOME"))
if (const char* home = getenv(XorStr("HOME")))
{
path = joinPaths(home, name);
}
else if (const char* userProfile = getenv("USERPROFILE"))
else if (const char* userProfile = getenv(XorStr("USERPROFILE")))
{
path = joinPaths(userProfile, name);
}
@ -482,16 +477,16 @@ static void runReplImpl(lua_State* L)
ic_set_default_completer(completeRepl, L);
// Reset the locale to C
setlocale(LC_ALL, "C");
setlocale(LC_ALL, XorStr("C"));
// Make brace matching easier to see
ic_style_def("ic-bracematch", "teal");
ic_style_def(XorStr("ic-bracematch"), XorStr("teal"));
// Prevent auto insertion of braces
ic_enable_brace_insertion(false);
// Loads history from the given file; isocline automatically saves the history on process exit
loadHistory(".luau_history");
loadHistory(XorStr(".lluz_history"));
std::string buffer;
@ -502,7 +497,7 @@ static void runReplImpl(lua_State* L)
if (!line)
break;
if (buffer.empty() && runCode(L, std::string("return ") + line.get()) == std::string())
if (buffer.empty() && runCode(L, std::string(XorStr("return ")) + line.get()) == std::string())
{
ic_history_add(line.get());
continue;
@ -514,7 +509,7 @@ static void runReplImpl(lua_State* L)
std::string error = runCode(L, buffer);
if (error.length() >= 5 && error.compare(error.length() - 5, 5, "<eof>") == 0)
if (error.length() >= 5 && error.compare(error.length() - 5, 5, XorStr("<eof>")) == 0)
{
continue;
}
@ -545,7 +540,7 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
std::optional<std::string> source = readFile(name);
if (!source)
{
fprintf(stderr, "Error opening %s\n", name);
fprintf(stderr, XorStr("Error opening %s\n"), name);
return false;
}
@ -557,10 +552,10 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
std::string chunkname = "=" + std::string(name);
std::string bytecode = Luau::compile(*source, copts());
std::string bytecode = lluz::compile(*source, copts());
int status = 0;
if (luau_load(L, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) == 0)
if (lluz_load(L, chunkname.c_str(), bytecode.data(), bytecode.size(), 0) == 0)
{
if (coverageActive())
coverageTrack(L, -1);
@ -578,14 +573,14 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
if (status == LUA_YIELD)
{
error = "thread yielded unexpectedly";
error = XorStr("thread yielded unexpectedly");
}
else if (const char* str = lua_tostring(L, -1))
{
error = str;
}
error += "\nstacktrace:\n";
error += XorStr("\nstacktrace:\n");
error += lua_debugtrace(L);
fprintf(stderr, "%s", error.c_str());
@ -599,19 +594,19 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
return status == 0;
}
static void report(const char* name, const Luau::Location& location, const char* type, const char* message)
static void report(const char* name, const lluz::Location& location, const char* type, const char* message)
{
fprintf(stderr, "%s(%d,%d): %s: %s\n", name, location.begin.line + 1, location.begin.column + 1, type, message);
fprintf(stderr, XorStr("%s(%d,%d): %s: %s\n"), name, location.begin.line + 1, location.begin.column + 1, type, message);
}
static void reportError(const char* name, const Luau::ParseError& error)
static void reportError(const char* name, const lluz::ParseError& error)
{
report(name, error.getLocation(), "SyntaxError", error.what());
report(name, error.getLocation(), XorStr("SyntaxError"), error.what());
}
static void reportError(const char* name, const Luau::CompileError& error)
static void reportError(const char* name, const lluz::CompileError& error)
{
report(name, error.getLocation(), "CompileError", error.what());
report(name, error.getLocation(), XorStr("CompileError"), error.what());
}
static bool compileFile(const char* name, CompileFormat format)
@ -619,22 +614,22 @@ static bool compileFile(const char* name, CompileFormat format)
std::optional<std::string> source = readFile(name);
if (!source)
{
fprintf(stderr, "Error opening %s\n", name);
fprintf(stderr, XorStr("Error opening %s\n"), name);
return false;
}
try
{
Luau::BytecodeBuilder bcb;
lluz::BytecodeBuilder bcb;
if (format == CompileFormat::Text)
{
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
Luau::BytecodeBuilder::Dump_Remarks);
bcb.setDumpFlags(lluz::BytecodeBuilder::Dump_Code | lluz::BytecodeBuilder::Dump_Source | lluz::BytecodeBuilder::Dump_Locals |
lluz::BytecodeBuilder::Dump_Remarks);
bcb.setDumpSource(*source);
}
Luau::compileOrThrow(bcb, *source, copts());
lluz::compileOrThrow(bcb, *source, copts());
switch (format)
{
@ -650,13 +645,13 @@ static bool compileFile(const char* name, CompileFormat format)
return true;
}
catch (Luau::ParseErrors& e)
catch (lluz::ParseErrors& e)
{
for (auto& error : e.getErrors())
reportError(name, error);
return false;
}
catch (Luau::CompileError& e)
catch (lluz::CompileError& e)
{
reportError(name, e);
return false;
@ -665,35 +660,84 @@ static bool compileFile(const char* name, CompileFormat format)
static void displayHelp(const char* argv0)
{
printf("Usage: %s [--mode] [options] [file list]\n", argv0);
printf("\n");
printf("When mode and file list are omitted, an interactive REPL is started instead.\n");
printf("\n");
printf("Available modes:\n");
printf(" omitted: compile and run input files one by one\n");
printf(" --compile[=format]: compile input files and output resulting formatted bytecode (binary or text)\n");
printf("\n");
printf("Available options:\n");
printf(" --coverage: collect code coverage while running the code and output results to coverage.out\n");
printf(" -h, --help: Display this usage message.\n");
printf(" -i, --interactive: Run an interactive REPL after executing the last script specified.\n");
printf(" -O<n>: compile with optimization level n (default 1, n should be between 0 and 2).\n");
printf(" -g<n>: compile with debug level n (default 1, n should be between 0 and 2).\n");
printf(" --profile[=N]: profile the code using N Hz sampling (default 10000) and output results to profile.out\n");
printf(" --timetrace: record compiler time tracing information into trace.json\n");
printf(XorStr("Usage: %s [--mode] [options] [file list]\n"), argv0);
printf(XorStr("\n"));
printf(XorStr("When mode and file list are omitted, an interactive REPL is started instead.\n"));
printf(XorStr("\n"));
printf(XorStr("Available modes:\n"));
printf(XorStr(" omitted: compile and run input files one by one\n"));
printf(XorStr(" --compile[=format]: compile input files and output resulting formatted bytecode (binary or text)\n"));
printf(XorStr("\n"));
printf(XorStr("Available options:\n"));
printf(XorStr(" --coverage: collect code coverage while running the code and output results to coverage.out\n"));
printf(XorStr(" -h, --help: Display this usage message.\n"));
printf(XorStr(" -i, --interactive: Run an interactive REPL after executing the last script specified.\n"));
printf(XorStr(" -O<n>: compile with optimization level n (default 1, n should be between 0 and 2).\n"));
printf(XorStr(" -g<n>: compile with debug level n (default 1, n should be between 0 and 2).\n"));
printf(XorStr(" --profile[=N]: profile the code using N Hz sampling (default 10000) and output results to profile.out\n"));
printf(XorStr(" --timetrace: record compiler time tracing information into trace.json\n"));
}
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
{
printf("%s(%d): ASSERTION FAILED: %s\n", file, line, expr);
printf(XorStr("%s(%d): ASSERTION FAILED: %s\n"), file, line, expr);
return 1;
}
static void setlluzFlags(bool state)
{
for (lluz::FValue<bool>* flag = lluz::FValue<bool>::list; flag; flag = flag->next)
{
if (strncmp(flag->name, XorStr("lluz"), 4) == 0)
flag->value = state;
}
}
static void setFlag(std::string_view name, bool state)
{
for (lluz::FValue<bool>* flag = lluz::FValue<bool>::list; flag; flag = flag->next)
{
if (name == flag->name)
{
flag->value = state;
return;
}
}
fprintf(stderr, XorStr("Warning: --fflag unrecognized flag '%.*s'.\n\n"), int(name.length()), name.data());
}
static void applyFlagKeyValue(std::string_view element)
{
if (size_t separator = element.find('='); separator != std::string_view::npos)
{
std::string_view key = element.substr(0, separator);
std::string_view value = element.substr(separator + 1);
if (value == XorStr("true"))
setFlag(key, true);
else if (value == XorStr("false"))
setFlag(key, false);
else
fprintf(stderr, XorStr("Warning: --fflag unrecognized value '%.*s' for flag '%.*s'.\n\n"), int(value.length()), value.data(), int(key.length()),
key.data());
}
else
{
if (element == XorStr("true"))
setlluzFlags(true);
else if (element == XorStr("false"))
setlluzFlags(false);
else
setFlag(element, true);
}
}
int replMain(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
lluz::assertHandler() = assertionHandler;
setLuauFlagsDefault();
setlluzFlags(true);
CliMode mode = CliMode::Unknown;
CompileFormat compileFormat{};
@ -703,50 +747,50 @@ int replMain(int argc, char** argv)
// Set the mode if the user has explicitly specified one.
int argStart = 1;
if (argc >= 2 && strncmp(argv[1], "--compile", strlen("--compile")) == 0)
if (argc >= 2 && strncmp(argv[1], XorStr("--compile"), strlen(XorStr("--compile"))) == 0)
{
argStart++;
mode = CliMode::Compile;
if (strcmp(argv[1], "--compile") == 0)
if (strcmp(argv[1], XorStr("--compile")) == 0)
{
compileFormat = CompileFormat::Text;
}
else if (strcmp(argv[1], "--compile=binary") == 0)
else if (strcmp(argv[1], XorStr("--compile=binary")) == 0)
{
compileFormat = CompileFormat::Binary;
}
else if (strcmp(argv[1], "--compile=text") == 0)
else if (strcmp(argv[1], XorStr("--compile=text")) == 0)
{
compileFormat = CompileFormat::Text;
}
else if (strcmp(argv[1], "--compile=null") == 0)
else if (strcmp(argv[1], XorStr("--compile=null")) == 0)
{
compileFormat = CompileFormat::Null;
}
else
{
fprintf(stderr, "Error: Unrecognized value for '--compile' specified.\n");
fprintf(stderr, XorStr("Error: Unrecognized value for '--compile' specified.\n"));
return 1;
}
}
for (int i = argStart; i < argc; i++)
{
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
if (strcmp(argv[i], XorStr("-h")) == 0 || strcmp(argv[i], XorStr("--help")) == 0)
{
displayHelp(argv[0]);
return 0;
}
else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0)
else if (strcmp(argv[i], XorStr("-i")) == 0 || strcmp(argv[i], XorStr("--interactive")) == 0)
{
interactive = true;
}
else if (strncmp(argv[i], "-O", 2) == 0)
else if (strncmp(argv[i], XorStr("-O"), 2) == 0)
{
int level = atoi(argv[i] + 2);
if (level < 0 || level > 2)
{
fprintf(stderr, "Error: Optimization level must be between 0 and 2 inclusive.\n");
fprintf(stderr, XorStr("Error: Optimization level must be between 0 and 2 inclusive.\n"));
return 1;
}
globalOptions.optimizationLevel = level;
@ -756,47 +800,56 @@ int replMain(int argc, char** argv)
int level = atoi(argv[i] + 2);
if (level < 0 || level > 2)
{
fprintf(stderr, "Error: Debug level must be between 0 and 2 inclusive.\n");
fprintf(stderr, XorStr("Error: Debug level must be between 0 and 2 inclusive.\n"));
return 1;
}
globalOptions.debugLevel = level;
}
else if (strcmp(argv[i], "--profile") == 0)
else if (strcmp(argv[i], XorStr("--profile")) == 0)
{
profile = 10000; // default to 10 KHz
}
else if (strncmp(argv[i], "--profile=", 10) == 0)
else if (strncmp(argv[i], XorStr("--profile="), 10) == 0)
{
profile = atoi(argv[i] + 10);
}
else if (strcmp(argv[i], "--coverage") == 0)
else if (strcmp(argv[i], XorStr("--coverage")) == 0)
{
coverage = true;
}
else if (strcmp(argv[i], "--timetrace") == 0)
else if (strcmp(argv[i], XorStr("--timetrace")) == 0)
{
FFlag::DebugLuauTimeTracing.value = true;
FFlag::DebugLluTimeTracing.value = true;
#if !defined(lluz_ENABLE_TIME_TRACE)
printf(XorStr("To run with --timetrace, lluz has to be built with lluz_ENABLE_TIME_TRACE enabled\n"));
return 1;
#endif
}
else if (strncmp(argv[i], "--fflags=", 9) == 0)
else if (strncmp(argv[i], XorStr("--fflags="), 9) == 0)
{
setLuauFlags(argv[i] + 9);
std::string_view list = argv[i] + 9;
while (!list.empty())
{
size_t ending = list.find(XorStr(","));
applyFlagKeyValue(list.substr(0, ending));
if (ending != std::string_view::npos)
list.remove_prefix(ending + 1);
else
break;
}
}
else if (argv[i][0] == '-')
{
fprintf(stderr, "Error: Unrecognized option '%s'.\n\n", argv[i]);
fprintf(stderr, XorStr("Error: Unrecognized option '%s'.\n\n"), argv[i]);
displayHelp(argv[0]);
return 1;
}
}
#if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing)
{
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
}
#endif
const std::vector<std::string> files = getSourceFiles(argc, argv);
if (mode == CliMode::Unknown)
{
@ -848,17 +901,17 @@ int replMain(int argc, char** argv)
if (profile)
{
profilerStop();
profilerDump("profile.out");
profilerDump(XorStr("profile.out"));
}
if (coverage)
coverageDump("coverage.out");
coverageDump(XorStr("coverage.out"));
return failed ? 1 : 0;
}
case CliMode::Unknown:
default:
LUAU_ASSERT(!"Unhandled cli mode.");
lluz_ASSERT(!XorStr("Unhandled cli mode."));
return 1;
}
}

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lua.h"

View file

@ -1,6 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Repl.h"
int main(int argc, char** argv)
{
return replMain(argc, argv);

View file

@ -1,9 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lua.h"
#include "lualib.h"
#include "luacode.h"
#include "Luau/Common.h"
#include "lluz/Common.h"
#include <string>
@ -19,8 +19,8 @@ static void setupState(lua_State* L)
static std::string runCode(lua_State* L, const std::string& source)
{
size_t bytecodeSize = 0;
char* bytecode = luau_compile(source.data(), source.length(), nullptr, &bytecodeSize);
int result = luau_load(L, "=stdin", bytecode, bytecodeSize, 0);
char* bytecode = lluz_compile(source.data(), source.length(), nullptr, &bytecodeSize);
int result = lluz_load(L, "=stdin", bytecode, bytecodeSize, 0);
free(bytecode);
if (result != 0)
@ -48,8 +48,8 @@ static std::string runCode(lua_State* L, const std::string& source)
if (n)
{
luaL_checkstack(T, LUA_MINSTACK, "too many results to print");
lua_getglobal(T, "print");
luaL_checkstack(T, LUA_MINSTACK, XorStr("too many results to print"));
lua_getglobal(T, XorStr("print"));
lua_insert(T, 1);
lua_pcall(T, n, 0, 0);
}
@ -90,8 +90,8 @@ static std::string runCode(lua_State* L, const std::string& source)
extern "C" const char* executeScript(const char* source)
{
// setup flags
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
for (lluz::FValue<bool>* flag = lluz::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "lluz", 4) == 0)
flag->value = true;
// create new state

View file

@ -0,0 +1,504 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
// clang-format off
// This header contains the bytecode definition for lluz interpreter
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
// # Bytecode definitions
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
//
// Instruction word can be encoded using one of the following encodings:
// ABC - least-significant byte for the opcode, followed by three bytes, A, B and C; each byte declares a register index, small index into some other table or an unsigned integral value
// AD - least-significant byte for the opcode, followed by A byte, followed by D half-word (16-bit integer). D is a signed integer that commonly specifies constant table index or jump offset
// E - least-significant byte for the opcode, followed by E (24-bit integer). E is a signed integer that commonly specifies a jump offset
//
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
// # Bytecode indices
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
//
// Registers: 0-254. Registers refer to the values on the function's stack frame, including arguments.
// Upvalues: 0-254. Upvalues refer to the values stored in the closure object.
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more.
// # Bytecode versions
// Bytecode serialized format embeds a version number, that dictates both the serialized form as well as the allowed instructions. As long as the bytecode version falls into supported
// range (indicated by LBC_BYTECODE_MIN / LBC_BYTECODE_MAX) and was produced by lluz compiler, it should load and execute correctly.
//
// Note that lluz runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected
// that lluz users can recompile bytecode from source on lluz version upgrades if necessary.
// Bytecode opcode, part of the instruction header
enum lluzOpcode
{
// NOP: noop
LOP_NOP,
// BREAK: debugger break
LOP_BREAK,
// LOADNIL: sets register to nil
// A: target register
LOP_LOADNIL,
// LOADB: sets register to boolean and jumps to a given short offset (used to compile comparison results into a boolean)
// A: target register
// B: value (0/1)
// C: jump offset
LOP_LOADB,
// LOADN: sets register to a number literal
// A: target register
// D: value (-32768..32767)
LOP_LOADN,
// LOADK: sets register to an entry from the constant table from the proto (number/string)
// A: target register
// D: constant table index (0..32767)
LOP_LOADK,
// MOVE: move (copy) value from one register to another
// A: target register
// B: source register
LOP_MOVE,
// GETGLOBAL: load value from global table using constant string as a key
// A: target register
// C: predicted slot index (based on hash)
// AUX: constant table index
LOP_GETGLOBAL,
// SETGLOBAL: set value in global table using constant string as a key
// A: source register
// C: predicted slot index (based on hash)
// AUX: constant table index
LOP_SETGLOBAL,
// GETUPVAL: load upvalue from the upvalue table for the current function
// A: target register
// B: upvalue index (0..255)
LOP_GETUPVAL,
// SETUPVAL: store value into the upvalue table for the current function
// A: target register
// B: upvalue index (0..255)
LOP_SETUPVAL,
// CLOSEUPVALS: close (migrate to heap) all upvalues that were captured for registers >= target
// A: target register
LOP_CLOSEUPVALS,
// GETIMPORT: load imported global table global from the constant table
// A: target register
// D: constant table index (0..32767); we assume that imports are loaded into the constant table
// AUX: 3 10-bit indices of constant strings that, combined, constitute an import path; length of the path is set by the top 2 bits (1,2,3)
LOP_GETIMPORT,
// GETTABLE: load value from table into target register using key from register
// A: target register
// B: table register
// C: index register
LOP_GETTABLE,
// SETTABLE: store source register into table using key from register
// A: source register
// B: table register
// C: index register
LOP_SETTABLE,
// GETTABLEKS: load value from table into target register using constant string as a key
// A: target register
// B: table register
// C: predicted slot index (based on hash)
// AUX: constant table index
LOP_GETTABLEKS,
// SETTABLEKS: store source register into table using constant string as a key
// A: source register
// B: table register
// C: predicted slot index (based on hash)
// AUX: constant table index
LOP_SETTABLEKS,
// GETTABLEN: load value from table into target register using small integer index as a key
// A: target register
// B: table register
// C: index-1 (index is 1..256)
LOP_GETTABLEN,
// SETTABLEN: store source register into table using small integer index as a key
// A: source register
// B: table register
// C: index-1 (index is 1..256)
LOP_SETTABLEN,
// NEWCLOSURE: create closure from a child proto; followed by a CAPTURE instruction for each upvalue
// A: target register
// D: child proto index (0..32767)
LOP_NEWCLOSURE,
// NAMECALL: prepare to call specified method by name by loading function from source register using constant index into target register and copying source register into target register + 1
// A: target register
// B: source register
// C: predicted slot index (based on hash)
// AUX: constant table index
// Note that this instruction must be followed directly by CALL; it prepares the arguments
// This instruction is roughly equivalent to GETTABLEKS + MOVE pair, but we need a special instruction to support custom __namecall metamethod
LOP_NAMECALL,
// CALL: call specified function
// A: register where the function object lives, followed by arguments; results are placed starting from the same register
// B: argument count + 1, or 0 to preserve all arguments up to top (MULTRET)
// C: result count + 1, or 0 to preserve all values and adjust top (MULTRET)
LOP_CALL,
// RETURN: returns specified values from the function
// A: register where the returned values start
// B: number of returned values + 1, or 0 to return all values up to top (MULTRET)
LOP_RETURN,
// JUMP: jumps to target offset
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
LOP_JUMP,
// JUMPBACK: jumps to target offset; this is equivalent to JUMP but is used as a safepoint to be able to interrupt while/repeat loops
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
LOP_JUMPBACK,
// JUMPIF: jumps to target offset if register is not nil/false
// A: source register
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
LOP_JUMPIF,
// JUMPIFNOT: jumps to target offset if register is nil/false
// A: source register
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
LOP_JUMPIFNOT,
// JUMPIFEQ, JUMPIFLE, JUMPIFLT, JUMPIFNOTEQ, JUMPIFNOTLE, JUMPIFNOTLT: jumps to target offset if the comparison is true (or false, for NOT variants)
// A: source register 1
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
// AUX: source register 2
LOP_JUMPIFEQ,
LOP_JUMPIFLE,
LOP_JUMPIFLT,
LOP_JUMPIFNOTEQ,
LOP_JUMPIFNOTLE,
LOP_JUMPIFNOTLT,
// ADD, SUB, MUL, DIV, MOD, POW: compute arithmetic operation between two source registers and put the result into target register
// A: target register
// B: source register 1
// C: source register 2
LOP_ADD,
LOP_SUB,
LOP_MUL,
LOP_DIV,
LOP_MOD,
LOP_POW,
// ADDK, SUBK, MULK, DIVK, MODK, POWK: compute arithmetic operation between the source register and a constant and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255)
LOP_ADDK,
LOP_SUBK,
LOP_MULK,
LOP_DIVK,
LOP_MODK,
LOP_POWK,
// AND, OR: perform `and` or `or` operation (selecting first or second register based on whether the first one is truthy) and put the result into target register
// A: target register
// B: source register 1
// C: source register 2
LOP_AND,
LOP_OR,
// ANDK, ORK: perform `and` or `or` operation (selecting source register or constant based on whether the source register is truthy) and put the result into target register
// A: target register
// B: source register
// C: constant table index (0..255)
LOP_ANDK,
LOP_ORK,
// CONCAT: concatenate all strings between B and C (inclusive) and put the result into A
// A: target register
// B: source register start
// C: source register end
LOP_CONCAT,
// NOT, MINUS, LENGTH: compute unary operation for source register and put the result into target register
// A: target register
// B: source register
LOP_NOT,
LOP_MINUS,
LOP_LENGTH,
// NEWTABLE: create table in target register
// A: target register
// B: table size, stored as 0 for v=0 and ceil(log2(v))+1 for v!=0
// AUX: array size
LOP_NEWTABLE,
// DUPTABLE: duplicate table using the constant table template to target register
// A: target register
// D: constant table index (0..32767)
LOP_DUPTABLE,
// SETLIST: set a list of values to table in target register
// A: target register
// B: source register start
// C: value count + 1, or 0 to use all values up to top (MULTRET)
// AUX: table index to start from
LOP_SETLIST,
// FORNPREP: prepare a numeric for loop, jump over the loop if first iteration doesn't need to run
// A: target register; numeric for loops assume a register layout [limit, step, index, variable]
// D: jump offset (-32768..32767)
// limit/step are immutable, index isn't visible to user code since it's copied into variable
LOP_FORNPREP,
// FORNLOOP: adjust loop variables for one iteration, jump back to the loop header if loop needs to continue
// A: target register; see FORNPREP for register layout
// D: jump offset (-32768..32767)
LOP_FORNLOOP,
// FORGLOOP: adjust loop variables for one iteration of a generic for loop, jump back to the loop header if loop needs to continue
// A: target register; generic for loops assume a register layout [generator, state, index, variables...]
// D: jump offset (-32768..32767)
// AUX: variable count (1..255) in the low 8 bits, high bit indicates whether to use ipairs-style traversal in the fast path
// loop variables are adjusted by calling generator(state, index) and expecting it to return a tuple that's copied to the user variables
// the first variable is then copied into index; generator/state are immutable, index isn't visible to user code
LOP_FORGLOOP,
// FORGPREP_INEXT/FORGLOOP_INEXT: FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_inext
// FORGPREP_INEXT prepares the index variable and jumps to FORGLOOP_INEXT
// FORGLOOP_INEXT has identical encoding and semantics to FORGLOOP (except for AUX encoding)
LOP_FORGPREP_INEXT,
LOP_FORGLOOP_INEXT,
// FORGPREP_NEXT/FORGLOOP_NEXT: FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_next
// FORGPREP_NEXT prepares the index variable and jumps to FORGLOOP_NEXT
// FORGLOOP_NEXT has identical encoding and semantics to FORGLOOP (except for AUX encoding)
LOP_FORGPREP_NEXT,
LOP_FORGLOOP_NEXT,
// GETVARARGS: copy variables into the target register from vararg storage for current function
// A: target register
// B: variable count + 1, or 0 to copy all variables and adjust top (MULTRET)
LOP_GETVARARGS,
// DUPCLOSURE: create closure from a pre-created function object (reusing it unless environments diverge)
// A: target register
// D: constant table index (0..32767)
LOP_DUPCLOSURE,
// PREPVARARGS: prepare stack for variadic functions so that GETVARARGS works correctly
// A: number of fixed arguments
LOP_PREPVARARGS,
// LOADKX: sets register to an entry from the constant table from the proto (number/string)
// A: target register
// AUX: constant table index
LOP_LOADKX,
// JUMPX: jumps to the target offset; like JUMPBACK, supports interruption
// E: jump offset (-2^23..2^23; 0 means "next instruction" aka "don't jump")
LOP_JUMPX,
// FASTCALL: perform a fast call of a built-in function
// A: builtin function id (see lluzBuiltinFunction)
// C: jump offset to get to following CALL
// FASTCALL is followed by one of (GETIMPORT, MOVE, GETUPVAL) instructions and by CALL instruction
// This is necessary so that if FASTCALL can't perform the call inline, it can continue normal execution
// If FASTCALL *can* perform the call, it jumps over the instructions *and* over the next CALL
// Note that FASTCALL will read the actual call arguments, such as argument/result registers and counts, from the CALL instruction
LOP_FASTCALL,
// COVERAGE: update coverage information stored in the instruction
// E: hit count for the instruction (0..2^23-1)
// The hit count is incremented by VM every time the instruction is executed, and saturates at 2^23-1
LOP_COVERAGE,
// CAPTURE: capture a local or an upvalue as an upvalue into a newly created closure; only valid after NEWCLOSURE
// A: capture type, see lluzCaptureType
// B: source register (for VAL/REF) or upvalue index (for UPVAL/UPREF)
LOP_CAPTURE,
// JUMPIFEQK, JUMPIFNOTEQK: jumps to target offset if the comparison with constant is true (or false, for NOT variants)
// A: source register 1
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
// AUX: constant table index
LOP_JUMPIFEQK,
LOP_JUMPIFNOTEQK,
// FASTCALL1: perform a fast call of a built-in function using 1 register argument
// A: builtin function id (see lluzBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
LOP_FASTCALL1,
// FASTCALL2: perform a fast call of a built-in function using 2 register arguments
// A: builtin function id (see lluzBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
// AUX: source register 2 in least-significant byte
LOP_FASTCALL2,
// FASTCALL2K: perform a fast call of a built-in function using 1 register argument and 1 constant argument
// A: builtin function id (see lluzBuiltinFunction)
// B: source argument register
// C: jump offset to get to following CALL
// AUX: constant index
LOP_FASTCALL2K,
// FORGPREP: prepare loop variables for a generic for loop, jump to the loop backedge unconditionally
// A: target register; generic for loops assume a register layout [generator, state, index, variables...]
// D: jump offset (-32768..32767)
LOP_FORGPREP,
// Enum entry for number of opcodes, not a valid opcode by itself!
LOP__COUNT
};
// Bytecode instruction header: it's always a 32-bit integer, with low byte (first byte in little endian) containing the opcode
// Some instruction types require more data and have more 32-bit integers following the header
#define lluz_INSN_OP(insn) ((insn) & 0xff)
// ABC encoding: three 8-bit values, containing registers or small numbers
#define lluz_INSN_A(insn) (((insn) >> 8) & 0xff)
#define lluz_INSN_B(insn) (((insn) >> 16) & 0xff)
#define lluz_INSN_C(insn) (((insn) >> 24) & 0xff)
// AD encoding: one 8-bit value, one signed 16-bit value
#define lluz_INSN_D(insn) (int32_t(insn) >> 16)
// E encoding: one signed 24-bit value
#define lluz_INSN_E(insn) (int32_t(insn) >> 8)
// Bytecode tags, used internally for bytecode encoded as a string
enum lluzBytecodeTag
{
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
LBC_VERSION_MIN = 2,
LBC_VERSION_MAX = 2,
LBC_VERSION_TARGET = 2,
// Types of constant table entries
LBC_CONSTANT_NIL = 0,
LBC_CONSTANT_BOOLEAN,
LBC_CONSTANT_NUMBER,
LBC_CONSTANT_STRING,
LBC_CONSTANT_IMPORT,
LBC_CONSTANT_TABLE,
LBC_CONSTANT_CLOSURE,
};
// Builtin function ids, used in LOP_FASTCALL
enum lluzBuiltinFunction
{
LBF_NONE = 0,
// assert()
LBF_ASSERT,
// math.
LBF_MATH_ABS,
LBF_MATH_ACOS,
LBF_MATH_ASIN,
LBF_MATH_ATAN2,
LBF_MATH_ATAN,
LBF_MATH_CEIL,
LBF_MATH_COSH,
LBF_MATH_COS,
LBF_MATH_DEG,
LBF_MATH_EXP,
LBF_MATH_FLOOR,
LBF_MATH_FMOD,
LBF_MATH_FREXP,
LBF_MATH_LDEXP,
LBF_MATH_LOG10,
LBF_MATH_LOG,
LBF_MATH_MAX,
LBF_MATH_MIN,
LBF_MATH_MODF,
LBF_MATH_POW,
LBF_MATH_RAD,
LBF_MATH_SINH,
LBF_MATH_SIN,
LBF_MATH_SQRT,
LBF_MATH_TANH,
LBF_MATH_TAN,
// bit32.
LBF_BIT32_ARSHIFT,
LBF_BIT32_BAND,
LBF_BIT32_BNOT,
LBF_BIT32_BOR,
LBF_BIT32_BXOR,
LBF_BIT32_BTEST,
LBF_BIT32_EXTRACT,
LBF_BIT32_LROTATE,
LBF_BIT32_LSHIFT,
LBF_BIT32_REPLACE,
LBF_BIT32_RROTATE,
LBF_BIT32_RSHIFT,
// type()
LBF_TYPE,
// string.
LBF_STRING_BYTE,
LBF_STRING_CHAR,
LBF_STRING_LEN,
// typeof()
LBF_TYPEOF,
// string.
LBF_STRING_SUB,
// math.
LBF_MATH_CLAMP,
LBF_MATH_SIGN,
LBF_MATH_ROUND,
// raw*
LBF_RAWSET,
LBF_RAWGET,
LBF_RAWEQUAL,
// table.
LBF_TABLE_INSERT,
LBF_TABLE_UNPACK,
// vector ctor
LBF_VECTOR,
// bit32.count
LBF_BIT32_COUNTLZ,
LBF_BIT32_COUNTRZ,
// select(_, ...)
LBF_SELECT_VARARG,
// rawlen
LBF_RAWLEN,
};
// Capture type, used in LOP_CAPTURE
enum lluzCaptureType
{
LCT_VAL = 0,
LCT_REF,
LCT_UPVAL,
};

View file

@ -0,0 +1,133 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
// Compiler codegen control macros
#ifdef _MSC_VER
#define lluz_NORETURN __declspec(noreturn)
#define lluz_NOINLINE __declspec(noinline)
#define lluz_FORCEINLINE __forceinline
#define lluz_LIKELY(x) x
#define lluz_UNLIKELY(x) x
#define lluz_UNREACHABLE() __assume(false)
#define lluz_DEBUGBREAK() __debugbreak()
#else
#define lluz_NORETURN __attribute__((__noreturn__))
#define lluz_NOINLINE __attribute__((noinline))
#define lluz_FORCEINLINE inline __attribute__((always_inline))
#define lluz_LIKELY(x) __builtin_expect(x, 1)
#define lluz_UNLIKELY(x) __builtin_expect(x, 0)
#define lluz_UNREACHABLE() __builtin_unreachable()
#define lluz_DEBUGBREAK() __builtin_trap()
#endif
namespace lluz
{
using AssertHandler = int (*)(const char* expression, const char* file, int line, const char* function);
inline AssertHandler& assertHandler()
{
static AssertHandler handler = nullptr;
return handler;
}
inline int assertCallHandler(const char* expression, const char* file, int line, const char* function)
{
if (AssertHandler handler = assertHandler())
return handler(expression, file, line, function);
return 1;
}
} // namespace lluz
#if !defined(NDEBUG) || defined(lluz_ENABLE_ASSERT)
#define lluz_ASSERT(expr) ((void)(!!(expr) || (lluz::assertCallHandler(#expr, __FILE__, __LINE__, __FUNCTION__) && (lluz_DEBUGBREAK(), 0))))
#define lluz_ASSERTENABLED
#else
#define lluz_ASSERT(expr) (void)sizeof(!!(expr))
#endif
namespace lluz
{
template<typename T>
struct FValue
{
static FValue* list;
T value;
bool dynamic;
const char* name;
FValue* next;
FValue(const char* name, T def, bool dynamic, void (*reg)(const char*, T*, bool) = nullptr)
: value(def)
, dynamic(dynamic)
, name(name)
, next(list)
{
list = this;
if (reg)
reg(name, &value, dynamic);
}
operator T() const
{
return value;
}
};
template<typename T>
FValue<T>* FValue<T>::list = nullptr;
} // namespace lluz
#define lluz_FASTFLAG(flag) \
namespace FFlag \
{ \
extern lluz::FValue<bool> flag; \
}
#define lluz_FASTFLAGVARIABLE(flag, def) \
namespace FFlag \
{ \
lluz::FValue<bool> flag(#flag, def, false, nullptr); \
}
#define lluz_FASTINT(flag) \
namespace FInt \
{ \
extern lluz::FValue<int> flag; \
}
#define lluz_FASTINTVARIABLE(flag, def) \
namespace FInt \
{ \
lluz::FValue<int> flag(#flag, def, false, nullptr); \
}
#define lluz_DYNAMIC_FASTFLAG(flag) \
namespace DFFlag \
{ \
extern lluz::FValue<bool> flag; \
}
#define lluz_DYNAMIC_FASTFLAGVARIABLE(flag, def) \
namespace DFFlag \
{ \
lluz::FValue<bool> flag(#flag, def, true, nullptr); \
}
#define lluz_DYNAMIC_FASTINT(flag) \
namespace DFInt \
{ \
extern lluz::FValue<int> flag; \
}
#define lluz_DYNAMIC_FASTINTVARIABLE(flag, def) \
namespace DFInt \
{ \
lluz::FValue<int> flag(#flag, def, true, nullptr); \
}

View file

@ -0,0 +1,262 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Bytecode.h"
#include "lluz/DenseHash.h"
#include "lluz/StringUtils.h"
#include <string>
namespace lluz
{
class BytecodeEncoder
{
public:
virtual ~BytecodeEncoder() {}
virtual uint8_t encodeOp(uint8_t op) = 0;
};
class BytecodeBuilder
{
public:
// BytecodeBuilder does *not* copy the data passed via StringRef; instead, it keeps the ref around until finalize()
// Please be careful with the lifetime of the data that's being passed because of this.
// The safe and correct pattern is to only build StringRefs out of pieces of AST (AstName or AstArray<>) that are backed by AstAllocator.
// Note that you must finalize() the builder before the Allocator backing the Ast is destroyed.
struct StringRef
{
// To construct a StringRef, use sref() from Compiler.cpp.
const char* data = nullptr;
size_t length = 0;
bool operator==(const StringRef& other) const;
};
struct TableShape
{
static const unsigned int kMaxLength = 32;
int32_t keys[kMaxLength];
unsigned int length = 0;
bool operator==(const TableShape& other) const;
};
BytecodeBuilder(BytecodeEncoder* encoder = 0);
uint32_t beginFunction(uint8_t numparams, bool isvararg = false);
void endFunction(uint8_t maxstacksize, uint8_t numupvalues);
void setMainFunction(uint32_t fid);
int32_t addConstantNil();
int32_t addConstantBoolean(bool value);
int32_t addConstantNumber(double value);
int32_t addConstantString(StringRef value);
int32_t addImport(uint32_t iid);
int32_t addConstantTable(const TableShape& shape);
int32_t addConstantClosure(uint32_t fid);
int16_t addChildFunction(uint32_t fid);
void emitABC(lluzOpcode op, uint8_t a, uint8_t b, uint8_t c);
void emitAD(lluzOpcode op, uint8_t a, int16_t d);
void emitE(lluzOpcode op, int32_t e);
void emitAux(uint32_t aux);
size_t emitLabel();
[[nodiscard]] bool patchJumpD(size_t jumpLabel, size_t targetLabel);
[[nodiscard]] bool patchSkipC(size_t jumpLabel, size_t targetLabel);
void foldJumps();
void expandJumps();
void setDebugFunctionName(StringRef name);
void setDebugFunctionLineDefined(int line);
void setDebugLine(int line);
void pushDebugLocal(StringRef name, uint8_t reg, uint32_t startpc, uint32_t endpc);
void pushDebugUpval(StringRef name);
uint32_t getDebugPC() const;
void addDebugRemark(const char* format, ...) lluz_PRINTF_ATTR(2, 3);
void finalize();
enum DumpFlags
{
Dump_Code = 1 << 0,
Dump_Lines = 1 << 1,
Dump_Source = 1 << 2,
Dump_Locals = 1 << 3,
Dump_Remarks = 1 << 4,
};
void setDumpFlags(uint32_t flags)
{
dumpFlags = flags;
dumpFunctionPtr = &BytecodeBuilder::dumpCurrentFunction;
}
void setDumpSource(const std::string& source);
const std::string& getBytecode() const
{
lluz_ASSERT(!bytecode.empty()); // did you forget to call finalize?
return bytecode;
}
std::string dumpFunction(uint32_t id) const;
std::string dumpEverything() const;
static uint32_t getImportId(int32_t id0);
static uint32_t getImportId(int32_t id0, int32_t id1);
static uint32_t getImportId(int32_t id0, int32_t id1, int32_t id2);
static uint32_t getStringHash(StringRef key);
static std::string getError(const std::string& message);
static uint8_t getVersion();
private:
struct Constant
{
enum Type
{
Type_Nil,
Type_Boolean,
Type_Number,
Type_String,
Type_Import,
Type_Table,
Type_Closure,
};
Type type;
union
{
bool valueBoolean;
double valueNumber;
unsigned int valueString; // index into string table
uint32_t valueImport; // 10-10-10-2 encoded import id
uint32_t valueTable; // index into tableShapes[]
uint32_t valueClosure; // index of function in global list
};
};
struct ConstantKey
{
Constant::Type type;
// Note: this stores value* from Constant; when type is Number_Double, this stores the same bits as double does but in uint64_t.
uint64_t value;
bool operator==(const ConstantKey& key) const
{
return type == key.type && value == key.value;
}
};
struct Function
{
std::string data;
uint8_t maxstacksize = 0;
uint8_t numparams = 0;
uint8_t numupvalues = 0;
bool isvararg = false;
unsigned int debugname = 0;
int debuglinedefined = 0;
std::string dump;
std::string dumpname;
};
struct DebugLocal
{
unsigned int name;
uint8_t reg;
uint32_t startpc;
uint32_t endpc;
};
struct DebugUpval
{
unsigned int name;
};
struct Jump
{
uint32_t source;
uint32_t target;
};
struct StringRefHash
{
size_t operator()(const StringRef& v) const;
};
struct ConstantKeyHash
{
size_t operator()(const ConstantKey& key) const;
};
struct TableShapeHash
{
size_t operator()(const TableShape& v) const;
};
std::vector<Function> functions;
uint32_t currentFunction = ~0u;
uint32_t mainFunction = ~0u;
std::vector<uint32_t> insns;
std::vector<int> lines;
std::vector<Constant> constants;
std::vector<uint32_t> protos;
std::vector<Jump> jumps;
std::vector<TableShape> tableShapes;
bool hasLongJumps = false;
DenseHashMap<ConstantKey, int32_t, ConstantKeyHash> constantMap;
DenseHashMap<TableShape, int32_t, TableShapeHash> tableShapeMap;
DenseHashMap<uint32_t, int16_t> protoMap;
int debugLine = 0;
std::vector<DebugLocal> debugLocals;
std::vector<DebugUpval> debugUpvals;
DenseHashMap<StringRef, unsigned int, StringRefHash> stringTable;
std::vector<std::pair<uint32_t, uint32_t>> debugRemarks;
std::string debugRemarkBuffer;
BytecodeEncoder* encoder = nullptr;
std::string bytecode;
uint32_t dumpFlags = 0;
std::vector<std::string> dumpSource;
std::string (BytecodeBuilder::*dumpFunctionPtr)() const = nullptr;
void validate() const;
std::string dumpCurrentFunction() const;
void dumpInstruction(const uint32_t* opcode, std::string& output, int targetLabel) const;
void writeFunction(std::string& ss, uint32_t id) const;
void writeLineInfo(std::string& ss) const;
void writeStringTable(std::string& ss) const;
int32_t addConstant(const ConstantKey& key, const Constant& value);
unsigned int addStringTableEntry(StringRef value);
};
} // namespace lluz

View file

@ -0,0 +1,68 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/ParseOptions.h"
#include "lluz/Location.h"
#include "lluz/StringUtils.h"
#include "lluz/Common.h"
namespace lluz
{
class AstStatBlock;
class AstNameTable;
class BytecodeBuilder;
class BytecodeEncoder;
// Note: this structure is duplicated in luacode.h, don't forget to change these in sync!
struct CompileOptions
{
// 0 - no optimization
// 1 - baseline optimization level that doesn't prevent debuggability
// 2 - includes optimizations that harm debuggability such as inlining
int optimizationLevel = 1;
// 0 - no debugging support
// 1 - line info & function names only; sufficient for backtraces
// 2 - full debug info with local & upvalue names; necessary for debugger
int debugLevel = 1;
// 0 - no code coverage support
// 1 - statement coverage
// 2 - statement and expression coverage (verbose)
int coverageLevel = 0;
// global builtin to construct vectors; disabled by default
const char* vectorLib = nullptr;
const char* vectorCtor = nullptr;
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these
const char** mutableGlobals = nullptr;
};
class CompileError : public std::exception
{
public:
CompileError(const Location& location, const std::string& message);
virtual ~CompileError() throw();
virtual const char* what() const throw();
const Location& getLocation() const;
static lluz_NORETURN void raise(const Location& location, const char* format, ...) lluz_PRINTF_ATTR(2, 3);
private:
Location location;
std::string message;
};
// compiles bytecode into bytecode builder using either a pre-parsed AST or parsing it from source; throws on errors
void compileOrThrow(BytecodeBuilder& bytecode, AstStatBlock* root, const AstNameTable& names, const CompileOptions& options = {});
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options = {}, const ParseOptions& parseOptions = {});
// compiles bytecode into a bytecode blob, that either contains the valid bytecode or an encoded error that lluz_load can decode
std::string compile(
const std::string& source, const CompileOptions& options = {}, const ParseOptions& parseOptions = {}, BytecodeEncoder* encoder = nullptr);
} // namespace lluz

View file

@ -1,4 +1,4 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <stddef.h>
@ -36,4 +36,4 @@ struct lua_CompileOptions
};
/* compile source to bytecode; when source compilation fails, the resulting bytecode contains the encoded error. use free() to destroy */
LUACODE_API char* luau_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize);
LUACODE_API char* lluz_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize);

View file

@ -1,12 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Builtins.h"
#include "Luau/Bytecode.h"
#include "Luau/Compiler.h"
#include "lluz/Bytecode.h"
#include "lluz/Compiler.h"
LUAU_FASTFLAGVARIABLE(LuauCompileRawlen, false)
#include "..\..\..\..\Security\XorString.h"
namespace Luau
lluz_FASTFLAGVARIABLE(LluCompileRawlen, false)
namespace lluz
{
namespace Compile
{
@ -40,143 +42,146 @@ Builtin getBuiltin(AstExpr* node, const DenseHashMap<AstName, Global>& globals,
}
}
static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
{
if (builtin.isGlobal("assert"))
if (builtin.empty())
return -1;
if (builtin.isGlobal(XorStr("assert")))
return LBF_ASSERT;
if (builtin.isGlobal("type"))
if (builtin.isGlobal(XorStr("type")))
return LBF_TYPE;
if (builtin.isGlobal("typeof"))
if (builtin.isGlobal(XorStr("typeof")))
return LBF_TYPEOF;
if (builtin.isGlobal("rawset"))
if (builtin.isGlobal(XorStr("rawset")))
return LBF_RAWSET;
if (builtin.isGlobal("rawget"))
if (builtin.isGlobal(XorStr("rawget")))
return LBF_RAWGET;
if (builtin.isGlobal("rawequal"))
if (builtin.isGlobal(XorStr("rawequal")))
return LBF_RAWEQUAL;
if (FFlag::LuauCompileRawlen && builtin.isGlobal("rawlen"))
if (FFlag::LluCompileRawlen && builtin.isGlobal(XorStr("rawlen")))
return LBF_RAWLEN;
if (builtin.isGlobal("unpack"))
if (builtin.isGlobal(XorStr("unpack")))
return LBF_TABLE_UNPACK;
if (builtin.isGlobal("select"))
if (builtin.isGlobal(XorStr("select")))
return LBF_SELECT_VARARG;
if (builtin.object == "math")
if (builtin.object == XorStr("math"))
{
if (builtin.method == "abs")
if (builtin.method == XorStr("abs"))
return LBF_MATH_ABS;
if (builtin.method == "acos")
if (builtin.method == XorStr("acos"))
return LBF_MATH_ACOS;
if (builtin.method == "asin")
if (builtin.method == XorStr("asin"))
return LBF_MATH_ASIN;
if (builtin.method == "atan2")
if (builtin.method == XorStr("atan2"))
return LBF_MATH_ATAN2;
if (builtin.method == "atan")
if (builtin.method == XorStr("atan"))
return LBF_MATH_ATAN;
if (builtin.method == "ceil")
if (builtin.method == XorStr("ceil"))
return LBF_MATH_CEIL;
if (builtin.method == "cosh")
if (builtin.method == XorStr("cosh"))
return LBF_MATH_COSH;
if (builtin.method == "cos")
if (builtin.method == XorStr("cos"))
return LBF_MATH_COS;
if (builtin.method == "deg")
if (builtin.method == XorStr("deg"))
return LBF_MATH_DEG;
if (builtin.method == "exp")
if (builtin.method == XorStr("exp"))
return LBF_MATH_EXP;
if (builtin.method == "floor")
if (builtin.method == XorStr("floor"))
return LBF_MATH_FLOOR;
if (builtin.method == "fmod")
if (builtin.method == XorStr("fmod"))
return LBF_MATH_FMOD;
if (builtin.method == "frexp")
if (builtin.method == XorStr("frexp"))
return LBF_MATH_FREXP;
if (builtin.method == "ldexp")
if (builtin.method == XorStr("ldexp"))
return LBF_MATH_LDEXP;
if (builtin.method == "log10")
if (builtin.method == XorStr("log10"))
return LBF_MATH_LOG10;
if (builtin.method == "log")
if (builtin.method == XorStr("log"))
return LBF_MATH_LOG;
if (builtin.method == "max")
if (builtin.method == XorStr("max"))
return LBF_MATH_MAX;
if (builtin.method == "min")
if (builtin.method == XorStr("min"))
return LBF_MATH_MIN;
if (builtin.method == "modf")
if (builtin.method == XorStr("modf"))
return LBF_MATH_MODF;
if (builtin.method == "pow")
if (builtin.method == XorStr("pow"))
return LBF_MATH_POW;
if (builtin.method == "rad")
if (builtin.method == XorStr("rad"))
return LBF_MATH_RAD;
if (builtin.method == "sinh")
if (builtin.method == XorStr("sinh"))
return LBF_MATH_SINH;
if (builtin.method == "sin")
if (builtin.method == XorStr("sin"))
return LBF_MATH_SIN;
if (builtin.method == "sqrt")
if (builtin.method == XorStr("sqrt"))
return LBF_MATH_SQRT;
if (builtin.method == "tanh")
if (builtin.method == XorStr("tanh"))
return LBF_MATH_TANH;
if (builtin.method == "tan")
if (builtin.method == XorStr("tan"))
return LBF_MATH_TAN;
if (builtin.method == "clamp")
if (builtin.method == XorStr("clamp"))
return LBF_MATH_CLAMP;
if (builtin.method == "sign")
if (builtin.method == XorStr("sign"))
return LBF_MATH_SIGN;
if (builtin.method == "round")
if (builtin.method == XorStr("round"))
return LBF_MATH_ROUND;
}
if (builtin.object == "bit32")
if (builtin.object == XorStr("bit32"))
{
if (builtin.method == "arshift")
if (builtin.method == XorStr("arshift"))
return LBF_BIT32_ARSHIFT;
if (builtin.method == "band")
if (builtin.method == XorStr("band"))
return LBF_BIT32_BAND;
if (builtin.method == "bnot")
if (builtin.method == XorStr("bnot"))
return LBF_BIT32_BNOT;
if (builtin.method == "bor")
if (builtin.method == XorStr("bor"))
return LBF_BIT32_BOR;
if (builtin.method == "bxor")
if (builtin.method == XorStr("bxor"))
return LBF_BIT32_BXOR;
if (builtin.method == "btest")
if (builtin.method == XorStr("btest"))
return LBF_BIT32_BTEST;
if (builtin.method == "extract")
if (builtin.method == XorStr("extract"))
return LBF_BIT32_EXTRACT;
if (builtin.method == "lrotate")
if (builtin.method == XorStr("lrotate"))
return LBF_BIT32_LROTATE;
if (builtin.method == "lshift")
if (builtin.method == XorStr("lshift"))
return LBF_BIT32_LSHIFT;
if (builtin.method == "replace")
if (builtin.method == XorStr("replace"))
return LBF_BIT32_REPLACE;
if (builtin.method == "rrotate")
if (builtin.method == XorStr("rrotate"))
return LBF_BIT32_RROTATE;
if (builtin.method == "rshift")
if (builtin.method == XorStr("rshift"))
return LBF_BIT32_RSHIFT;
if (builtin.method == "countlz")
if (builtin.method == XorStr("countlz"))
return LBF_BIT32_COUNTLZ;
if (builtin.method == "countrz")
if (builtin.method == XorStr("countrz"))
return LBF_BIT32_COUNTRZ;
}
if (builtin.object == "string")
if (builtin.object == XorStr("string"))
{
if (builtin.method == "byte")
if (builtin.method == XorStr("byte"))
return LBF_STRING_BYTE;
if (builtin.method == "char")
if (builtin.method == XorStr("char"))
return LBF_STRING_CHAR;
if (builtin.method == "len")
if (builtin.method == XorStr("len"))
return LBF_STRING_LEN;
if (builtin.method == "sub")
if (builtin.method == XorStr("sub"))
return LBF_STRING_SUB;
}
if (builtin.object == "table")
if (builtin.object == XorStr("table"))
{
if (builtin.method == "insert")
if (builtin.method == XorStr("insert"))
return LBF_TABLE_INSERT;
if (builtin.method == "unpack")
if (builtin.method == XorStr("unpack"))
return LBF_TABLE_UNPACK;
}
@ -197,49 +202,5 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
return -1;
}
struct BuiltinVisitor : AstVisitor
{
DenseHashMap<AstExprCall*, int>& result;
const DenseHashMap<AstName, Global>& globals;
const DenseHashMap<AstLocal*, Variable>& variables;
const CompileOptions& options;
BuiltinVisitor(DenseHashMap<AstExprCall*, int>& result, const DenseHashMap<AstName, Global>& globals,
const DenseHashMap<AstLocal*, Variable>& variables, const CompileOptions& options)
: result(result)
, globals(globals)
, variables(variables)
, options(options)
{
}
bool visit(AstExprCall* node) override
{
Builtin builtin = node->self ? Builtin() : getBuiltin(node->func, globals, variables);
if (builtin.empty())
return true;
int bfid = getBuiltinFunctionId(builtin, options);
// getBuiltinFunctionId optimistically assumes all select() calls are builtin but actually the second argument must be a vararg
if (bfid == LBF_SELECT_VARARG && !(node->args.size == 2 && node->args.data[1]->is<AstExprVarargs>()))
bfid = -1;
if (bfid >= 0)
result[node] = bfid;
return true; // propagate to nested calls
}
};
void analyzeBuiltins(DenseHashMap<AstExprCall*, int>& result, const DenseHashMap<AstName, Global>& globals,
const DenseHashMap<AstLocal*, Variable>& variables, const CompileOptions& options, AstNode* root)
{
BuiltinVisitor visitor{result, globals, variables, options};
root->visit(&visitor);
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,14 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "ValueTracking.h"
namespace Luau
namespace lluz
{
struct CompileOptions;
}
namespace Luau
namespace lluz
{
namespace Compile
{
@ -35,9 +35,7 @@ struct Builtin
};
Builtin getBuiltin(AstExpr* node, const DenseHashMap<AstName, Global>& globals, const DenseHashMap<AstLocal*, Variable>& variables);
void analyzeBuiltins(DenseHashMap<AstExprCall*, int>& result, const DenseHashMap<AstName, Global>& globals,
const DenseHashMap<AstLocal*, Variable>& variables, const CompileOptions& options, AstNode* root);
int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options);
} // namespace Compile
} // namespace Luau
} // namespace lluz

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,18 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "ConstantFolding.h"
#include "BuiltinFolding.h"
#include "..\..\..\..\Security\XorString.h"
#include <math.h>
namespace Luau
namespace lluz
{
namespace Compile
{
static bool constantsEqual(const Constant& la, const Constant& ra)
{
LUAU_ASSERT(la.type != Constant::Type_Unknown && ra.type != Constant::Type_Unknown);
lluz_ASSERT(la.type != Constant::Type_Unknown && ra.type != Constant::Type_Unknown);
switch (la.type)
{
@ -29,7 +29,7 @@ static bool constantsEqual(const Constant& la, const Constant& ra)
return ra.type == Constant::Type_String && la.stringLength == ra.stringLength && memcmp(la.valueString, ra.valueString, la.stringLength) == 0;
default:
LUAU_ASSERT(!"Unexpected constant type in comparison");
lluz_ASSERT(!"Unexpected constant type in comparison");
return false;
}
}
@ -63,7 +63,7 @@ static void foldUnary(Constant& result, AstExprUnary::Op op, const Constant& arg
break;
default:
LUAU_ASSERT(!"Unexpected unary operation");
lluz_ASSERT(!"Unexpected unary operation");
}
}
@ -185,7 +185,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
break;
default:
LUAU_ASSERT(!"Unexpected binary operation");
lluz_ASSERT(!"Unexpected binary operation");
}
}
@ -195,18 +195,13 @@ struct ConstantVisitor : AstVisitor
DenseHashMap<AstLocal*, Variable>& variables;
DenseHashMap<AstLocal*, Constant>& locals;
const DenseHashMap<AstExprCall*, int>* builtins;
bool wasEmpty = false;
std::vector<Constant> builtinArgs;
ConstantVisitor(DenseHashMap<AstExpr*, Constant>& constants, DenseHashMap<AstLocal*, Variable>& variables,
DenseHashMap<AstLocal*, Constant>& locals, const DenseHashMap<AstExprCall*, int>* builtins)
ConstantVisitor(
DenseHashMap<AstExpr*, Constant>& constants, DenseHashMap<AstLocal*, Variable>& variables, DenseHashMap<AstLocal*, Constant>& locals)
: constants(constants)
, variables(variables)
, locals(locals)
, builtins(builtins)
{
// since we do a single pass over the tree, if the initial state was empty we don't need to clear out old entries
wasEmpty = constants.empty() && locals.empty();
@ -260,37 +255,8 @@ struct ConstantVisitor : AstVisitor
{
analyze(expr->func);
if (const int* bfid = builtins ? builtins->find(expr) : nullptr)
{
// since recursive calls to analyze() may reuse the vector we need to be careful and preserve existing contents
size_t offset = builtinArgs.size();
bool canFold = true;
builtinArgs.reserve(offset + expr->args.size);
for (size_t i = 0; i < expr->args.size; ++i)
{
Constant ac = analyze(expr->args.data[i]);
if (ac.type == Constant::Type_Unknown)
canFold = false;
else
builtinArgs.push_back(ac);
}
if (canFold)
{
LUAU_ASSERT(builtinArgs.size() == offset + expr->args.size);
result = foldBuiltin(*bfid, builtinArgs.data() + offset, expr->args.size);
}
builtinArgs.resize(offset);
}
else
{
for (size_t i = 0; i < expr->args.size; ++i)
analyze(expr->args.data[i]);
}
for (size_t i = 0; i < expr->args.size; ++i)
analyze(expr->args.data[i]);
}
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
{
@ -351,7 +317,7 @@ struct ConstantVisitor : AstVisitor
}
else
{
LUAU_ASSERT(!"Unknown expression type");
lluz_ASSERT(!"Unknown expression type");
}
recordConstant(constants, node, result);
@ -374,7 +340,7 @@ struct ConstantVisitor : AstVisitor
{
// note: we rely on trackValues to have been run before us
Variable* v = variables.find(local);
LUAU_ASSERT(v);
lluz_ASSERT(v);
if (!v->written)
{
@ -431,11 +397,11 @@ struct ConstantVisitor : AstVisitor
};
void foldConstants(DenseHashMap<AstExpr*, Constant>& constants, DenseHashMap<AstLocal*, Variable>& variables,
DenseHashMap<AstLocal*, Constant>& locals, const DenseHashMap<AstExprCall*, int>* builtins, AstNode* root)
DenseHashMap<AstLocal*, Constant>& locals, AstNode* root)
{
ConstantVisitor visitor{constants, variables, locals, builtins};
ConstantVisitor visitor{constants, variables, locals};
root->visit(&visitor);
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,9 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "ValueTracking.h"
namespace Luau
namespace lluz
{
namespace Compile
{
@ -26,24 +26,24 @@ struct Constant
{
bool valueBoolean;
double valueNumber;
const char* valueString = nullptr; // length stored in stringLength
char* valueString = nullptr; // length stored in stringLength
};
bool isTruthful() const
{
LUAU_ASSERT(type != Type_Unknown);
lluz_ASSERT(type != Type_Unknown);
return type != Type_Nil && !(type == Type_Boolean && valueBoolean == false);
}
AstArray<const char> getString() const
AstArray<char> getString() const
{
LUAU_ASSERT(type == Type_String);
lluz_ASSERT(type == Type_String);
return {valueString, stringLength};
}
};
void foldConstants(DenseHashMap<AstExpr*, Constant>& constants, DenseHashMap<AstLocal*, Variable>& variables,
DenseHashMap<AstLocal*, Constant>& locals, const DenseHashMap<AstExprCall*, int>* builtins, AstNode* root);
DenseHashMap<AstLocal*, Constant>& locals, AstNode* root);
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,12 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "CostModel.h"
#include "Luau/Common.h"
#include "Luau/DenseHash.h"
#include "lluz/Common.h"
#include "lluz/DenseHash.h"
#include "..\..\..\..\Security\XorString.h"
#include <limits.h>
namespace Luau
namespace lluz
{
namespace Compile
{
@ -113,14 +115,11 @@ struct Cost
struct CostVisitor : AstVisitor
{
const DenseHashMap<AstExprCall*, int>& builtins;
DenseHashMap<AstLocal*, uint64_t> vars;
Cost result;
CostVisitor(const DenseHashMap<AstExprCall*, int>& builtins)
: builtins(builtins)
, vars(nullptr)
CostVisitor()
: vars(nullptr)
{
}
@ -151,21 +150,14 @@ struct CostVisitor : AstVisitor
}
else if (AstExprCall* expr = node->as<AstExprCall>())
{
// builtin cost modeling is different from regular calls because we use FASTCALL to compile these
// thus we use a cheaper baseline, don't account for function, and assume constant/local copy is free
bool builtin = builtins.find(expr) != nullptr;
bool builtinShort = builtin && expr->args.size <= 2; // FASTCALL1/2
Cost cost = builtin ? 2 : 3;
if (!builtin)
cost += model(expr->func);
Cost cost = 3;
cost += model(expr->func);
for (size_t i = 0; i < expr->args.size; ++i)
{
Cost ac = model(expr->args.data[i]);
// for constants/locals we still need to copy them to the argument list
cost += ac.model == 0 && !builtinShort ? Cost(1) : ac;
cost += ac.model == 0 ? Cost(1) : ac;
}
return cost;
@ -217,7 +209,7 @@ struct CostVisitor : AstVisitor
}
else
{
LUAU_ASSERT(!"Unknown expression type");
lluz_ASSERT(!"Unknown expression type");
return {};
}
}
@ -337,9 +329,9 @@ struct CostVisitor : AstVisitor
}
};
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap<AstExprCall*, int>& builtins)
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount)
{
CostVisitor visitor{builtins};
CostVisitor visitor;
for (size_t i = 0; i < varCount && i < 7; ++i)
visitor.vars[vars[i]] = 0xffull << (i * 8 + 8);
@ -379,4 +371,4 @@ int getTripCount(double from, double to, double step)
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,16 +1,15 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/DenseHash.h"
#include "lluz/Ast.h"
namespace Luau
namespace lluz
{
namespace Compile
{
// cost model: 8 bytes, where first byte is the baseline cost, and the next 7 bytes are discounts for when variable #i is constant
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap<AstExprCall*, int>& builtins);
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount);
// cost is computed as B - sum(Di * Ci), where B is baseline cost, Di is the discount for each variable and Ci is 1 when variable #i is constant
int computeCost(uint64_t model, const bool* varsConst, size_t varCount);
@ -19,4 +18,4 @@ int computeCost(uint64_t model, const bool* varsConst, size_t varCount);
int getTripCount(double from, double to, double step);
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,7 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "TableShape.h"
namespace Luau
#include "..\..\..\..\Security\XorString.h"
namespace lluz
{
namespace Compile
{
@ -17,7 +19,7 @@ static AstExprTable* getTableHint(AstExpr* expr)
// setmetatable(table literal, ...)
if (AstExprCall* call = expr->as<AstExprCall>(); call && !call->self && call->args.size == 2)
if (AstExprGlobal* func = call->func->as<AstExprGlobal>(); func && func->name == "setmetatable")
if (AstExprGlobal* func = call->func->as<AstExprGlobal>(); func && func->name == XorStr("setmetatable"))
if (AstExprTable* table = call->args.data[0]->as<AstExprTable>())
return table;
@ -155,4 +157,4 @@ void predictTableShapes(DenseHashMap<AstExprTable*, TableShape>& shapes, AstNode
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,10 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/DenseHash.h"
#include "lluz/Ast.h"
#include "lluz/DenseHash.h"
namespace Luau
namespace lluz
{
namespace Compile
{
@ -18,4 +18,4 @@ struct TableShape
void predictTableShapes(DenseHashMap<AstExprTable*, TableShape>& shapes, AstNode* root);
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,9 +1,11 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "ValueTracking.h"
#include "Luau/Lexer.h"
#include "lluz/Lexer.h"
namespace Luau
#include "..\..\..\..\Security\XorString.h"
namespace lluz
{
namespace Compile
{
@ -84,7 +86,7 @@ struct ValueVisitor : AstVisitor
void assignMutable(DenseHashMap<AstName, Global>& globals, const AstNameTable& names, const char** mutableGlobals)
{
if (AstName name = names.get("_G"); name.value)
if (AstName name = names.get(XorStr("_G")); name.value)
globals[name] = Global::Mutable;
if (mutableGlobals)
@ -100,4 +102,4 @@ void trackValues(DenseHashMap<AstName, Global>& globals, DenseHashMap<AstLocal*,
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,15 +1,15 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Ast.h"
#include "Luau/DenseHash.h"
#include "lluz/Ast.h"
#include "lluz/DenseHash.h"
namespace Luau
namespace lluz
{
class AstNameTable;
}
namespace Luau
namespace lluz
{
namespace Compile
{
@ -39,4 +39,4 @@ inline Global getGlobalState(const DenseHashMap<AstName, Global>& globals, AstNa
}
} // namespace Compile
} // namespace Luau
} // namespace lluz

View file

@ -1,19 +1,19 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "luacode.h"
#include "Luau/Compiler.h"
#include "lluz/Compiler.h"
#include <string.h>
char* luau_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize)
char* lluz_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize)
{
LUAU_ASSERT(outsize);
lluz_ASSERT(outsize);
Luau::CompileOptions opts;
lluz::CompileOptions opts;
if (options)
{
static_assert(sizeof(lua_CompileOptions) == sizeof(Luau::CompileOptions), "C and C++ interface must match");
static_assert(sizeof(lua_CompileOptions) == sizeof(lluz::CompileOptions), "C and C++ interface must match");
memcpy(static_cast<void*>(&opts), options, sizeof(opts));
}