mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Add files via upload
This commit is contained in:
parent
681359d4d1
commit
05d9edddb6
30 changed files with 1906 additions and 1021 deletions
155
CLI/Analyze.cpp
155
CLI/Analyze.cpp
|
@ -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;
|
||||
|
|
35
CLI/Ast.cpp
35
CLI/Ast.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
291
CLI/Repl.cpp
291
CLI/Repl.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
16
CLI/Web.cpp
16
CLI/Web.cpp
|
@ -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
|
||||
|
|
504
Common/include/lluz/Bytecode.h
Normal file
504
Common/include/lluz/Bytecode.h
Normal 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,
|
||||
};
|
133
Common/include/lluz/Common.h
Normal file
133
Common/include/lluz/Common.h
Normal 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); \
|
||||
}
|
262
Compiler/include/lluz/BytecodeBuilder.h
Normal file
262
Compiler/include/lluz/BytecodeBuilder.h
Normal 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
|
68
Compiler/include/lluz/Compiler.h
Normal file
68
Compiler/include/lluz/Compiler.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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,38 +255,9 @@ 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]);
|
||||
}
|
||||
}
|
||||
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
|
||||
{
|
||||
analyze(expr->expr);
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue