Files
carbon-lang/explorer/fuzzing/fuzzer_util.cpp
T
pk19604014 150057f260 Added GetRunfilesFile() to be able to reliably determine prelude location under various invocation scenarios
The logic is using bazel Runfiles API per Jon's suggestion.

One of the scenarios used by the fuzzing framework is to place the binary and its runfiles into a folder, resulting in a directory layout like this:

[temp dir]
fuzzer
fuzzer.runfiles/

Tested:
bazel test -c opt explorer/fuzzing:explorer_fuzzer
bazel build -c opt explorer/fuzzing:explorer_fuzzer + run the binary from various locations
2022-05-02 10:36:21 -04:00

80 lines
2.8 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "explorer/fuzzing/fuzzer_util.h"
#include "common/check.h"
#include "common/fuzzing/proto_to_carbon.h"
#include "explorer/interpreter/exec_program.h"
#include "explorer/syntax/parse.h"
#include "explorer/syntax/prelude.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "tools/cpp/runfiles/runfiles.h"
namespace Carbon {
// Appended to fuzzer-generated Carbon source when the source is missing
// `Main()` definition, to prevent early error return in semantic analysis.
static constexpr char EmptyMain[] = R"(
fn Main() -> i32 {
return 0;
}
)";
auto Internal::GetRunfilesFile(const std::string& file)
-> ErrorOr<std::string> {
using bazel::tools::cpp::runfiles::Runfiles;
std::string error;
// `Runfiles::Create()` fails if passed an empty `argv0`.
std::unique_ptr<Runfiles> runfiles(Runfiles::Create(
/*argv0=*/llvm::sys::fs::getMainExecutable(nullptr, nullptr), &error));
if (runfiles == nullptr) {
return Error(error);
}
std::string full_path = runfiles->Rlocation(file);
if (!llvm::sys::fs::exists(full_path)) {
return ErrorBuilder() << full_path << " doesn't exist";
}
return full_path;
}
auto ProtoToCarbonWithMain(const Fuzzing::CompilationUnit& compilation_unit)
-> std::string {
const bool has_main = std::any_of(
compilation_unit.declarations().begin(),
compilation_unit.declarations().end(),
[](const Fuzzing::Declaration& decl) {
return decl.kind_case() == Fuzzing::Declaration::kFunction &&
decl.function().name() == "Main";
});
return Carbon::ProtoToCarbon(compilation_unit) + (has_main ? "" : EmptyMain);
}
void ParseAndExecute(const Fuzzing::CompilationUnit& compilation_unit) {
const std::string source = ProtoToCarbonWithMain(compilation_unit);
Arena arena;
ErrorOr<AST> ast = ParseFromString(&arena, "Fuzzer.carbon", source,
/*parser_debug=*/false);
if (!ast.ok()) {
llvm::errs() << "Parsing failed: " << ast.error().message() << "\n";
return;
}
const ErrorOr<std::string> prelude_path =
Internal::GetRunfilesFile("carbon/explorer/data/prelude.carbon");
CHECK(prelude_path.ok()) << prelude_path.error().message();
AddPrelude(*prelude_path, &arena, &ast->declarations);
const ErrorOr<int> result =
ExecProgram(&arena, *ast, /*trace_stream=*/std::nullopt);
if (!result.ok()) {
llvm::errs() << "Execution failed: " << result.error().message() << "\n";
return;
}
llvm::outs() << "Executed OK: " << *result << "\n";
}
} // namespace Carbon