mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:37:43 +00:00
LibWasm: Use some template magic to greatly simplify stack validation
This also auto-fixes a few bugs that were present before as we were manually checking the stack.
This commit is contained in:
parent
18c5b0f1cc
commit
a6c4b6848b
2 changed files with 302 additions and 747 deletions
File diff suppressed because it is too large
Load diff
|
@ -7,13 +7,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
|
#include <AK/SourceLocation.h>
|
||||||
|
#include <AK/Tuple.h>
|
||||||
#include <LibWasm/Forward.h>
|
#include <LibWasm/Forward.h>
|
||||||
#include <LibWasm/Types.h>
|
#include <LibWasm/Types.h>
|
||||||
|
|
||||||
#if WASM_VALIDATOR_DEBUG
|
|
||||||
# include <AK/SourceLocation.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Wasm {
|
namespace Wasm {
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
|
@ -177,6 +175,9 @@ public:
|
||||||
// This is a wrapper that can model "polymorphic" stacks,
|
// This is a wrapper that can model "polymorphic" stacks,
|
||||||
// by treating unknown stack entries as a potentially infinite number of entries
|
// by treating unknown stack entries as a potentially infinite number of entries
|
||||||
class Stack : private Vector<StackEntry> {
|
class Stack : private Vector<StackEntry> {
|
||||||
|
template<typename, typename>
|
||||||
|
friend struct AK::Formatter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// The unknown entry will never be popped off, so we can safely use the original `is_empty`.
|
// The unknown entry will never be popped off, so we can safely use the original `is_empty`.
|
||||||
using Vector<StackEntry>::is_empty;
|
using Vector<StackEntry>::is_empty;
|
||||||
|
@ -196,6 +197,28 @@ public:
|
||||||
Vector<StackEntry>::append(entry);
|
Vector<StackEntry>::append(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<void, ValidationError> take(ValueType type, SourceLocation location = SourceLocation::current())
|
||||||
|
{
|
||||||
|
if (is_empty())
|
||||||
|
return Errors::invalid("stack state", type, "<nothing>", location);
|
||||||
|
|
||||||
|
auto type_on_stack = take_last();
|
||||||
|
if (type_on_stack != type)
|
||||||
|
return Errors::invalid("stack state", type, type_on_stack, location);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto... kinds>
|
||||||
|
ErrorOr<void, ValidationError> take(SourceLocation location = SourceLocation::current())
|
||||||
|
{
|
||||||
|
ErrorOr<void, ValidationError> result;
|
||||||
|
if (((result = take(Wasm::ValueType(kinds), location)).is_error(), ...)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
size_t actual_size() const { return Vector<StackEntry>::size(); }
|
size_t actual_size() const { return Vector<StackEntry>::size(); }
|
||||||
size_t size() const { return m_did_insert_unknown_entry ? static_cast<size_t>(-1) : actual_size(); }
|
size_t size() const { return m_did_insert_unknown_entry ? static_cast<size_t>(-1) : actual_size(); }
|
||||||
|
|
||||||
|
@ -235,9 +258,12 @@ private:
|
||||||
static ValidationError invalid(StringView name) { return String::formatted("Invalid {}", name); }
|
static ValidationError invalid(StringView name) { return String::formatted("Invalid {}", name); }
|
||||||
|
|
||||||
template<typename Expected, typename Given>
|
template<typename Expected, typename Given>
|
||||||
static ValidationError invalid(StringView name, Expected expected, Given given)
|
static ValidationError invalid(StringView name, Expected expected, Given given, SourceLocation location = SourceLocation::current())
|
||||||
{
|
{
|
||||||
return String::formatted("Invalid {}, expected {} but got {}", name, expected, given);
|
if constexpr (WASM_VALIDATOR_DEBUG)
|
||||||
|
return String::formatted("Invalid {} in {}, expected {} but got {}", name, find_instruction_name(location), expected, given);
|
||||||
|
else
|
||||||
|
return String::formatted("Invalid {}, expected {} but got {}", name, expected, given);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
@ -251,11 +277,40 @@ private:
|
||||||
template<typename T, typename U, typename V>
|
template<typename T, typename U, typename V>
|
||||||
static ValidationError out_of_bounds(StringView name, V value, T min, U max) { return String::formatted("Value {} for {} is out of bounds ({},{})", value, name, min, max); }
|
static ValidationError out_of_bounds(StringView name, V value, T min, U max) { return String::formatted("Value {} for {} is out of bounds ({},{})", value, name, min, max); }
|
||||||
|
|
||||||
#if WASM_VALIDATOR_DEBUG
|
template<typename... Expected>
|
||||||
static ValidationError invalid_stack_state(SourceLocation location = SourceLocation::current());
|
static ValidationError invalid_stack_state(Stack const& stack, Tuple<Expected...> expected, SourceLocation location = SourceLocation::current())
|
||||||
#else
|
{
|
||||||
static ValidationError invalid_stack_state();
|
constexpr size_t count = expected.size();
|
||||||
#endif
|
StringBuilder builder;
|
||||||
|
if constexpr (WASM_VALIDATOR_DEBUG)
|
||||||
|
builder.appendff("Invalid stack state in {}: ", find_instruction_name(location));
|
||||||
|
else
|
||||||
|
builder.appendff("Invalid stack state in <unknown>: ");
|
||||||
|
|
||||||
|
builder.append("Expected [ ");
|
||||||
|
|
||||||
|
expected.apply_as_args([&]<typename... Ts>(Ts const&... args) {
|
||||||
|
(builder.appendff("{} ", args), ...);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.append("], but found [ ");
|
||||||
|
|
||||||
|
auto actual_size = stack.actual_size();
|
||||||
|
for (size_t i = 1; i <= min(count, actual_size); ++i) {
|
||||||
|
auto& entry = stack.at(actual_size - i);
|
||||||
|
if (entry.is_known) {
|
||||||
|
builder.appendff("{} ", entry.concrete_type);
|
||||||
|
} else {
|
||||||
|
builder.appendff("<polymorphic stack>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append("]");
|
||||||
|
return { builder.to_string() };
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static String find_instruction_name(SourceLocation const&);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ChildScopeKind {
|
enum class ChildScopeKind {
|
||||||
|
@ -293,6 +348,14 @@ struct AK::Formatter<Wasm::Validator::StackEntry> : public AK::Formatter<StringV
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct AK::Formatter<Wasm::Validator::Stack> : public AK::Formatter<Vector<Wasm::Validator::StackEntry>> {
|
||||||
|
ErrorOr<void> format(FormatBuilder& builder, Wasm::Validator::Stack const& value)
|
||||||
|
{
|
||||||
|
return Formatter<Vector<Wasm::Validator::StackEntry>>::format(builder, static_cast<Vector<Wasm::Validator::StackEntry> const&>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct AK::Formatter<Wasm::ValueType> : public AK::Formatter<StringView> {
|
struct AK::Formatter<Wasm::ValueType> : public AK::Formatter<StringView> {
|
||||||
ErrorOr<void> format(FormatBuilder& builder, Wasm::ValueType const& value)
|
ErrorOr<void> format(FormatBuilder& builder, Wasm::ValueType const& value)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue