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
Notes:
sideshowbarker
2024-07-17 18:49:10 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/a6c4b6848b Pull-request: https://github.com/SerenityOS/serenity/pull/13142 Reviewed-by: https://github.com/ADKaster
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
|
||||
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/SourceLocation.h>
|
||||
#include <AK/Tuple.h>
|
||||
#include <LibWasm/Forward.h>
|
||||
#include <LibWasm/Types.h>
|
||||
|
||||
#if WASM_VALIDATOR_DEBUG
|
||||
# include <AK/SourceLocation.h>
|
||||
#endif
|
||||
|
||||
namespace Wasm {
|
||||
|
||||
struct Context {
|
||||
|
@ -177,6 +175,9 @@ public:
|
|||
// This is a wrapper that can model "polymorphic" stacks,
|
||||
// by treating unknown stack entries as a potentially infinite number of entries
|
||||
class Stack : private Vector<StackEntry> {
|
||||
template<typename, typename>
|
||||
friend struct AK::Formatter;
|
||||
|
||||
public:
|
||||
// The unknown entry will never be popped off, so we can safely use the original `is_empty`.
|
||||
using Vector<StackEntry>::is_empty;
|
||||
|
@ -196,6 +197,28 @@ public:
|
|||
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 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); }
|
||||
|
||||
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>
|
||||
|
@ -251,11 +277,40 @@ private:
|
|||
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); }
|
||||
|
||||
#if WASM_VALIDATOR_DEBUG
|
||||
static ValidationError invalid_stack_state(SourceLocation location = SourceLocation::current());
|
||||
#else
|
||||
static ValidationError invalid_stack_state();
|
||||
#endif
|
||||
template<typename... Expected>
|
||||
static ValidationError invalid_stack_state(Stack const& stack, Tuple<Expected...> expected, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
constexpr size_t count = expected.size();
|
||||
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 {
|
||||
|
@ -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<>
|
||||
struct AK::Formatter<Wasm::ValueType> : public AK::Formatter<StringView> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, Wasm::ValueType const& value)
|
||||
|
|
Loading…
Add table
Reference in a new issue