From 98ebf1d013b522919881814b2f98fe4c292c83a9 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 7 Mar 2023 03:31:39 +0300 Subject: [PATCH 01/41] [feat] Add core features for error-guided search --- include/klee/Core/Interpreter.h | 35 +- include/klee/Core/TerminationTypes.h | 22 +- include/klee/Expr/SourceBuilder.h | 9 +- include/klee/Expr/SymbolicSource.h | 30 +- include/klee/Module/KModule.h | 13 +- include/klee/Module/Target.h | 109 +++ include/klee/Module/TargetForest.h | 203 +++++ include/klee/Module/TargetHash.h | 51 ++ include/klee/Solver/Solver.h | 24 + include/klee/Solver/SolverImpl.h | 15 + include/klee/Support/OptionCategories.h | 1 + lib/Core/AddressSpace.cpp | 12 +- lib/Core/AddressSpace.h | 5 +- lib/Core/CMakeLists.txt | 3 +- lib/Core/ExecutionState.cpp | 64 +- lib/Core/ExecutionState.h | 38 +- lib/Core/Executor.cpp | 744 ++++++++++++++---- lib/Core/Executor.h | 69 +- lib/Core/PForest.cpp | 49 ++ lib/Core/PForest.h | 50 ++ lib/Core/PTree.cpp | 14 +- lib/Core/PTree.h | 23 +- lib/Core/Searcher.cpp | 494 ++++++++---- lib/Core/Searcher.h | 92 ++- lib/Core/SpecialFunctionHandler.cpp | 16 +- lib/Core/SpecialFunctionHandler.h | 1 + lib/Core/StatsTracker.cpp | 4 +- lib/Core/{Target.cpp => TargetCalculator.cpp} | 26 +- lib/Core/{Target.h => TargetCalculator.h} | 51 +- lib/Core/TimingSolver.cpp | 20 + lib/Core/TimingSolver.h | 3 + lib/Core/UserSearcher.cpp | 27 +- lib/Core/UserSearcher.h | 2 + lib/Expr/SourceBuilder.cpp | 18 +- lib/Module/CMakeLists.txt | 3 + lib/Module/KModule.cpp | 8 +- lib/Module/Target.cpp | 89 +++ lib/Module/TargetForest.cpp | 285 +++++++ lib/Module/TargetHash.cpp | 47 ++ lib/Runner/run_klee.cpp | 107 ++- lib/Solver/Solver.cpp | 11 + lib/Solver/SolverImpl.cpp | 23 + test/Feature/FunctionPointer.c | 2 +- .../LazyInitialization/LazyInitialization.c | 2 +- test/Feature/MakeConcreteSymbolic.c | 2 +- test/Feature/MaxStaticForkPct.c | 8 +- test/Feature/MultipleFreeResolution.c | 2 +- test/Merging/batching_break.c | 2 +- test/Merging/easy_merge.c | 12 +- test/Merging/incomplete_merge.c | 2 +- test/Merging/indirect_value.c | 2 +- test/Merging/loop_merge.c | 8 +- test/Merging/merge_fail.c | 8 +- test/Merging/nested_merge.c | 10 +- test/Merging/split_merge.c | 10 +- test/Merging/state_termination.c | 2 +- test/Merging/unexpected_close.c | 2 +- test/Runtime/POSIX/DirConsistency.c | 4 +- unittests/Searcher/SearcherTest.cpp | 79 +- 59 files changed, 2506 insertions(+), 561 deletions(-) create mode 100644 include/klee/Module/Target.h create mode 100644 include/klee/Module/TargetForest.h create mode 100644 include/klee/Module/TargetHash.h create mode 100644 lib/Core/PForest.cpp create mode 100644 lib/Core/PForest.h rename lib/Core/{Target.cpp => TargetCalculator.cpp} (91%) rename lib/Core/{Target.h => TargetCalculator.h} (66%) create mode 100644 lib/Module/Target.cpp create mode 100644 lib/Module/TargetForest.cpp create mode 100644 lib/Module/TargetHash.cpp diff --git a/include/klee/Core/Interpreter.h b/include/klee/Core/Interpreter.h index dde7f8fa43..af35eee06a 100644 --- a/include/klee/Core/Interpreter.h +++ b/include/klee/Core/Interpreter.h @@ -9,15 +9,19 @@ #ifndef KLEE_INTERPRETER_H #define KLEE_INTERPRETER_H +#include "TerminationTypes.h" + #include #include #include #include +#include #include struct KTest; namespace llvm { +class BasicBlock; class Function; class LLVMContext; class Module; @@ -29,6 +33,7 @@ namespace klee { class ExecutionState; class Interpreter; class TreeStreamWriter; +class TargetForest; class InterpreterHandler { public: @@ -50,6 +55,14 @@ class InterpreterHandler { class Interpreter { public: + enum class GuidanceKind { + NoGuidance, // Default symbolic execution + CoverageGuidance, // Use GuidedSearcher and guidedRun to maximize full code + // coverage + ErrorGuidance // Use GuidedSearcher and guidedRun to maximize specified + // targets coverage + }; + /// ModuleOptions - Module level options which can be set when /// registering a module with the interpreter. struct ModuleOptions { @@ -57,18 +70,21 @@ class Interpreter { std::string EntryPoint; std::string OptSuffix; bool Optimize; + bool Simplify; bool CheckDivZero; bool CheckOvershift; bool WithFPRuntime; + bool WithPOSIXRuntime; ModuleOptions(const std::string &_LibraryDir, const std::string &_EntryPoint, const std::string &_OptSuffix, - bool _Optimize, bool _CheckDivZero, bool _CheckOvershift, - bool _WithFPRuntime) + bool _Optimize, bool _Simplify, bool _CheckDivZero, + bool _CheckOvershift, bool _WithFPRuntime, + bool _WithPOSIXRuntime) : LibraryDir(_LibraryDir), EntryPoint(_EntryPoint), - OptSuffix(_OptSuffix), Optimize(_Optimize), + OptSuffix(_OptSuffix), Optimize(_Optimize), Simplify(_Simplify), CheckDivZero(_CheckDivZero), CheckOvershift(_CheckOvershift), - WithFPRuntime(_WithFPRuntime) {} + WithFPRuntime(_WithFPRuntime), WithPOSIXRuntime(_WithPOSIXRuntime) {} }; enum LogType { @@ -84,12 +100,17 @@ class Interpreter { /// symbolic values. This is used to test the correctness of the /// symbolic execution on concrete programs. unsigned MakeConcreteSymbolic; + GuidanceKind Guidance; + std::unordered_map &Targets; - InterpreterOptions() : MakeConcreteSymbolic(false) {} + InterpreterOptions( + std::unordered_map &Targets) + : MakeConcreteSymbolic(false), Guidance(GuidanceKind::NoGuidance), + Targets(Targets) {} }; protected: - const InterpreterOptions interpreterOpts; + const InterpreterOptions &interpreterOpts; Interpreter(const InterpreterOptions &_interpreterOpts) : interpreterOpts(_interpreterOpts) {} @@ -140,7 +161,7 @@ class Interpreter { /*** Runtime options ***/ - virtual void setHaltExecution(bool value) = 0; + virtual void setHaltExecution(HaltExecution::Reason value) = 0; virtual void setInhibitForking(bool value) = 0; diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 2c77c05cf1..7ea2f01ee6 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -42,7 +42,8 @@ TTYPE(Replay, 27U, "") \ TTYPE(Merge, 28U, "") \ TTYPE(SilentExit, 29U, "") \ - MARK(END, 29U) + TTYPE(Paused, 30U, "") \ + MARK(END, 30U) ///@brief Reason an ExecutionState got terminated. enum class StateTerminationType : std::uint8_t { @@ -53,4 +54,23 @@ enum class StateTerminationType : std::uint8_t { #undef MARK }; +namespace HaltExecution { +enum Reason { + NotHalt = 0, + MaxTests, + MaxInstructions, + MaxSteppedInstructions, + MaxTime, + CovCheck, + NoMoreStates, + ReachedTarget, + ErrorOnWhichShouldExit, + Interrupt, + MaxDepth, + MaxStackFrames, + MaxSolverTime, + Unspecified +}; +}; + #endif diff --git a/include/klee/Expr/SourceBuilder.h b/include/klee/Expr/SourceBuilder.h index 94b25b02e9..99349558fa 100644 --- a/include/klee/Expr/SourceBuilder.h +++ b/include/klee/Expr/SourceBuilder.h @@ -10,7 +10,10 @@ class SourceBuilder { private: static ref constantSource; static ref makeSymbolicSource; - static ref lazyInitializationMakeSymbolicSource; + static ref symbolicAddressSource; + static ref lazyInitializationSymbolicSource; + static ref irreproducibleSource; + static ref symbolicValueSource; public: SourceBuilder() = delete; @@ -20,7 +23,9 @@ class SourceBuilder { static ref makeSymbolic(); static ref symbolicAddress(); static ref symbolicSize(); - static ref lazyInitializationMakeSymbolic(); + static ref lazyInitializationSymbolic(); + static ref irreproducible(); + static ref symbolicValue(); }; }; // namespace klee diff --git a/include/klee/Expr/SymbolicSource.h b/include/klee/Expr/SymbolicSource.h index dc53e4db3e..1b70205192 100644 --- a/include/klee/Expr/SymbolicSource.h +++ b/include/klee/Expr/SymbolicSource.h @@ -19,7 +19,9 @@ class SymbolicSource { LazyInitializationSymbolic, MakeSymbolic, SymbolicAddress, - SymbolicSize + SymbolicSize, + Irreproducible, + SymbolicValue }; public: @@ -120,7 +122,7 @@ class LazyInitializationSymbolicSource : public SymbolicSource { public: Kind getKind() const override { return Kind::LazyInitializationSymbolic; } virtual std::string getName() const override { - return "lazyInitializationMakeSymbolic"; + return "lazyInitializationSymbolic"; } virtual bool isSymcrete() const override { return false; } @@ -129,6 +131,30 @@ class LazyInitializationSymbolicSource : public SymbolicSource { } }; +class IrreproducibleSource : public SymbolicSource { +public: + Kind getKind() const override { return Kind::Irreproducible; } + virtual std::string getName() const override { return "irreproducible"; } + virtual bool isSymcrete() const override { return false; } + + static bool classof(const SymbolicSource *S) { + return S->getKind() == Kind::Irreproducible; + } + static bool classof(const IrreproducibleSource *) { return true; } +}; + +class SymbolicValueSource : public SymbolicSource { +public: + Kind getKind() const override { return Kind::SymbolicValue; } + virtual std::string getName() const override { return "symbolicValue"; } + virtual bool isSymcrete() const override { return false; } + + static bool classof(const SymbolicSource *S) { + return S->getKind() == Kind::SymbolicValue; + } + static bool classof(const SymbolicValueSource *) { return true; } +}; + } // namespace klee #endif /* KLEE_SYMBOLICSOURCE_H */ diff --git a/include/klee/Module/KModule.h b/include/klee/Module/KModule.h index 6b496f3377..42ba9ed619 100644 --- a/include/klee/Module/KModule.h +++ b/include/klee/Module/KModule.h @@ -15,10 +15,13 @@ #include "klee/Module/KCallable.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/CFG.h" +#include #include #include #include +#include #include #include #include @@ -145,7 +148,9 @@ struct KFunction : public KCallable { unsigned getArgRegister(unsigned index) const { return index; } - llvm::StringRef getName() const override { return function->getName(); } + llvm::StringRef getName() const override { + return function ? function->getName() : ""; + } llvm::FunctionType *getFunctionType() const override { return function->getFunctionType(); @@ -174,6 +179,9 @@ class KConstant { }; class KModule { +private: + bool withPosixRuntime; + public: std::unique_ptr module; std::unique_ptr targetData; @@ -201,6 +209,7 @@ class KModule { // Functions which are part of KLEE runtime std::set internalFunctions; + // Mark function with functionName as part of the KLEE runtime void addInternalFunction(const char *functionName); // Replace std functions with KLEE intrinsics void replaceFunction(const std::unique_ptr &m, @@ -248,6 +257,8 @@ class KModule { KBlock *getKBlock(llvm::BasicBlock *bb); bool inMainModule(llvm::Function *f); + + bool WithPOSIXRuntime() { return withPosixRuntime; } }; } // namespace klee diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h new file mode 100644 index 0000000000..f2d3e877f5 --- /dev/null +++ b/include/klee/Module/Target.h @@ -0,0 +1,109 @@ +//===-- Target.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TARGET_H +#define KLEE_TARGET_H + +#include "klee/ADT/RNG.h" +#include "klee/ADT/Ref.h" +#include "klee/Module/KModule.h" +#include "klee/System/Time.h" + +#include "klee/Support/OptionCategories.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include + +namespace klee { +struct EquivTargetCmp; +struct TargetHash; +struct TargetCmp; + +enum ReachWithError { + DoubleFree = 0, + UseAfterFree, + NullPointerException, + NullCheckAfterDerefException, + Reachable, + None, +}; + +static const char *ReachWithErrorNames[] = { + "DoubleFree", + "UseAfterFree", + "NullPointerException", + "NullCheckAfterDerefException", + "Reachable", + "None", +}; + +struct Target { +private: + typedef std::unordered_set + EquivTargetHashSet; + typedef std::unordered_set TargetHashSet; + static EquivTargetHashSet cachedTargets; + static TargetHashSet targets; + KBlock *block; + ReachWithError error; + unsigned id; + + Target(KBlock *block, ReachWithError error, unsigned id) + : block(block), error(error), id(id) {} + + Target(KBlock *block, ReachWithError error) : Target(block, error, 0) {} + + Target(KBlock *block) : Target(block, ReachWithError::None) {} + + static ref getFromCacheOrReturn(Target *target); + +public: + bool isReported = false; + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + static ref create(KBlock *block, ReachWithError error, unsigned id); + static ref create(KBlock *block); + + int compare(const Target &other) const; + + bool equals(const Target &other) const; + + bool operator<(const Target &other) const; + + bool operator==(const Target &other) const; + + bool atReturn() const { return isa(block); } + + KBlock *getBlock() const { return block; } + + bool isNull() const { return block == nullptr; } + + explicit operator bool() const noexcept { return !isNull(); } + + unsigned hash() const { return reinterpret_cast(block); } + + bool shouldFailOnThisTarget() const { return error != ReachWithError::None; } + + unsigned getId() const { return id; } + + ReachWithError getError() const { return error; } + + std::string toString() const; + ~Target(); +}; +} // namespace klee + +#endif /* KLEE_TARGET_H */ diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h new file mode 100644 index 0000000000..e91f843995 --- /dev/null +++ b/include/klee/Module/TargetForest.h @@ -0,0 +1,203 @@ +//===-- TargetForest.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Class to represent prefix tree of Targets +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TARGETFOREST_H +#define KLEE_TARGETFOREST_H + +#include "klee/ADT/Ref.h" +#include "klee/Expr/Expr.h" +#include "klee/Module/KModule.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" + +#include + +namespace klee { +struct RefTargetHash; +struct RefTargetCmp; +struct TargetsHistoryHash; +struct EquivTargetsHistoryCmp; +struct TargetsHistoryCmp; + +class TargetForest { +public: + using TargetsSet = + std::unordered_set, RefTargetHash, RefTargetCmp>; + +private: + class Layer { + using InternalLayer = std::unordered_map, ref, + RefTargetHash, RefTargetCmp>; + InternalLayer forest; + + Layer(const InternalLayer &forest) : forest(forest) {} + explicit Layer(const Layer *layer) : Layer(layer->forest) {} + explicit Layer(const ref layer) : Layer(layer.get()) {} + void unionWith(Layer *other); + void block(ref target); + + public: + using iterator = InternalLayer::const_iterator; + + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + explicit Layer() {} + + iterator find(ref b) const { return forest.find(b); } + iterator begin() const { return forest.begin(); } + iterator end() const { return forest.end(); } + void insert(ref loc, ref nextLayer) { + forest[loc] = nextLayer; + } + bool empty() const { return forest.empty(); } + bool deepFind(ref target) const; + bool deepFindIn(ref child, ref target) const; + size_t size() const { return forest.size(); } + Layer *replaceChildWith(ref child, Layer *other) const; + Layer *removeChild(ref child) const; + Layer *addChild(ref child) const; + Layer *blockLeafInChild(ref child, ref leaf) const; + Layer *blockLeaf(ref leaf) const; + void dump(unsigned n) const; + ref deepCopy(); + Layer *copy(); + }; + + ref forest; + +public: + class History { + private: + typedef std::unordered_set + EquivTargetsHistoryHashSet; + typedef std::unordered_set + TargetsHistoryHashSet; + + static EquivTargetsHistoryHashSet cachedHistories; + static TargetsHistoryHashSet histories; + unsigned hashValue; + + explicit History(ref _target, ref _visitedTargets) + : target(_target), visitedTargets(_visitedTargets) { + computeHash(); + } + + public: + const ref target; + const ref visitedTargets; + + static ref create(ref _target, + ref _visitedTargets); + static ref create(ref _target); + static ref create(); + + ref add(ref _target) { + return History::create(_target, this); + } + + unsigned hash() const { return hashValue; } + + int compare(const History &h) const; + bool equals(const History &h) const; + + void computeHash() { + unsigned res = 0; + if (target) { + res = target->hash() * Expr::MAGIC_HASH_CONSTANT; + } + if (visitedTargets) { + res ^= visitedTargets->hash() * Expr::MAGIC_HASH_CONSTANT; + } + hashValue = res; + } + + void dump() const; + + ~History(); + + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + }; + +private: + ref history; + KFunction *entryFunction; + +public: + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + KFunction *getEntryFunction() { return entryFunction; } + + TargetForest(ref layer, KFunction *entryFunction) + : forest(layer), history(History::create()), + entryFunction(entryFunction) {} + TargetForest() : TargetForest(new Layer(), nullptr) {} + + bool empty() const { return forest.isNull() || forest->empty(); } + Layer::iterator begin() const { return forest->begin(); } + Layer::iterator end() const { return forest->end(); } + bool contains(ref b) { return forest->find(b) != forest->end(); } + + /// @brief Number of children of this layer (immediate successors) + size_t successorCount() const { return forest->size(); } + + void stepTo(ref); + void add(ref); + void remove(ref); + void blockIn(ref, ref); + const ref getHistory() { return history; }; + const ref getTargets() { return forest; }; + void dump() const; + ref deepCopy(); +}; + +struct TargetsHistoryHash { + unsigned operator()(const TargetForest::History *t) const { + return t ? t->hash() : 0; + } +}; + +struct TargetsHistoryCmp { + bool operator()(const TargetForest::History *a, + const TargetForest::History *b) const { + return a == b; + } +}; + +struct EquivTargetsHistoryCmp { + bool operator()(const TargetForest::History *a, + const TargetForest::History *b) const { + if (a == NULL || b == NULL) + return false; + return a->compare(*b) == 0; + } +}; + +struct RefTargetsHistoryHash { + unsigned operator()(const ref &t) const { + return t->hash(); + } +}; + +struct RefTargetsHistoryCmp { + bool operator()(const ref &a, + const ref &b) const { + return a.get() == b.get(); + } +}; + +} // namespace klee + +#endif /* KLEE_TARGETFOREST_H */ diff --git a/include/klee/Module/TargetHash.h b/include/klee/Module/TargetHash.h new file mode 100644 index 0000000000..c7bd9525d2 --- /dev/null +++ b/include/klee/Module/TargetHash.h @@ -0,0 +1,51 @@ +//===-- TargetHash.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TARGETHASH_H +#define KLEE_TARGETHASH_H + +#include "klee/ADT/Ref.h" + +#include +#include + +namespace llvm { +class BasicBlock; +} + +namespace klee { +struct Target; + +struct TargetHash { + unsigned operator()(const Target *t) const; +}; + +struct TargetCmp { + bool operator()(const Target *a, const Target *b) const; +}; + +struct EquivTargetCmp { + bool operator()(const Target *a, const Target *b) const; +}; + +struct RefTargetHash { + unsigned operator()(const ref &t) const; +}; + +struct RefTargetCmp { + bool operator()(const ref &a, const ref &b) const; +}; + +typedef std::pair Transition; + +struct TransitionHash { + std::size_t operator()(const Transition &p) const; +}; +} // namespace klee +#endif /* KLEE_TARGETHASH_H */ diff --git a/include/klee/Solver/Solver.h b/include/klee/Solver/Solver.h index 4ee955b6a4..b3cd796512 100644 --- a/include/klee/Solver/Solver.h +++ b/include/klee/Solver/Solver.h @@ -280,6 +280,28 @@ class Solver { public: enum Validity { True = 1, False = -1, Unknown = 0 }; + enum PartialValidity { + /// The query is provably true. + MustBeTrue = 1, + + /// The query is provably false. + MustBeFalse = -1, + + /// The query is not provably false (a true assignment is known to + /// exist). + MayBeTrue = 2, + + /// The query is not provably true (a false assignment is known to + /// exist). + MayBeFalse = -2, + + /// The query is known to have both true and false assignments. + TrueOrFalse = 0, + + /// The validity of the query is unknown. + None = 3 + }; + public: /// validity_to_str - Return the name of given Validity enum value. static const char *validity_to_str(Validity v); @@ -308,6 +330,8 @@ class Solver { bool evaluate(const Query &, ref &queryResult, ref &negateQueryResult); + Solver::PartialValidity evaluate(const Query &query); + /// mustBeTrue - Determine if the expression is provably true. /// /// This evaluates the following logical formula: diff --git a/include/klee/Solver/SolverImpl.h b/include/klee/Solver/SolverImpl.h index a579cc6fca..8d3271304e 100644 --- a/include/klee/Solver/SolverImpl.h +++ b/include/klee/Solver/SolverImpl.h @@ -68,6 +68,8 @@ class SolverImpl { ref &queryResult, ref &negatedQueryResult); + virtual Solver::PartialValidity computePartialValidity(const Query &query); + /// computeTruth - Determine whether the given query expression is provably /// true given the constraints. /// @@ -117,6 +119,19 @@ class SolverImpl { return nullptr; } + virtual Solver::PartialValidity validityToPartial(Solver::Validity v) { + switch (v) { + case Solver::Validity::True: + return Solver::PartialValidity::MustBeTrue; + case Solver::Validity::False: + return Solver::PartialValidity::MustBeFalse; + case Solver::Validity::Unknown: + return Solver::PartialValidity::TrueOrFalse; + default: // Silence compiler warning + return Solver::PartialValidity::None; + } + } + virtual void setCoreSolverTimeout(time::Span timeout){}; }; diff --git a/include/klee/Support/OptionCategories.h b/include/klee/Support/OptionCategories.h index be37ba06e3..879249fa2c 100644 --- a/include/klee/Support/OptionCategories.h +++ b/include/klee/Support/OptionCategories.h @@ -17,6 +17,7 @@ #include "llvm/Support/CommandLine.h" namespace klee { +extern llvm::cl::OptionCategory ExecCat; extern llvm::cl::OptionCategory DebugCat; extern llvm::cl::OptionCategory ExecCat; extern llvm::cl::OptionCategory MergeCat; diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp index 1aa4a060a1..fac4dc0fc9 100644 --- a/lib/Core/AddressSpace.cpp +++ b/lib/Core/AddressSpace.cpp @@ -145,7 +145,8 @@ bool AddressSpace::resolveOneIfUnique(ExecutionState &state, bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, MOPredicate predicate, - bool &success) const { + bool &success, + const std::atomic_bool &haltExecution) const { if (ref CE = dyn_cast(address)) { if (resolveOne(CE, objectType, result)) { success = true; @@ -197,6 +198,10 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, continue; } + if (haltExecution) { + break; + } + bool mayBeTrue; if (!solver->mayBeTrue(state.constraints, mo->getBoundsCheckPointer(address), mayBeTrue, @@ -215,7 +220,8 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, - IDType &result, bool &success) const { + IDType &result, bool &success, + const std::atomic_bool &haltExecution) const { MOPredicate predicate([](const MemoryObject *mo) { return true; }); if (UseTimestamps) { ref base = @@ -243,7 +249,7 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, } return resolveOne(state, solver, address, objectType, result, predicate, - success); + success, haltExecution); } int AddressSpace::checkPointerInObject(ExecutionState &state, diff --git a/lib/Core/AddressSpace.h b/lib/Core/AddressSpace.h index 25095dd89b..01d907cdb8 100644 --- a/lib/Core/AddressSpace.h +++ b/lib/Core/AddressSpace.h @@ -95,10 +95,11 @@ class AddressSpace { /// \return true iff an object was found at \a address. bool resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, - bool &success) const; + bool &success, const std::atomic_bool &haltExecution) const; bool resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, - MOPredicate predicate, bool &success) const; + MOPredicate predicate, bool &success, + const std::atomic_bool &haltExecution) const; /// @brief Tries to resolve the pointer in the concrete object /// if it value is unique. diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 2369a10ae2..b30fc75fc8 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -21,12 +21,13 @@ klee_add_component(kleeCore ImpliedValue.cpp Memory.cpp MemoryManager.cpp + PForest.cpp PTree.cpp Searcher.cpp SeedInfo.cpp SpecialFunctionHandler.cpp StatsTracker.cpp - Target.cpp + TargetCalculator.cpp TimingSolver.cpp TypeManager.cpp UserSearcher.cpp diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 5b8cebccf7..f47b3429f7 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -25,11 +25,13 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include #include #include +#include using namespace llvm; using namespace klee; @@ -72,11 +74,28 @@ StackFrame::StackFrame(const StackFrame &s) StackFrame::~StackFrame() { delete[] locals; } /***/ +ExecutionState::ExecutionState() + : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), + depth(0), ptreeNode(nullptr), steppedInstructions(0), + steppedMemoryInstructions(0), instsSinceCovNew(0), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false) { + setID(); +} ExecutionState::ExecutionState(KFunction *kf) : initPC(kf->instructions), pc(initPC), prevPC(pc), roundingMode(llvm::APFloat::rmNearestTiesToEven) { pushFrame(nullptr, kf); +} + +ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) + : initPC(kb->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), + depth(0), ptreeNode(nullptr), steppedInstructions(0), + steppedMemoryInstructions(0), instsSinceCovNew(0), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false) { + pushFrame(nullptr, kf); setID(); } @@ -94,9 +113,9 @@ ExecutionState::ExecutionState(const ExecutionState &state) stack(state.stack), incomingBBIndex(state.incomingBBIndex), depth(state.depth), multilevel(state.multilevel), level(state.level), addressSpace(state.addressSpace), constraints(state.constraints), - pathOS(state.pathOS), symPathOS(state.symPathOS), - coveredLines(state.coveredLines), symbolics(state.symbolics), - symbolicSizes(state.symbolicSizes), + targetForest(state.targetForest), pathOS(state.pathOS), + symPathOS(state.symPathOS), coveredLines(state.coveredLines), + symbolics(state.symbolics), symbolicSizes(state.symbolicSizes), resolvedPointers(state.resolvedPointers), cexPreferences(state.cexPreferences), arrayNames(state.arrayNames), openMergeStack(state.openMergeStack), @@ -108,8 +127,7 @@ ExecutionState::ExecutionState(const ExecutionState &state) ? state.unwindingInformation->clone() : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), - targets(state.targets), gepExprBases(state.gepExprBases), - gepExprOffsets(state.gepExprOffsets) { + gepExprBases(state.gepExprBases), gepExprOffsets(state.gepExprOffsets) { for (const auto &cur_mergehandler : openMergeStack) cur_mergehandler->addOpenState(this); } @@ -134,6 +152,36 @@ bool ExecutionState::inSymbolics(const MemoryObject *mo) const { return false; } +ExecutionState *ExecutionState::withStackFrame(KInstIterator caller, + KFunction *kf) { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + newState->pushFrame(caller, kf); + newState->initPC = kf->blockMap[&*kf->function->begin()]->instructions; + newState->pc = newState->initPC; + newState->prevPC = newState->pc; + return newState; +} + +ExecutionState *ExecutionState::withKFunction(KFunction *kf) { + return withStackFrame(nullptr, kf); +} + +ExecutionState *ExecutionState::withKBlock(KBlock *kb) { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + newState->initPC = kb->instructions; + newState->pc = newState->initPC; + newState->prevPC = newState->pc; + return newState; +} + +ExecutionState *ExecutionState::copy() { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + return newState; +} + void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { stack.emplace_back(StackFrame(caller, kf)); } @@ -503,6 +551,12 @@ void ExecutionState::increaseLevel() { transitionLevel.insert(std::make_pair(srcbb, dstbb)); } +bool ExecutionState::isTransfered() { return getPrevPCBlock() != getPCBlock(); } + bool ExecutionState::isGEPExpr(ref expr) const { return UseGEPOptimization && gepExprBases.find(expr) != gepExprBases.end(); } + +bool ExecutionState::visited(KBlock *block) const { + return level.find(block->basicBlock) != level.end(); +} diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index e7bcca858a..4fc11f3e82 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -12,14 +12,18 @@ #include "AddressSpace.h" #include "MergeHandler.h" -#include "Target.h" #include "klee/ADT/ImmutableSet.h" #include "klee/ADT/TreeStream.h" +#include "klee/Core/TerminationTypes.h" +#include "klee/Expr/Assignment.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" #include "klee/Expr/ExprHashMap.h" #include "klee/Module/KInstIterator.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetForest.h" +#include "klee/Module/TargetHash.h" #include "klee/Solver/Solver.h" #include "klee/System/Time.h" @@ -28,6 +32,7 @@ #include #include #include +#include #include #include @@ -42,6 +47,8 @@ struct KInstruction; class MemoryObject; class PTreeNode; struct InstructionInfo; +struct Target; +struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); @@ -163,9 +170,6 @@ struct Symbolic { } }; -// typedef std::pair, const Array *> Symbolic; -typedef std::pair Transition; - /// @brief ExecutionState representing a path under exploration class ExecutionState { #ifdef KLEE_UNITTEST @@ -196,7 +200,7 @@ class ExecutionState { /// @brief Remember from which Basic Block control flow arrived /// (i.e. to select the right phi values) - std::uint32_t incomingBBIndex; + std::int32_t incomingBBIndex; // Overall state of the state - Data specific @@ -215,6 +219,9 @@ class ExecutionState { /// @brief Constraints collected so far ConstraintSet constraints; + /// @brief Key points which should be visited through execution + TargetForest targetForest; + /// Statistics and information /// @brief Metadata utilized and collected by solvers for this state @@ -287,19 +294,18 @@ class ExecutionState { /// @brief Disables forking for this state. Set by user code bool forkDisabled = false; - /// @brief The targets that the state must achieve - std::set targets; - ExprHashMap, llvm::Type *>> gepExprBases; ExprHashMap> gepExprOffsets; + ReachWithError error = ReachWithError::None; + std::atomic terminationReasonType{ + HaltExecution::NotHalt}; + public: -#ifdef KLEE_UNITTEST - // provide this function only in the context of unittests - ExecutionState() = default; -#endif // only to create the initial state + explicit ExecutionState(); explicit ExecutionState(KFunction *kf); + explicit ExecutionState(KFunction *kf, KBlock *kb); // no copy assignment, use copy constructor ExecutionState &operator=(const ExecutionState &) = delete; // no move ctor @@ -310,6 +316,11 @@ class ExecutionState { ~ExecutionState(); ExecutionState *branch(); + ExecutionState *withKFunction(KFunction *kf); + ExecutionState *withStackFrame(KInstIterator caller, KFunction *kf); + ExecutionState *withKBlock(KBlock *kb); + ExecutionState *empty(); + ExecutionState *copy(); bool inSymbolics(const MemoryObject *mo) const; @@ -336,12 +347,15 @@ class ExecutionState { bool merge(const ExecutionState &b); void dumpStack(llvm::raw_ostream &out) const; + bool visited(KBlock *block) const; + std::uint32_t getID() const { return id; }; void setID() { id = nextID++; }; llvm::BasicBlock *getInitPCBlock() const; llvm::BasicBlock *getPrevPCBlock() const; llvm::BasicBlock *getPCBlock() const; void increaseLevel(); + bool isTransfered(); bool isGEPExpr(ref expr) const; }; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 3eed4642da..c87878ffb8 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -19,11 +19,13 @@ #include "ImpliedValue.h" #include "Memory.h" #include "MemoryManager.h" +#include "PForest.h" #include "PTree.h" #include "Searcher.h" #include "SeedInfo.h" #include "SpecialFunctionHandler.h" #include "StatsTracker.h" +#include "TargetCalculator.h" #include "TimingSolver.h" #include "TypeManager.h" #include "UserSearcher.h" @@ -34,6 +36,7 @@ #include "klee/Config/config.h" #include "klee/Core/Interpreter.h" #include "klee/Expr/ArrayExprOptimizer.h" +#include "klee/Expr/ArrayExprVisitor.h" #include "klee/Expr/Assignment.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" @@ -101,6 +104,7 @@ typedef unsigned TypeSize; #include #include #include +#include #include #include #include @@ -145,6 +149,9 @@ cl::opt cl::desc("Enable lazy initialization (default=true)"), cl::cat(ExecCat)); +cl::opt ForkPartialValidity("fork-partial-validity", cl::init(false), + cl::cat(ExecCat)); + cl::opt TypeSystem("type-system", cl::desc("Use information about type system from specified " @@ -194,6 +201,12 @@ cl::opt EmitAllErrors( "(default=false, i.e. one per (error,instruction) pair)"), cl::cat(TestGenCat)); +cl::opt SkipNotLazyAndSymbolicPointers( + "skip-not-lazy-and-symbolic-pointers", cl::init(false), + cl::desc("Set pointers only on lazy and make_symbolic variables " + "(default=false)"), + cl::cat(TestGenCat)); + /* Constraint solving options */ cl::opt MaxSymArraySize( @@ -335,6 +348,12 @@ cl::opt "Set to 0 to disable (default=0)"), cl::init(0), cl::cat(TerminationCat)); +cl::opt MaxSteppedInstructions( + "max-stepped-instructions", + cl::desc("Stop state execution after this many instructions. Set to 0 to " + "disable (default=0)"), + cl::init(0), cl::cat(TerminationCat)); + cl::opt MaxForks( "max-forks", cl::desc("Only fork this many times. Set to -1 to disable (default=-1)"), @@ -459,8 +478,8 @@ extern llvm::cl::opt MaxConstantAllocationSize; extern llvm::cl::opt MaxSymbolicAllocationSize; // XXX hack -extern "C" unsigned dumpStates, dumpPTree; -unsigned dumpStates = 0, dumpPTree = 0; +extern "C" unsigned dumpStates, dumpPForest; +unsigned dumpStates = 0, dumpPForest = 0; const std::unordered_set Executor::supportedFPIntrinsics = { // Intrinsic::fabs, //handled individually because of its presence in @@ -482,14 +501,16 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, concretizationManager(new ConcretizationManager(EqualitySubstitution)), codeGraphDistance(new CodeGraphDistance()), replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), - haltExecution(false), ivcEnabled(false), + haltExecution(HaltExecution::NotHalt), ivcEnabled(false), debugLogBuffer(debugBufferString) { + guidanceKind = opts.Guidance; + const time::Span maxTime{MaxTime}; if (maxTime) timers.add(std::make_unique(maxTime, [&] { klee_message("HaltTimer invoked"); - setHaltExecution(true); + setHaltExecution(HaltExecution::MaxTime); })); coreSolverTimeout = time::Span{MaxCoreSolverTime}; @@ -971,7 +992,7 @@ void Executor::branch(ExecutionState &state, ExecutionState *ns = es->branch(); addedStates.push_back(ns); result.push_back(ns); - processTree->attach(es->ptreeNode, ns, es, reason); + processForest->attach(es->ptreeNode, ns, es, reason); } } @@ -1087,7 +1108,6 @@ ref Executor::maxStaticPctChecks(ExecutionState ¤t, Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, bool isInternal, BranchType reason) { - Solver::Validity res; std::map>::iterator it = seedMap.find(¤t); bool isSeeding = it != seedMap.end(); @@ -1099,10 +1119,34 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, if (isSeeding) timeout *= static_cast(it->second.size()); solver->setTimeout(timeout); - bool success = solver->evaluate(current.constraints, condition, res, - current.queryMetaData); + Solver::PartialValidity res; + if (ForkPartialValidity) { + res = + solver->evaluate(current.constraints, condition, current.queryMetaData); + } else { + Solver::Validity val; + bool success = solver->evaluate(current.constraints, condition, val, + current.queryMetaData); + if (!success) { + res = Solver::PartialValidity::None; + } else { + switch (val) { + case Solver::Validity::True: + res = Solver::PartialValidity::MustBeTrue; + break; + case Solver::Validity::False: + res = Solver::PartialValidity::MustBeFalse; + break; + case Solver::Validity::Unknown: + res = Solver::PartialValidity::TrueOrFalse; + break; + default: + assert(0 && "Validity did not match any switch case"); + } + } + } solver->setTimeout(time::Span()); - if (!success) { + if (res == Solver::PartialValidity::None) { current.pc = current.prevPC; terminateStateOnSolverError(current, "Query timed out (fork)."); return StatePair(nullptr, nullptr); @@ -1114,31 +1158,31 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, "ran out of branches in replay path mode"); bool branch = (*replayPath)[replayPosition++]; - if (res == Solver::True) { + if (res == Solver::PartialValidity::MustBeTrue) { assert(branch && "hit invalid branch in replay path mode"); - } else if (res == Solver::False) { + } else if (res == Solver::PartialValidity::MustBeFalse) { assert(!branch && "hit invalid branch in replay path mode"); } else { // add constraints if (branch) { - res = Solver::True; + res = Solver::PartialValidity::MustBeTrue; addConstraint(current, condition); } else { - res = Solver::False; + res = Solver::PartialValidity::MustBeFalse; addConstraint(current, Expr::createIsZero(condition)); } } - } else if (res == Solver::Unknown) { + } else if (res == Solver::PartialValidity::TrueOrFalse) { assert(!replayKTest && "in replay mode, only one branch can be true."); if (!branchingPermitted(current)) { TimerStatIncrementer timer(stats::forkTime); if (theRNG.getBool()) { addConstraint(current, condition); - res = Solver::True; + res = Solver::PartialValidity::MustBeTrue; } else { addConstraint(current, Expr::createIsZero(condition)); - res = Solver::False; + res = Solver::PartialValidity::MustBeFalse; } } } @@ -1147,7 +1191,7 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, // Fix branch in only-replay-seed mode, if we don't have both true // and false seeds. if (isSeeding && (current.forkDisabled || OnlyReplaySeeds) && - res == Solver::Unknown) { + res == Solver::PartialValidity::TrueOrFalse) { bool trueSeed = false, falseSeed = false; // Is seed extension still ok here? for (std::vector::iterator siit = it->second.begin(), @@ -1170,7 +1214,8 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, if (!(trueSeed && falseSeed)) { assert(trueSeed || falseSeed); - res = trueSeed ? Solver::True : Solver::False; + res = trueSeed ? Solver::PartialValidity::MustBeTrue + : Solver::PartialValidity::MustBeFalse; addConstraint(current, trueSeed ? condition : Expr::createIsZero(condition)); } @@ -1183,21 +1228,29 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, // the value it has been fixed at, we should take this as a nice // hint to just use the single constraint instead of all the binary // search ones. If that makes sense. - if (res == Solver::True) { + if (res == Solver::PartialValidity::MustBeTrue || + res == Solver::PartialValidity::MayBeTrue) { if (!isInternal) { if (pathWriter) { current.pathOS << "1"; } } + if (res == Solver::PartialValidity::MayBeTrue) { + current.addConstraint(condition); + } return StatePair(¤t, nullptr); - } else if (res == Solver::False) { + } else if (res == Solver::PartialValidity::MustBeFalse || + res == Solver::PartialValidity::MayBeFalse) { if (!isInternal) { if (pathWriter) { current.pathOS << "0"; } } + if (res == Solver::PartialValidity::MayBeFalse) { + current.addConstraint(Expr::createIsZero(condition)); + } return StatePair(nullptr, ¤t); } else { TimerStatIncrementer timer(stats::forkTime); @@ -1246,7 +1299,7 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, } } - processTree->attach(current.ptreeNode, falseState, trueState, reason); + processForest->attach(current.ptreeNode, falseState, trueState, reason); if (pathWriter) { // Need to update the pathOS.id field of falseState, otherwise the same id @@ -1331,7 +1384,7 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { } const Cell &Executor::eval(KInstruction *ki, unsigned index, - ExecutionState &state) const { + ExecutionState &state, bool isSymbolic) { assert(index < ki->inst->getNumOperands()); int vnumber = ki->operands[index]; @@ -1345,6 +1398,10 @@ const Cell &Executor::eval(KInstruction *ki, unsigned index, } else { unsigned index = vnumber; StackFrame &sf = state.stack.back(); + ref reg = sf.locals[index].value; + if (isSymbolic && reg.isNull()) { + prepareSymbolicRegister(state, sf, index); + } return sf.locals[index]; } } @@ -1501,7 +1558,11 @@ void Executor::stepInstruction(ExecutionState &state) { ++state.pc; if (stats::instructions == MaxInstructions) - haltExecution = true; + haltExecution = HaltExecution::MaxInstructions; + + if (MaxSteppedInstructions && + state.steppedInstructions >= MaxSteppedInstructions) + haltExecution = HaltExecution::MaxSteppedInstructions; } static inline const llvm::fltSemantics *fpWidthToSemantics(unsigned width) { @@ -2177,7 +2238,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, } } -void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, +void Executor::transferToBasicBlock(KBlock *kdst, BasicBlock *src, ExecutionState &state) { // Note that in general phi nodes can reuse phi values from the same // block but the incoming value is the eval() result *before* the @@ -2192,8 +2253,7 @@ void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, // instructions know which argument to eval, set the pc, and continue. // XXX this lookup has to go ? - KFunction *kf = state.stack.back().kf; - state.pc = kf->blockMap[dst]->instructions; + state.pc = kdst->instructions; state.increaseLevel(); if (state.pc->inst->getOpcode() == Instruction::PHI) { PHINode *first = static_cast(state.pc->inst); @@ -2201,8 +2261,28 @@ void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, } } +void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, + ExecutionState &state) { + KFunction *kf = state.stack.back().kf; + auto kdst = kf->blockMap[dst]; + transferToBasicBlock(kdst, src, state); +} + void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { Instruction *i = ki->inst; + + if (guidanceKind == GuidanceKind::ErrorGuidance && + state.prevPC->inst->isTerminator()) { + for (auto kvp : state.targetForest) { + auto target = kvp.first; + if (target->getError() == ReachWithError::Reachable && + target->getBlock() == ki->parent) { + terminateStateOnTargetError(state, ReachWithError::Reachable); + return; + } + } + } + switch (i->getOpcode()) { // Control flow case Instruction::Ret: { @@ -2303,7 +2383,12 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // We check that the return value has no users instead of // checking the type, since C defaults to returning int for // undeclared functions. - if (!caller->use_empty()) { + if (kmodule->WithPOSIXRuntime() && + cast(kcaller->parent)->getKFunction() && + cast(kcaller->parent)->getKFunction()->getName() == + "__klee_posix_wrapped_main") { + bindLocal(kcaller, state, ConstantExpr::alloc(0, Expr::Int32)); + } else if (!caller->use_empty()) { terminateStateOnExecError( state, "return void when caller expected a result"); } @@ -2662,13 +2747,18 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { first = false; free = res.second; - } while (free); + } while (free && !haltExecution); } break; } case Instruction::PHI: { - ref result = eval(ki, state.incomingBBIndex, state).value; - bindLocal(ki, state, result); + if (state.incomingBBIndex == -1) + prepareSymbolicValue(state, ki); + else { + ref result; + result = eval(ki, state.incomingBBIndex, state).value; + bindLocal(ki, state, result); + } break; } @@ -3704,7 +3794,7 @@ void Executor::updateStates(ExecutionState *current) { seedMap.find(es); if (it3 != seedMap.end()) seedMap.erase(it3); - processTree->remove(es->ptreeNode); + processForest->remove(es->ptreeNode); delete es; } removedStates.clear(); @@ -3844,82 +3934,90 @@ void Executor::doDumpStates() { updateStates(nullptr); for (const auto &state : states) terminateStateEarly(*state, "Execution halting.", - StateTerminationType::Interrupted); + guidanceKind == GuidanceKind::ErrorGuidance + ? StateTerminationType::SilentExit + : StateTerminationType::Interrupted); updateStates(nullptr); } -void Executor::run(ExecutionState &initialState) { - // Delay init till now so that ticks don't accrue during optimization and - // such. - timers.reset(); +void Executor::seed(ExecutionState &initialState) { + std::vector &v = seedMap[&initialState]; - states.insert(&initialState); + for (std::vector::const_iterator it = usingSeeds->begin(), + ie = usingSeeds->end(); + it != ie; ++it) + v.push_back(SeedInfo(*it)); - if (usingSeeds) { - std::vector &v = seedMap[&initialState]; - - for (std::vector::const_iterator it = usingSeeds->begin(), - ie = usingSeeds->end(); - it != ie; ++it) - v.push_back(SeedInfo(*it)); - - int lastNumSeeds = usingSeeds->size() + 10; - time::Point lastTime, startTime = lastTime = time::getWallTime(); - ExecutionState *lastState = 0; - while (!seedMap.empty()) { - if (haltExecution) { - doDumpStates(); - return; - } - - std::map>::iterator it = - seedMap.upper_bound(lastState); - if (it == seedMap.end()) - it = seedMap.begin(); - lastState = it->first; - ExecutionState &state = *lastState; - KInstruction *ki = state.pc; - stepInstruction(state); - - executeInstruction(state, ki); - timers.invoke(); - if (::dumpStates) - dumpStates(); - if (::dumpPTree) - dumpPTree(); - updateStates(&state); + int lastNumSeeds = usingSeeds->size() + 10; + time::Point lastTime, startTime = lastTime = time::getWallTime(); + ExecutionState *lastState = 0; + while (!seedMap.empty()) { + if (haltExecution) { + doDumpStates(); + return; + } - if ((stats::instructions % 1000) == 0) { - int numSeeds = 0, numStates = 0; - for (std::map>::iterator - it = seedMap.begin(), - ie = seedMap.end(); - it != ie; ++it) { - numSeeds += it->second.size(); - numStates++; - } - const auto time = time::getWallTime(); - const time::Span seedTime(SeedTime); - if (seedTime && time > startTime + seedTime) { - klee_warning("seed time expired, %d seeds remain over %d states", - numSeeds, numStates); - break; - } else if (numSeeds <= lastNumSeeds - 10 || - time - lastTime >= time::seconds(10)) { - lastTime = time; - lastNumSeeds = numSeeds; - klee_message("%d seeds remaining over: %d states", numSeeds, - numStates); - } + std::map>::iterator it = + seedMap.upper_bound(lastState); + if (it == seedMap.end()) + it = seedMap.begin(); + lastState = it->first; + ExecutionState &state = *lastState; + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + timers.invoke(); + if (::dumpStates) + dumpStates(); + if (::dumpPForest) + dumpPForest(); + updateStates(&state); + + if ((stats::instructions % 1000) == 0) { + int numSeeds = 0, numStates = 0; + for (std::map>::iterator + it = seedMap.begin(), + ie = seedMap.end(); + it != ie; ++it) { + numSeeds += it->second.size(); + numStates++; + } + const auto time = time::getWallTime(); + const time::Span seedTime(SeedTime); + if (seedTime && time > startTime + seedTime) { + klee_warning("seed time expired, %d seeds remain over %d states", + numSeeds, numStates); + break; + } else if (numSeeds <= lastNumSeeds - 10 || + time - lastTime >= time::seconds(10)) { + lastTime = time; + lastNumSeeds = numSeeds; + klee_message("%d seeds remaining over: %d states", numSeeds, numStates); } } + } - klee_message("seeding done (%d states remain)", (int)states.size()); + klee_message("seeding done (%d states remain)", (int)states.size()); - if (OnlySeed) { - doDumpStates(); - return; - } + if (OnlySeed) { + doDumpStates(); + return; + } +} + +void Executor::run(std::vector initialStates) { + // Delay init till now so that ticks don't accrue during optimization and + // such. + if (guidanceKind != GuidanceKind::ErrorGuidance) + timers.reset(); + + states.insert(initialStates.begin(), initialStates.end()); + + if (usingSeeds) { + assert(initialStates.size() == 1); + ExecutionState *initialState = initialStates.back(); + seed(*initialState); } searcher = constructUserSearcher(*this); @@ -3942,14 +4040,38 @@ void Executor::run(ExecutionState &initialState) { } if (searcher->empty()) - haltExecution = true; + haltExecution = HaltExecution::NoMoreStates; + } + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + if (searcher->empty()) + haltExecution = HaltExecution::NoMoreStates; } delete searcher; searcher = nullptr; doDumpStates(); - haltExecution = false; + haltExecution = HaltExecution::NotHalt; +} + +void Executor::runWithTarget(ExecutionState &state, KFunction *kf, + KBlock *target) { + if (pathWriter) + state.pathOS = pathWriter->open(); + if (symPathWriter) + state.symPathOS = symPathWriter->open(); + + if (statsTracker) + statsTracker->framePushed(state, 0); + + processForest = std::make_unique(); + processForest->addRoot(&state); + targetedRun(state, target); + processForest = nullptr; + + if (statsTracker) + statsTracker->done(); } void Executor::initializeTypeManager() { @@ -3974,8 +4096,8 @@ void Executor::executeStep(ExecutionState &state) { timers.invoke(); if (::dumpStates) dumpStates(); - if (::dumpPTree) - dumpPTree(); + if (::dumpPForest) + dumpPForest(); updateStates(&state); @@ -3985,6 +4107,48 @@ void Executor::executeStep(ExecutionState &state) { } } +void Executor::targetedRun(ExecutionState &initialState, KBlock *target, + ExecutionState **resultState) { + // Delay init till now so that ticks don't accrue during optimization and + // such. + if (guidanceKind != GuidanceKind::ErrorGuidance) + timers.reset(); + + states.insert(&initialState); + + TargetedSearcher *targetedSearcher = + new TargetedSearcher(Target::create(target), *codeGraphDistance); + searcher = targetedSearcher; + + std::vector newStates(states.begin(), states.end()); + searcher->update(0, newStates, std::vector()); + // main interpreter loop + KInstruction *terminator = + target != nullptr ? target->getFirstInstruction() : nullptr; + while (!searcher->empty() && !haltExecution) { + ExecutionState &state = searcher->selectState(); + + KInstruction *ki = state.pc; + + if (ki == terminator) { + *resultState = state.copy(); + terminateStateOnTerminator(state); + updateStates(&state); + haltExecution = HaltExecution::ReachedTarget; + break; + } + + executeStep(state); + } + + delete searcher; + searcher = nullptr; + + doDumpStates(); + if (*resultState) + haltExecution = HaltExecution::NotHalt; +} + std::string Executor::getAddressInfo(ExecutionState &state, ref address, const MemoryObject *mo) const { std::string Str; @@ -4038,7 +4202,22 @@ std::string Executor::getAddressInfo(ExecutionState &state, ref address, return info.str(); } -void Executor::terminateState(ExecutionState &state) { +HaltExecution::Reason fromStateTerminationType(StateTerminationType t) { + switch (t) { + case StateTerminationType::MaxDepth: + return HaltExecution::MaxDepth; + case StateTerminationType::OutOfStackMemory: + return HaltExecution::MaxStackFrames; + case StateTerminationType::Solver: + return HaltExecution::MaxSolverTime; + default: + return HaltExecution::Unspecified; + } +} + +void Executor::terminateState(ExecutionState &state, + StateTerminationType terminationType) { + state.terminationReasonType = fromStateTerminationType(terminationType); if (replayKTest && replayPosition != replayKTest->numObjects) { klee_warning_once(replayKTest, "replay did not consume all objects in test input."); @@ -4054,22 +4233,8 @@ void Executor::terminateState(ExecutionState &state) { interpreterHandler->incPathsExplored(); - std::vector::iterator it = - std::find(addedStates.begin(), addedStates.end(), &state); - if (it == addedStates.end()) { - state.pc = state.prevPC; - - removedStates.push_back(&state); - } else { - // never reached searcher, just delete immediately - std::map>::iterator it3 = - seedMap.find(&state); - if (it3 != seedMap.end()) - seedMap.erase(it3); - addedStates.erase(it); - processTree->remove(state.ptreeNode); - delete &state; - } + state.pc = state.prevPC; + removedStates.push_back(&state); } static bool shouldWriteTest(const ExecutionState &state) { @@ -4100,14 +4265,14 @@ void Executor::unpauseState(ExecutionState &state) { } void Executor::terminateStateOnExit(ExecutionState &state) { + auto terminationType = StateTerminationType::Exit; if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) interpreterHandler->processTestCase( - state, nullptr, - terminationTypeFileExtension(StateTerminationType::Exit).c_str()); + state, nullptr, terminationTypeFileExtension(terminationType).c_str()); interpreterHandler->incPathsCompleted(); targetCalculator->update(state); - terminateState(state); + terminateState(state, terminationType); } void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, @@ -4119,8 +4284,15 @@ void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, state, (message + "\n").str().c_str(), terminationTypeFileExtension(terminationType).c_str()); } + terminateState(state, terminationType); +} - terminateState(state); +void Executor::terminateStateOnTerminator(ExecutionState &state) { + if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) { + interpreterHandler->processTestCase(state, nullptr, nullptr); + } + targetCalculator->update(state); + terminateState(state, StateTerminationType::SilentExit); } void Executor::terminateStateOnUserError(ExecutionState &state, @@ -4178,6 +4350,40 @@ bool shouldExitOn(StateTerminationType reason) { return it != ExitOnErrorType.end(); } +void Executor::terminateStateOnTargetError(ExecutionState &state, + ReachWithError error) { + // Proceed with normal `terminateStateOnError` call + std::string messaget; + StateTerminationType terminationType; + switch (error) { + case ReachWithError::NullPointerException: + messaget = "memory error: null pointer exception"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::DoubleFree: + messaget = "double free error"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::UseAfterFree: + messaget = "use after free error"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::Reachable: + messaget = ""; + terminationType = StateTerminationType::Exit; + break; + case ReachWithError::None: + default: + messaget = "unspecified error"; + terminationType = StateTerminationType::User; + } + if (error == ReachWithError::Reachable) { + terminateStateEarly(state, messaget, terminationType); + } else { + terminateStateOnError(state, messaget, terminationType); + } +} + void Executor::terminateStateOnError(ExecutionState &state, const llvm::Twine &messaget, StateTerminationType terminationType, @@ -4224,10 +4430,10 @@ void Executor::terminateStateOnError(ExecutionState &state, } targetCalculator->update(state); - terminateState(state); + terminateState(state, terminationType); if (shouldExitOn(terminationType)) - haltExecution = true; + haltExecution = HaltExecution::ErrorOnWhichShouldExit; } void Executor::terminateStateOnExecError(ExecutionState &state, @@ -4525,8 +4731,13 @@ void Executor::executeFree(ExecutionState &state, ref address, } if (zeroPointer.second) { // address != 0 ExactResolutionList rl; - resolveExact(*zeroPointer.second, address, - typeSystemManager->getUnknownType(), rl, "free"); + if (!resolveExact(*zeroPointer.second, address, + typeSystemManager->getUnknownType(), rl, "free") && + guidanceKind == GuidanceKind::ErrorGuidance) { + terminateStateOnTargetError(*zeroPointer.second, + ReachWithError::DoubleFree); + return; + } for (Executor::ExactResolutionList::iterator it = rl.begin(), ie = rl.end(); it != ie; ++it) { @@ -4551,7 +4762,7 @@ void Executor::executeFree(ExecutionState &state, ref address, } } -void Executor::resolveExact(ExecutionState &state, ref p, KType *type, +bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, ExactResolutionList &results, const std::string &name) { p = optimizer.optimizeExpr(p, true); @@ -4579,11 +4790,16 @@ void Executor::resolveExact(ExecutionState &state, ref p, KType *type, break; } + if (guidanceKind == GuidanceKind::ErrorGuidance && rl.size() == 0) { + return false; + } + if (unbound) { terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, StateTerminationType::Ptr, getAddressInfo(*unbound, p)); } + return true; } MemoryObject *Executor::allocate(ExecutionState &state, ref size, @@ -4700,7 +4916,7 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, } void Executor::executeMemoryOperation( - ExecutionState &state, bool isWrite, KType *targetType, ref address, + ExecutionState &estate, bool isWrite, KType *targetType, ref address, ref value /* undef if read */, KInstruction *target /* undef if write */) { Expr::Width type = (isWrite ? value->getWidth() @@ -4711,26 +4927,45 @@ void Executor::executeMemoryOperation( unsigned size = bytes; KType *baseTargetType = targetType; - if (state.isGEPExpr(address)) { - base = state.gepExprBases[address].first; + if (estate.isGEPExpr(address)) { + base = estate.gepExprBases[address].first; size = kmodule->targetData->getTypeStoreSize( - state.gepExprBases[address].second); + estate.gepExprBases[address].second); baseTargetType = typeSystemManager->getWrappedType( - llvm::PointerType::get(state.gepExprBases[address].second, + llvm::PointerType::get(estate.gepExprBases[address].second, kmodule->targetData->getAllocaAddrSpace())); } if (SimplifySymIndices) { if (!isa(address)) - address = ConstraintManager::simplifyExpr(state.constraints, address); + address = ConstraintManager::simplifyExpr(estate.constraints, address); if (!isa(base)) - base = ConstraintManager::simplifyExpr(state.constraints, base); + base = ConstraintManager::simplifyExpr(estate.constraints, base); if (isWrite && !isa(value)) - value = ConstraintManager::simplifyExpr(state.constraints, value); + value = ConstraintManager::simplifyExpr(estate.constraints, value); } address = optimizer.optimizeExpr(address, true); + ref uniqueBase = + ConstraintManager::simplifyExpr(estate.constraints, base); + uniqueBase = toUnique(estate, uniqueBase); + + StatePair branches = + fork(estate, Expr::createIsZero(base), true, BranchType::MemOp); + ExecutionState *bound = branches.first; + if (bound) { + if (!isReadFromSymbolicArray(uniqueBase) || + guidanceKind != GuidanceKind::ErrorGuidance) { + terminateStateOnTargetError(*bound, ReachWithError::NullPointerException); + } else { + terminateStateEarly(*bound, "", StateTerminationType::SilentExit); + } + } + if (!branches.second) + return; + ExecutionState &state = *branches.second; + // fast path: single in-bounds resolution IDType idFastResult; bool success; @@ -4742,7 +4977,7 @@ void Executor::executeMemoryOperation( solver->setTimeout(coreSolverTimeout); if (!state.addressSpace.resolveOne(state, solver, address, targetType, - idFastResult, success)) { + idFastResult, success, haltExecution)) { address = toConstant(state, address, "resolveOne failure"); success = state.addressSpace.resolveOne(cast(address), targetType, idFastResult); @@ -4803,6 +5038,10 @@ void Executor::executeMemoryOperation( return; } + } else if (isa(address) && + guidanceKind == GuidanceKind::ErrorGuidance) { + terminateStateOnTargetError(state, ReachWithError::UseAfterFree); + return; } // we are on an error path (no resolution, multiple resolution, one @@ -4820,6 +5059,7 @@ void Executor::executeMemoryOperation( // XXX there is some query wasteage here. who cares? ref checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); + ref canLazyInitialize = ConstantExpr::create(1, Expr::Bool); std::vector> resolveConditions; std::vector resolvedMemoryObjects; @@ -4828,6 +5068,7 @@ void Executor::executeMemoryOperation( const MemoryObject *mo = state.addressSpace.findObject(*i).first; ref inBounds = mo->getBoundsCheckPointer(address, bytes); ref outOfBound = NotExpr::create(inBounds); + canLazyInitialize = AndExpr::create(outOfBound, canLazyInitialize); if (state.isGEPExpr(address)) { inBounds = AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, 1)); inBounds = @@ -4893,14 +5134,6 @@ void Executor::executeMemoryOperation( } ExecutionState *unbound = statesForMemoryOperation.back(); - StatePair branches = - fork(*unbound, Expr::createIsZero(base), true, BranchType::MemOp); - ExecutionState *bound = branches.first; - if (bound) { - terminateStateOnError(*bound, "memory error: null pointer exception", - StateTerminationType::Ptr); - } - unbound = branches.second; // XXX should we distinguish out of bounds and overlapped cases? if (unbound) { @@ -5002,8 +5235,7 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, const Array *lazyInstantiationSize = makeArray( state, Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), - "lazy_instantiation_size", - SourceBuilder::lazyInitializationMakeSymbolic()); + "lazy_instantiation_size", SourceBuilder::lazyInitializationSymbolic()); ref sizeExpr = Expr::createTempRead(lazyInstantiationSize, Context::get().getPointerWidth()); addConstraint(state, UgeExpr::create(sizeExpr, Expr::createPointer(size))); @@ -5043,7 +5275,7 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, addConstraint(state, EqExpr::create(mo->addressExpr, address)); executeMakeSymbolic(state, mo, targetType, name, - SourceBuilder::lazyInitializationMakeSymbolic(), false); + SourceBuilder::lazyInitializationSymbolic(), false); return mo->id; } @@ -5193,8 +5425,15 @@ void Executor::executeMakeSymbolic(ExecutionState &state, /***/ -void Executor::runFunctionAsMain(Function *f, int argc, char **argv, - char **envp) { +ExecutionState *Executor::formState(Function *f) { + ExecutionState *state = new ExecutionState( + kmodule->functionMap[f], kmodule->functionMap[f]->blockMap[&*f->begin()]); + initializeGlobals(*state); + return state; +} + +ExecutionState *Executor::formState(Function *f, int argc, char **argv, + char **envp) { std::vector> arguments; // force deterministic initialization of memory objects @@ -5282,6 +5521,162 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, } initializeGlobals(*state); + return state; +} + +void Executor::clearGlobal() { + globalObjects.clear(); + globalAddresses.clear(); +} + +void Executor::clearMemory() { + // hack to clear memory objects + delete memory; + memory = new MemoryManager(NULL); +} + +void Executor::prepareSymbolicValue(ExecutionState &state, + KInstruction *target) { + Instruction *allocSite = target->inst; + uint64_t size = kmodule->targetData->getTypeStoreSize(allocSite->getType()); + uint64_t width = kmodule->targetData->getTypeSizeInBits(allocSite->getType()); + ref result = + makeSymbolicValue(allocSite, state, size, width, "symbolic_value"); + bindLocal(target, state, result); + if (isa(allocSite)) { + AllocaInst *ai = cast(allocSite); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(ai->getAllocatedType()); + ref size = Expr::createPointer(elementSize); + if (ai->isArrayAllocation()) { + ref count = eval(target, 0, state).value; + count = Expr::createZExtToPointerWidth(count); + size = MulExpr::create(size, count); + } + lazyInitializeObject(state, result, target, + typeSystemManager->getWrappedType(ai->getType()), + elementSize); + } +} + +void Executor::prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, + unsigned regNum) { + KInstruction *allocInst = sf.kf->registerToInstructionMap[regNum]; + prepareSymbolicValue(state, allocInst); +} + +void Executor::prepareSymbolicArgs(ExecutionState &state, KFunction *kf) { + for (auto ai = kf->function->arg_begin(), ae = kf->function->arg_end(); + ai != ae; ai++) { + Argument *arg = *&ai; + uint64_t size = kmodule->targetData->getTypeStoreSize(arg->getType()); + uint64_t width = kmodule->targetData->getTypeSizeInBits(arg->getType()); + ref result = + makeSymbolicValue(arg, state, size, width, "symbolic_arg"); + bindArgument(state.stack.back().kf, arg->getArgNo(), state, result); + } +} + +ref Executor::makeSymbolicValue(Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name) { + MemoryObject *mo = memory->allocate(size, true, + /*isGlobal=*/false, value, + /*allocationAlignment=*/8); + const Array *array = makeArray(state, Expr::createPointer(size), name, + SourceBuilder::symbolicValue()); + state.addSymbolic(mo, array, + typeSystemManager->getWrappedType(value->getType())); + assert(value && "Attempted to make symbolic value from nullptr Value"); + ObjectState *os = bindObjectInState( + state, mo, typeSystemManager->getWrappedType(value->getType()), false, + array); + ref result = os->read(0, width); + return result; +} + +void Executor::runFunctionAsMain(Function *f, int argc, char **argv, + char **envp) { + if (guidanceKind == GuidanceKind::ErrorGuidance && + interpreterOpts.Targets.empty()) { + klee_warning("No targets found in error-guided mode"); + return; + } + + ExecutionState *state = formState(f, argc, argv, envp); + bindModuleConstants(llvm::APFloat::rmNearestTiesToEven); + std::vector states; + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + KInstIterator caller; + if (kmodule->WithPOSIXRuntime()) { + state = prepareStateForPOSIX(caller, state->copy()); + } else { + state->popFrame(); + } + + auto &targets = interpreterOpts.Targets; + for (auto &startFunctionAndWhiteList : targets) { + auto kf = kmodule->functionMap.at(startFunctionAndWhiteList.first); + if (startFunctionAndWhiteList.second.empty()) { + klee_warning("No targets found for %s", + kf->function->getName().str().c_str()); + continue; + } + auto whitelist = startFunctionAndWhiteList.second; + ExecutionState *initialState = state->withStackFrame(caller, kf); + prepareSymbolicArgs(*initialState, kf); + prepareTargetedExecution(initialState, whitelist); + states.push_back(initialState); + } + } else { + ExecutionState *initialState = state->copy(); + states.push_back(initialState); + delete state; + } + + TreeOStream pathOS; + TreeOStream symPathOS; + if (pathWriter) { + pathOS = pathWriter->open(); + } + + if (symPathWriter) { + symPathOS = symPathWriter->open(); + } + + processForest = std::make_unique(); + for (auto &state : states) { + if (statsTracker) + statsTracker->framePushed(*state, 0); + + if (pathWriter) + state->pathOS = pathOS; + if (symPathWriter) + state->symPathOS = symPathOS; + + processForest->addRoot(state); + } + + run(states); + processForest = nullptr; + + if (statsTracker) + statsTracker->done(); + + clearMemory(); + clearGlobal(); + + if (statsTracker) + statsTracker->done(); +} + +ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, + ExecutionState *state) { + Function *mainFn = kmodule->module->getFunction("__klee_posix_wrapped_main"); + + assert(mainFn && "klee_posix_wrapped_main not found"); + KBlock *target = kmodule->functionMap[mainFn]->entryKBlock; if (pathWriter) state->pathOS = pathWriter->open(); @@ -5291,20 +5686,34 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, if (statsTracker) statsTracker->framePushed(*state, 0); - processTree = std::make_unique(state); - bindModuleConstants(llvm::APFloat::rmNearestTiesToEven); - run(*state); - processTree = nullptr; - - // hack to clear memory objects - delete memory; - memory = new MemoryManager(NULL); + processForest = std::make_unique(); + processForest->addRoot(state); + ExecutionState *original = state->copy(); + ExecutionState *initialState = nullptr; + state->targetForest.add(Target::create(target)); + targetedRun(*state, target, &initialState); + state = initialState; + if (state) { + auto frame = state->stack.back(); + caller = frame.caller; + state->popFrame(); + delete original; + } else { + state = original; + state->popFrame(); + } - globalObjects.clear(); - globalAddresses.clear(); + processForest = nullptr; if (statsTracker) statsTracker->done(); + + return state; +} + +void Executor::prepareTargetedExecution(ExecutionState *initialState, + TargetForest whitelist) { + initialState->targetForest = whitelist; } unsigned Executor::getPathStreamID(const ExecutionState &state) { @@ -5413,7 +5822,12 @@ void Executor::setInitializationGraph(const ExecutionState &state, for (const auto &offset : innerTypeOffset.second) { ref address = Expr::createTempRead( symbolic.array, Context::get().getPointerWidth(), offset); - ref constantAddress = model.evaluate(address); + ref addressInModel = model.evaluate(address); + if (!isa(addressInModel)) { + continue; + } + ref constantAddress = cast(addressInModel); + ref mo; IDType idResult; if (state.resolveOnSymbolics(constantAddress, idResult)) { @@ -5699,18 +6113,18 @@ int *Executor::getErrnoLocation(const ExecutionState &state) const { #endif } -void Executor::dumpPTree() { - if (!::dumpPTree) +void Executor::dumpPForest() { + if (!::dumpPForest) return; char name[32]; - snprintf(name, sizeof(name), "ptree%08d.dot", (int)stats::instructions); + snprintf(name, sizeof(name), "pforest%08d.dot", (int)stats::instructions); auto os = interpreterHandler->openOutputFile(name); if (os) { - processTree->dump(*os); + processForest->dump(*os); } - ::dumpPTree = 0; + ::dumpPForest = 0; } void Executor::dumpStates() { diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 2c94a3eae0..aa5a9cded1 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -37,6 +37,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -80,7 +81,7 @@ class KModule; class MemoryManager; class MemoryObject; class ObjectState; -class PTree; +class PForest; class Searcher; class SeedInfo; class SpecialFunctionHandler; @@ -114,6 +115,7 @@ class Executor : public Interpreter { RNG theRNG; private: + using SetOfStates = std::set; /* Set of Intrinsic::ID. Plain type is used here to avoid including llvm in * the header */ static const std::unordered_set supportedFPIntrinsics; @@ -129,14 +131,14 @@ class Executor : public Interpreter { MemoryManager *memory; TypeManager *typeSystemManager; - std::set states; - std::set pausedStates; + SetOfStates states; + SetOfStates pausedStates; StatsTracker *statsTracker; TreeStreamWriter *pathWriter, *symPathWriter; SpecialFunctionHandler *specialFunctionHandler; TimerGroup timers; std::unique_ptr concretizationManager; - std::unique_ptr processTree; + std::unique_ptr processForest; std::unique_ptr codeGraphDistance; std::unique_ptr targetCalculator; @@ -195,7 +197,7 @@ class Executor : public Interpreter { /// Signals the executor to halt execution at the next instruction /// step. - std::atomic_bool haltExecution; + HaltExecution::Reason haltExecution = HaltExecution::NotHalt; /// Whether implied-value concretization is enabled. Currently /// false, it is buggy (it needs to validate its writes). @@ -230,6 +232,8 @@ class Executor : public Interpreter { /// Typeids used during exception handling std::vector> eh_typeids; + GuidanceKind guidanceKind; + /// Return the typeid corresponding to a certain `type_info` ref getEhTypeidFor(ref type_info); @@ -237,7 +241,12 @@ class Executor : public Interpreter { void executeInstruction(ExecutionState &state, KInstruction *ki); - void run(ExecutionState &initialState); + void targetedRun(ExecutionState &initialState, KBlock *target, + ExecutionState **resultState = nullptr); + + void seed(ExecutionState &initialState); + void run(std::vector initialStates); + void runWithTarget(ExecutionState &state, KFunction *kf, KBlock *target); void initializeTypeManager(); @@ -258,6 +267,8 @@ class Executor : public Interpreter { void updateStates(ExecutionState *current); void transferToBasicBlock(llvm::BasicBlock *dst, llvm::BasicBlock *src, ExecutionState &state); + void transferToBasicBlock(KBlock *dst, llvm::BasicBlock *src, + ExecutionState &state); void callExternalFunction(ExecutionState &state, KInstruction *target, KCallable *callable, @@ -276,7 +287,7 @@ class Executor : public Interpreter { /// state) pairs for each object the given address can point to the /// beginning of. typedef std::vector> ExactResolutionList; - void resolveExact(ExecutionState &state, ref p, KType *type, + bool resolveExact(ExecutionState &state, ref p, KType *type, ExactResolutionList &results, const std::string &name); MemoryObject *allocate(ExecutionState &state, ref size, bool isLocal, @@ -373,8 +384,8 @@ class Executor : public Interpreter { // Used for testing. ref replaceReadWithSymbolic(ExecutionState &state, ref e); - const Cell &eval(KInstruction *ki, unsigned index, - ExecutionState &state) const; + const Cell &eval(KInstruction *ki, unsigned index, ExecutionState &state, + bool isSymbolic = true); Cell &getArgumentCell(ExecutionState &state, KFunction *kf, unsigned index) { return state.stack.back().locals[kf->getArgRegister(index)]; @@ -437,7 +448,8 @@ class Executor : public Interpreter { llvm::Instruction **lastInstruction); /// Remove state from queue and delete state - void terminateState(ExecutionState &state); + void terminateState(ExecutionState &state, + StateTerminationType terminationType); // pause state void pauseState(ExecutionState &state); @@ -454,6 +466,10 @@ class Executor : public Interpreter { void terminateStateEarly(ExecutionState &state, const llvm::Twine &message, StateTerminationType terminationType); + /// Save extra information in targeted mode + /// Then just call `terminateStateOnError` + void terminateStateOnTargetError(ExecutionState &state, ReachWithError error); + /// Call error handler and terminate state in case of program errors /// (e.g. free()ing globals, out-of-bound accesses) void terminateStateOnError(ExecutionState &state, const llvm::Twine &message, @@ -461,6 +477,8 @@ class Executor : public Interpreter { const llvm::Twine &longMessage = "", const char *suffix = nullptr); + void terminateStateOnTerminator(ExecutionState &state); + /// Call error handler and terminate state in case of execution errors /// (things that should not be possible, like illegal instruction or /// unlowered intrinsic, or unsupported intrinsics, like inline assembly) @@ -485,6 +503,12 @@ class Executor : public Interpreter { const std::string &name, const ref source); + ExecutionState *prepareStateForPOSIX(KInstIterator &caller, + ExecutionState *state); + + void prepareTargetedExecution(ExecutionState *initialState, + TargetForest whitelist); + template void computeOffsetsSeqTy(KGEPInstruction *kgepi, ref &constantOffset, uint64_t index, @@ -513,7 +537,7 @@ class Executor : public Interpreter { /// Only for debug purposes; enable via debugger or klee-control void dumpStates(); - void dumpPTree(); + void dumpPForest(); public: Executor(llvm::LLVMContext &ctx, const InterpreterOptions &opts, @@ -550,12 +574,33 @@ class Executor : public Interpreter { usingSeeds = seeds; } + ExecutionState *formState(llvm::Function *f); + ExecutionState *formState(llvm::Function *f, int argc, char **argv, + char **envp); + + void clearGlobal(); + + void clearMemory(); + + void prepareSymbolicValue(ExecutionState &state, KInstruction *targetW); + + void prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, + unsigned index); + + void prepareSymbolicArgs(ExecutionState &state, KFunction *kf); + + ref makeSymbolicValue(llvm::Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name); + void runFunctionAsMain(llvm::Function *f, int argc, char **argv, char **envp) override; /*** Runtime options ***/ - void setHaltExecution(bool value) override { haltExecution = value; } + void setHaltExecution(HaltExecution::Reason value) override { + haltExecution = value; + } void setInhibitForking(bool value) override { inhibitForking = value; } diff --git a/lib/Core/PForest.cpp b/lib/Core/PForest.cpp new file mode 100644 index 0000000000..8a97410510 --- /dev/null +++ b/lib/Core/PForest.cpp @@ -0,0 +1,49 @@ +//===-- PForest.cpp -------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PForest.h" + +#include "ExecutionState.h" + +#include "klee/Expr/Expr.h" +#include "klee/Expr/ExprPPrinter.h" +#include "klee/Support/OptionCategories.h" + +#include +#include + +using namespace klee; +using namespace llvm; + +void PForest::addRoot(ExecutionState *initialState) { + PTree *tree = new PTree(initialState, nextID++); + trees[tree->getID()] = tree; +} + +void PForest::attach(PTreeNode *node, ExecutionState *leftState, + ExecutionState *rightState, BranchType reason) { + assert(trees.find(node->getTreeID()) != trees.end()); + trees[node->getTreeID()]->attach(node, leftState, rightState, reason); +} + +void PForest::remove(PTreeNode *node) { + assert(trees.find(node->getTreeID()) != trees.end()); + trees[node->getTreeID()]->remove(node); +} + +void PForest::dump(llvm::raw_ostream &os) { + for (auto &ntree : trees) + ntree.second->dump(os); +} + +PForest::~PForest() { + for (auto &ntree : trees) + delete ntree.second; + trees.clear(); +} diff --git a/lib/Core/PForest.h b/lib/Core/PForest.h new file mode 100644 index 0000000000..dd4236e948 --- /dev/null +++ b/lib/Core/PForest.h @@ -0,0 +1,50 @@ +//===-- PForest.h -------------------------------------------------*- C++ +//-*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===------------------------------------------------------------------------===// + +#ifndef KLEE_PFOREST_H +#define KLEE_PFOREST_H + +#include "PTree.h" + +#include + +namespace klee { +class ExecutionState; +class PTreeNode; +class PTree; + +class PForest { + // Number of registered ID + int registeredIds = 0; + std::map trees; + // The global tree counter + std::uint32_t nextID = 1; + +public: + PForest() = default; + ~PForest(); + void addRoot(ExecutionState *initialState); + void attach(PTreeNode *node, ExecutionState *leftState, + ExecutionState *rightState, BranchType reason); + void remove(PTreeNode *node); + const std::map &getPTrees() { return trees; } + void dump(llvm::raw_ostream &os); + std::uint8_t getNextId() { + std::uint8_t id = 1 << registeredIds++; + if (registeredIds > PtrBitCount) { + klee_error("PForest cannot support more than %d RandomPathSearchers", + PtrBitCount); + } + return id; + } +}; +} // namespace klee + +#endif /* KLEE_PFOREST_H */ diff --git a/lib/Core/PTree.cpp b/lib/Core/PTree.cpp index 482afd510d..64d83bd2dd 100644 --- a/lib/Core/PTree.cpp +++ b/lib/Core/PTree.cpp @@ -31,8 +31,9 @@ cl::opt } // namespace -PTree::PTree(ExecutionState *initialState) - : root(PTreeNodePtr(new PTreeNode(nullptr, initialState))) { +PTree::PTree(ExecutionState *initialState, uint32_t treeID) { + id = treeID; + root = PTreeNodePtr(new PTreeNode(nullptr, initialState, id)); initialState->ptreeNode = root.getPointer(); } @@ -42,14 +43,15 @@ void PTree::attach(PTreeNode *node, ExecutionState *leftState, assert(node == rightState->ptreeNode && "Attach assumes the right state is the current state"); node->state = nullptr; - node->left = PTreeNodePtr(new PTreeNode(node, leftState)); + node->left = PTreeNodePtr(new PTreeNode(node, leftState, id)); // The current node inherits the tag uint8_t currentNodeTag = root.getInt(); if (node->parent) currentNodeTag = node->parent->left.getPointer() == node ? node->parent->left.getInt() : node->parent->right.getInt(); - node->right = PTreeNodePtr(new PTreeNode(node, rightState), currentNodeTag); + node->right = + PTreeNodePtr(new PTreeNode(node, rightState, id), currentNodeTag); } void PTree::remove(PTreeNode *n) { @@ -128,8 +130,8 @@ void PTree::dump(llvm::raw_ostream &os) { delete pp; } -PTreeNode::PTreeNode(PTreeNode *parent, ExecutionState *state) - : parent{parent}, state{state} { +PTreeNode::PTreeNode(PTreeNode *parent, ExecutionState *state, uint32_t id) + : parent{parent}, state{state}, treeID{id} { state->ptreeNode = this; left = PTreeNodePtr(nullptr); right = PTreeNodePtr(nullptr); diff --git a/lib/Core/PTree.h b/lib/Core/PTree.h index f1f101737f..02602a247c 100644 --- a/lib/Core/PTree.h +++ b/lib/Core/PTree.h @@ -34,32 +34,31 @@ class PTreeNode { PTreeNodePtr right; ExecutionState *state = nullptr; + std::uint32_t treeID; + PTreeNode(const PTreeNode &) = delete; - PTreeNode(PTreeNode *parent, ExecutionState *state); + PTreeNode(PTreeNode *parent, ExecutionState *state, std::uint32_t id); ~PTreeNode() = default; + + std::uint32_t getTreeID() const { return treeID; }; }; class PTree { - // Number of registered ID - int registeredIds = 0; +private: + // The tree id + uint32_t id; public: PTreeNodePtr root; - explicit PTree(ExecutionState *initialState); + PTree(ExecutionState *initialState, uint32_t id); + explicit PTree(ExecutionState *initialState) : PTree(initialState, 0) {} ~PTree() = default; void attach(PTreeNode *node, ExecutionState *leftState, ExecutionState *rightState, BranchType reason); void remove(PTreeNode *node); void dump(llvm::raw_ostream &os); - std::uint8_t getNextId() { - std::uint8_t id = 1 << registeredIds++; - if (registeredIds > PtrBitCount) { - klee_error("PTree cannot support more than %d RandomPathSearchers", - PtrBitCount); - } - return id; - } + std::uint32_t getID() const { return id; }; }; } // namespace klee diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index dc5249e6ba..1b293faa54 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -15,7 +15,7 @@ #include "MergeHandler.h" #include "PTree.h" #include "StatsTracker.h" -#include "Target.h" +#include "TargetCalculator.h" #include "klee/ADT/DiscretePDF.h" #include "klee/ADT/RNG.h" @@ -24,6 +24,7 @@ #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" +#include "klee/Module/Target.h" #include "klee/Statistics/Statistics.h" #include "klee/Support/ErrorHandling.h" #include "klee/System/Time.h" @@ -147,12 +148,15 @@ static unsigned int ulog2(unsigned int val) { return ret; } -TargetedSearcher::TargetedSearcher(Target target, CodeGraphDistance &_distance) +/// + +TargetedSearcher::TargetedSearcher(ref target, + CodeGraphDistance &_distance) : states(std::make_unique< WeightedQueue>()), target(target), codeGraphDistance(_distance), distanceToTargetFunction( - codeGraphDistance.getBackwardDistance(target.getBlock()->parent)) {} + codeGraphDistance.getBackwardDistance(target->getBlock()->parent)) {} ExecutionState &TargetedSearcher::selectState() { return *states->choose(0); } @@ -161,7 +165,7 @@ bool TargetedSearcher::distanceInCallGraph(KFunction *kf, KBlock *kb, distance = UINT_MAX; const std::unordered_map &dist = codeGraphDistance.getDistance(kb); - KBlock *targetBB = target.getBlock(); + KBlock *targetBB = target->getBlock(); KFunction *targetF = targetBB->parent; if (kf == targetF && dist.count(targetBB) != 0) { @@ -187,6 +191,7 @@ TargetedSearcher::tryGetLocalWeight(ExecutionState *es, weight_type &weight, const std::vector &localTargets) { unsigned int intWeight = es->steppedMemoryInstructions; KFunction *currentKF = es->pc->parent->parent; + KBlock *initKB = es->initPC->parent; KBlock *currentKB = currentKF->blockMap[es->getPCBlock()]; KBlock *prevKB = currentKF->blockMap[es->getPrevPCBlock()]; const std::unordered_map &dist = @@ -201,8 +206,10 @@ TargetedSearcher::tryGetLocalWeight(ExecutionState *es, weight_type &weight, if (localWeight == UINT_MAX) return Miss; - if (localWeight == 0 && prevKB != currentKB) + if (localWeight == 0 && (initKB == currentKB || prevKB != currentKB || + target->shouldFailOnThisTarget())) { return Done; + } intWeight += localWeight; weight = ulog2(intWeight); // number on [0,32)-discrete-interval @@ -246,28 +253,34 @@ TargetedSearcher::tryGetPostTargetWeight(ExecutionState *es, TargetedSearcher::WeightResult TargetedSearcher::tryGetTargetWeight(ExecutionState *es, weight_type &weight) { - std::vector localTargets = {target.getBlock()}; + std::vector localTargets = {target->getBlock()}; WeightResult res = tryGetLocalWeight(es, weight, localTargets); return res; } TargetedSearcher::WeightResult TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { - if (target.atReturn()) { - if (es->prevPC->parent == target.getBlock() && - es->prevPC == target.getBlock()->getLastInstruction()) { + if (target->atReturn() && !target->shouldFailOnThisTarget()) { + if (es->prevPC->parent == target->getBlock() && + es->prevPC == target->getBlock()->getLastInstruction()) { return Done; - } else if (es->pc->parent == target.getBlock()) { + } else if (es->pc->parent == target->getBlock()) { weight = 0; return Continue; } } + if (target->shouldFailOnThisTarget() && + target->getBlock() == es->prevPC->parent && + es->error == target->getError()) { + return Done; + } + BasicBlock *bb = es->getPCBlock(); KBlock *kb = es->pc->parent->parent->blockMap[bb]; KInstruction *ki = es->pc; - if (kb->numInstructions && kb->getFirstInstruction() != ki && - states->tryGetWeight(es, weight)) { + if (!target->shouldFailOnThisTarget() && kb->numInstructions && + kb->getFirstInstruction() != ki && states->tryGetWeight(es, weight)) { return Continue; } unsigned int minCallWeight = UINT_MAX, minSfNum = UINT_MAX, sfNum = 0; @@ -276,7 +289,15 @@ TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { unsigned callWeight; if (distanceInCallGraph(sfi->kf, kb, callWeight)) { callWeight *= 2; - callWeight += sfNum; + if (callWeight == 0 && target->shouldFailOnThisTarget()) { + weight = 0; + return target->getBlock() == kb && es->error == target->getError() + ? Done + : Continue; + } else { + callWeight += sfNum; + } + if (callWeight < minCallWeight) { minCallWeight = callWeight; minSfNum = sfNum; @@ -300,6 +321,11 @@ TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { res = tryGetPreTargetWeight(es, weight); else if (minSfNum != UINT_MAX) res = tryGetPostTargetWeight(es, weight); + if (Done == res && target->shouldFailOnThisTarget()) { + if (es->error != target->getError()) { + res = Continue; + } + } return res; } @@ -316,10 +342,10 @@ void TargetedSearcher::update( states->update(current, weight); break; case Done: - reachedOnLastUpdate.push_back(current); + reachedOnLastUpdate.insert(current); break; case Miss: - current->targets.erase(target); + current->targetForest.remove(target); states->remove(current); break; } @@ -333,26 +359,26 @@ void TargetedSearcher::update( break; case Done: states->insert(state, weight); - reachedOnLastUpdate.push_back(state); + reachedOnLastUpdate.insert(state); break; case Miss: - state->targets.erase(target); + state->targetForest.remove(target); break; } } // remove states for (const auto state : removedStates) { - if (target.atReturn() && - state->prevPC == target.getBlock()->getLastInstruction()) - reachedOnLastUpdate.push_back(state); - else { + if (target->atReturn() && !target->shouldFailOnThisTarget() && + state->prevPC == target->getBlock()->getLastInstruction()) { + reachedOnLastUpdate.insert(state); + } else { switch (tryGetWeight(state, weight)) { case Done: - reachedOnLastUpdate.push_back(state); + reachedOnLastUpdate.insert(state); break; case Miss: - state->targets.erase(target); + state->targetForest.remove(target); states->remove(state); break; case Continue: @@ -372,19 +398,19 @@ void TargetedSearcher::printName(llvm::raw_ostream &os) { TargetedSearcher::~TargetedSearcher() { while (!states->empty()) { auto &state = selectState(); - state.targets.erase(target); + state.targetForest.remove(target); states->remove(&state); } } -std::vector TargetedSearcher::reached() { +std::set TargetedSearcher::reached() { return reachedOnLastUpdate; } void TargetedSearcher::removeReached() { for (auto state : reachedOnLastUpdate) { states->remove(state); - state->targets.erase(target); + state->targetForest.stepTo(target); } reachedOnLastUpdate.clear(); } @@ -395,23 +421,38 @@ GuidedSearcher::GuidedSearcher( Searcher *baseSearcher, CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, std::set &pausedStates, - std::size_t bound, RNG &rng, bool stopAfterReachingTarget) - : baseSearcher(baseSearcher), codeGraphDistance(codeGraphDistance), - stateHistory(stateHistory), pausedStates(pausedStates), bound(bound), - theRNG(rng), stopAfterReachingTarget(stopAfterReachingTarget) {} + std::size_t bound, RNG &rng) + : guidance(CoverageGuidance), baseSearcher(baseSearcher), + codeGraphDistance(codeGraphDistance), stateHistory(&stateHistory), + pausedStates(pausedStates), bound(bound), theRNG(rng) {} + +GuidedSearcher::GuidedSearcher( + CodeGraphDistance &codeGraphDistance, + std::set &pausedStates, + std::size_t bound, RNG &rng) + : guidance(ErrorGuidance), baseSearcher(nullptr), + codeGraphDistance(codeGraphDistance), stateHistory(nullptr), + pausedStates(pausedStates), bound(bound), theRNG(rng) {} ExecutionState &GuidedSearcher::selectState() { - unsigned size = targets.size(); + unsigned size = historiesAndTargets.size(); index = theRNG.getInt32() % (size + 1); - if (stopAfterReachingTarget && index == size) { - return baseSearcher->selectState(); + ExecutionState *state = nullptr; + if (CoverageGuidance == guidance && index == size) { + assert(baseSearcher); + state = &baseSearcher->selectState(); } else { index = index % size; - Target target = targets[index]; - assert(targetedSearchers.find(target) != targetedSearchers.end() && - !targetedSearchers[target]->empty()); - return targetedSearchers[target]->selectState(); + auto &historyTargetPair = historiesAndTargets[index]; + ref history = historyTargetPair.first; + ref target = historyTargetPair.second; + assert(targetedSearchers.find(history) != targetedSearchers.end() && + targetedSearchers.at(history).find(target) != + targetedSearchers.at(history).end() && + !targetedSearchers.at(history)[target]->empty()); + state = &targetedSearchers.at(history).at(target)->selectState(); } + return *state; } bool GuidedSearcher::isStuck(ExecutionState &state) { @@ -420,6 +461,60 @@ bool GuidedSearcher::isStuck(ExecutionState &state) { state.multilevel.count(state.getPCBlock()) > bound); } +void GuidedSearcher::updateTargetedSearcher( + ref history, ref target, + ExecutionState *current, std::vector &addedStates, + std::vector &removedStates) { + auto &historiedTargetedSearchers = targetedSearchers[history]; + + if (historiedTargetedSearchers.count(target) != 0 || + tryAddTarget(history, target)) { + + if (reachedTargets.count(history) != 0) { + if (current) { + assert(current->targetForest.contains(target)); + for (auto &reached : reachedTargets[history]) { + current->targetForest.blockIn(target, reached); + } + if (!current->targetForest.contains(target)) { + removedStates.push_back(current); + } + } + for (std::vector::iterator is = addedStates.begin(), + ie = addedStates.end(); + is != ie;) { + ExecutionState *state = *is; + assert(state->targetForest.contains(target)); + for (auto &reached : reachedTargets[history]) { + state->targetForest.blockIn(target, reached); + } + if (!state->targetForest.contains(target)) { + is = addedStates.erase(is); + ie = addedStates.end(); + } else { + ++is; + } + } + } + + historiedTargetedSearchers[target]->update(current, addedStates, + removedStates); + if (historiedTargetedSearchers[target]->empty()) { + removeTarget(history, target); + } + } else if (isReached(history, target)) { + if (current) { + current->targetForest.remove(target); + } + assert(removedStates.empty()); + for (auto &state : addedStates) { + state->targetForest.remove(target); + } + } + if (targetedSearchers[history].empty()) + targetedSearchers.erase(history); +} + void GuidedSearcher::innerUpdate( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { @@ -428,37 +523,43 @@ void GuidedSearcher::innerUpdate( baseRemovedStates.insert(baseRemovedStates.end(), removedStates.begin(), removedStates.end()); - std::set innerTargets; - - for (const auto state : baseAddedStates) { - if (!state->targets.empty()) { - for (auto target : state->targets) { - innerTargets.insert(target); - addedTStates[target].push_back(state); + if (ErrorGuidance == guidance) { + if (current && isStuck(*current)) { + pausedStates.insert(current); + baseRemovedStates.push_back(current); + } + for (const auto state : addedStates) { + if (isStuck(*state)) { + pausedStates.insert(state); + auto is = + std::find(baseAddedStates.begin(), baseAddedStates.end(), state); + assert(is != baseAddedStates.end()); + baseAddedStates.erase(is); } - } else { - targetlessStates.push_back(state); } } - for (const auto state : baseRemovedStates) { - for (auto target : state->targets) { - innerTargets.insert(target); - removedTStates[target].push_back(state); + for (const auto state : baseAddedStates) { + if (!state->targetForest.empty()) { + targetedAddedStates.push_back(state); + } else { + targetlessStates.push_back(state); } } - std::set currTargets; + TargetForest::TargetsSet currTargets; if (current) { - currTargets = current->targets; + auto targets = current->targetForest.getTargets(); + for (auto &targetF : *targets) { + auto target = targetF.first; + assert(target && "Target should be not null!"); + currTargets.insert(target); + } } - if (current && !currTargets.empty()) { - for (auto target : current->targets) { - innerTargets.insert(target); - } - } else if (current && std::find(removedStates.begin(), removedStates.end(), - current) == removedStates.end()) { + if (current && currTargets.empty() && + std::find(baseRemovedStates.begin(), baseRemovedStates.end(), current) == + baseRemovedStates.end()) { targetlessStates.push_back(current); } @@ -475,74 +576,110 @@ void GuidedSearcher::innerUpdate( } } - for (const auto state : targetlessStates) { - KInstruction *prevKI = state->prevPC; - if (isStuck(*state)) { - Target target(stateHistory.calculate(*state)); - if (target) { - state->targets.insert(target); - innerTargets.insert(target); - addedTStates[target].push_back(state); - } else { - pausedStates.insert(state); - if (std::find(addedStates.begin(), addedStates.end(), state) != - addedStates.end()) { - auto is = - std::find(baseAddedStates.begin(), baseAddedStates.end(), state); - baseAddedStates.erase(is); + std::vector tmpAddedStates; + std::vector tmpRemovedStates; + + if (CoverageGuidance == guidance) { + assert(stateHistory); + for (const auto state : targetlessStates) { + if (isStuck(*state)) { + ref target(stateHistory->calculate(*state)); + if (target) { + state->targetForest.add(target); + auto history = state->targetForest.getHistory(); + tmpAddedStates.push_back(state); + updateTargetedSearcher(history, target, nullptr, tmpAddedStates, + tmpRemovedStates); + auto is = std::find(targetedAddedStates.begin(), + targetedAddedStates.end(), state); + if (is != targetedAddedStates.end()) { + targetedAddedStates.erase(is); + } + tmpAddedStates.clear(); + tmpRemovedStates.clear(); } else { - baseRemovedStates.push_back(state); + pausedStates.insert(state); + if (std::find(baseAddedStates.begin(), baseAddedStates.end(), + state) != baseAddedStates.end()) { + auto is = std::find(baseAddedStates.begin(), baseAddedStates.end(), + state); + baseAddedStates.erase(is); + } else { + baseRemovedStates.push_back(state); + } } } } } targetlessStates.clear(); - for (auto target : innerTargets) { - ExecutionState *currTState = - currTargets.count(target) != 0 ? current : nullptr; + if (current && !currTargets.empty()) { + auto history = current->targetForest.getHistory(); + for (auto &target : currTargets) { + updateTargetedSearcher(history, target, current, tmpAddedStates, + tmpRemovedStates); + tmpAddedStates.clear(); + tmpRemovedStates.clear(); + } + } - if (targetedSearchers.count(target) == 0) { - addTarget(target); + for (const auto state : targetedAddedStates) { + auto history = state->targetForest.getHistory(); + auto targets = state->targetForest.getTargets(); + for (auto &targetF : *targets) { + tmpAddedStates.push_back(state); + assert(!state->targetForest.empty()); + auto target = targetF.first; + updateTargetedSearcher(history, target, nullptr, tmpAddedStates, + tmpRemovedStates); + tmpAddedStates.clear(); + tmpRemovedStates.clear(); } - targetedSearchers[target]->update(currTState, addedTStates[target], - removedTStates[target]); - if (targetedSearchers[target]->empty()) { - removeTarget(target); + } + targetedAddedStates.clear(); + + for (const auto state : baseRemovedStates) { + auto history = state->targetForest.getHistory(); + auto targets = state->targetForest.getTargets(); + for (auto &targetF : *targets) { + tmpRemovedStates.push_back(state); + auto target = targetF.first; + updateTargetedSearcher(history, target, nullptr, tmpAddedStates, + tmpRemovedStates); + tmpAddedStates.clear(); + tmpRemovedStates.clear(); } - addedTStates[target].clear(); - removedTStates[target].clear(); } - baseSearcher->update(current, baseAddedStates, baseRemovedStates); + if (CoverageGuidance == guidance) { + assert(baseSearcher); + baseSearcher->update(current, baseAddedStates, baseRemovedStates); + } baseAddedStates.clear(); baseRemovedStates.clear(); } -void GuidedSearcher::update( - ExecutionState *current, const std::vector &addedStates, - const std::vector &removedStates, - std::map> &reachedStates) { +void GuidedSearcher::update(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates, + TargetToStateSetMap &reachedStates) { innerUpdate(current, addedStates, removedStates); collectReached(reachedStates); - clearReached(); + clearReached(removedStates); } void GuidedSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { innerUpdate(current, addedStates, removedStates); - clearReached(); + clearReached(removedStates); } -void GuidedSearcher::collectReached( - std::map> &reachedStates) { - std::map> ret; - std::vector targets; - for (auto const &targetSearcher : targetedSearchers) - targets.push_back(targetSearcher.first); - for (auto target : targets) { - auto reached = targetedSearchers[target]->reached(); +void GuidedSearcher::collectReached(TargetToStateSetMap &reachedStates) { + for (auto &historyTarget : historiesAndTargets) { + auto &history = historyTarget.first; + auto &target = historyTarget.second; + auto reached = targetedSearchers.at(history).at(target)->reached(); if (!reached.empty()) { for (auto state : reached) reachedStates[target].insert(state); @@ -550,49 +687,119 @@ void GuidedSearcher::collectReached( } } -void GuidedSearcher::clearReached() { - std::vector targets; - for (auto const &targetSearcher : targetedSearchers) - targets.push_back(targetSearcher.first); - for (auto target : targets) { - auto reached = targetedSearchers[target]->reached(); - if (!reached.empty()) { - targetedSearchers[target]->removeReached(); - if (stopAfterReachingTarget || targetedSearchers[target]->empty()) { - removeTarget(target); +void GuidedSearcher::clearReached( + const std::vector &removedStates) { + std::vector addedStates; + std::vector tmpAddedStates; + std::vector tmpRemovedStates; + TargetToStateSetMap reachedStates; + collectReached(reachedStates); + + for (auto &targetState : reachedStates) { + auto target = targetState.first; + auto states = targetState.second; + for (auto &state : states) { + auto history = state->targetForest.getHistory(); + auto targets = state->targetForest.getTargets(); + if (std::find(removedStates.begin(), removedStates.end(), state) == + removedStates.end()) { + tmpRemovedStates.push_back(state); + for (auto &targetF : *targets) { + auto anotherTarget = targetF.first; + if (target != anotherTarget) { + updateTargetedSearcher(history, anotherTarget, nullptr, + tmpAddedStates, tmpRemovedStates); + } + } + tmpRemovedStates.clear(); + addedStates.push_back(state); } } } + + if (!reachedStates.empty()) { + for (auto &targetState : reachedStates) { + auto target = targetState.first; + auto states = targetState.second; + for (auto &state : states) { + auto history = state->targetForest.getHistory(); + if (target->shouldFailOnThisTarget()) { + reachedTargets[history].insert(target); + } + targetedSearchers.at(history).at(target)->removeReached(); + if (CoverageGuidance == guidance || + (ErrorGuidance == guidance && target->shouldFailOnThisTarget()) || + targetedSearchers.at(history).at(target)->empty()) { + removeTarget(history, target); + } + if (targetedSearchers.at(history).empty()) + targetedSearchers.erase(history); + } + } + } + + for (const auto state : addedStates) { + auto history = state->targetForest.getHistory(); + auto targets = state->targetForest.getTargets(); + tmpAddedStates.push_back(state); + for (auto &targetF : *targets) { + auto target = targetF.first; + updateTargetedSearcher(history, target, nullptr, tmpAddedStates, + tmpRemovedStates); + } + tmpAddedStates.clear(); + } } bool GuidedSearcher::empty() { - return stopAfterReachingTarget ? baseSearcher->empty() - : targetedSearchers.empty(); + return CoverageGuidance == guidance ? baseSearcher->empty() + : targetedSearchers.empty(); } void GuidedSearcher::printName(llvm::raw_ostream &os) { - os << "GuidedSearcher"; + os << "GuidedSearcher\n"; } -void GuidedSearcher::addTarget(Target target) { - targetedSearchers[target] = +bool GuidedSearcher::isReached(ref history, + ref target) { + return reachedTargets.count(history) != 0 && + reachedTargets[history].count(target) != 0; +} + +bool GuidedSearcher::tryAddTarget(ref history, + ref target) { + if (isReached(history, target)) { + return false; + } + assert(targetedSearchers.count(history) == 0 || + targetedSearchers.at(history).count(target) == 0); + targetedSearchers[history][target] = std::make_unique(target, codeGraphDistance); auto it = std::find_if( - targets.begin(), targets.end(), - [target](const Target &element) { return element == target; }); - assert(it == targets.end()); - targets.push_back(target); - assert(targets.size() == targetedSearchers.size()); + historiesAndTargets.begin(), historiesAndTargets.end(), + [&history, &target]( + const std::pair, ref> &element) { + return element.first.get() == history.get() && + element.second.get() == target.get(); + }); + assert(it == historiesAndTargets.end()); + historiesAndTargets.push_back({history, target}); + return true; } -void GuidedSearcher::removeTarget(Target target) { - targetedSearchers.erase(target); +GuidedSearcher::TargetForestHisoryTargetVector::iterator +GuidedSearcher::removeTarget(ref history, + ref target) { + targetedSearchers.at(history).erase(target); auto it = std::find_if( - targets.begin(), targets.end(), - [target](const Target &element) { return element == target; }); - assert(it != targets.end()); - targets.erase(it); - assert(targets.size() == targetedSearchers.size()); + historiesAndTargets.begin(), historiesAndTargets.end(), + [&history, &target]( + const std::pair, ref> &element) { + return element.first.get() == history.get() && + element.second.get() == target.get(); + }); + assert(it != historiesAndTargets.end()); + return historiesAndTargets.erase(it); } /// @@ -721,15 +928,19 @@ void WeightedRandomSearcher::printName(llvm::raw_ostream &os) { #define IS_OUR_NODE_VALID(n) \ (((n).getPointer() != nullptr) && (((n).getInt() & idBitMask) != 0)) -RandomPathSearcher::RandomPathSearcher(PTree &processTree, RNG &rng) - : processTree{processTree}, theRNG{rng}, idBitMask{ - processTree.getNextId()} {}; +RandomPathSearcher::RandomPathSearcher(PForest &processForest, RNG &rng) + : processForest{processForest}, theRNG{rng}, + idBitMask{processForest.getNextId()} {}; ExecutionState &RandomPathSearcher::selectState() { - unsigned flips = 0, bits = 0; - assert(processTree.root.getInt() & idBitMask && - "Root should belong to the searcher"); - PTreeNode *n = processTree.root.getPointer(); + unsigned flips = 0, bits = 0, range = 0; + PTreeNodePtr *root = nullptr; + while (!root || !IS_OUR_NODE_VALID(*root)) + root = &processForest.getPTrees() + .at(range++ % processForest.getPTrees().size() + 1) + ->root; + assert(root->getInt() & idBitMask && "Root should belong to the searcher"); + PTreeNode *n = root->getPointer(); while (!n->state) { if (!IS_OUR_NODE_VALID(n->left)) { assert(IS_OUR_NODE_VALID(n->right) && @@ -757,13 +968,14 @@ void RandomPathSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { // insert states - for (auto es : addedStates) { + for (auto &es : addedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; + PTreeNodePtr &root = processForest.getPTrees().at(pnode->getTreeID())->root; PTreeNodePtr *childPtr; childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; while (pnode && !IS_OUR_NODE_VALID(*childPtr)) { childPtr->setInt(childPtr->getInt() | idBitMask); pnode = parent; @@ -773,20 +985,21 @@ void RandomPathSearcher::update( childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; } } // remove states for (auto es : removedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; + PTreeNodePtr &root = processForest.getPTrees().at(pnode->getTreeID())->root; while (pnode && !IS_OUR_NODE_VALID(pnode->left) && !IS_OUR_NODE_VALID(pnode->right)) { auto childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; assert(IS_OUR_NODE_VALID(*childPtr) && "Removing pTree child not ours"); childPtr->setInt(childPtr->getInt() & ~idBitMask); pnode = parent; @@ -797,7 +1010,10 @@ void RandomPathSearcher::update( } bool RandomPathSearcher::empty() { - return !IS_OUR_NODE_VALID(processTree.root); + bool res = true; + for (const auto &ntree : processForest.getPTrees()) + res = res && !IS_OUR_NODE_VALID(ntree.second->root); + return res; } void RandomPathSearcher::printName(llvm::raw_ostream &os) { diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 52120d58bb..1e5120c696 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -11,6 +11,7 @@ #define KLEE_SEARCHER_H #include "ExecutionState.h" +#include "PForest.h" #include "PTree.h" #include "klee/ADT/RNG.h" #include "klee/Module/KModule.h" @@ -22,6 +23,7 @@ #include #include #include +#include #include namespace llvm { @@ -37,6 +39,8 @@ template class DiscretePDF; template class WeightedQueue; class ExecutionState; class Executor; +class TargetCalculator; +class TargetForest; /// A Searcher implements an exploration strategy for the Executor by selecting /// states for further exploration using different strategies or heuristics. @@ -137,10 +141,10 @@ class TargetedSearcher final : public Searcher { std::unique_ptr> states; - Target target; + ref target; CodeGraphDistance &codeGraphDistance; const std::unordered_map &distanceToTargetFunction; - std::vector reachedOnLastUpdate; + std::set reachedOnLastUpdate; bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance); WeightResult tryGetLocalWeight(ExecutionState *es, weight_type &weight, @@ -151,7 +155,7 @@ class TargetedSearcher final : public Searcher { WeightResult tryGetWeight(ExecutionState *es, weight_type &weight); public: - TargetedSearcher(Target target, CodeGraphDistance &distance); + TargetedSearcher(ref target, CodeGraphDistance &distance); ~TargetedSearcher() override; ExecutionState &selectState() override; @@ -160,57 +164,97 @@ class TargetedSearcher final : public Searcher { const std::vector &removedStates) override; bool empty() override; void printName(llvm::raw_ostream &os) override; - std::vector reached(); + std::set reached(); void removeReached(); }; class GuidedSearcher final : public Searcher { - private: + template + class TargetHashMap + : public std::unordered_map, T, RefTargetHash, RefTargetCmp> { + }; + using TargetToSearcherMap = TargetHashMap>; + using TargetToStateSetMap = + TargetHashMap>; + using TargetToStateVectorMap = TargetHashMap>; + + template + class TargetForestHistoryHashMap + : public std::unordered_map, T, + RefTargetsHistoryHash, RefTargetsHistoryCmp> { + }; + template + class TargetForestHistoryTargetsHashMap + : public TargetForestHistoryHashMap> {}; + + using TargetForestHistoryToSearcherMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHistoryToStateSetMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHisoryToStateVectorMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHisoryToTargetSet = TargetForestHistoryHashMap< + std::unordered_set, RefTargetHash, RefTargetCmp>>; + using TargetForestHisoryToTargetVector = + TargetForestHistoryHashMap>>; + using TargetForestHisoryTargetVector = + std::vector, ref>>; + + enum Guidance { CoverageGuidance, ErrorGuidance }; + + Guidance guidance; std::unique_ptr baseSearcher; - std::map> targetedSearchers; + TargetForestHistoryToSearcherMap targetedSearchers; CodeGraphDistance &codeGraphDistance; - TargetCalculator &stateHistory; + TargetCalculator *stateHistory; + TargetForestHisoryToTargetSet reachedTargets; std::set &pausedStates; std::size_t bound; RNG &theRNG; unsigned index{1}; - bool stopAfterReachingTarget; std::vector baseAddedStates; std::vector baseRemovedStates; - std::map> addedTStates; - std::map> removedTStates; + std::vector targetedAddedStates; std::vector targetlessStates; - std::vector targets; + TargetForestHisoryTargetVector historiesAndTargets; - void addTarget(Target target); - void removeTarget(Target target); + bool tryAddTarget(ref history, ref target); + TargetForestHisoryTargetVector::iterator + removeTarget(ref history, ref target); bool isStuck(ExecutionState &state); void innerUpdate(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); + void updateTargetedSearcher(ref history, + ref target, ExecutionState *current, + std::vector &addedStates, + std::vector &removedStates); - void clearReached(); - void collectReached( - std::map> &reachedStates); + void clearReached(const std::vector &removedStates); + void collectReached(TargetToStateSetMap &reachedStates); + bool isReached(ref history, ref target); public: GuidedSearcher( Searcher *baseSearcher, CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, std::set &pausedStates, - std::size_t bound, RNG &rng, bool stopAfterReachingTarget = true); + std::size_t bound, RNG &rng); + GuidedSearcher( + CodeGraphDistance &codeGraphDistance, + std::set &pausedStates, + std::size_t bound, RNG &rng); ~GuidedSearcher() override = default; ExecutionState &selectState() override; void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) override; - void - update(ExecutionState *current, - const std::vector &addedStates, - const std::vector &removedStates, - std::map> &reachedStates); + void update(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates, + TargetToStateSetMap &reachedStates); bool empty() override; void printName(llvm::raw_ostream &os) override; @@ -270,7 +314,7 @@ class WeightedRandomSearcher final : public Searcher { /// /// The ownership bits are maintained in the update method. class RandomPathSearcher final : public Searcher { - PTree &processTree; + PForest &processForest; RNG &theRNG; // Unique bitmask of this searcher @@ -279,7 +323,7 @@ class RandomPathSearcher final : public Searcher { public: /// \param processTree The process tree. /// \param RNG A random number generator. - RandomPathSearcher(PTree &processTree, RNG &rng); + RandomPathSearcher(PForest &processForest, RNG &rng); ~RandomPathSearcher() override = default; ExecutionState &selectState() override; diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index 9963e50931..a4e3307046 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -37,6 +37,7 @@ #include #include +#include using namespace llvm; using namespace klee; @@ -138,6 +139,8 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = { // operator new[](unsigned long) add("_Znam", handleNewArray, true), + // operator new[](unsigned long, std::nothrow_t const&) + add("_ZnamRKSt9nothrow_t", handleNewNothrowArray, true), // operator new(unsigned long) add("_Znwm", handleNew, true), @@ -499,6 +502,17 @@ void SpecialFunctionHandler::handleNewArray(ExecutionState &state, executor.typeSystemManager->handleAlloc(arguments[0])); } +void SpecialFunctionHandler::handleNewNothrowArray( + ExecutionState &state, KInstruction *target, + std::vector> &arguments) { + // XXX should type check args + assert(arguments.size() == 2 && + "invalid number of arguments to new[](unsigned long, std::nothrow_t " + "const&)"); + executor.executeAlloc(state, arguments[0], false, target, + executor.typeSystemManager->handleAlloc(arguments[0])); +} + void SpecialFunctionHandler::handleDeleteArray( ExecutionState &state, KInstruction *target, std::vector> &arguments) { @@ -606,7 +620,7 @@ void SpecialFunctionHandler::handleAssume(ExecutionState &state, assert(success && "FIXME: Unhandled solver failure"); if (res) { if (SilentKleeAssume) { - executor.terminateState(state); + executor.terminateState(state, StateTerminationType::SilentExit); } else { executor.terminateStateOnUserError( state, "invalid klee_assume call (provably false)"); diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h index d21e49c066..6769febe25 100644 --- a/lib/Core/SpecialFunctionHandler.h +++ b/lib/Core/SpecialFunctionHandler.h @@ -128,6 +128,7 @@ class SpecialFunctionHandler { HANDLER(handleCloseMerge); HANDLER(handleNew); HANDLER(handleNewArray); + HANDLER(handleNewNothrowArray); HANDLER(handlePreferCex); HANDLER(handlePosixPreferCex); HANDLER(handlePrintExpr); diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index 862010d9f6..d69b85a028 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -1120,7 +1120,7 @@ void StatsTracker::checkCoverage() { case CoverageCheckPoliciy::Halting: klee_message("HaltTimer invoked due to absense of progress in branch " "coverage"); - executor.setHaltExecution(true); + executor.setHaltExecution(HaltExecution::CovCheck); break; case CoverageCheckPoliciy::Release: if (!releaseStates) { @@ -1139,7 +1139,7 @@ void StatsTracker::checkCoverage() { } else { klee_message("HaltTimer invoked due to absense of progress in branch " "coverage"); - executor.setHaltExecution(true); + executor.setHaltExecution(HaltExecution::CovCheck); } break; } diff --git a/lib/Core/Target.cpp b/lib/Core/TargetCalculator.cpp similarity index 91% rename from lib/Core/Target.cpp rename to lib/Core/TargetCalculator.cpp index 9f884c2463..8975b32e21 100644 --- a/lib/Core/Target.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -1,4 +1,4 @@ -//===-- Target.cpp --------------------------------------------------------===// +//===-- TargetCalculator.cpp ---------- -----------------------------------===// // // The KLEE Symbolic Virtual Machine // @@ -7,11 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "Target.h" +#include "TargetCalculator.h" + #include "ExecutionState.h" #include "klee/Module/CodeGraphDistance.h" #include "klee/Module/KInstruction.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" #include #include @@ -34,15 +37,6 @@ llvm::cl::opt TargetCalculatorMode( cl::init(TargetCalculateBy::Default), cl::cat(ExecCat)); } // namespace klee -std::string Target::toString() const { - std::string repr = "Target: "; - repr += block->getAssemblyLocation(); - if (atReturn()) { - repr += " (at the end)"; - } - return repr; -} - void TargetCalculator::update(const ExecutionState &state) { switch (TargetCalculatorMode) { case TargetCalculateBy::Default: @@ -91,7 +85,7 @@ bool TargetCalculator::differenceIsEmpty( return diff.empty(); } -Target TargetCalculator::calculate(ExecutionState &state) { +ref TargetCalculator::calculate(ExecutionState &state) { BasicBlock *initialBlock = state.getInitPCBlock(); std::unordered_map &history = blocksHistory[initialBlock]; @@ -100,7 +94,7 @@ Target TargetCalculator::calculate(ExecutionState &state) { BasicBlock *bb = state.getPCBlock(); KFunction *kf = module.functionMap.at(bb->getParent()); KBlock *kb = kf->blockMap[bb]; - KBlock *nearestBlock = nullptr; + ref nearestBlock; unsigned int minDistance = UINT_MAX; unsigned int sfNum = 0; bool newCov = false; @@ -135,13 +129,13 @@ Target TargetCalculator::calculate(ExecutionState &state) { } else { newCov = true; } - nearestBlock = target; + nearestBlock = Target::create(target); minDistance = distance; } } if (nearestBlock) { - return Target(nearestBlock); + return nearestBlock; } if (sfi->caller) { @@ -149,5 +143,5 @@ Target TargetCalculator::calculate(ExecutionState &state) { } } - return Target(nearestBlock); + return nearestBlock; } diff --git a/lib/Core/Target.h b/lib/Core/TargetCalculator.h similarity index 66% rename from lib/Core/Target.h rename to lib/Core/TargetCalculator.h index bf6b564628..b63f3673c8 100644 --- a/lib/Core/Target.h +++ b/lib/Core/TargetCalculator.h @@ -1,4 +1,4 @@ -//===-- Target.h ------------------------------------------------*- C++ -*-===// +//===-- TargetCalculator.h --------------------------------------*- C++ -*-===// // // The KLEE Symbolic Virtual Machine // @@ -7,54 +7,35 @@ // //===----------------------------------------------------------------------===// -#ifndef KLEE_TARGET_H -#define KLEE_TARGET_H +#ifndef KLEE_TARGETCALCULATOR_H +#define KLEE_TARGETCALCULATOR_H +#include "klee/ADT/RNG.h" #include "klee/Module/KModule.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" +#include "klee/System/Time.h" + #include "klee/Support/OptionCategories.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" #include +#include +#include #include +#include namespace klee { class CodeGraphDistance; class ExecutionState; -class Executor; +struct TransitionHash; enum TargetCalculateBy { Default, Blocks, Transitions }; -struct Target { -private: - KBlock *block; - -public: - explicit Target(KBlock *_block) : block(_block) {} - - bool operator<(const Target &other) const { return block < other.block; } - - bool operator==(const Target &other) const { return block == other.block; } - - bool atReturn() const { return llvm::isa(block); } - - KBlock *getBlock() const { return block; } - - bool isNull() const { return block == nullptr; } - - explicit operator bool() const noexcept { return !isNull(); } - - std::string toString() const; -}; - typedef std::pair Transition; -struct TransitionHash { - std::size_t operator()(const Transition &p) const { - return reinterpret_cast(p.first) * 31 + - reinterpret_cast(p.second); - } -}; - class TargetCalculator { typedef std::unordered_set VisitedBlocks; typedef std::unordered_set VisitedTransitions; @@ -75,7 +56,7 @@ class TargetCalculator { void update(const ExecutionState &state); - Target calculate(ExecutionState &state); + ref calculate(ExecutionState &state); private: const KModule &module; @@ -94,4 +75,4 @@ class TargetCalculator { }; } // namespace klee -#endif /* KLEE_TARGET_H */ +#endif /* KLEE_TARGETCALCULATOR_H */ diff --git a/lib/Core/TimingSolver.cpp b/lib/Core/TimingSolver.cpp index 1fbbdd5614..63ba715614 100644 --- a/lib/Core/TimingSolver.cpp +++ b/lib/Core/TimingSolver.cpp @@ -95,6 +95,26 @@ bool TimingSolver::tryGetUnique(const ConstraintSet &constraints, ref e, return true; } +Solver::PartialValidity TimingSolver::evaluate(const ConstraintSet &constraints, + ref expr, + SolverQueryMetaData &metaData) { + // Fast path, to avoid timer and OS overhead. + if (ConstantExpr *CE = dyn_cast(expr)) { + return CE->isTrue() ? Solver::MustBeTrue : Solver::MustBeFalse; + } + + TimerStatIncrementer timer(stats::solverTime); + + if (simplifyExprs) + expr = ConstraintManager::simplifyExpr(constraints, expr); + + auto validity = solver->evaluate(Query(constraints, expr)); + + metaData.queryCost += timer.delta(); + + return validity; +} + bool TimingSolver::mustBeTrue(const ConstraintSet &constraints, ref expr, bool &result, SolverQueryMetaData &metaData, bool produceValidityCore) { diff --git a/lib/Core/TimingSolver.h b/lib/Core/TimingSolver.h index 22e39f4868..1e56309847 100644 --- a/lib/Core/TimingSolver.h +++ b/lib/Core/TimingSolver.h @@ -62,6 +62,9 @@ class TimingSolver { bool tryGetUnique(const ConstraintSet &, ref, ref &result, SolverQueryMetaData &metaData); + Solver::PartialValidity evaluate(const ConstraintSet &, ref, + SolverQueryMetaData &metaData); + bool mustBeTrue(const ConstraintSet &, ref, bool &result, SolverQueryMetaData &metaData, bool produceValidityCore = false); diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp index 407864b12c..562c9d2182 100644 --- a/lib/Core/UserSearcher.cpp +++ b/lib/Core/UserSearcher.cpp @@ -13,6 +13,7 @@ #include "MergeHandler.h" #include "Searcher.h" +#include "klee/Core/Interpreter.h" #include "klee/Support/ErrorHandling.h" #include "klee/Support/OptionCategories.h" @@ -64,15 +65,6 @@ cl::opt UseBatchingSearch( "(default=false)"), cl::init(false), cl::cat(SearchCat)); -cl::opt UseGuidedSearch( - "use-guided-search", - cl::desc("Takes place in two steps. First, all acyclic paths are executed, " - "then the execution is guided to sections of the program not yet " - "covered. These steps are repeated until all blocks of the " - "program are covered" - "(default=true)"), - cl::init(true), cl::cat(SearchCat)); - cl::opt BatchInstructions( "batch-instructions", cl::desc("Number of instructions to batch when using " @@ -120,7 +112,7 @@ bool klee::userSearcherRequiresMD2U() { } Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, - PTree &processTree) { + PForest &processForest) { Searcher *searcher = nullptr; switch (type) { case Searcher::DFS: @@ -133,7 +125,7 @@ Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, searcher = new RandomSearcher(rng); break; case Searcher::RandomPath: - searcher = new RandomPathSearcher(processTree, rng); + searcher = new RandomPathSearcher(processForest, rng); break; case Searcher::NURS_CovNew: searcher = @@ -169,7 +161,7 @@ Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, Searcher *klee::constructUserSearcher(Executor &executor) { Searcher *searcher = - getNewSearcher(CoreSearch[0], executor.theRNG, *executor.processTree); + getNewSearcher(CoreSearch[0], executor.theRNG, *executor.processForest); if (CoreSearch.size() > 1) { std::vector s; @@ -177,7 +169,7 @@ Searcher *klee::constructUserSearcher(Executor &executor) { for (unsigned i = 1; i < CoreSearch.size(); i++) s.push_back(getNewSearcher(CoreSearch[i], executor.theRNG, - *executor.processTree)); + *executor.processForest)); searcher = new InterleavedSearcher(s); } @@ -198,12 +190,19 @@ Searcher *klee::constructUserSearcher(Executor &executor) { searcher = ms; } - if (UseGuidedSearch) { + if (executor.guidanceKind == Interpreter::GuidanceKind::CoverageGuidance) { searcher = new GuidedSearcher( searcher, *executor.codeGraphDistance.get(), *executor.targetCalculator, executor.pausedStates, MaxCycles - 1, executor.theRNG); } + if (executor.guidanceKind == Interpreter::GuidanceKind::ErrorGuidance) { + delete searcher; + searcher = new GuidedSearcher(*executor.codeGraphDistance.get(), + executor.pausedStates, MaxCycles - 1, + executor.theRNG); + } + llvm::raw_ostream &os = executor.getHandler().getInfoStream(); os << "BEGIN searcher description\n"; diff --git a/lib/Core/UserSearcher.h b/lib/Core/UserSearcher.h index 0931d24379..69d07d9cfd 100644 --- a/lib/Core/UserSearcher.h +++ b/lib/Core/UserSearcher.h @@ -10,6 +10,8 @@ #ifndef KLEE_USERSEARCHER_H #define KLEE_USERSEARCHER_H +#include "llvm/Support/CommandLine.h" + namespace klee { class Executor; class Searcher; diff --git a/lib/Expr/SourceBuilder.cpp b/lib/Expr/SourceBuilder.cpp index 63394f9bda..9095623c3e 100644 --- a/lib/Expr/SourceBuilder.cpp +++ b/lib/Expr/SourceBuilder.cpp @@ -8,8 +8,12 @@ ref SourceBuilder::constantSource = ref(new ConstantSource()); ref SourceBuilder::makeSymbolicSource = ref(new MakeSymbolicSource()); -ref SourceBuilder::lazyInitializationMakeSymbolicSource = +ref SourceBuilder::lazyInitializationSymbolicSource = ref(new LazyInitializationSymbolicSource()); +ref SourceBuilder::irreproducibleSource = + ref(new IrreproducibleSource()); +ref SourceBuilder::symbolicValueSource = + ref(new SymbolicValueSource()); ref SourceBuilder::constant() { return SourceBuilder::constantSource; @@ -32,6 +36,14 @@ ref SourceBuilder::symbolicSize() { return new SymbolicSizeSource(); } -ref SourceBuilder::lazyInitializationMakeSymbolic() { - return SourceBuilder::lazyInitializationMakeSymbolicSource; +ref SourceBuilder::lazyInitializationSymbolic() { + return SourceBuilder::lazyInitializationSymbolicSource; +} + +ref SourceBuilder::irreproducible() { + return SourceBuilder::irreproducibleSource; +} + +ref SourceBuilder::symbolicValue() { + return SourceBuilder::symbolicValueSource; } diff --git a/lib/Module/CMakeLists.txt b/lib/Module/CMakeLists.txt index e1dde40d26..4e71e1509e 100644 --- a/lib/Module/CMakeLists.txt +++ b/lib/Module/CMakeLists.txt @@ -25,6 +25,9 @@ set(KLEE_MODULE_COMPONENT_SRCS RaiseAsm.cpp ReturnSplitter.cpp StreamWithLine.cpp + Target.cpp + TargetHash.cpp + TargetForest.cpp ) if (USE_WORKAROUND_LLVM_PR39177) diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp index 889b5e3a6a..79e03fca43 100644 --- a/lib/Module/KModule.cpp +++ b/lib/Module/KModule.cpp @@ -184,7 +184,7 @@ injectStaticConstructorsAndDestructors(Module *m, GlobalVariable *ctors = m->getNamedGlobal("llvm.global_ctors"); GlobalVariable *dtors = m->getNamedGlobal("llvm.global_dtors"); - if (!ctors && !dtors) + if ((!ctors && !dtors) || entryFunction.empty()) return; Function *mainFn = m->getFunction(entryFunction); @@ -273,6 +273,7 @@ void KModule::instrument(const Interpreter::ModuleOptions &opts) { pm.add(new IntrinsicCleanerPass(*targetData, opts.WithFPRuntime)); pm.run(*module); + withPosixRuntime = opts.WithPOSIXRuntime; } void KModule::optimiseAndPrepare( @@ -318,7 +319,8 @@ void KModule::optimiseAndPrepare( // going to be unresolved. We really need to handle the intrinsics // directly I think? legacy::PassManager pm3; - pm3.add(createCFGSimplificationPass()); + if (opts.Simplify) + pm3.add(createCFGSimplificationPass()); switch (SwitchType) { case eSwitchTypeInternal: break; @@ -423,7 +425,7 @@ void KModule::checkModule() { legacy::PassManager pm; if (!DontVerify) - pm.add(createVerifierPass()); + pm.add(createVerifierPass(false)); pm.add(operandTypeCheckPass); pm.run(*module); diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp new file mode 100644 index 0000000000..ac549e70e1 --- /dev/null +++ b/lib/Module/Target.cpp @@ -0,0 +1,89 @@ +//===-- Target.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" + +#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/KInstruction.h" + +#include +#include +#include + +using namespace llvm; +using namespace klee; + +std::string Target::toString() const { + std::ostringstream repr; + repr << "Target " << getId() << ": "; + if (shouldFailOnThisTarget()) { + repr << "error in "; + } + repr << block->getAssemblyLocation(); + if (atReturn()) { + repr << " (at the end)"; + } + return repr.str(); +} + +Target::EquivTargetHashSet Target::cachedTargets; +Target::TargetHashSet Target::targets; + +ref Target::getFromCacheOrReturn(Target *target) { + std::pair success = + cachedTargets.insert(target); + if (success.second) { + // Cache miss + targets.insert(target); + return target; + } + // Cache hit + delete target; + target = *(success.first); + return target; +} + +ref Target::create(KBlock *block, ReachWithError error, unsigned id) { + Target *target = new Target(block, error, id); + return getFromCacheOrReturn(target); +} + +ref Target::create(KBlock *block) { + Target *target = new Target(block); + return getFromCacheOrReturn(target); +} + +int Target::compare(const Target &other) const { + if (block != other.block) { + return block < other.block ? -1 : 1; + } + if (error != other.error) { + return error < other.error ? -1 : 1; + } + if (id != other.id) { + return id < other.id ? -1 : 1; + } + return 0; +} + +bool Target::equals(const Target &other) const { return compare(other) == 0; } + +bool Target::operator<(const Target &other) const { + return compare(other) == -1; +} + +bool Target::operator==(const Target &other) const { return equals(other); } + +Target::~Target() { + if (targets.find(this) != targets.end()) { + cachedTargets.erase(this); + targets.erase(this); + } +} diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp new file mode 100644 index 0000000000..96aa98270d --- /dev/null +++ b/lib/Module/TargetForest.cpp @@ -0,0 +1,285 @@ +//===-- TargetForest.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/TargetForest.h" + +#include "klee/Expr/Expr.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Module/TargetHash.h" + +using namespace klee; +using namespace llvm; + +void TargetForest::Layer::unionWith(TargetForest::Layer *other) { + if (other->forest.empty()) + return; + for (const auto &kv : other->forest) { + auto it = forest.find(kv.first); + if (it == forest.end()) { + forest.insert(std::make_pair(kv.first, kv.second)); + continue; + } + auto layer = new Layer(it->second); + layer->unionWith(kv.second.get()); + it->second = layer; + } +} + +void TargetForest::Layer::block(ref target) { + if (empty()) + return; + auto res = forest.find(target); + if (res != forest.end()) { + forest.erase(res); + } + for (InternalLayer::iterator itf = forest.begin(), eitf = forest.end(); + itf != eitf;) { + ref layer = itf->second->blockLeaf(target); + itf->second = layer; + if (itf->second->empty()) { + itf = forest.erase(itf); + eitf = forest.end(); + } else { + ++itf; + } + } +} + +bool TargetForest::Layer::deepFind(ref target) const { + if (empty()) + return false; + auto res = forest.find(target); + if (res != forest.end()) { + return true; + } + for (auto &f : forest) { + if (f.second->deepFind(target)) + return true; + } + return false; +} + +bool TargetForest::Layer::deepFindIn(ref child, + ref target) const { + auto res = forest.find(child); + if (res == forest.end()) { + return false; + } + return res->second->deepFind(target); +} + +TargetForest::Layer *TargetForest::Layer::removeChild(ref child) const { + auto result = new Layer(this); + result->forest.erase(child); + return result; +} + +TargetForest::Layer *TargetForest::Layer::addChild(ref child) const { + auto result = new Layer(this); + result->forest.insert({child, new Layer()}); + return result; +} + +TargetForest::Layer * +TargetForest::Layer::blockLeafInChild(ref child, + ref leaf) const { + auto subtree = forest.find(child); + assert(subtree != forest.end()); + if (subtree->second->forest.empty()) { + if (subtree->first != leaf) { + return new Layer(this); + } else { + return removeChild(child); + } + } + + ref sublayer = new Layer(subtree->second); + sublayer->block(leaf); + if (sublayer->empty()) { + return removeChild(child); + } else { + InternalLayer subforest; + subforest[child] = sublayer; + sublayer = new Layer(subforest); + auto result = replaceChildWith(child, sublayer.get()); + return result; + } +} + +TargetForest::Layer *TargetForest::Layer::blockLeaf(ref leaf) const { + auto result = new Layer(this); + if (forest.empty()) { + return result; + } + + for (auto &layer : forest) { + result = ref(result)->blockLeafInChild(layer.first, leaf); + } + return result; +} + +TargetForest::Layer * +TargetForest::Layer::replaceChildWith(ref const child, + TargetForest::Layer *other) const { + auto result = removeChild(child); + result->unionWith(other); + return result; +} + +void TargetForest::Layer::dump(unsigned n) const { + llvm::errs() << "THE " << n << " LAYER:\n"; + for (const auto &kv : forest) { + llvm::errs() << kv.first->toString() << "\n"; + } + llvm::errs() << "-----------------------\n"; + if (!forest.empty()) { + for (const auto &kv : forest) { + kv.second->dump(n + 1); + } + llvm::errs() << "++++++++++++++++++++++\n"; + } +} + +TargetForest::History::EquivTargetsHistoryHashSet + TargetForest::History::cachedHistories; +TargetForest::History::TargetsHistoryHashSet TargetForest::History::histories; + +ref +TargetForest::History::create(ref _target, + ref _visitedTargets) { + History *history = new History(_target, _visitedTargets); + + std::pair success = + cachedHistories.insert(history); + if (success.second) { + // Cache miss + histories.insert(history); + return history; + } + // Cache hit + delete history; + history = *(success.first); + return history; +} + +ref TargetForest::History::create(ref _target) { + return create(_target, nullptr); +} + +ref TargetForest::History::create() { + return create(nullptr); +} + +int TargetForest::History::compare(const History &h) const { + if (this == &h) + return 0; + + if (target && h.target) { + if (target != h.target) + return (target < h.target) ? -1 : 1; + } else { + return h.target ? -1 : (target ? 1 : 0); + } + + if (visitedTargets && h.visitedTargets) { + if (h.visitedTargets != h.visitedTargets) + return (visitedTargets < h.visitedTargets) ? -1 : 1; + } else { + return h.visitedTargets ? -1 : (visitedTargets ? 1 : 0); + } + + return 0; +} + +bool TargetForest::History::equals(const History &h) const { + return compare(h) == 0; +} + +void TargetForest::History::dump() const { + if (target) { + llvm::errs() << target->toString() << "\n"; + } else { + llvm::errs() << "end.\n"; + assert(!visitedTargets); + return; + } + if (visitedTargets) + visitedTargets->dump(); +} + +TargetForest::History::~History() { + if (histories.find(this) != histories.end()) { + histories.erase(this); + cachedHistories.erase(this); + } +} + +void TargetForest::stepTo(ref loc) { + if (forest->empty()) + return; + auto res = forest->find(loc); + if (res == forest->end()) { + return; + } + if (loc->shouldFailOnThisTarget()) { + forest = new Layer(); + } else { + history = history->add(loc); + forest = forest->replaceChildWith(loc, res->second.get()); + loc->isReported = true; + } + if (forest->empty() && !loc->shouldFailOnThisTarget()) { + history = History::create(); + } +} + +void TargetForest::add(ref target) { + auto res = forest->find(target); + if (res != forest->end()) { + return; + } + forest = forest->addChild(target); +} + +void TargetForest::remove(ref target) { + auto res = forest->find(target); + if (res == forest->end()) { + return; + } + forest = forest->removeChild(target); +} + +void TargetForest::blockIn(ref subtarget, ref target) { + if (!forest->deepFindIn(subtarget, target)) { + return; + } + forest = forest->blockLeafInChild(subtarget, target); +} + +void TargetForest::dump() const { + llvm::errs() << "History:\n"; + history->dump(); + llvm::errs() << "Forest:\n"; + forest->dump(1); +} + +ref TargetForest::deepCopy() { + return new TargetForest(forest->deepCopy(), entryFunction); +} + +ref TargetForest::Layer::deepCopy() { + auto copyForest = new TargetForest::Layer(this); + for (auto &targetAndForest : forest) { + auto target = targetAndForest.first; + auto layer = targetAndForest.second; + copyForest->forest[target] = layer->deepCopy(); + } + return copyForest; +} diff --git a/lib/Module/TargetHash.cpp b/lib/Module/TargetHash.cpp new file mode 100644 index 0000000000..b187311b83 --- /dev/null +++ b/lib/Module/TargetHash.cpp @@ -0,0 +1,47 @@ +//===-- TargetHash.c-------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/TargetHash.h" + +#include "klee/Module/Target.h" + +using namespace llvm; +using namespace klee; + +unsigned TargetHash::operator()(const Target *t) const { + if (t) { + return t->hash(); + } else { + return 0; + } +} + +bool TargetCmp::operator()(const Target *a, const Target *b) const { + return a == b; +} + +bool EquivTargetCmp::operator()(const Target *a, const Target *b) const { + if (a == NULL || b == NULL) + return false; + return a->compare(*b) == 0; +} + +unsigned RefTargetHash::operator()(const ref &t) const { + return t->hash(); +} + +bool RefTargetCmp::operator()(const ref &a, + const ref &b) const { + return a.get() == b.get(); +} + +std::size_t TransitionHash::operator()(const Transition &p) const { + return reinterpret_cast(p.first) * 31 + + reinterpret_cast(p.second); +} diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 74f04438dc..7313b52c79 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -15,6 +15,7 @@ #include "klee/ADT/TreeStream.h" #include "klee/Config/Version.h" #include "klee/Core/Interpreter.h" +#include "klee/Module/TargetForest.h" #include "klee/Solver/SolverCmdLine.h" #include "klee/Statistics/Statistics.h" #include "klee/Support/Debug.h" @@ -172,12 +173,34 @@ cl::opt OptimizeModule( "optimize", cl::desc("Optimize the code before execution (default=false)."), cl::init(false), cl::cat(StartCat)); +cl::opt SimplifyModule( + "simplify", cl::desc("Simplify the code before execution (default=true)."), + cl::init(true), cl::cat(StartCat)); + cl::opt WarnAllExternals( "warn-all-external-symbols", cl::desc( "Issue a warning on startup for all external symbols (default=false)."), cl::cat(StartCat)); +cl::opt UseGuidedSearch( + "use-guided-search", + cl::values(clEnumValN(Interpreter::GuidanceKind::NoGuidance, "none", + "Use basic klee symbolic execution"), + clEnumValN(Interpreter::GuidanceKind::CoverageGuidance, + "coverage", + "Takes place in two steps. First, all acyclic " + "paths are executed, " + "then the execution is guided to sections of the " + "program not yet covered. " + "These steps are repeated until all blocks of the " + "program are covered"), + clEnumValN(Interpreter::GuidanceKind::ErrorGuidance, "error", + "The execution is guided to sections of the " + "program errors not yet covered")), + cl::init(Interpreter::GuidanceKind::CoverageGuidance), + cl::desc("Kind of execution mode"), cl::cat(StartCat)); + /*** Linking options ***/ cl::OptionCategory LinkCat("Linking options", @@ -611,7 +634,7 @@ void KleeHandler::processTestCase(const ExecutionState &state, } if (m_numGeneratedTests == MaxTests) - m_interpreter->setHaltExecution(true); + m_interpreter->setHaltExecution(HaltExecution::MaxTests); if (WriteTestInfo) { time::Span elapsed_time(time::getWallTime() - start_time); @@ -1006,10 +1029,12 @@ void externalsAndGlobalsCheck(const llvm::Module *m) { static Interpreter *theInterpreter = 0; -static bool interrupted = false; +static std::atomic_bool interrupted{false}; // Pulled out so it can be easily called from a debugger. -extern "C" void halt_execution() { theInterpreter->setHaltExecution(true); } +extern "C" void halt_execution() { + theInterpreter->setHaltExecution(HaltExecution::Interrupt); +} extern "C" void stop_forking() { theInterpreter->setInhibitForking(true); } @@ -1182,12 +1207,20 @@ linkWithUclibc(StringRef libDir, std::string opt_suffix, } #endif -static int run_klee_on_function(int pArgc, char **pArgv, char **pEnvp, - KleeHandler *handler, Interpreter *interpreter, - llvm::Module *finalModule, - std::vector &replayPath) { +static std::unordered_map +parseStaticAnalysisInput() { + return std::unordered_map(); +} + +static int run_klee_on_function( + int pArgc, char **pArgv, char **pEnvp, + std::unique_ptr &handler, + std::unique_ptr &interpreter, llvm::Module *finalModule, + std::vector &replayPath, + std::unordered_map &targets) { Function *mainFn = finalModule->getFunction(EntryPoint); - if (!mainFn) { + if ((UseGuidedSearch != Interpreter::GuidanceKind::ErrorGuidance) && + !mainFn) { // in error guided mode we do not need main function klee_error("Entry function '%s' not found in module.", EntryPoint.c_str()); } @@ -1295,6 +1328,7 @@ static int run_klee_on_function(int pArgc, char **pArgv, char **pEnvp, sys::StrError(errno).c_str()); } } + interpreter->runFunctionAsMain(mainFn, pArgc, pArgv, pEnvp); while (!seeds.empty()) { @@ -1479,6 +1513,26 @@ int run_klee(int argc, char **argv, char **envp) { llvm::Module *mainModule = loadedUserModules.front().get(); + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + std::vector args; + args.push_back(llvm::Type::getInt32Ty(ctx)); // argc + args.push_back(llvm::PointerType::get( + Type::getInt8PtrTy(ctx), + mainModule->getDataLayout().getAllocaAddrSpace())); // argv + args.push_back(llvm::PointerType::get( + Type::getInt8PtrTy(ctx), + mainModule->getDataLayout().getAllocaAddrSpace())); // envp + std::string stubEntryPoint = "__klee_entry_point_main"; + Function *stub = Function::Create( + llvm::FunctionType::get(llvm::Type::getInt32Ty(ctx), args, false), + GlobalVariable::ExternalLinkage, stubEntryPoint, mainModule); + BasicBlock *bb = BasicBlock::Create(ctx, "entry", stub); + + llvm::IRBuilder<> Builder(bb); + Builder.CreateRet(ConstantInt::get(Type::getInt32Ty(ctx), 0)); + EntryPoint = stubEntryPoint; + } + std::vector mainModuleFunctions; for (auto &Function : *mainModule) { if (!Function.isDeclaration()) { @@ -1505,12 +1559,18 @@ int run_klee(int argc, char **argv, char **envp) { // Add additional user-selected suffix opt_suffix += "_" + RuntimeBuild.getValue(); + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + SimplifyModule = false; + } + std::string LibraryDir = KleeHandler::getRunTimeLibraryPath(argv[0]); Interpreter::ModuleOptions Opts(LibraryDir.c_str(), EntryPoint, opt_suffix, /*Optimize=*/OptimizeModule, + /*Simplify*/ SimplifyModule, /*CheckDivZero=*/CheckDivZero, /*CheckOvershift=*/CheckOvershift, - /*WithFPRuntime=*/WithFPRuntime); + /*WithFPRuntime=*/WithFPRuntime, + /*WithPOSIXRuntime=*/WithPOSIXRuntime); if (WithPOSIXRuntime) { SmallString<128> Path(Opts.LibraryDir); @@ -1657,13 +1717,23 @@ int run_klee(int argc, char **argv, char **envp) { KleeHandler::loadPathFile(ReplayPathFile, replayPath); } - Interpreter::InterpreterOptions IOpts; + std::unique_ptr handler = + std::make_unique(pArgc, pArgv); + + std::unordered_map targetForest; + + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + targetForest = parseStaticAnalysisInput(); + } + + Interpreter::InterpreterOptions IOpts(targetForest); IOpts.MakeConcreteSymbolic = MakeConcreteSymbolic; - KleeHandler *handler = new KleeHandler(pArgc, pArgv); - Interpreter *interpreter = theInterpreter = - Interpreter::create(ctx, IOpts, handler); + IOpts.Guidance = UseGuidedSearch; + std::unique_ptr interpreter( + Interpreter::create(ctx, IOpts, handler.get())); + theInterpreter = interpreter.get(); assert(interpreter); - handler->setInterpreter(interpreter); + handler->setInterpreter(interpreter.get()); for (int i = 0; i < argc; i++) { handler->getInfoStream() << argv[i] << (i + 1 < argc ? " " : "\n"); @@ -1732,7 +1802,7 @@ int run_klee(int argc, char **argv, char **envp) { sys::path::append(newOutputDirectory, entrypoint); handler->setOutputDirectory(newOutputDirectory.c_str()); run_klee_on_function(pArgc, pArgv, pEnvp, handler, interpreter, - finalModule, replayPath); + finalModule, replayPath, targetForest); exit(0); } else { child_processes.emplace_back(pid, std::chrono::steady_clock::now()); @@ -1755,7 +1825,7 @@ int run_klee(int argc, char **argv, char **envp) { } } else { run_klee_on_function(pArgc, pArgv, pEnvp, handler, interpreter, finalModule, - replayPath); + replayPath, targetForest); } // Free all the args. @@ -1763,8 +1833,6 @@ int run_klee(int argc, char **argv, char **envp) { delete[] pArgv[i]; delete[] pArgv; - delete interpreter; - uint64_t queries = *theStatisticManager->getStatisticByName("Queries"); uint64_t queriesValid = *theStatisticManager->getStatisticByName("QueriesValid"); @@ -1817,8 +1885,5 @@ int run_klee(int argc, char **argv, char **envp) { llvm::errs().resetColor(); handler->getInfoStream() << stats.str(); - - delete handler; - return 0; } diff --git a/lib/Solver/Solver.cpp b/lib/Solver/Solver.cpp index ce92b17974..6a71a6a57d 100644 --- a/lib/Solver/Solver.cpp +++ b/lib/Solver/Solver.cpp @@ -49,6 +49,17 @@ bool Solver::evaluate(const Query &query, Validity &result) { return impl->computeValidity(query, result); } +Solver::PartialValidity Solver::evaluate(const Query &query) { + assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!"); + + if (ConstantExpr *CE = dyn_cast(query.expr)) { + return CE->isTrue() ? PartialValidity::MustBeTrue + : PartialValidity::MustBeFalse; + } + + return impl->computePartialValidity(query); +} + bool Solver::mustBeTrue(const Query &query, bool &result) { assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!"); diff --git a/lib/Solver/SolverImpl.cpp b/lib/Solver/SolverImpl.cpp index f726d8f765..93774feabf 100644 --- a/lib/Solver/SolverImpl.cpp +++ b/lib/Solver/SolverImpl.cpp @@ -217,6 +217,29 @@ bool SolverImpl::computeMinimalUnsignedValue(const Query &query, return true; } +Solver::PartialValidity SolverImpl::computePartialValidity(const Query &query) { + bool isTrue, isFalse; + bool trueSuccess, falseSuccess; + trueSuccess = computeTruth(query, isTrue); + if (trueSuccess && isTrue) { + return Solver::PartialValidity::MustBeTrue; + } + falseSuccess = computeTruth(query.negateExpr(), isFalse); + if (falseSuccess && isFalse) { + return Solver::PartialValidity::MustBeFalse; + } + if (trueSuccess && falseSuccess) { + return Solver::PartialValidity::TrueOrFalse; + } + if (trueSuccess && !falseSuccess) { + return Solver::PartialValidity::MayBeFalse; + } + if (!trueSuccess && falseSuccess) { + return Solver::PartialValidity::MayBeTrue; + } + return Solver::PartialValidity::None; +} + const char *SolverImpl::getOperationStatusString(SolverRunStatus statusCode) { switch (statusCode) { case SOLVER_RUN_STATUS_SUCCESS_SOLVABLE: diff --git a/test/Feature/FunctionPointer.c b/test/Feature/FunctionPointer.c index e8c51d22dd..070d1ac91d 100644 --- a/test/Feature/FunctionPointer.c +++ b/test/Feature/FunctionPointer.c @@ -1,6 +1,6 @@ // RUN: %clang %s -emit-llvm -g -c -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --write-no-tests --exit-on-error %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --write-no-tests --exit-on-error %t.bc 2>&1 | FileCheck %s #include "klee/klee.h" diff --git a/test/Feature/LazyInitialization/LazyInitialization.c b/test/Feature/LazyInitialization/LazyInitialization.c index 31eb53bede..74356f31b3 100644 --- a/test/Feature/LazyInitialization/LazyInitialization.c +++ b/test/Feature/LazyInitialization/LazyInitialization.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -emit-llvm %O0opt -c -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out -solver-backend=z3 --skip-not-symbolic-objects --use-timestamps=false --use-guided-search=false %t.bc > %t.log +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-symbolic-objects --use-timestamps=false --use-guided-search=none %t.bc > %t.log // RUN: FileCheck %s -input-file=%t.log struct Node { diff --git a/test/Feature/MakeConcreteSymbolic.c b/test/Feature/MakeConcreteSymbolic.c index 36ed808079..0e172b6b49 100644 --- a/test/Feature/MakeConcreteSymbolic.c +++ b/test/Feature/MakeConcreteSymbolic.c @@ -4,7 +4,7 @@ // RUN: grep "done: total queries = 0" %t.klee-out/info // RUN: rm -rf %t.klee-out -// RUN: %klee --optimize=false --output-dir=%t.klee-out --make-concrete-symbolic=1 --exit-on-error %t1.bc +// RUN: %klee --optimize=false --output-dir=%t.klee-out --make-concrete-symbolic=1 --use-guided-search=none --exit-on-error %t1.bc // RUN: grep "done: total queries = 2" %t.klee-out/info #include diff --git a/test/Feature/MaxStaticForkPct.c b/test/Feature/MaxStaticForkPct.c index 42a51958f1..056744053b 100644 --- a/test/Feature/MaxStaticForkPct.c +++ b/test/Feature/MaxStaticForkPct.c @@ -1,12 +1,12 @@ // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.2 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt2 %s +// RUN: %klee -max-static-fork-pct=0.2 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt2 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.5 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt5 %s +// RUN: %klee -max-static-fork-pct=0.5 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt5 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.8 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt8 %s +// RUN: %klee -max-static-fork-pct=0.8 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt8 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-cpfork-pct=0.5 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-cppt5 %s +// RUN: %klee -max-static-cpfork-pct=0.5 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-cppt5 %s #include "klee/klee.h" #include diff --git a/test/Feature/MultipleFreeResolution.c b/test/Feature/MultipleFreeResolution.c index d62cdeb9b2..a938e87ce0 100644 --- a/test/Feature/MultipleFreeResolution.c +++ b/test/Feature/MultipleFreeResolution.c @@ -1,6 +1,6 @@ // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --emit-all-errors %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --emit-all-errors %t1.bc 2>&1 | FileCheck %s // RUN: ls %t.klee-out/ | grep .ktest | wc -l | grep 4 // RUN: ls %t.klee-out/ | grep .err | wc -l | grep 3 diff --git a/test/Merging/batching_break.c b/test/Merging/batching_break.c index 4384f618ac..11e94c8b50 100644 --- a/test/Merging/batching_break.c +++ b/test/Merging/batching_break.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/easy_merge.c b/test/Merging/easy_merge.c index 64ba770d0b..38e0cadedc 100644 --- a/test/Merging/easy_merge.c +++ b/test/Merging/easy_merge.c @@ -1,16 +1,16 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/incomplete_merge.c b/test/Merging/incomplete_merge.c index ccdeed0276..15e7bbe642 100644 --- a/test/Merging/incomplete_merge.c +++ b/test/Merging/incomplete_merge.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --use-incomplete-merge --debug-log-incomplete-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --use-incomplete-merge --debug-log-incomplete-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // 5 close merges diff --git a/test/Merging/indirect_value.c b/test/Merging/indirect_value.c index f073266e04..50ec733433 100644 --- a/test/Merging/indirect_value.c +++ b/test/Merging/indirect_value.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // CHECK: generated tests = 2{{$}} diff --git a/test/Merging/loop_merge.c b/test/Merging/loop_merge.c index edf06c262f..d87f8b625e 100644 --- a/test/Merging/loop_merge.c +++ b/test/Merging/loop_merge.c @@ -1,12 +1,12 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // There will be 20 'close merge' statements. Only checking a few, the generated diff --git a/test/Merging/merge_fail.c b/test/Merging/merge_fail.c index fd2a5e5dd2..5d9463420b 100644 --- a/test/Merging/merge_fail.c +++ b/test/Merging/merge_fail.c @@ -1,12 +1,12 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: generated tests = 2{{$}} diff --git a/test/Merging/nested_merge.c b/test/Merging/nested_merge.c index 8bc80a69a5..985beab041 100644 --- a/test/Merging/nested_merge.c +++ b/test/Merging/nested_merge.c @@ -1,14 +1,14 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // 5 close merges diff --git a/test/Merging/split_merge.c b/test/Merging/split_merge.c index 477180f224..d5d7dc2230 100644 --- a/test/Merging/split_merge.c +++ b/test/Merging/split_merge.c @@ -1,14 +1,14 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/state_termination.c b/test/Merging/state_termination.c index 13ad43790e..2d1492ea7c 100644 --- a/test/Merging/state_termination.c +++ b/test/Merging/state_termination.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc #include "klee/klee.h" diff --git a/test/Merging/unexpected_close.c b/test/Merging/unexpected_close.c index 5cc999741f..239101ec9b 100644 --- a/test/Merging/unexpected_close.c +++ b/test/Merging/unexpected_close.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --search=nurs:covnew --max-time=2 %t.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --search=nurs:covnew --max-time=2 %t.bc // CHECK: ran into a close at // CHECK: generated tests = 2{{$}} diff --git a/test/Runtime/POSIX/DirConsistency.c b/test/Runtime/POSIX/DirConsistency.c index d92766f7ec..a2f8c07709 100644 --- a/test/Runtime/POSIX/DirConsistency.c +++ b/test/Runtime/POSIX/DirConsistency.c @@ -1,11 +1,11 @@ // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out %t.klee-out-tmp // RUN: %gentmp %t.klee-out-tmp -// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=false --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t1.log +// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=none --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t1.log // RUN: %clang -D_FILE_OFFSET_BITS=64 %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out %t.klee-out-tmp // RUN: %gentmp %t.klee-out-tmp -// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=false --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t2.log +// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=none --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t2.log // RUN: sort %t1.log %t2.log | uniq -c > %t3.log // RUN: grep -q "4 COUNT" %t3.log diff --git a/unittests/Searcher/SearcherTest.cpp b/unittests/Searcher/SearcherTest.cpp index 7cb976e51b..bdb6226de4 100644 --- a/unittests/Searcher/SearcherTest.cpp +++ b/unittests/Searcher/SearcherTest.cpp @@ -12,6 +12,7 @@ #include "gtest/gtest.h" #include "Core/ExecutionState.h" +#include "Core/PForest.h" #include "Core/PTree.h" #include "Core/Searcher.h" #include "klee/ADT/RNG.h" @@ -25,11 +26,14 @@ namespace { TEST(SearcherTest, RandomPath) { // First state ExecutionState es; - PTree processTree(&es); - es.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&es); + es.ptreeNode = processForest.getPTrees() + .at(es.ptreeNode->getTreeID()) + ->root.getPointer(); RNG rng; - RandomPathSearcher rp(processTree, rng); + RandomPathSearcher rp(processForest, rng); EXPECT_TRUE(rp.empty()); rp.update(nullptr, {&es}, {}); @@ -38,7 +42,7 @@ TEST(SearcherTest, RandomPath) { // Two states ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); rp.update(&es, {&es1}, {}); // Random path seed dependant @@ -52,28 +56,31 @@ TEST(SearcherTest, RandomPath) { } rp.update(&es, {&es1}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); for (int i = 0; i < 100; i++) { EXPECT_EQ(&rp.selectState(), &es1); } rp.update(&es1, {}, {&es1}); - processTree.remove(es1.ptreeNode); + processForest.remove(es1.ptreeNode); EXPECT_TRUE(rp.empty()); } TEST(SearcherTest, TwoRandomPath) { // Root state ExecutionState root; - PTree processTree(&root); - root.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&root); + root.ptreeNode = processForest.getPTrees() + .at(root.ptreeNode->getTreeID()) + ->root.getPointer(); ExecutionState es(root); - processTree.attach(root.ptreeNode, &es, &root, BranchType::NONE); + processForest.attach(root.ptreeNode, &es, &root, BranchType::NONE); RNG rng, rng1; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng1); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng1); EXPECT_TRUE(rp.empty()); EXPECT_TRUE(rp1.empty()); @@ -84,7 +91,7 @@ TEST(SearcherTest, TwoRandomPath) { // Two states ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); rp.update(&es, {}, {}); rp1.update(nullptr, {&es1}, {}); @@ -104,16 +111,16 @@ TEST(SearcherTest, TwoRandomPath) { } rp1.update(&es, {}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); EXPECT_TRUE(rp1.empty()); EXPECT_EQ(&rp.selectState(), &es1); rp.update(&es1, {}, {&es1}); - processTree.remove(es1.ptreeNode); + processForest.remove(es1.ptreeNode); EXPECT_TRUE(rp.empty()); EXPECT_TRUE(rp1.empty()); - processTree.remove(root.ptreeNode); + processForest.remove(root.ptreeNode); } TEST(SearcherTest, TwoRandomPathDot) { @@ -123,23 +130,26 @@ TEST(SearcherTest, TwoRandomPathDot) { // Root state ExecutionState root; - PTree processTree(&root); - root.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&root); + root.ptreeNode = processForest.getPTrees() + .at(root.ptreeNode->getTreeID()) + ->root.getPointer(); rootPNode = root.ptreeNode; ExecutionState es(root); - processTree.attach(root.ptreeNode, &es, &root, BranchType::NONE); + processForest.attach(root.ptreeNode, &es, &root, BranchType::NONE); rightLeafPNode = root.ptreeNode; esParentPNode = es.ptreeNode; RNG rng; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng); rp.update(nullptr, {&es}, {}); ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); esLeafPNode = es.ptreeNode; es1LeafPNode = es1.ptreeNode; @@ -168,14 +178,14 @@ TEST(SearcherTest, TwoRandomPathDot) { << "}\n"; std::string pTreeDot; llvm::raw_string_ostream pTreeDotStream(pTreeDot); - processTree.dump(pTreeDotStream); + processForest.dump(pTreeDotStream); EXPECT_EQ(modelPTreeDot.str(), pTreeDotStream.str()); rp.update(&es, {&es1}, {&es}); rp1.update(nullptr, {&es}, {&es1}); rp1.update(&es, {}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); modelPTreeDot.str(""); modelPTreeDot @@ -197,22 +207,25 @@ TEST(SearcherTest, TwoRandomPathDot) { << "}\n"; pTreeDot = ""; - processTree.dump(pTreeDotStream); + processForest.dump(pTreeDotStream); EXPECT_EQ(modelPTreeDot.str(), pTreeDotStream.str()); - processTree.remove(es1.ptreeNode); - processTree.remove(root.ptreeNode); + processForest.remove(es1.ptreeNode); + processForest.remove(root.ptreeNode); } TEST(SearcherDeathTest, TooManyRandomPaths) { // First state ExecutionState es; - PTree processTree(&es); - es.ptreeNode = processTree.root.getPointer(); - processTree.remove(es.ptreeNode); // Need to remove to avoid leaks + PForest processForest = PForest(); + processForest.addRoot(&es); + es.ptreeNode = processForest.getPTrees() + .at(es.ptreeNode->getTreeID()) + ->root.getPointer(); + processForest.remove(es.ptreeNode); // Need to remove to avoid leaks RNG rng; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng); - RandomPathSearcher rp2(processTree, rng); - ASSERT_DEATH({ RandomPathSearcher rp3(processTree, rng); }, ""); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng); + RandomPathSearcher rp2(processForest, rng); + ASSERT_DEATH({ RandomPathSearcher rp3(processForest, rng); }, ""); } } // namespace From b06940f5a9117fe2afe2c38386c64163542bef5e Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 11 Apr 2023 18:36:55 +0300 Subject: [PATCH 02/41] [feat] Use `escapingFunctions` --- include/klee/Module/KModule.h | 6 ++--- lib/Core/ExecutionState.cpp | 3 +++ lib/Core/Searcher.cpp | 21 ++++++++------- lib/Module/CodeGraphDistance.cpp | 19 +++++++------- lib/Module/KModule.cpp | 45 +++++++++++++++++++++++++------- 5 files changed, 63 insertions(+), 31 deletions(-) diff --git a/include/klee/Module/KModule.h b/include/klee/Module/KModule.h index 42ba9ed619..6cef626590 100644 --- a/include/klee/Module/KModule.h +++ b/include/klee/Module/KModule.h @@ -89,13 +89,13 @@ struct KBlock { struct KCallBlock : KBlock { KInstruction *kcallInstruction; - llvm::Function *calledFunction; + std::set calledFunctions; public: KCallBlock(KFunction *, llvm::BasicBlock *, KModule *, std::unordered_map &, - std::unordered_map &, llvm::Function *, - KInstruction **); + std::unordered_map &, + std::set, KInstruction **); static bool classof(const KCallBlock *) { return true; } static bool classof(const KBlock *E) { return E->getKBlockType() == KBlockType::Call; diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index f47b3429f7..ef8def246c 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -229,6 +229,9 @@ bool ExecutionState::getBase( case Expr::Concat: { ref base = ArrayExprHelper::hasOrderedReads(*dyn_cast(expr)); + if (!base) { + return false; + } auto parent = findMemoryObject(base->updates.root); if (!parent) { return false; diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 1b293faa54..7293de5d0b 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -175,11 +175,12 @@ bool TargetedSearcher::distanceInCallGraph(KFunction *kf, KBlock *kb, for (auto &kCallBlock : kf->kCallBlocks) { if (dist.count(kCallBlock) != 0) { - KFunction *calledKFunction = - kf->parent->functionMap[kCallBlock->calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) != 0 && - distance > distanceToTargetFunction.at(calledKFunction) + 1) { - distance = distanceToTargetFunction.at(calledKFunction) + 1; + for (auto &calledFunction : kCallBlock->calledFunctions) { + KFunction *calledKFunction = kf->parent->functionMap[calledFunction]; + if (distanceToTargetFunction.count(calledKFunction) != 0 && + distance > distanceToTargetFunction.at(calledKFunction) + 1) { + distance = distanceToTargetFunction.at(calledKFunction) + 1; + } } } } @@ -222,10 +223,12 @@ TargetedSearcher::tryGetPreTargetWeight(ExecutionState *es, KFunction *currentKF = es->pc->parent->parent; std::vector localTargets; for (auto &kCallBlock : currentKF->kCallBlocks) { - KFunction *calledKFunction = - currentKF->parent->functionMap[kCallBlock->calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) > 0) { - localTargets.push_back(kCallBlock); + for (auto &calledFunction : kCallBlock->calledFunctions) { + KFunction *calledKFunction = + currentKF->parent->functionMap[calledFunction]; + if (distanceToTargetFunction.count(calledKFunction) > 0) { + localTargets.push_back(kCallBlock); + } } } diff --git a/lib/Module/CodeGraphDistance.cpp b/lib/Module/CodeGraphDistance.cpp index 0cc5dbcd18..9fb483355b 100644 --- a/lib/Module/CodeGraphDistance.cpp +++ b/lib/Module/CodeGraphDistance.cpp @@ -71,15 +71,16 @@ void CodeGraphDistance::calculateDistance(KFunction *kf) { while (!nodes.empty()) { KFunction *currKF = nodes.front(); for (auto &callBlock : currKF->kCallBlocks) { - if (!callBlock->calledFunction || - callBlock->calledFunction->isDeclaration()) { - continue; - } - KFunction *callKF = functionMap[callBlock->calledFunction]; - if (dist.count(callKF) == 0) { - dist[callKF] = dist[currKF] + 1; - sort.push_back({callKF, dist[currKF] + 1}); - nodes.push_back(callKF); + for (auto &calledFunction : callBlock->calledFunctions) { + if (!calledFunction || calledFunction->isDeclaration()) { + continue; + } + KFunction *callKF = functionMap[calledFunction]; + if (dist.count(callKF) == 0) { + dist[callKF] = dist[currKF] + 1; + sort.push_back({callKF, dist[currKF] + 1}); + nodes.push_back(callKF); + } } } nodes.pop_front(); diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp index 79e03fca43..912650a489 100644 --- a/lib/Module/KModule.cpp +++ b/lib/Module/KModule.cpp @@ -404,7 +404,23 @@ void KModule::manifest(InterpreterHandler *ih, bool forceSourceOutput) { for (auto &kfp : functions) { for (auto &kcb : kfp.get()->kCallBlocks) { - callMap[kcb->calledFunction].insert(kfp.get()->function); + bool isInlineAsm = false; +#if LLVM_VERSION_CODE >= LLVM_VERSION(8, 0) + const CallBase &cs = cast(*kcb->kcallInstruction->inst); + if (isa(cs.getCalledOperand())) { +#else + const CallSite cs(kcb->kcallInstruction->inst); + if (isa(cs.getCalledValue())) { +#endif + isInlineAsm = true; + } + if (kcb->calledFunctions.empty() && !isInlineAsm) { + kcb->calledFunctions.insert(escapingFunctions.begin(), + escapingFunctions.end()); + } + for (auto &calledFunction : kcb->calledFunctions) { + callMap[calledFunction].insert(kfp.get()->function); + } } } @@ -591,9 +607,13 @@ KFunction::KFunction(llvm::Function *_function, KModule *_km) Value *fp = cs.getCalledValue(); #endif Function *f = getTargetFunction(fp); - KCallBlock *ckb = - new KCallBlock(this, &*bbit, parent, instructionToRegisterMap, - registerToInstructionMap, f, &instructions[n]); + std::set calledFunctions; + if (f) { + calledFunctions.insert(f); + } + KCallBlock *ckb = new KCallBlock( + this, &*bbit, parent, instructionToRegisterMap, + registerToInstructionMap, calledFunctions, &instructions[n]); kCallBlocks.push_back(ckb); kb = ckb; } else if (SplitReturns && isa(lit)) { @@ -659,22 +679,27 @@ KCallBlock::KCallBlock( KFunction *_kfunction, llvm::BasicBlock *block, KModule *km, std::unordered_map &instructionToRegisterMap, std::unordered_map ®isterToInstructionMap, - llvm::Function *_calledFunction, KInstruction **instructionsKF) + std::set _calledFunctions, KInstruction **instructionsKF) : KBlock::KBlock(_kfunction, block, km, instructionToRegisterMap, registerToInstructionMap, instructionsKF), - kcallInstruction(this->instructions[0]), calledFunction(_calledFunction) { -} + kcallInstruction(this->instructions[0]), + calledFunctions(_calledFunctions) {} bool KCallBlock::intrinsic() const { - return calledFunction->getIntrinsicID() != llvm::Intrinsic::not_intrinsic; + return calledFunctions.size() == 1 && + (*calledFunctions.begin())->getIntrinsicID() != + llvm::Intrinsic::not_intrinsic; } bool KCallBlock::internal() const { - return parent->parent->functionMap[calledFunction] != nullptr; + return calledFunctions.size() == 1 && + parent->parent->functionMap[*calledFunctions.begin()] != nullptr; } KFunction *KCallBlock::getKFunction() const { - return parent->parent->functionMap[calledFunction]; + return calledFunctions.size() == 1 + ? parent->parent->functionMap[*calledFunctions.begin()] + : nullptr; } KReturnBlock::KReturnBlock( From 0d349e426c04463e8b417e7e099a9ff8368c9247 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 11 Apr 2023 18:59:41 +0300 Subject: [PATCH 03/41] [fix] Remove reached targets for taregeted forests with any histories --- include/klee/Module/TargetForest.h | 1 + lib/Core/Searcher.cpp | 55 +++++++++++++++--------------- lib/Core/Searcher.h | 4 ++- lib/Module/TargetForest.cpp | 3 ++ 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index e91f843995..6119abd43d 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -149,6 +149,7 @@ class TargetForest { Layer::iterator begin() const { return forest->begin(); } Layer::iterator end() const { return forest->end(); } bool contains(ref b) { return forest->find(b) != forest->end(); } + bool deepContains(ref b) { return forest->deepFind(b); } /// @brief Number of children of this layer (immediate successors) size_t successorCount() const { return forest->size(); } diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 7293de5d0b..cb8abf08f6 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -473,30 +473,28 @@ void GuidedSearcher::updateTargetedSearcher( if (historiedTargetedSearchers.count(target) != 0 || tryAddTarget(history, target)) { - if (reachedTargets.count(history) != 0) { - if (current) { - assert(current->targetForest.contains(target)); - for (auto &reached : reachedTargets[history]) { - current->targetForest.blockIn(target, reached); - } - if (!current->targetForest.contains(target)) { - removedStates.push_back(current); - } + if (current) { + assert(current->targetForest.contains(target)); + for (auto &reached : reachedTargets) { + current->targetForest.blockIn(target, reached); } - for (std::vector::iterator is = addedStates.begin(), - ie = addedStates.end(); - is != ie;) { - ExecutionState *state = *is; - assert(state->targetForest.contains(target)); - for (auto &reached : reachedTargets[history]) { - state->targetForest.blockIn(target, reached); - } - if (!state->targetForest.contains(target)) { - is = addedStates.erase(is); - ie = addedStates.end(); - } else { - ++is; - } + if (!current->targetForest.contains(target)) { + removedStates.push_back(current); + } + } + for (std::vector::iterator is = addedStates.begin(), + ie = addedStates.end(); + is != ie;) { + ExecutionState *state = *is; + assert(state->targetForest.contains(target)); + for (auto &reached : reachedTargets) { + state->targetForest.blockIn(target, reached); + } + if (!state->targetForest.contains(target)) { + is = addedStates.erase(is); + ie = addedStates.end(); + } else { + ++is; } } @@ -527,7 +525,10 @@ void GuidedSearcher::innerUpdate( removedStates.end()); if (ErrorGuidance == guidance) { - if (current && isStuck(*current)) { + if (current && + (std::find(baseRemovedStates.begin(), baseRemovedStates.end(), + current) == baseRemovedStates.end()) && + isStuck(*current)) { pausedStates.insert(current); baseRemovedStates.push_back(current); } @@ -727,7 +728,7 @@ void GuidedSearcher::clearReached( for (auto &state : states) { auto history = state->targetForest.getHistory(); if (target->shouldFailOnThisTarget()) { - reachedTargets[history].insert(target); + reachedTargets.insert(target); } targetedSearchers.at(history).at(target)->removeReached(); if (CoverageGuidance == guidance || @@ -765,10 +766,8 @@ void GuidedSearcher::printName(llvm::raw_ostream &os) { bool GuidedSearcher::isReached(ref history, ref target) { - return reachedTargets.count(history) != 0 && - reachedTargets[history].count(target) != 0; + return reachedTargets.count(target) != 0; } - bool GuidedSearcher::tryAddTarget(ref history, ref target) { if (isReached(history, target)) { diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 1e5120c696..12b3b67cc4 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -174,6 +174,8 @@ class GuidedSearcher final : public Searcher { class TargetHashMap : public std::unordered_map, T, RefTargetHash, RefTargetCmp> { }; + class TargetHashSet + : public std::unordered_set, RefTargetHash, RefTargetCmp> {}; using TargetToSearcherMap = TargetHashMap>; using TargetToStateSetMap = TargetHashMap>; @@ -208,7 +210,7 @@ class GuidedSearcher final : public Searcher { TargetForestHistoryToSearcherMap targetedSearchers; CodeGraphDistance &codeGraphDistance; TargetCalculator *stateHistory; - TargetForestHisoryToTargetSet reachedTargets; + TargetHashSet reachedTargets; std::set &pausedStates; std::size_t bound; RNG &theRNG; diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp index 96aa98270d..746058c953 100644 --- a/lib/Module/TargetForest.cpp +++ b/lib/Module/TargetForest.cpp @@ -72,6 +72,9 @@ bool TargetForest::Layer::deepFindIn(ref child, if (res == forest.end()) { return false; } + if (child == target) { + return true; + } return res->second->deepFind(target); } From 4bda1c802b9d6df3088ee4019b79b7b6cfa09f6f Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 9 Mar 2023 02:19:49 +0300 Subject: [PATCH 04/41] [feat] Add `NullCheckAfterDerefException` checker --- lib/Core/Executor.cpp | 56 +++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index c87878ffb8..8919119ede 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2422,6 +2422,29 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (branches.second) transferToBasicBlock(bi->getSuccessor(1), bi->getParent(), *branches.second); + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + ref eqPointerCheck = nullptr; + if (isa(cond) && cast(cond)->left->getWidth() == + Context::get().getPointerWidth()) { + eqPointerCheck = cast(cond); + } + if (isa(Expr::createIsZero(cond)) && + cast(Expr::createIsZero(cond))->left->getWidth() == + Context::get().getPointerWidth()) { + eqPointerCheck = cast(Expr::createIsZero(cond)); + } + if (eqPointerCheck && eqPointerCheck->left->isZero()) { + if (isa(cond) && !branches.first) { + terminateStateOnTargetError( + *branches.second, ReachWithError::NullCheckAfterDerefException); + } + if (isa(Expr::createIsZero(cond)) && !branches.second) { + terminateStateOnTargetError( + *branches.first, ReachWithError::NullCheckAfterDerefException); + } + } + } } break; } @@ -3034,16 +3057,14 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (kgepi->offset) offset = AddExpr::create(offset, Expr::createPointer(kgepi->offset)); ref address = AddExpr::create(base, offset); - if (!isa(address)) { - ref extractedOffset = ExtractExpr::create(offset, 0, Expr::Int32); - if (state.isGEPExpr(base)) { - state.gepExprBases[address] = state.gepExprBases[base]; - state.gepExprOffsets[address] = - AddExpr::create(state.gepExprOffsets[base], extractedOffset); - } else { - state.gepExprBases[address] = {base, gepInst->getSourceElementType()}; - state.gepExprOffsets[address] = extractedOffset; - } + ref extractedOffset = ExtractExpr::create(offset, 0, Expr::Int32); + if (state.isGEPExpr(base)) { + state.gepExprBases[address] = state.gepExprBases[base]; + state.gepExprOffsets[address] = + AddExpr::create(state.gepExprOffsets[base], extractedOffset); + } else { + state.gepExprBases[address] = {base, gepInst->getSourceElementType()}; + state.gepExprOffsets[address] = extractedOffset; } bindLocal(ki, state, address); break; @@ -4368,6 +4389,10 @@ void Executor::terminateStateOnTargetError(ExecutionState &state, messaget = "use after free error"; terminationType = StateTerminationType::Ptr; break; + case ReachWithError::NullCheckAfterDerefException: + messaget = "null check after deref"; + terminationType = StateTerminationType::Exit; + break; case ReachWithError::Reachable: messaget = ""; terminationType = StateTerminationType::Exit; @@ -5040,8 +5065,15 @@ void Executor::executeMemoryOperation( } } else if (isa(address) && guidanceKind == GuidanceKind::ErrorGuidance) { - terminateStateOnTargetError(state, ReachWithError::UseAfterFree); - return; + solver->setTimeout(coreSolverTimeout); + state.addressSpace.resolveOne(state, solver, base, baseTargetType, + idFastResult, success, haltExecution); + solver->setTimeout(time::Span()); + + if (!success) { + terminateStateOnTargetError(state, ReachWithError::UseAfterFree); + return; + } } // we are on an error path (no resolution, multiple resolution, one From e41fa70a939330f4c250fa76052d1fe62eadc9ff Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 9 Mar 2023 03:19:44 +0300 Subject: [PATCH 05/41] [refactor] Add `reportStateOnTargetError` --- lib/Core/Executor.cpp | 17 +++++++++++++++-- lib/Core/Executor.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 8919119ede..31015e1770 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2436,11 +2436,11 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } if (eqPointerCheck && eqPointerCheck->left->isZero()) { if (isa(cond) && !branches.first) { - terminateStateOnTargetError( + reportStateOnTargetError( *branches.second, ReachWithError::NullCheckAfterDerefException); } if (isa(Expr::createIsZero(cond)) && !branches.second) { - terminateStateOnTargetError( + reportStateOnTargetError( *branches.first, ReachWithError::NullCheckAfterDerefException); } } @@ -4371,8 +4371,21 @@ bool shouldExitOn(StateTerminationType reason) { return it != ExitOnErrorType.end(); } +void Executor::reportStateOnTargetError(ExecutionState &state, + ReachWithError error) { + if (guidanceKind == GuidanceKind::ErrorGuidance) { + bool reportedTruePositive = + targetedExecutionManager.reportTruePositive(state, error); + if (!reportedTruePositive) { + targetedExecutionManager.reportFalseNegative(state, error); + } + } +} + void Executor::terminateStateOnTargetError(ExecutionState &state, ReachWithError error) { + reportStateOnTargetError(state, error); + // Proceed with normal `terminateStateOnError` call std::string messaget; StateTerminationType terminationType; diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index aa5a9cded1..00a7e41a0f 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -466,6 +466,8 @@ class Executor : public Interpreter { void terminateStateEarly(ExecutionState &state, const llvm::Twine &message, StateTerminationType terminationType); + void reportStateOnTargetError(ExecutionState &state, ReachWithError error); + /// Save extra information in targeted mode /// Then just call `terminateStateOnError` void terminateStateOnTargetError(ExecutionState &state, ReachWithError error); From 6e3731b24671a7b9f15a3c9e1b2f895d3c93a323 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 9 Mar 2023 19:46:40 +0400 Subject: [PATCH 06/41] [fix] Add recognizing "arm" as a 32-bit architecture --- lib/Runner/run_klee.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 7313b52c79..1ba2ec0f26 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -1553,7 +1553,8 @@ int run_klee(int argc, char **argv, char **envp) { if (module_triple.find("i686") != std::string::npos || module_triple.find("i586") != std::string::npos || module_triple.find("i486") != std::string::npos || - module_triple.find("i386") != std::string::npos) + module_triple.find("i386") != std::string::npos || + module_triple.find("arm") != std::string::npos) opt_suffix = "32"; // Add additional user-selected suffix From 3158ed5da3e165b1aa233dc05a10154386defe15 Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Tue, 7 Mar 2023 19:41:11 +0300 Subject: [PATCH 07/41] Remove QF_AUFBV choosing for queries with constant arrays. --- lib/Solver/Z3Solver.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Solver/Z3Solver.cpp b/lib/Solver/Z3Solver.cpp index bc1ce143b5..5013db8dfa 100644 --- a/lib/Solver/Z3Solver.cpp +++ b/lib/Solver/Z3Solver.cpp @@ -425,8 +425,17 @@ bool Z3SolverImpl::internalRunSolver( exprs.insert(sideConstraint); } + std::vector arrays = query.gatherArrays(); + bool forceTactic = true; + for (const Array *array : arrays) { + if (isa(array->source)) { + forceTactic = false; + break; + } + } + Z3_solver theSolver; - if (Z3_probe_apply(builder->ctx, probe, goal)) { + if (forceTactic && Z3_probe_apply(builder->ctx, probe, goal)) { theSolver = Z3_mk_solver_for_logic( builder->ctx, Z3_mk_string_symbol(builder->ctx, "QF_AUFBV")); } else { From b893d16b7d7c7ffdf05ece0eba561f39b0e3c39b Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 14 Mar 2023 19:56:10 +0400 Subject: [PATCH 08/41] [fix] Fix `WarnAllExternals` option --- lib/Runner/run_klee.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 1ba2ec0f26..17a7909f5c 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -1010,7 +1010,7 @@ void externalsAndGlobalsCheck(const llvm::Module *m) { if (ext.compare(0, 5, "llvm.") != 0) { // not an LLVM reserved name if (unsafe.count(ext)) { foundUnsafe.insert(*it); - } else { + } else if (WarnAllExternals) { klee_warning("undefined reference to %s: %s", it->second ? "variable" : "function", ext.c_str()); } From 73b3438a51d7dc86d12c7df151e2b1f9397b493b Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Wed, 15 Mar 2023 17:01:35 +0400 Subject: [PATCH 09/41] [fix] Fix `NullCheckAfterDeref` checker --- lib/Core/Executor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 31015e1770..baa22b466f 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2434,7 +2434,8 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { Context::get().getPointerWidth()) { eqPointerCheck = cast(Expr::createIsZero(cond)); } - if (eqPointerCheck && eqPointerCheck->left->isZero()) { + if (eqPointerCheck && eqPointerCheck->left->isZero() && + state.resolvedPointers.count(eqPointerCheck->right)) { if (isa(cond) && !branches.first) { reportStateOnTargetError( *branches.second, ReachWithError::NullCheckAfterDerefException); From dae5e9dbdce7d47b4d7d78df082339879371aaed Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 11 Apr 2023 19:10:13 +0300 Subject: [PATCH 10/41] [fix] `prepareSymbolicValue` --- lib/Core/Executor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index baa22b466f..81ecff654d 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5598,6 +5598,8 @@ void Executor::prepareSymbolicValue(ExecutionState &state, ref count = eval(target, 0, state).value; count = Expr::createZExtToPointerWidth(count); size = MulExpr::create(size, count); + assert(isa(size)); + elementSize = cast(size)->getZExtValue(); } lazyInitializeObject(state, result, target, typeSystemManager->getWrappedType(ai->getType()), From d46a8e6849b1a8a9603988d1e170c0e67f5c6b20 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 17 Mar 2023 16:23:02 +0400 Subject: [PATCH 11/41] [fix] Handle `smt tactic failed` solver failure --- lib/Solver/Z3Solver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Solver/Z3Solver.cpp b/lib/Solver/Z3Solver.cpp index 5013db8dfa..eb0d4d0f26 100644 --- a/lib/Solver/Z3Solver.cpp +++ b/lib/Solver/Z3Solver.cpp @@ -654,6 +654,9 @@ SolverImpl::SolverRunStatus Z3SolverImpl::handleSolverResponse( if (strcmp(reason, "unknown") == 0) { return SolverImpl::SOLVER_RUN_STATUS_FAILURE; } + if (strcmp(reason, "smt tactic failed to show goal to be sat/unsat") == 0) { + return SolverImpl::SOLVER_RUN_STATUS_FAILURE; + } if (strcmp(reason, "interrupted from keyboard") == 0) { return SolverImpl::SOLVER_RUN_STATUS_INTERRUPTED; } From 23ce012b406a62c26466ca70fe181a759f244d12 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 17 Mar 2023 16:23:28 +0400 Subject: [PATCH 12/41] [fix] Fix segfault --- lib/Core/Executor.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 81ecff654d..2cd9baa142 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5189,8 +5189,7 @@ void Executor::executeMemoryOperation( terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); } else if (LazyInitialization && !isa(baseUniqueExpr) && (isa(address) || isa(address) || - state.isGEPExpr(address)) && - size <= MaxSymbolicAllocationSize) { + state.isGEPExpr(address))) { IDType idLazyInitialization = lazyInitializeObject(*unbound, base, target, baseTargetType, size); @@ -5279,15 +5278,20 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, timestamp = moBasePair.first->timestamp; } - const Array *lazyInstantiationSize = makeArray( - state, Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), - "lazy_instantiation_size", SourceBuilder::lazyInitializationSymbolic()); - ref sizeExpr = Expr::createTempRead(lazyInstantiationSize, - Context::get().getPointerWidth()); - addConstraint(state, UgeExpr::create(sizeExpr, Expr::createPointer(size))); - addConstraint( - state, UleExpr::create(sizeExpr, - Expr::createPointer(MaxSymbolicAllocationSize))); + ref sizeExpr; + if (size <= MaxSymbolicAllocationSize) { + const Array *lazyInstantiationSize = makeArray( + state, Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), + "lazy_instantiation_size", SourceBuilder::lazyInitializationSymbolic()); + sizeExpr = Expr::createTempRead(lazyInstantiationSize, + Context::get().getPointerWidth()); + addConstraint(state, UgeExpr::create(sizeExpr, Expr::createPointer(size))); + addConstraint( + state, UleExpr::create(sizeExpr, + Expr::createPointer(MaxSymbolicAllocationSize))); + } else { + sizeExpr = Expr::createPointer(size); + } std::string name = "lazy_initialization_content"; MemoryObject *mo = allocate(state, sizeExpr, false, @@ -5319,7 +5323,7 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, return 0; } - addConstraint(state, EqExpr::create(mo->addressExpr, address)); + addConstraint(state, EqExpr::create(mo->getBaseExpr(), address)); executeMakeSymbolic(state, mo, targetType, name, SourceBuilder::lazyInitializationSymbolic(), false); return mo->id; From 17668671fee40a4b4c10fa9bba95da21cd92637e Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 18 Mar 2023 19:31:04 +0400 Subject: [PATCH 13/41] [fix] Small fixes --- lib/Core/Executor.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 2cd9baa142..15723c5fce 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4187,12 +4187,12 @@ std::string Executor::getAddressInfo(ExecutionState &state, ref address, (void)success; example = value->getZExtValue(); info << "\texample: " << example << "\n"; - std::pair, ref> res = - mo ? std::make_pair( - mo->getBaseExpr(), - AddExpr::create(mo->getBaseExpr(), mo->getSizeExpr())) - : solver->getRange(state.constraints, address, state.queryMetaData); - info << "\trange: [" << res.first << ", " << res.second << "]\n"; + if (mo) { + std::pair, ref> res = + std::make_pair(mo->getBaseExpr(), + AddExpr::create(mo->getBaseExpr(), mo->getSizeExpr())); + info << "\trange: [" << res.first << ", " << res.second << "]\n"; + } } MemoryObject hack((unsigned)example); @@ -4810,7 +4810,10 @@ bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, /* We do not need this variable here, just a placeholder for resolve */ ResolutionList rlSkipped; - state.addressSpace.resolve(state, solver, p, type, rl, rlSkipped); + solver->setTimeout(coreSolverTimeout); + bool incomplete = state.addressSpace.resolve(state, solver, p, type, rl, + rlSkipped, 0, coreSolverTimeout); + solver->setTimeout(time::Span()); ExecutionState *unbound = &state; for (ResolutionList::iterator it = rl.begin(), ie = rl.end(); it != ie; @@ -4834,9 +4837,13 @@ bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, } if (unbound) { - terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, - StateTerminationType::Ptr, - getAddressInfo(*unbound, p)); + if (incomplete) { + terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); + } else { + terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, + StateTerminationType::Ptr, + getAddressInfo(*unbound, p)); + } } return true; } From ab30140dbeacb0ba299ecc9138a20364ec4eebc2 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 24 Mar 2023 14:23:09 +0400 Subject: [PATCH 14/41] [chore] Update .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9dc7e8df5d..e27fc5e755 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,5 @@ autoconf/aclocal.m4 autoconf/autom4te.cache/ autom4te.cache/ -.vscode/launch.json -.vscode/settings.json +.vscode/ .cache/ From 879c396cc66b0c4759785fe9331c69dfba4d981c Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 11 Apr 2023 19:17:40 +0300 Subject: [PATCH 15/41] [feat] Add mocks --- .github/workflows/build.yaml | 2 +- include/klee/Core/TerminationTypes.h | 3 +- lib/Core/ExecutionState.cpp | 4 + lib/Core/Executor.cpp | 430 ++++++++++++++++++--------- lib/Core/Executor.h | 14 +- lib/Core/PForest.cpp | 2 +- lib/Core/Searcher.cpp | 1 + lib/Core/SpecialFunctionHandler.cpp | 32 +- lib/Core/SpecialFunctionHandler.h | 1 + 9 files changed, 332 insertions(+), 157 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 58ad51bafa..b905595881 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -52,7 +52,7 @@ jobs: "Latest klee-uclibc", "Asserts enabled", "No TCMalloc, optimised runtime", - ] + ] include: - name: "LLVM 13" env: diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 7ea2f01ee6..2735c4eb3c 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -33,7 +33,8 @@ TTYPE(ReadOnly, 17U, "read_only.err") \ TTYPE(ReportError, 18U, "report_error.err") \ TTYPE(UndefinedBehavior, 19U, "undefined_behavior.err") \ - MARK(PROGERR, 19U) \ + TTYPE(InternalOutOfMemory, 20U, "out_of_memory.er") \ + MARK(PROGERR, 20U) \ TTYPE(User, 23U, "user.err") \ MARK(USERERR, 23U) \ TTYPE(Execution, 25U, "exec.err") \ diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index ef8def246c..8dcf8b7554 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -87,6 +87,7 @@ ExecutionState::ExecutionState(KFunction *kf) : initPC(kf->instructions), pc(initPC), prevPC(pc), roundingMode(llvm::APFloat::rmNearestTiesToEven) { pushFrame(nullptr, kf); + setID(); } ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) @@ -346,6 +347,7 @@ bool ExecutionState::merge(const ExecutionState &b) { std::set> aConstraints(constraints.begin(), constraints.end()); std::set> bConstraints(b.constraints.begin(), b.constraints.end()); + std::set> commonConstraints, aSuffix, bSuffix; std::set_intersection( aConstraints.begin(), aConstraints.end(), bConstraints.begin(), @@ -357,6 +359,7 @@ bool ExecutionState::merge(const ExecutionState &b) { std::set_difference(bConstraints.begin(), bConstraints.end(), commonConstraints.begin(), commonConstraints.end(), std::inserter(bSuffix, bSuffix.end())); + if (DebugLogStateMerge) { llvm::errs() << "\tconstraint prefix: ["; for (std::set>::iterator it = commonConstraints.begin(), @@ -472,6 +475,7 @@ bool ExecutionState::merge(const ExecutionState &b) { constraints = ConstraintSet(); ConstraintManager m(constraints); + for (const auto &constraint : commonConstraints) m.addConstraint(constraint); m.addConstraint(OrExpr::create(inA, inB)); diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 15723c5fce..f553c9b0c0 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -160,25 +160,50 @@ cl::opt "Use plain type system from LLVM"), clEnumValN(TypeSystemKind::CXX, "CXX", "Use type system from CXX")), - cl::init(TypeSystemKind::LLVM)); + cl::init(TypeSystemKind::LLVM), cl::cat(ExecCat)); cl::opt UseTBAA("use-tbaa", cl::desc("Turns on restrictions based on types compatibility for " "symbolic pointers (default=false)"), - cl::init(false)); + cl::init(false), cl::cat(ExecCat)); cl::opt AlignSymbolicPointers("align-symbolic-pointers", cl::desc("Makes symbolic pointers aligned according" "to the used type system (default=true)"), - cl::init(true)); + cl::init(true), cl::cat(ExecCat)); cl::opt OutOfMemAllocs("out-of-mem-allocs", cl::desc("Model malloc behavior, i.e. model NULL on 0 " "or huge symbolic allocations."), - cl::init(false)); + cl::init(false), cl::cat(ExecCat)); + +cl::opt + ExternCallsCanReturnNull("extern-calls-can-return-null", cl::init(false), + cl::desc("Enable case when extern call can crash " + "and return null (default=false)"), + cl::cat(ExecCat)); + +enum class MockMutableGlobalsPolicy { + None, + PrimitiveFields, + All, +}; + +cl::opt MockMutableGlobals( + "mock-mutable-globals", + cl::values(clEnumValN(MockMutableGlobalsPolicy::None, "none", + "No mutable global object are allowed o mock except " + "external (default)"), + clEnumValN(MockMutableGlobalsPolicy::PrimitiveFields, + "primitive-fields", + "Only primitive fileds of mutable global objects are " + "allowed to mock."), + clEnumValN(MockMutableGlobalsPolicy::All, "all", + "All mutable global object are allowed o mock.")), + cl::init(MockMutableGlobalsPolicy::None), cl::cat(ExecCat)); } // namespace klee namespace { @@ -201,12 +226,6 @@ cl::opt EmitAllErrors( "(default=false, i.e. one per (error,instruction) pair)"), cl::cat(TestGenCat)); -cl::opt SkipNotLazyAndSymbolicPointers( - "skip-not-lazy-and-symbolic-pointers", cl::init(false), - cl::desc("Set pointers only on lazy and make_symbolic variables " - "(default=false)"), - cl::cat(TestGenCat)); - /* Constraint solving options */ cl::opt MaxSymArraySize( @@ -263,6 +282,18 @@ cl::opt AllExternalWarnings( "as opposed to once per function (default=false)"), cl::cat(ExtCallsCat)); +cl::opt + MockExternalCalls("mock-external-calls", cl::init(false), + cl::desc("If true, failed external calls are mocked, " + "i.e. return values are made symbolic " + "and then added to generated test cases. " + "If false, fails on externall calls."), + cl::cat(ExtCallsCat)); + +cl::opt MockAllExternals("mock-all-externals", cl::init(false), + cl::desc("If true, all externals are mocked."), + cl::cat(ExtCallsCat)); + /*** Seeding options ***/ cl::opt AlwaysOutputSeeds( @@ -854,6 +885,7 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { /*alignment=*/globalObjectAlignment); if (!mo) klee_error("out of memory"); + globalObjects.emplace(&v, mo); globalAddresses.emplace(&v, mo->getBaseConstantExpr()); } @@ -919,12 +951,17 @@ void Executor::initializeGlobalObjects(ExecutionState &state) { } else { addr = externalDispatcher->resolveSymbol(v.getName().str()); } - if (!addr) { + if (MockAllExternals && !addr) { + executeMakeSymbolic( + state, mo, typeSystemManager->getWrappedType(v.getType()), + "mocked_extern", SourceBuilder::irreproducible(), false); + } else if (!addr) { klee_error("Unable to load symbol(%.*s) while initializing globals", static_cast(v.getName().size()), v.getName().data()); - } - for (unsigned offset = 0; offset < mo->size; offset++) { - os->write8(offset, static_cast(addr)[offset]); + } else { + for (unsigned offset = 0; offset < mo->size; offset++) { + os->write8(offset, static_cast(addr)[offset]); + } } } else if (v.hasInitializer()) { initializeGlobalObject(state, os, v.getInitializer(), 0); @@ -2406,6 +2443,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { ref cond = eval(ki, 0, state).value; cond = optimizer.optimizeExpr(cond, false); + Executor::StatePair branches = fork(state, cond, false, BranchType::ConditionalBranch); @@ -2733,45 +2771,53 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } else { ref v = eval(ki, 0, state).value; - ExecutionState *free = &state; - bool hasInvalid = false, first = true; - - /* XXX This is wasteful, no need to do a full evaluate since we - have already got a value. But in the end the caches should - handle it for us, albeit with some overhead. */ - do { - v = optimizer.optimizeExpr(v, true); - ref value; - bool success = - solver->getValue(free->constraints, v, value, free->queryMetaData); - assert(success && "FIXME: Unhandled solver failure"); - (void)success; - StatePair res = - fork(*free, EqExpr::create(v, value), true, BranchType::Call); - if (res.first) { - uint64_t addr = value->getZExtValue(); - auto it = legalFunctions.find(addr); - if (it != legalFunctions.end()) { - f = it->second; - - // Don't give warning on unique resolution - if (res.second || !first) - klee_warning_once(reinterpret_cast(addr), - "resolved symbolic function pointer to: %s", - f->getName().data()); - - executeCall(*res.first, ki, f, arguments); - } else { - if (!hasInvalid) { - terminateStateOnExecError(state, "invalid function pointer"); - hasInvalid = true; + if (!isa(v) && MockExternalCalls) { + if (ki->inst->getType()->isSized()) { + prepareSymbolicValue(state, ki, "mocked_extern", + SourceBuilder::irreproducible()); + } + } else { + ExecutionState *free = &state; + bool hasInvalid = false, first = true; + + /* XXX This is wasteful, no need to do a full evaluate since we + have already got a value. But in the end the caches should + handle it for us, albeit with some overhead. */ + do { + v = optimizer.optimizeExpr(v, true); + ref value; + bool success = solver->getValue(free->constraints, v, value, + free->queryMetaData); + assert(success && "FIXME: Unhandled solver failure"); + (void)success; + StatePair res = + fork(*free, EqExpr::create(v, value), true, BranchType::Call); + if (res.first) { + uint64_t addr = value->getZExtValue(); + auto it = legalFunctions.find(addr); + if (it != legalFunctions.end()) { + f = it->second; + + // Don't give warning on unique resolution + if (res.second || !first) + klee_warning_once(reinterpret_cast(addr), + "resolved symbolic function pointer to: %s", + f->getName().data()); + + executeCall(*res.first, ki, f, arguments); + } else { + if (!hasInvalid) { + terminateStateOnExecError(state, "invalid function pointer"); + hasInvalid = true; + } } } - } - first = false; - free = res.second; - } while (free && !haltExecution); + first = false; + free = res.second; + timers.invoke(); + } while (free && !haltExecution); + } } break; } @@ -4375,11 +4421,12 @@ bool shouldExitOn(StateTerminationType reason) { void Executor::reportStateOnTargetError(ExecutionState &state, ReachWithError error) { if (guidanceKind == GuidanceKind::ErrorGuidance) { - bool reportedTruePositive = - targetedExecutionManager.reportTruePositive(state, error); - if (!reportedTruePositive) { - targetedExecutionManager.reportFalseNegative(state, error); - } + // TODO: uncomment when `targetedExecutionManager` will be added + // bool reportedTruePositive = + // targetedExecutionManager.reportTruePositive(state, error); + // if (!reportedTruePositive) { + // targetedExecutionManager.reportFalseNegative(state, error); + // } } } @@ -4510,6 +4557,29 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, return; } + if (ExternalCalls == ExternalCallPolicy::All && MockAllExternals) { + std::string TmpStr; + llvm::raw_string_ostream os(TmpStr); + os << "calling external: " << callable->getName().str() << "("; + for (unsigned i = 0; i < arguments.size(); i++) { + os << arguments[i]; + if (i != arguments.size() - 1) + os << ", "; + } + os << ") at " << state.pc->getSourceLocation(); + + if (AllExternalWarnings) + klee_warning("%s", os.str().c_str()); + else if (!SuppressExternalWarnings) + klee_warning_once(callable->getValue(), "%s", os.str().c_str()); + + if (target->inst->getType()->isSized()) { + prepareSymbolicValue(state, target, "mocked_extern", + SourceBuilder::irreproducible()); + } + return; + } + // normal external function handling path // allocate 128 bits for each argument (+return value) to support fp80's; // we could iterate through all the arguments first and determine the exact @@ -4634,8 +4704,16 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, roundingMode); if (!success) { - terminateStateOnError(state, "failed external call: " + callable->getName(), - StateTerminationType::External); + if (MockExternalCalls) { + if (target->inst->getType()->isSized()) { + prepareSymbolicValue(state, target, "mocked_extern", + SourceBuilder::irreproducible()); + } + } else { + terminateStateOnError(state, + "failed external call: " + callable->getName(), + StateTerminationType::External); + } return; } @@ -4656,6 +4734,18 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, if (resultType != Type::getVoidTy(kmodule->module->getContext())) { ref e = ConstantExpr::fromMemory((void *)args, getWidthForLLVMType(resultType)); + if (ExternCallsCanReturnNull && + e->getWidth() == Context::get().getPointerWidth()) { + const Array *symExternCallsCanReturnNull = makeArray( + state, Expr::createPointer(1), "symExternCallsCanReturnNull", + SourceBuilder::irreproducible()); + + ref symExternCallsCanReturnNullExpr = + Expr::createTempRead(symExternCallsCanReturnNull, Expr::Bool); + e = SelectExpr::create( + symExternCallsCanReturnNullExpr, + ConstantExpr::alloc(0, Context::get().getPointerWidth()), e); + } bindLocal(target, state, e); } } @@ -4689,6 +4779,16 @@ ref Executor::replaceReadWithSymbolic(ExecutionState &state, return res; } +ref Executor::mockValue(ExecutionState &state, ref result) { + static unsigned id; + const Array *array = makeArray( + state, Expr::createPointer(Expr::getMinBytesForWidth(result->getWidth())), + "mockedGlobalValue" + llvm::utostr(++id), + SourceBuilder::irreproducible()); + result = Expr::createTempRead(array, result->getWidth()); + return result; +} + ObjectState *Executor::bindObjectInState(ExecutionState &state, const MemoryObject *mo, KType *dynamicType, bool isLocal, @@ -4710,7 +4810,7 @@ ObjectState *Executor::bindObjectInState(ExecutionState &state, void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, KInstruction *target, KType *type, bool zeroMemory, const ObjectState *reallocFrom, - size_t allocationAlignment) { + size_t allocationAlignment, bool checkOutOfMemory) { const llvm::Value *allocSite = state.prevPC->inst; if (allocationAlignment == 0) { allocationAlignment = getAllocationAlignment(allocSite); @@ -4737,7 +4837,20 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, } else { os->initializeToRandom(); } - bindLocal(target, *bound, mo->getBaseExpr()); + + ref address = mo->getBaseExpr(); + if (checkOutOfMemory) { + const Array *symCheckOutOfMemory = + makeArray(state, Expr::createPointer(1), "symCheckOutOfMemory", + SourceBuilder::irreproducible()); + + ref symCheckOutOfMemoryExpr = + Expr::createTempRead(symCheckOutOfMemory, Expr::Bool); + address = SelectExpr::create( + symCheckOutOfMemoryExpr, + Expr::createPointer(0), address); + } + bindLocal(target, state, address); if (reallocFrom) { unsigned count = std::min(reallocFrom->size, os->size); @@ -5079,6 +5192,14 @@ void Executor::executeMemoryOperation( if (interpreterOpts.MakeConcreteSymbolic) result = replaceReadWithSymbolic(state, result); + if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + mo->isGlobal && !wos->readOnly && !isa(result) && + (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || + !targetType->getRawType()->isPointerTy())) { + result = mockValue(state, result); + wos->write(offset, result); + } + bindLocal(target, state, result); } @@ -5104,90 +5225,105 @@ void Executor::executeMemoryOperation( ResolutionList rl; ResolutionList rlSkipped; - solver->setTimeout(coreSolverTimeout); - bool incomplete = state.addressSpace.resolve( - state, solver, address, targetType, rl, rlSkipped, 0, coreSolverTimeout); - solver->setTimeout(time::Span()); + bool incomplete = false; + ExecutionState *unbound = &state; + ref checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); + if (guidanceKind != GuidanceKind::ErrorGuidance) { + solver->setTimeout(coreSolverTimeout); + incomplete = + state.addressSpace.resolve(state, solver, address, targetType, rl, + rlSkipped, 0, coreSolverTimeout); + solver->setTimeout(time::Span()); - // XXX there is some query wasteage here. who cares? + // XXX there is some query wasteage here. who cares? - ref checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); - ref canLazyInitialize = ConstantExpr::create(1, Expr::Bool); + checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); - std::vector> resolveConditions; - std::vector resolvedMemoryObjects; + std::vector> resolveConditions; + std::vector resolvedMemoryObjects; - for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { - const MemoryObject *mo = state.addressSpace.findObject(*i).first; - ref inBounds = mo->getBoundsCheckPointer(address, bytes); - ref outOfBound = NotExpr::create(inBounds); - canLazyInitialize = AndExpr::create(outOfBound, canLazyInitialize); - if (state.isGEPExpr(address)) { - inBounds = AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, 1)); - inBounds = - AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); - outOfBound = AndExpr::create(outOfBound, mo->getBoundsCheckPointer(base)); - } + for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { + const MemoryObject *mo = state.addressSpace.findObject(*i).first; + ref inBounds = mo->getBoundsCheckPointer(address, bytes); + ref outOfBound = NotExpr::create(inBounds); + if (state.isGEPExpr(address)) { + inBounds = + AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, 1)); + inBounds = + AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); + outOfBound = + AndExpr::create(outOfBound, mo->getBoundsCheckPointer(base)); + } - bool mayBeInBounds; - solver->setTimeout(coreSolverTimeout); - bool success = solver->mayBeTrue(state.constraints, inBounds, mayBeInBounds, - state.queryMetaData); - solver->setTimeout(time::Span()); - if (!success) { - terminateStateOnSolverError(state, "Query timed out (resolve)"); - return; - } + bool mayBeInBounds; + solver->setTimeout(coreSolverTimeout); + bool success = solver->mayBeTrue(state.constraints, inBounds, + mayBeInBounds, state.queryMetaData); + solver->setTimeout(time::Span()); + if (!success) { + terminateStateOnSolverError(state, "Query timed out (resolve)"); + return; + } - if (mayBeInBounds) { - resolveConditions.push_back(inBounds); - resolvedMemoryObjects.push_back(mo->id); + if (mayBeInBounds) { + resolveConditions.push_back(inBounds); + resolvedMemoryObjects.push_back(mo->id); + } + checkOutOfBounds = AndExpr::create(outOfBound, checkOutOfBounds); } - checkOutOfBounds = AndExpr::create(outOfBound, checkOutOfBounds); - } - // Fictive condition just to keep state for unbound case. - resolveConditions.push_back(ConstantExpr::create(1, Expr::Bool)); + // Fictive condition just to keep state for unbound case. + resolveConditions.push_back(ConstantExpr::create(1, Expr::Bool)); - std::vector statesForMemoryOperation; - branch(state, resolveConditions, statesForMemoryOperation, BranchType::MemOp); + std::vector statesForMemoryOperation; + branch(state, resolveConditions, statesForMemoryOperation, + BranchType::MemOp); - for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { - ExecutionState *bound = statesForMemoryOperation[i]; - ObjectPair op = bound->addressSpace.findObject(resolvedMemoryObjects[i]); - const MemoryObject *mo = op.first; - const ObjectState *os = op.second; - - bound->addPointerResolution(base, address, mo); - - /* FIXME: Notice, that here we are creating a new instance of object - for every memory operation in order to handle type changes. This might - waste too much memory as we do now always modify something. To fix this - we can ask memory if we will make anything, and create a copy if - required. */ - ObjectState *wos = bound->addressSpace.getWriteable(mo, os); - - if (isWrite) { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); - if (wos->readOnly) { - terminateStateOnError(*bound, "memory error: object read only", - StateTerminationType::ReadOnly); + for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { + ExecutionState *bound = statesForMemoryOperation[i]; + ObjectPair op = bound->addressSpace.findObject(resolvedMemoryObjects[i]); + const MemoryObject *mo = op.first; + const ObjectState *os = op.second; + + bound->addPointerResolution(base, address, mo); + + /* FIXME: Notice, that here we are creating a new instance of object + for every memory operation in order to handle type changes. This might + waste too much memory as we do now always modify something. To fix this + we can ask memory if we will make anything, and create a copy if + required. */ + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); + + if (isWrite) { + wos->getDynamicType()->handleMemoryAccess( + targetType, mo->getOffsetExpr(address), + ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); + if (wos->readOnly) { + terminateStateOnError(*bound, "memory error: object read only", + StateTerminationType::ReadOnly); + } else { + wos->write(mo->getOffsetExpr(address), value); + } } else { - wos->write(mo->getOffsetExpr(address), value); + wos->getDynamicType()->handleMemoryAccess( + targetType, mo->getOffsetExpr(address), + ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); + ref result = wos->read(mo->getOffsetExpr(address), type); + + if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + mo->isGlobal && !wos->readOnly && !isa(result) && + (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || + !targetType->getRawType()->isPointerTy())) { + result = mockValue(state, result); + wos->write(mo->getOffsetExpr(address), result); + } + + bindLocal(target, *bound, result); } - } else { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); - ref result = wos->read(mo->getOffsetExpr(address), type); - bindLocal(target, *bound, result); } + unbound = statesForMemoryOperation.back(); } - ExecutionState *unbound = statesForMemoryOperation.back(); - // XXX should we distinguish out of bounds and overlapped cases? if (unbound) { ref baseUniqueExpr = toUnique(*unbound, base); @@ -5565,6 +5701,7 @@ ExecutionState *Executor::formState(Function *f, int argc, char **argv, /*allocSite=*/state->pc->inst, /*alignment=*/8); if (!arg) klee_error("Could not allocate memory for function arguments"); + ObjectState *os = bindObjectInState( *state, arg, typeSystemManager->getWrappedType(argumentType), false); @@ -5592,13 +5729,13 @@ void Executor::clearMemory() { memory = new MemoryManager(NULL); } -void Executor::prepareSymbolicValue(ExecutionState &state, - KInstruction *target) { +void Executor::prepareSymbolicValue(ExecutionState &state, KInstruction *target, + std::string name, + ref source) { Instruction *allocSite = target->inst; uint64_t size = kmodule->targetData->getTypeStoreSize(allocSite->getType()); uint64_t width = kmodule->targetData->getTypeSizeInBits(allocSite->getType()); - ref result = - makeSymbolicValue(allocSite, state, size, width, "symbolic_value"); + ref result = makeSymbolic(allocSite, state, size, width, name, source); bindLocal(target, state, result); if (isa(allocSite)) { AllocaInst *ai = cast(allocSite); @@ -5636,24 +5773,28 @@ void Executor::prepareSymbolicArgs(ExecutionState &state, KFunction *kf) { } } -ref Executor::makeSymbolicValue(Value *value, ExecutionState &state, - uint64_t size, Expr::Width width, - const std::string &name) { - MemoryObject *mo = memory->allocate(size, true, - /*isGlobal=*/false, value, +ref Executor::makeSymbolic(Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name, + ref source) { + MemoryObject *mo = memory->allocate(size, true, /*isGlobal=*/false, value, /*allocationAlignment=*/8); - const Array *array = makeArray(state, Expr::createPointer(size), name, - SourceBuilder::symbolicValue()); + const Array *array = + makeArray(state, Expr::createPointer(size), name, source); state.addSymbolic(mo, array, typeSystemManager->getWrappedType(value->getType())); assert(value && "Attempted to make symbolic value from nullptr Value"); - ObjectState *os = bindObjectInState( - state, mo, typeSystemManager->getWrappedType(value->getType()), false, - array); - ref result = os->read(0, width); + ref result = Expr::createTempRead(array, width); return result; } +ref Executor::makeSymbolicValue(Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name) { + return makeSymbolic(value, state, size, width, name, + SourceBuilder::symbolicValue()); +} + void Executor::runFunctionAsMain(Function *f, int argc, char **argv, char **envp) { if (guidanceKind == GuidanceKind::ErrorGuidance && @@ -5734,7 +5875,7 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, ExecutionState *state) { Function *mainFn = kmodule->module->getFunction("__klee_posix_wrapped_main"); - assert(mainFn && "klee_posix_wrapped_main not found"); + assert(mainFn && "__klee_posix_wrapped_main not found"); KBlock *target = kmodule->functionMap[mainFn]->entryKBlock; if (pathWriter) @@ -5886,7 +6027,6 @@ void Executor::setInitializationGraph(const ExecutionState &state, continue; } ref constantAddress = cast(addressInModel); - ref mo; IDType idResult; if (state.resolveOnSymbolics(constantAddress, idResult)) { diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 00a7e41a0f..56975f73e4 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -318,7 +318,8 @@ class Executor : public Interpreter { void executeAlloc(ExecutionState &state, ref size, bool isLocal, KInstruction *target, KType *type, bool zeroMemory = false, const ObjectState *reallocFrom = 0, - size_t allocationAlignment = 0); + size_t allocationAlignment = 0, + bool checkOutOfMemory = false); /// Free the given address with checking for errors. If target is /// given it will be bound to 0 in the resulting states (this is a @@ -384,6 +385,8 @@ class Executor : public Interpreter { // Used for testing. ref replaceReadWithSymbolic(ExecutionState &state, ref e); + ref mockValue(ExecutionState &state, ref result); + const Cell &eval(KInstruction *ki, unsigned index, ExecutionState &state, bool isSymbolic = true); @@ -584,13 +587,20 @@ class Executor : public Interpreter { void clearMemory(); - void prepareSymbolicValue(ExecutionState &state, KInstruction *targetW); + void prepareSymbolicValue( + ExecutionState &state, KInstruction *targetW, + std::string name = "symbolic_value", + ref source = SourceBuilder::symbolicValue()); void prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, unsigned index); void prepareSymbolicArgs(ExecutionState &state, KFunction *kf); + ref makeSymbolic(llvm::Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name, ref source); + ref makeSymbolicValue(llvm::Value *value, ExecutionState &state, uint64_t size, Expr::Width width, const std::string &name); diff --git a/lib/Core/PForest.cpp b/lib/Core/PForest.cpp index 8a97410510..d1374f7be4 100644 --- a/lib/Core/PForest.cpp +++ b/lib/Core/PForest.cpp @@ -1,4 +1,4 @@ -//===-- PForest.cpp -------------------------------------------------------===// +//===-- PForest.cpp-------------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index cb8abf08f6..bf4deca0dd 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -768,6 +768,7 @@ bool GuidedSearcher::isReached(ref history, ref target) { return reachedTargets.count(target) != 0; } + bool GuidedSearcher::tryAddTarget(ref history, ref target) { if (isReached(history, target)) { diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index a4e3307046..24caa37e49 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -57,6 +57,11 @@ cl::opt "condition given to klee_assume() rather than " "emitting an error (default=false)"), cl::cat(TerminationCat)); + +cl::opt CheckOutOfMemory("check-out-of-memory", cl::init(false), + cl::desc("Enable out-of-memory checking during " + "memory allocation (default=false)"), + cl::cat(ExecCat)); } // namespace /// \todo Almost all of the demands in this file should be replaced @@ -87,6 +92,7 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = { add("calloc", handleCalloc, true), add("free", handleFree, false), add("klee_assume", handleAssume, false), + add("klee_sleep", handleSleep, false), add("klee_check_memory_access", handleCheckMemoryAccess, false), add("klee_get_valuef", handleGetValue, true), add("klee_get_valued", handleGetValue, true), @@ -479,7 +485,8 @@ void SpecialFunctionHandler::handleNew(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to new"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleDelete(ExecutionState &state, @@ -499,7 +506,8 @@ void SpecialFunctionHandler::handleNewArray(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to new[]"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleNewNothrowArray( @@ -510,7 +518,8 @@ void SpecialFunctionHandler::handleNewNothrowArray( "invalid number of arguments to new[](unsigned long, std::nothrow_t " "const&)"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleDeleteArray( @@ -527,7 +536,8 @@ void SpecialFunctionHandler::handleMalloc(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to malloc"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleMemalign(ExecutionState &state, @@ -604,6 +614,12 @@ void SpecialFunctionHandler::handleEhTypeid(ExecutionState &state, } #endif // SUPPORT_KLEE_EH_CXX +void SpecialFunctionHandler::handleSleep(ExecutionState &state, + KInstruction *target, + std::vector> &arguments) { + nanosleep((const struct timespec[]){{1, 0}}, NULL); +} + void SpecialFunctionHandler::handleAssume(ExecutionState &state, KInstruction *target, std::vector> &arguments) { @@ -825,7 +841,8 @@ void SpecialFunctionHandler::handleCalloc(ExecutionState &state, ref size = MulExpr::create(arguments[0], arguments[1]); executor.executeAlloc(state, size, false, target, - executor.typeSystemManager->handleAlloc(size), true); + executor.typeSystemManager->handleAlloc(size), true, + nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleRealloc(ExecutionState &state, @@ -849,7 +866,8 @@ void SpecialFunctionHandler::handleRealloc(ExecutionState &state, if (zeroPointer.first) { // address == 0 executor.executeAlloc(*zeroPointer.first, size, false, target, - executor.typeSystemManager->handleAlloc(size)); + executor.typeSystemManager->handleAlloc(size), + false, nullptr, 0, CheckOutOfMemory); } if (zeroPointer.second) { // address != 0 Executor::ExactResolutionList rl; @@ -865,7 +883,7 @@ void SpecialFunctionHandler::handleRealloc(ExecutionState &state, executor.executeAlloc(*it->second, size, false, target, executor.typeSystemManager->handleRealloc( os->getDynamicType(), size), - false, os); + false, os, 0, CheckOutOfMemory); } } } diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h index 6769febe25..10ec81217a 100644 --- a/lib/Core/SpecialFunctionHandler.h +++ b/lib/Core/SpecialFunctionHandler.h @@ -103,6 +103,7 @@ class SpecialFunctionHandler { HANDLER(handleAbort); HANDLER(handleAssert); HANDLER(handleAssertFail); + HANDLER(handleSleep); HANDLER(handleAssume); HANDLER(handleCalloc); HANDLER(handleCheckMemoryAccess); From 2badbfce8222d0cc460a1884f483171f839575b0 Mon Sep 17 00:00:00 2001 From: Victor Samoilov Date: Thu, 20 Apr 2023 12:51:35 +0300 Subject: [PATCH 16/41] [chore] Add headers for nlohmann::json and nonstd::optional --- include/klee/Misc/.clang-format | 2 + include/klee/Misc/json.hpp | 24640 ++++++++++++++++++++++++++++++ include/klee/Misc/optional.hpp | 1847 +++ 3 files changed, 26489 insertions(+) create mode 100644 include/klee/Misc/.clang-format create mode 100644 include/klee/Misc/json.hpp create mode 100644 include/klee/Misc/optional.hpp diff --git a/include/klee/Misc/.clang-format b/include/klee/Misc/.clang-format new file mode 100644 index 0000000000..a43d914ec3 --- /dev/null +++ b/include/klee/Misc/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false \ No newline at end of file diff --git a/include/klee/Misc/json.hpp b/include/klee/Misc/json.hpp new file mode 100644 index 0000000000..e11f529688 --- /dev/null +++ b/include/klee/Misc/json.hpp @@ -0,0 +1,24640 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_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, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template