#pragma once // Step 445: API Boundary Preservation — ensures migrated modules maintain // their public API surface with FFI annotations and @Contract verification. #include "MigrationPlanGenerator.h" #include #include struct FunctionSignature { std::string name; std::string returnType; std::vector paramTypes; std::vector paramNames; bool isPublic = true; }; struct TypeMapping { std::string sourceName; std::string sourceLanguage; std::string targetName; std::string targetLanguage; std::string note; // compatibility note }; struct APIMigrationContract { std::string functionName; std::string precondition; std::string postcondition; }; struct FFIBoundary { std::string functionName; std::string sourceLanguage; std::string targetLanguage; std::string annotation; // e.g. @FFI(extern "C"), #[no_mangle], etc. }; struct APIBoundaryReport { std::string moduleName; std::string sourceLanguage; std::string targetLanguage; std::vector publicAPI; std::vector typeMappings; std::vector contracts; std::vector ffiBoundaries; bool apiPreserved = true; bool hasFunction(const std::string& name) const { for (const auto& f : publicAPI) if (f.name == name) return true; return false; } bool hasTypeMapping(const std::string& sourceName) const { for (const auto& t : typeMappings) if (t.sourceName == sourceName) return true; return false; } bool hasContract(const std::string& funcName) const { for (const auto& c : contracts) if (c.functionName == funcName) return true; return false; } bool hasFFI(const std::string& funcName) const { for (const auto& f : ffiBoundaries) if (f.functionName == funcName) return true; return false; } }; class APIBoundaryPreserver { public: static APIBoundaryReport analyze(const std::string& source, const MigrationUnit& unit) { APIBoundaryReport report; report.moduleName = unit.moduleName; report.sourceLanguage = unit.sourceLanguage; report.targetLanguage = unit.targetLanguage; // Extract public API from source report.publicAPI = extractPublicFunctions(source, unit.exports); // Generate type mappings report.typeMappings = generateTypeMappings(source, unit); // Generate contracts for public functions report.contracts = generateContracts(report.publicAPI, source); // Generate FFI boundaries for mixed-language operation report.ffiBoundaries = generateFFIBoundaries(report.publicAPI, unit); report.apiPreserved = true; return report; } static bool verifyPreservation(const APIBoundaryReport& original, const APIBoundaryReport& migrated) { // Every original public function must exist in migrated for (const auto& f : original.publicAPI) { if (!migrated.hasFunction(f.name)) return false; } return true; } private: static std::vector extractPublicFunctions( const std::string& source, const std::vector& exports) { std::vector sigs; for (const auto& name : exports) { FunctionSignature sig; sig.name = name; sig.isPublic = true; // Extract return type and params from source (heuristic) extractSignatureDetails(source, name, sig); sigs.push_back(std::move(sig)); } return sigs; } static void extractSignatureDetails(const std::string& source, const std::string& name, FunctionSignature& sig) { // Find "type name(" pattern size_t namePos = source.find(name + "("); if (namePos == std::string::npos) { sig.returnType = "int"; // default for C return; } // Extract return type (word before function name) size_t start = namePos; while (start > 0 && source[start - 1] == ' ') --start; size_t typeEnd = start; while (start > 0 && (std::isalnum((unsigned char)source[start - 1]) || source[start - 1] == '_' || source[start - 1] == '*')) --start; sig.returnType = source.substr(start, typeEnd - start); if (sig.returnType.empty()) sig.returnType = "int"; // Extract parameter list size_t parenOpen = source.find('(', namePos); size_t parenClose = source.find(')', parenOpen); if (parenOpen != std::string::npos && parenClose != std::string::npos) { std::string params = source.substr(parenOpen + 1, parenClose - parenOpen - 1); parseParams(params, sig); } } static void parseParams(const std::string& params, FunctionSignature& sig) { if (params.empty() || params == "void") return; size_t pos = 0; while (pos < params.size()) { size_t comma = params.find(',', pos); if (comma == std::string::npos) comma = params.size(); std::string param = trim(params.substr(pos, comma - pos)); if (!param.empty()) { // Split "type name" — last word is name, rest is type size_t lastSpace = param.rfind(' '); if (lastSpace != std::string::npos) { sig.paramTypes.push_back(trim(param.substr(0, lastSpace))); sig.paramNames.push_back(trim(param.substr(lastSpace + 1))); } else { sig.paramTypes.push_back(param); sig.paramNames.push_back("arg" + std::to_string(sig.paramNames.size())); } } pos = comma + 1; } } static std::vector generateTypeMappings(const std::string& source, const MigrationUnit& unit) { std::vector mappings; std::string tgt = unit.targetLanguage; // Common C → target type mappings auto addIf = [&](const std::string& pattern, const std::string& src, const std::string& dst, const std::string& note) { if (source.find(pattern) != std::string::npos) mappings.push_back({src, unit.sourceLanguage, dst, tgt, note}); }; if (tgt == "rust") { addIf("int ", "int", "i32", "C int maps to Rust i32"); addIf("char*", "char*", "&str / String", "C string to Rust string type"); addIf("void*", "void*", "*mut c_void", "Opaque pointer in FFI"); addIf("struct ", "struct", "struct", "C struct maps to Rust struct"); } else if (tgt == "java") { addIf("int ", "int", "int", "Direct mapping"); addIf("char*", "char*", "String", "C string to Java String"); addIf("struct ", "struct", "class", "C struct maps to Java class"); } else if (tgt == "python") { addIf("int ", "int", "int", "Direct mapping"); addIf("char*", "char*", "str", "C string to Python str"); } return mappings; } static std::vector generateContracts( const std::vector& api, const std::string& source) { std::vector contracts; for (const auto& f : api) { APIMigrationContract c; c.functionName = f.name; // Generate pre/post conditions from signature if (!f.paramTypes.empty()) { // Pointer params get non-null precondition for (size_t i = 0; i < f.paramTypes.size(); ++i) { if (f.paramTypes[i].find('*') != std::string::npos) { c.precondition += f.paramNames[i] + " != NULL; "; } } } if (f.returnType.find('*') != std::string::npos) { c.postcondition = "result may be NULL (caller must check)"; } else if (f.returnType == "int") { c.postcondition = "returns int result"; } if (!c.precondition.empty() || !c.postcondition.empty()) contracts.push_back(std::move(c)); } return contracts; } static std::vector generateFFIBoundaries( const std::vector& api, const MigrationUnit& unit) { std::vector boundaries; for (const auto& f : api) { FFIBoundary ffi; ffi.functionName = f.name; ffi.sourceLanguage = unit.sourceLanguage; ffi.targetLanguage = unit.targetLanguage; if (unit.targetLanguage == "rust") { ffi.annotation = "@FFI(#[no_mangle] pub extern \"C\")"; } else if (unit.targetLanguage == "java") { ffi.annotation = "@FFI(JNI native)"; } else { ffi.annotation = "@FFI(extern)"; } boundaries.push_back(std::move(ffi)); } return boundaries; } static std::string trim(const std::string& s) { size_t start = 0, end = s.size(); while (start < end && std::isspace((unsigned char)s[start])) ++start; while (end > start && std::isspace((unsigned char)s[end - 1])) --end; return s.substr(start, end - start); } };