fold/tools/fold-swift/swan_sil_parser.hpp
2026-07-20 14:52:50 -04:00

436 lines
14 KiB
C++

// swan_sil_parser.hpp — SWAN-inspired SIL parser for fold-swift
//
// This is a focused C++ reimplementation of the SIL parser concepts from
// SWAN (themaplelab/swan, ESEC/FSE '20). It parses canonical SIL text
// (output of `swiftc -emit-sil`) into a lightweight AST and exports
// RustExprNode-compatible JSON for the fold symbolic IR encoder.
//
// Architecture:
// SIL text → recursive-descent parser → SIL AST nodes
// SIL AST → SWIRL-like operator extractor → RustExprNode JSON
//
// The parser focuses on security-relevant SIL operations:
// - unchecked casts (ref_cast, addr_cast, trivial_bit_cast)
// - load/store through pointer types
// - function calls (apply)
// - cond_fail (force-unwrap)
// - struct/tuple/enum access patterns
// - reference-counting operations
//
// The existing regex-based parser is kept as a fallback for SIL constructs
// that this parser doesn't yet handle.
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <nlohmann/json.hpp>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
namespace fold_swift {
// =========================================================================
// SIL Lexer / Source Location
// =========================================================================
struct SilLoc {
std::string file;
std::uint32_t line = 0;
std::uint32_t column = 0;
[[nodiscard]] bool valid() const { return !file.empty() && line != 0; }
};
struct SilSourceInfo {
std::uint32_t scope_ref = 0;
SilLoc loc;
[[nodiscard]] bool valid() const { return loc.valid(); }
};
// =========================================================================
// SIL Type (simplified — only what we need for security analysis)
// =========================================================================
enum class SilTypeKind : std::uint8_t {
Unknown,
Named, // e.g. Int, String, UnsafePointer<Int>
Address, // @inout or *T
Function, // function type
Tuple, // tuple type
Optional, // Optional<T>
};
struct SilType {
SilTypeKind kind = SilTypeKind::Unknown;
std::string name; // for Named
std::vector<SilType> params; // generic params / tuple elements / function params
bool is_optional = false;
bool is_pointer = false; // UnsafePointer, UnsafeMutablePointer, etc.
bool is_raw_pointer = false; // UnsafeRawPointer
};
// =========================================================================
// SIL Operand / Result
// =========================================================================
struct SilOperand {
std::string value; // %0, %1, etc.
SilType type;
};
struct SilResult {
std::vector<std::string> value_names; // %0 = ...
};
// =========================================================================
// SIL DeclRef (declaration reference — class/struct member, etc.)
// =========================================================================
struct SilDeclRef {
std::vector<std::string> parts; // e.g. ["MyClass", "myMethod"]
std::string kind; // func, allocator, enumelt, etc.
};
// =========================================================================
// SIL Operators (the ~75 SIL instructions)
// We model the security-relevant subset explicitly; the rest go through
// a generic "Other" path.
// =========================================================================
enum class SilOperatorKind : std::uint8_t {
// Allocation
AllocStack,
AllocRef,
AllocBox,
DeallocStack,
DeallocRef,
DeallocBox,
ProjectBox,
// Memory Access
Load,
Store,
LoadBorrow,
BeginBorrow,
EndBorrow,
EndLifetime,
CopyAddr,
DestroyAddr,
IndexAddr,
IndexRawPointer,
BeginAccess,
EndAccess,
// Reference Counting
StrongRetain,
StrongRelease,
SetDeallocating,
UnownedRetain,
UnownedRelease,
LoadWeak,
StoreWeak,
FixLifetime,
IsUnique,
IsEscapingClosure,
// Literals
FunctionRef,
DynamicFunctionRef,
GlobalAddr,
IntegerLiteral,
FloatLiteral,
StringLiteral,
// Dynamic Dispatch
ClassMethod,
ObjcMethod,
SuperMethod,
WitnessMethod,
// Function Application
Apply,
BeginApply,
AbortApply,
EndApply,
PartialApply,
Builtin,
// Aggregate Types
RetainValue,
ReleaseValue,
CopyValue,
DestroyValue,
Tuple,
TupleExtract,
TupleElementAddr,
DestructureTuple,
Struct,
StructExtract,
StructElementAddr,
DestructureStruct,
RefElementAddr,
RefTailAddr,
// Enums
Enum,
UncheckedEnumData,
InitEnumDataAddr,
InjectEnumAddr,
UncheckedTakeEnumDataAddr,
SelectEnum,
SelectEnumAddr,
// Protocol / Existential
InitExistentialAddr,
DeinitExistentialAddr,
OpenExistentialAddr,
OpenExistentialRef,
// Unchecked Conversions (security-critical)
Upcast,
AddressToPointer,
PointerToAddress,
UncheckedRefCast,
UncheckedRefCastAddr,
UncheckedAddrCast,
UncheckedTrivialBitCast,
UncheckedBitwiseCast,
UncheckedOwnershipConversion,
RefToRawPointer,
RawPointerToRef,
RefToUnowned,
UnownedToRef,
RefToUnmanaged,
UnmanagedToRef,
ConvertFunction,
ConvertEscapeToNoescape,
// Checked Conversions
UnconditionalCheckedCast,
UnconditionalCheckedCastAddr,
// Runtime Failures
CondFail,
// Other
Metatype,
ValueMetatype,
ExistentialMetatype,
SelectValue,
Keypath,
DebugValue,
DebugValueAddr,
Unknown,
};
struct SilOperator {
SilOperatorKind kind = SilOperatorKind::Unknown;
SilResult result;
std::vector<SilOperand> operands;
SilType type; // result type or target type for casts
SilType from_type; // source type for addr casts
SilDeclRef decl_ref; // for struct_extract, enum, etc.
std::int64_t int_value = 0;
std::string string_value;
std::string callee_name; // for function_ref, apply
std::vector<std::string> apply_args; // value operands for apply
bool nothrow = false;
SilSourceInfo source_info;
// Metadata
bool is_unchecked_cast = false;
bool is_load_through_pointer = false;
bool is_cond_fail = false;
std::string raw_text; // original SIL text line for fallback
};
// =========================================================================
// SIL Terminators
// =========================================================================
enum class SilTerminatorKind : std::uint8_t {
Unreachable,
Return,
Throw,
Yield,
Unwind,
Branch,
CondBranch,
SwitchValue,
SwitchEnum,
SwitchEnumAddr,
DynamicMethodBranch,
CheckedCastBranch,
CheckedCastAddrBranch,
TryApply,
Unknown,
};
struct SilTerminator {
SilTerminatorKind kind = SilTerminatorKind::Unknown;
SilOperand operand; // ret operand, switch operand, etc.
std::vector<SilOperand> args; // branch args
std::string target_label; // branch / cond_br target
std::string true_label; // cond_br true
std::string false_label; // cond_br false
std::string normal_label; // try_apply normal
std::string error_label; // try_apply error
SilSourceInfo source_info;
};
// =========================================================================
// SIL Basic Block
// =========================================================================
struct SilBlock {
std::string identifier;
std::vector<SilOperand> arguments;
std::vector<SilOperator> operators;
SilTerminator terminator;
};
// =========================================================================
// SIL Function
// =========================================================================
struct SilFunction {
std::string name; // demangled
std::string mangled_name;
SilType type;
std::vector<SilBlock> blocks;
int start_line = 0;
};
// =========================================================================
// SIL Module (full parse result)
// =========================================================================
struct SilModule {
std::string source_file;
std::vector<SilFunction> functions;
std::vector<std::string> imports;
bool parse_success = false;
std::string parse_error;
// Statistics
std::uint32_t total_blocks = 0;
std::uint32_t total_operators = 0;
};
// =========================================================================
// SIL Parser (recursive-descent, character-at-a-time)
// =========================================================================
class SilTextParser {
public:
explicit SilTextParser(std::string text, std::string source_path = "<memory>");
SilModule parseModule();
void setDemangleHook(std::function<std::string(const std::string&)> hook);
private:
// --- Lexer ---
char peek() const;
char peekAt(int offset) const;
bool peekStr(std::string_view s) const;
void skipWhitespace();
void skipWhitespaceAndComments();
bool skipToken(std::string_view s);
void takeToken(std::string_view s);
std::string takeIdentifier();
std::string takeUntil(char delim);
std::string takeWhile(int (*pred)(int));
bool tryTake(const std::string& s);
bool atEnd() const;
[[noreturn]] void parseError(const std::string& msg) const;
// --- Parser helpers ---
std::int64_t parseInt();
std::string parseMangledName();
std::string parseValueOperand();
int parseScopeRef();
SilLoc parseLoc();
SilSourceInfo parseSourceInfo();
SilType parseType();
SilType parseFunctionType();
SilType parseNamedType();
SilType parseOptionalType(SilType base);
SilType parseAddressType();
SilType parseTupleType();
SilType parseAttributedType();
SilDeclRef parseDeclRef();
std::string parseQuotedString();
void resolveSourceInfo(SilSourceInfo& info) const;
// --- Module-level ---
void parseImport();
SilFunction parseFunction();
SilBlock parseBlock();
SilOperator parseOperator();
SilTerminator parseTerminator();
SilOperand parseOperand();
SilResult parseResult();
// --- Operator parsers (grouped by category) ---
SilOperator parseAllocationOp(const std::string& name);
SilOperator parseMemoryAccessOp(const std::string& name);
SilOperator parseRefCountingOp(const std::string& name);
SilOperator parseLiteralOp(const std::string& name);
SilOperator parseDispatchOp(const std::string& name);
SilOperator parseApplyOp(const std::string& name);
SilOperator parseAggregateOp(const std::string& name);
SilOperator parseEnumOp(const std::string& name);
SilOperator parseConversionOp(const std::string& name);
SilOperator parseMiscOp(const std::string& name);
// --- Source location tracking ---
// SIL embeds source locations in comments: // file:line:col
// Track the most recently seen location to attach to next operator.
void trackCommentLoc();
// --- State ---
std::string source_;
std::string text_;
std::size_t cursor_ = 0;
std::size_t len_ = 0;
SilLoc last_comment_loc_; // tracked from // file:line:col comments
std::unordered_map<int, SilLoc> scope_map_; // scope_id -> loc
std::function<std::string(const std::string&)> demangle_hook_;
};
// =========================================================================
// SIL → RustExprNode JSON Converter
// =========================================================================
// Converts parsed SIL operators/terminators to RustExprNode-compatible JSON
// trees. Also produces finding NDJSON lines for security-relevant operations.
class SilToJsonConverter {
public:
// Convert a full SIL module to fold NDJSON output lines.
// Each line is a JSON object written to `out`.
void convertModule(const SilModule& mod, std::ostream& out);
private:
// Emit fold NDJSON record kinds
void emitInfo(const std::string& unit, std::ostream& out);
void emitFunction(const SilFunction& fn, const std::string& file, std::ostream& out);
void emitInterface(const SilFunction& fn, const std::string& file, std::ostream& out);
void emitIr(const SilFunction& fn, const std::string& file, std::ostream& out);
void emitFinding(const std::string& file, int line, int col, const std::string& function,
const std::string& kind, const std::string& hazard, const std::string& message,
std::ostream& out);
// Build RustExprNode JSON from a SIL operator
nlohmann::json operatorToExpr(const SilOperator& op, const std::string& file);
nlohmann::json buildCallExpr(const std::string& callee, const std::vector<nlohmann::json>& args,
int line, int col, const std::string& file);
nlohmann::json buildLiteralExpr(std::int64_t val, int width, bool signedness, int line, int col,
const std::string& file);
nlohmann::json buildStringLiteralExpr(const std::string& val, int line, int col,
const std::string& file);
nlohmann::json buildDeclRefExpr(const std::string& symbol, int line, int col,
const std::string& file);
nlohmann::json buildUnaryExpr(const std::string& op_name, nlohmann::json sub, int line, int col,
const std::string& file);
nlohmann::json buildBinaryExpr(const std::string& op_name, nlohmann::json lhs,
nlohmann::json rhs, int line, int col, const std::string& file);
nlohmann::json buildCastExpr(const std::string& target_type, nlohmann::json sub, int line,
int col, const std::string& file);
nlohmann::json buildMemberExpr(nlohmann::json base, const std::string& field, int line, int col,
const std::string& file);
// Build a source location object
nlohmann::json makeLoc(int line, int col, const std::string& file);
};
} // namespace fold_swift