-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathVMFactory.cpp
More file actions
189 lines (161 loc) · 5.53 KB
/
VMFactory.cpp
File metadata and controls
189 lines (161 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Aleth: Ethereum C++ client, tools and libraries.
// Copyright 2014-2019 Aleth Authors.
// Licensed under the GNU General Public License, Version 3.
#include "VMFactory.h"
#include "EVMC.h"
#include "LegacyVM.h"
#include <libdevcore/Log.h>
#include <libinterpreter/interpreter.h>
#include <evmc/loader.h>
namespace po = boost::program_options;
namespace dev
{
namespace eth
{
namespace
{
//auto g_kind = VMKind::Legacy;
auto g_kind = VMKind::Interpreter;
/// The pointer to EVMC create function in DLL EVMC VM.
///
/// This variable is only written once when processing command line arguments,
/// so access is thread-safe.
std::unique_ptr<EVMC> g_evmcDll;
/// A helper type to build the tabled of VM implementations.
///
/// More readable than std::tuple.
/// Fields are not initialized to allow usage of construction with initializer lists {}.
struct VMKindTableEntry
{
VMKind kind;
const char* name;
};
/// The table of available VM implementations.
///
/// We don't use a map to avoid complex dynamic initialization. This list will never be long,
/// so linear search only to parse command line arguments is not a problem.
VMKindTableEntry vmKindsTable[] = {
{VMKind::Interpreter, "interpreter"},
{VMKind::Legacy, "legacy"},
};
void setVMKind(const std::string& _name)
{
for (auto& entry : vmKindsTable)
{
// Try to find a match in the table of VMs.
if (_name == entry.name)
{
g_kind = entry.kind;
return;
}
}
// If no match for predefined VM names, try loading it as an EVMC VM DLL.
g_kind = VMKind::DLL;
// Release previous instance
g_evmcDll.reset();
evmc_loader_error_code ec;
evmc_vm* vm = evmc_load_and_create(_name.c_str(), &ec);
assert(ec == EVMC_LOADER_SUCCESS || vm == nullptr);
switch (ec)
{
case EVMC_LOADER_SUCCESS:
break;
case EVMC_LOADER_CANNOT_OPEN:
BOOST_THROW_EXCEPTION(
po::validation_error(po::validation_error::invalid_option_value, "vm", _name, 1));
case EVMC_LOADER_SYMBOL_NOT_FOUND:
BOOST_THROW_EXCEPTION(std::system_error(std::make_error_code(std::errc::invalid_seek),
"loading " + _name + " failed: EVMC create function not found"));
case EVMC_LOADER_ABI_VERSION_MISMATCH:
BOOST_THROW_EXCEPTION(std::system_error(std::make_error_code(std::errc::invalid_argument),
"loading " + _name + " failed: EVMC ABI version mismatch"));
default:
BOOST_THROW_EXCEPTION(
std::system_error(std::error_code(static_cast<int>(ec), std::generic_category()),
"loading " + _name + " failed"));
}
g_evmcDll.reset(new EVMC{vm});
cnote << "Loaded EVMC module: " << g_evmcDll->name() << " " << g_evmcDll->version() << " ("
<< _name << ")";
}
} // namespace
namespace
{
/// The name of the program option --evmc. The boost will trim the tailing
/// space and we can reuse this variable in exception message.
const char c_evmcPrefix[] = "evmc ";
/// The list of EVMC options stored as pairs of (name, value).
std::vector<std::pair<std::string, std::string>> s_evmcOptions;
/// The additional parser for EVMC options. The options should look like
/// `--evmc name=value` or `--evmc=name=value`. The boost pass the strings
/// of `name=value` here. This function splits the name and value or reports
/// the syntax error if the `=` character is missing.
void parseEvmcOptions(const std::vector<std::string>& _opts)
{
for (auto& s : _opts)
{
auto separatorPos = s.find('=');
if (separatorPos == s.npos)
throw po::invalid_syntax{po::invalid_syntax::missing_parameter, c_evmcPrefix + s};
auto name = s.substr(0, separatorPos);
auto value = s.substr(separatorPos + 1);
s_evmcOptions.emplace_back(std::move(name), std::move(value));
}
}
} // namespace
std::vector<std::pair<std::string, std::string>>& evmcOptions() noexcept
{
return s_evmcOptions;
};
po::options_description vmProgramOptions(unsigned _lineLength)
{
// It must be a static object because boost expects const char*.
static const std::string description = [] {
std::string names;
for (auto& entry : vmKindsTable)
{
if (!names.empty())
names += ", ";
names += entry.name;
}
return "Select VM implementation. Available options are: " + names + ".";
}();
po::options_description opts("VM OPTIONS", _lineLength);
auto add = opts.add_options();
add("vm",
po::value<std::string>()
->value_name("<name>|<path>")
->default_value("legacy")
->notifier(setVMKind),
description.data());
add(c_evmcPrefix,
po::value<std::vector<std::string>>()
->multitoken()
->value_name("<option>=<value>")
->notifier(parseEvmcOptions),
"EVMC option\n");
return opts;
}
VMPtr VMFactory::create()
{
return create(g_kind);
}
VMPtr VMFactory::create(VMKind _kind)
{
static const auto default_delete = [](VMFace * _vm) noexcept { delete _vm; };
static const auto null_delete = [](VMFace*) noexcept {};
switch (_kind)
{
case VMKind::Interpreter:
return {new EVMC{evmc_create_aleth_interpreter()}, default_delete};
case VMKind::DLL:
assert(g_evmcDll != nullptr);
// Return "fake" owning pointer to global EVMC DLL VM.
return {g_evmcDll.get(), null_delete};
case VMKind::Legacy:
default:
return {new LegacyVM, default_delete};
}
}
} // namespace eth
} // namespace dev