Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion Compiler/Cli/Commands/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ namespace Visual::XSharp::Cli::Frontend
std::optional<OutputKind> kind;
std::vector<std::uint8_t> bytes;
std::string error;
// Only the testing route sets this. It lets one CorePrep payload
// follow a Core payload; every other route keeps the
// single-payload contract.
bool acceptCorePrep{};
std::optional<std::vector<std::uint8_t>> corePrep;
};

auto
Expand Down Expand Up @@ -313,10 +318,26 @@ namespace Visual::XSharp::Cli::Frontend
const std::uint8_t *bytes,
std::size_t size) noexcept -> std::int32_t
{
if (context == nullptr || rawKind > 3U
if (context == nullptr || rawKind > 4U
|| (size != 0U && bytes == nullptr))
return 1;
auto &captured = *static_cast<CapturedOutput *>(context);
if (static_cast<OutputKind>(rawKind) == OutputKind::CorePrepWire)
{
// CorePrep is accepted once, only after Core, and only on
// the route that asked for it.
if (!captured.acceptCorePrep
|| captured.kind != OutputKind::CoreWire
|| captured.corePrep.has_value()
|| size > kMaximumCoreBytes)
{
captured.error = "frontend emitted an unexpected CorePrep "
"payload";
return 1;
}
captured.corePrep.emplace(bytes, bytes + size);
return 0;
}
if (captured.kind.has_value())
{
captured.error
Expand Down Expand Up @@ -450,8 +471,39 @@ namespace Visual::XSharp::Cli::Frontend
{},
frontend.Error() };
CapturedOutput output;
output.acceptCorePrep = true;
const auto status
= frontend.FuzzCompile(source.data(), source.size(), output);
return MakeResult(status, std::move(output));
}

auto
FuzzCompileStages(std::span<const std::uint8_t> source) -> StageResult
{
auto &frontend = GetFrontend();
if (!frontend.IsReady())
return { { Status::InternalError,
OutputKind::ErrorText,
{},
frontend.Error() },
{} };
CapturedOutput output;
output.acceptCorePrep = true;
const auto status
= frontend.FuzzCompile(source.data(), source.size(), output);
auto corePrep = std::move(output.corePrep);
StageResult result{ MakeResult(status, std::move(output)), {} };
if (result.core.succeeded() && result.core.kind == OutputKind::CoreWire)
{
if (!corePrep)
{
result.core.status = Status::InternalError;
result.core.error
= "frontend delivered Core without its CorePrep lowering";
return result;
}
result.corePrep = std::move(*corePrep);
}
return result;
}
} // namespace Visual::XSharp::Cli::Frontend
22 changes: 21 additions & 1 deletion Compiler/Cli/Commands/Frontend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ namespace Visual::XSharp::Cli::Frontend
CoreWire = 0, ///< Versioned, verified Core wire bytes.
ProjectSourceList = 1, ///< NUL-delimited UTF-8 source paths.
DiagnosticWire = 2, ///< Structured source diagnostics.
ErrorText = 3 ///< Human-readable UTF-8 failure text.
ErrorText = 3, ///< Human-readable UTF-8 failure text.
CorePrepWire = 4 ///< Frontend CorePrep lowering; testing only.
};

/// @brief Stable operation outcomes returned by the C ABI.
Expand Down Expand Up @@ -79,4 +80,23 @@ namespace Visual::XSharp::Cli::Frontend
/// failures.
[[nodiscard]] auto
FuzzCompile(std::span<const std::uint8_t> source) -> Result;

/// @brief Core and the frontend's own CorePrep from one compilation.
struct StageResult final
{
Result core; ///< Status and Core wire, as returned by FuzzCompile.
/// Frontend-lowered CorePrep wire; empty unless core succeeded.
std::vector<std::uint8_t> corePrep;
};

/// @brief Compile source bytes and keep both frontend artifacts.
///
/// The native pipeline lowers Core to CorePrep with its own adapter. This
/// testing route additionally returns the Haskell lowering of the same
/// Core so the two can be compared.
/// @param source Candidate source bytes; malformed input is permitted.
/// @return Owned Core and CorePrep buffers; a successful status without
/// CorePrep is reported as an internal error.
[[nodiscard]] auto
FuzzCompileStages(std::span<const std::uint8_t> source) -> StageResult;
} // namespace Visual::XSharp::Cli::Frontend
14 changes: 14 additions & 0 deletions Compiler/Fuzzing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,28 @@ cc_binary(
deps = [":wire_fuzz_harness"],
)

# Structural comparison of two CorePrep lowerings. It has no frontend or LLVM
# dependency so its own contract can be tested without either.
cc_library(
name = "coreprep_parity",
srcs = ["CorePrepParity.cpp"],
hdrs = ["CorePrepParity.hpp"],
visibility = ["//Compiler/Fuzzing/Tests:__pkg__"],
deps = ["//Compiler/Headers/Visual/XSharp/Core:core"],
)

cc_library(
name = "source_fuzz_harness",
srcs = ["SourceFuzz.cpp"],
hdrs = ["SourceFuzz.hpp"],
deps = [
":coreprep_parity",
"//Compiler/Backend/LLVM:llvm_backend",
"//Compiler/Cli/Commands:frontend",
"//Compiler/Core:core",
"//Compiler/Driver:pipeline",
"//Compiler/Headers/Visual/XSharp/Core:core",
"//Compiler/Headers/Visual/XSharp/Core:coreprep_wire",
],
)

Expand Down
Loading
Loading