ArkScript
A small, fast, functional and scripting language for video games
State.cpp
Go to the documentation of this file.
1#include <Ark/VM/State.hpp>
2
3#include <Ark/Constants.hpp>
4#include <Ark/Files.hpp>
6
7#ifdef _MSC_VER
8# pragma warning(push)
9# pragma warning(disable : 4996)
10#endif
11
12#include <picosha2.h>
14#include <fmt/core.h>
15#include <fmt/color.h>
16
17namespace Ark
18{
19 State::State(const std::vector<std::filesystem::path>& libenv) noexcept :
20 m_debug_level(0),
21 m_libenv(libenv),
22 m_filename(ARK_NO_NAME_FILE)
23 {}
24
25 bool State::feed(const std::string& bytecode_filename)
26 {
27 if (!Utils::fileExists(bytecode_filename))
28 return false;
29
30 return feed(Utils::readFileAsBytes(bytecode_filename));
31 }
32
33 bool State::feed(const bytecode_t& bytecode)
34 {
36 bcr.feed(bytecode);
37 if (!bcr.checkMagic())
38 return false;
39
40 m_bytecode = bytecode;
41
42 try
43 {
44 configure(bcr);
45 return true;
46 }
47 catch (const std::exception& e) // FIXME I don't like this shit
48 {
49 fmt::println("{}", e.what());
50 return false;
51 }
52 }
53
54 bool State::compile(const std::string& file, const std::string& output, const uint16_t features) const
55 {
56 Welder welder(m_debug_level, m_libenv, features);
57 for (auto& p : m_binded)
58 welder.registerSymbol(p.first);
59
60 if (!welder.computeASTFromFile(file))
61 return false;
62 if (!welder.generateBytecode())
63 return false;
64
65 const std::string destination = output.empty() ? (file.substr(0, file.find_last_of('.')) + ".arkc") : output;
66 if (!welder.saveBytecodeToFile(destination))
67 return false;
68
69 return true;
70 }
71
72 bool State::doFile(const std::string& file, const uint16_t features)
73 {
74 if (!Utils::fileExists(file))
75 {
76 fmt::print(fmt::fg(fmt::color::red), "Can not find file '{}'\n", file);
77 return false;
78 }
79 m_filename = file;
80
81 const bytecode_t bytecode = Utils::readFileAsBytes(file);
83 bcr.feed(bytecode);
84 if (!bcr.checkMagic()) // couldn't read magic number, it's a source file
85 {
86 // check if it's in the arkscript cache
87 const std::string short_filename = (std::filesystem::path(file)).filename().string();
88 const std::string filename = short_filename.substr(0, short_filename.find_last_of('.')) + ".arkc";
89 const std::filesystem::path directory = (std::filesystem::path(file)).parent_path() / ARK_CACHE_DIRNAME;
90 const std::string path = (directory / filename).string();
91
92 if (!exists(directory)) // create ark cache directory
93 create_directory(directory);
94
95 if (compile(file, path, features) && feed(path))
96 return true;
97 }
98 else if (feed(bytecode)) // it's a bytecode file
99 return true;
100 return false;
101 }
102
103 bool State::doString(const std::string& code, const uint16_t features)
104 {
105 Welder welder(m_debug_level, m_libenv, features);
106 for (auto& p : m_binded)
107 welder.registerSymbol(p.first);
108
109 if (!welder.computeASTFromString(code))
110 return false;
111 if (!welder.generateBytecode())
112 return false;
113 return feed(welder.bytecode());
114 }
115
116 void State::loadFunction(const std::string& name, const Value::ProcType function) noexcept
117 {
118 m_binded[name] = Value(function);
119 }
120
121 void State::setArgs(const std::vector<std::string>& args) noexcept
122 {
124 std::ranges::transform(args, std::back_inserter(val.list()), [](const std::string& arg) {
125 return Value(arg);
126 });
127
128 m_binded[std::string(internal::Language::SysArgs)] = val;
130 }
131
132 void State::setDebug(const unsigned level) noexcept
133 {
134 m_debug_level = level;
135 }
136
137 void State::setLibDirs(const std::vector<std::filesystem::path>& libenv) noexcept
138 {
139 m_libenv = libenv;
140 }
141
143 {
144 using namespace internal;
145
146 const auto [major, minor, patch] = bcr.version();
147 if (major != ARK_VERSION_MAJOR)
148 {
149 std::string str_version = std::to_string(major) + "." +
150 std::to_string(minor) + "." +
151 std::to_string(patch);
152 throwStateError(fmt::format("Compiler and VM versions don't match: got {} while running {}", str_version, ARK_VERSION));
153 }
154
155 const auto bytecode_hash = bcr.sha256();
156
157 std::vector<unsigned char> hash(picosha2::k_digest_size);
158 picosha2::hash256(m_bytecode.begin() + 18 + picosha2::k_digest_size, m_bytecode.end(), hash);
159 // checking integrity
160 for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
161 {
162#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
163 if (hash[j] != bytecode_hash[j])
164 throwStateError("Integrity check failed");
165#endif
166 }
167
168 const auto syms = bcr.symbols();
169 const auto vals = bcr.values(syms);
170 const auto [pages, _] = bcr.code(vals);
171
172 m_symbols = syms.symbols;
173 m_constants = vals.values;
174 m_pages = pages;
175 }
176
177 void State::reset() noexcept
178 {
179 m_symbols.clear();
180 m_constants.clear();
181 m_pages.clear();
182 m_binded.clear();
183 }
184}
185
186#ifdef _MSC_VER
187# pragma warning(pop)
188#endif
A bytecode disassembler for ArkScript.
Constants used by ArkScript.
constexpr std::string_view ARK_VERSION
Definition Constants.hpp:22
#define ARK_PLATFORM_NAME
Definition Constants.hpp:40
constexpr int ARK_VERSION_MAJOR
Definition Constants.hpp:18
#define ARK_NO_NAME_FILE
Definition Constants.hpp:27
#define ARK_CACHE_DIRNAME
Definition Constants.hpp:26
Lots of utilities about the filesystem.
State used by the virtual machine: it loads the bytecode, can compile it if needed,...
In charge of welding everything needed to compile code.
This class is just a helper to.
Symbols symbols() const
Version version() const
Values values(const Symbols &symbols) const
std::vector< unsigned char > sha256() const
Code code(const Values &values) const
void feed(const std::string &file)
Construct needed data before displaying information about a given file.
std::vector< std::filesystem::path > m_libenv
Definition State.hpp:145
void setLibDirs(const std::vector< std::filesystem::path > &libenv) noexcept
Set the std search paths.
Definition State.cpp:137
void configure(const BytecodeReader &bcr)
Called to configure the state (set the bytecode, debug level, call the compiler......
Definition State.cpp:142
bytecode_t m_bytecode
Definition State.hpp:144
std::string m_filename
Definition State.hpp:146
std::vector< Value > m_constants
Definition State.hpp:150
static void throwStateError(const std::string &message)
Definition State.hpp:137
bool feed(const std::string &bytecode_filename)
Feed the state by giving it the path to an existing bytecode file.
Definition State.cpp:25
bool doFile(const std::string &file, uint16_t features=DefaultFeatures)
Compile a file, and use the resulting bytecode.
Definition State.cpp:72
void reset() noexcept
Reset State (all member variables related to execution)
Definition State.cpp:177
std::unordered_map< std::string, Value > m_binded
Definition State.hpp:154
std::vector< std::string > m_symbols
Definition State.hpp:149
bool compile(const std::string &file, const std::string &output, uint16_t features) const
Reads and compiles code of file.
Definition State.cpp:54
void setArgs(const std::vector< std::string > &args) noexcept
Set the script arguments in sys:args.
Definition State.cpp:121
bool doString(const std::string &code, uint16_t features=DefaultFeatures)
Compile a string (representing ArkScript code) and store resulting bytecode in m_bytecode.
Definition State.cpp:103
std::vector< bytecode_t > m_pages
Definition State.hpp:151
void loadFunction(const std::string &name, Value::ProcType function) noexcept
Register a function in the virtual machine.
Definition State.cpp:116
unsigned m_debug_level
Definition State.hpp:142
State(const std::vector< std::filesystem::path > &libenv={}) noexcept
Construct a new State object.
Definition State.cpp:19
void setDebug(unsigned level) noexcept
Set the debug level.
Definition State.cpp:132
std::vector< Value > & list()
Definition Value.hpp:129
Value(*)(std::vector< Value > &, VM *) ProcType
Definition Value.hpp:62
The welder joins all the compiler passes.
Definition Welder.hpp:38
void registerSymbol(const std::string &name)
Register a symbol as a global in the compiler.
Definition Welder.cpp:30
bool computeASTFromString(const std::string &code)
Definition Welder.cpp:43
const bytecode_t & bytecode() const noexcept
Definition Welder.cpp:108
bool saveBytecodeToFile(const std::string &filename)
Save the generated bytecode to a given file.
Definition Welder.cpp:81
bool generateBytecode()
Compile the AST processed by computeASTFromFile / computeASTFromString.
Definition Welder.cpp:50
bool computeASTFromFile(const std::string &filename)
Definition Welder.cpp:35
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
Definition Files.hpp:29
std::vector< uint8_t > readFileAsBytes(const std::string &name)
Helper to read the bytes of a file.
Definition Files.hpp:63
constexpr std::string_view SysPlatform
Definition Common.hpp:113
constexpr std::string_view SysArgs
Definition Common.hpp:112
std::vector< uint8_t > bytecode_t
Definition Common.hpp:22