diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 352fa0d..f110b61 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2956,4 +2956,13 @@ target_link_libraries(step452_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step453_test tests/step453_test.cpp) +target_include_directories(step453_test PRIVATE src) +target_link_libraries(step453_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/MemoryModelTranslator.h b/editor/src/MemoryModelTranslator.h new file mode 100644 index 0000000..beb6bde --- /dev/null +++ b/editor/src/MemoryModelTranslator.h @@ -0,0 +1,249 @@ +#pragma once + +// Step 453: Memory Model Translation — translates ownership/lifecycle across +// memory models with safety tracking annotations. + +#include +#include + +enum class OwnershipModel { Manual, RAII, GC, BorrowChecked, ARC }; +enum class SafetyDelta { Gained, Lost, Neutral }; + +struct MemoryTranslation { + std::string pattern; // e.g. "@Owner(Unique)", "@Owner(Shared_ARC)" + OwnershipModel sourceModel; + OwnershipModel targetModel; + std::string sourceLanguage; + std::string targetLanguage; + std::string sourceCode; + std::string targetCode; + SafetyDelta safetyChange = SafetyDelta::Neutral; + std::string annotation; + std::string note; +}; + +struct MemoryAnalysisResult { + std::string sourceLanguage; + std::string targetLanguage; + OwnershipModel sourceModel = OwnershipModel::Manual; + OwnershipModel targetModel = OwnershipModel::Manual; + std::vector translations; + + bool hasPattern(const std::string& p) const { + for (const auto& t : translations) + if (t.pattern == p) return true; + return false; + } + + int countSafetyGained() const { + int n = 0; + for (const auto& t : translations) + if (t.safetyChange == SafetyDelta::Gained) ++n; + return n; + } + + int countSafetyLost() const { + int n = 0; + for (const auto& t : translations) + if (t.safetyChange == SafetyDelta::Lost) ++n; + return n; + } +}; + +class MemoryModelTranslator { +public: + static MemoryAnalysisResult analyze(const std::string& source, + const std::string& srcLang, + const std::string& tgtLang) { + MemoryAnalysisResult result; + result.sourceLanguage = srcLang; + result.targetLanguage = tgtLang; + result.sourceModel = detectOwnership(source, srcLang); + result.targetModel = defaultOwnership(tgtLang); + + if (hasUniqueOwnership(source, srcLang)) + result.translations.push_back(translateUnique(srcLang, tgtLang)); + if (hasSharedOwnership(source, srcLang)) + result.translations.push_back(translateShared(srcLang, tgtLang)); + if (hasManualMemory(source)) + result.translations.push_back(translateManual(srcLang, tgtLang)); + if (hasScopeLifetime(source)) + result.translations.push_back(translateScope(srcLang, tgtLang)); + + return result; + } + +private: + static OwnershipModel detectOwnership(const std::string& src, const std::string& lang) { + if (lang == "rust") return OwnershipModel::BorrowChecked; + if (lang == "python" || lang == "java" || lang == "go" || lang == "javascript") + return OwnershipModel::GC; + if (src.find("unique_ptr") != std::string::npos || + src.find("shared_ptr") != std::string::npos) return OwnershipModel::RAII; + if (src.find("malloc") != std::string::npos || + src.find("free(") != std::string::npos) return OwnershipModel::Manual; + if (lang == "cpp" || lang == "c++") return OwnershipModel::RAII; + if (lang == "c") return OwnershipModel::Manual; + return OwnershipModel::Manual; + } + + static OwnershipModel defaultOwnership(const std::string& lang) { + if (lang == "rust") return OwnershipModel::BorrowChecked; + if (lang == "cpp" || lang == "c++") return OwnershipModel::RAII; + if (lang == "c") return OwnershipModel::Manual; + if (lang == "python" || lang == "java" || lang == "go" || lang == "javascript") + return OwnershipModel::GC; + return OwnershipModel::Manual; + } + + static bool hasUniqueOwnership(const std::string& src, const std::string& lang) { + if (lang == "rust") return src.find("Box<") != std::string::npos || src.find("let mut") != std::string::npos; + if (lang == "cpp") return src.find("unique_ptr") != std::string::npos; + return false; + } + + static bool hasSharedOwnership(const std::string& src, const std::string& lang) { + if (lang == "rust") return src.find("Arc<") != std::string::npos || src.find("Rc<") != std::string::npos; + if (lang == "cpp") return src.find("shared_ptr") != std::string::npos; + return false; + } + + static bool hasManualMemory(const std::string& src) { + return src.find("malloc(") != std::string::npos || + src.find("calloc(") != std::string::npos || + src.find("free(") != std::string::npos || + src.find("new ") != std::string::npos; + } + + static bool hasScopeLifetime(const std::string& src) { + return src.find("RAII") != std::string::npos || + src.find("defer ") != std::string::npos || + src.find("try-finally") != std::string::npos || + src.find("with ") != std::string::npos || + src.find("using (") != std::string::npos; + } + + static MemoryTranslation translateUnique(const std::string& src, const std::string& tgt) { + MemoryTranslation t; + t.pattern = "@Owner(Unique)"; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "Box::new(value) or owned value"; + t.safetyChange = (src == "c") ? SafetyDelta::Gained : SafetyDelta::Neutral; + t.annotation = "@Owner(Unique), @Lifetime(owned)"; + } else if (tgt == "cpp" || tgt == "c++") { + t.targetCode = "std::unique_ptr(new T(...))"; + t.safetyChange = (src == "c") ? SafetyDelta::Gained : SafetyDelta::Neutral; + t.annotation = "@Owner(Unique)"; + } else if (tgt == "c") { + t.targetCode = "T* p = malloc(sizeof(T)); /* single owner, must free */"; + t.safetyChange = SafetyDelta::Lost; + t.annotation = "@Owner(Manual), @Risk(high)"; + t.note = "Unique ownership lost — manual free required"; + } else { + t.targetCode = "value // GC handles lifetime"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Owner(GC)"; + t.note = "GC language — no explicit ownership needed"; + } + t.sourceModel = (src == "rust") ? OwnershipModel::BorrowChecked : + (src == "cpp") ? OwnershipModel::RAII : OwnershipModel::Manual; + t.targetModel = defaultOwnership(tgt); + return t; + } + + static MemoryTranslation translateShared(const std::string& src, const std::string& tgt) { + MemoryTranslation t; + t.pattern = "@Owner(Shared_ARC)"; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "Arc::new(value) or Rc::new(value)"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Owner(Shared_ARC)"; + } else if (tgt == "cpp" || tgt == "c++") { + t.targetCode = "std::shared_ptr(new T(...))"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Owner(Shared_ARC)"; + } else if (tgt == "c") { + t.targetCode = "T* p = malloc(sizeof(T)); /* manual refcount */"; + t.safetyChange = SafetyDelta::Lost; + t.annotation = "@Owner(Manual), @Risk(high)"; + } else { + t.targetCode = "value // GC handles sharing"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Owner(GC)"; + } + t.sourceModel = (src == "rust") ? OwnershipModel::BorrowChecked : OwnershipModel::RAII; + t.targetModel = defaultOwnership(tgt); + return t; + } + + static MemoryTranslation translateManual(const std::string& src, const std::string& tgt) { + MemoryTranslation t; + t.pattern = "@Owner(Manual)"; + t.sourceLanguage = src; + t.targetLanguage = tgt; + t.sourceModel = OwnershipModel::Manual; + + if (tgt == "rust") { + t.targetCode = "let value = T::new(...); // owned, borrow checker enforces lifetime"; + t.safetyChange = SafetyDelta::Gained; + t.annotation = "@Owner(BorrowChecked), @Risk(lowered)"; + t.note = "Borrow checker eliminates use-after-free and double-free"; + } else if (tgt == "cpp" || tgt == "c++") { + t.targetCode = "auto p = std::make_unique(...);"; + t.safetyChange = SafetyDelta::Gained; + t.annotation = "@Owner(RAII)"; + } else if (tgt == "java" || tgt == "python" || tgt == "go") { + t.targetCode = "T value = new T(...) // GC manages lifetime"; + t.safetyChange = SafetyDelta::Gained; + t.annotation = "@Owner(GC), @Risk(lowered)"; + } else { + t.targetCode = "malloc/free // manual"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Owner(Manual)"; + } + t.targetModel = defaultOwnership(tgt); + return t; + } + + static MemoryTranslation translateScope(const std::string& src, const std::string& tgt) { + MemoryTranslation t; + t.pattern = "@Lifetime(Scope)"; + t.sourceLanguage = src; + t.targetLanguage = tgt; + + if (tgt == "rust") { + t.targetCode = "{ let resource = acquire(); /* dropped at end of scope */ }"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Lifetime(Scope), @Owner(RAII)"; + } else if (tgt == "cpp" || tgt == "c++") { + t.targetCode = "{ auto resource = Resource(); /* destructor at scope end */ }"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Lifetime(Scope)"; + } else if (tgt == "python") { + t.targetCode = "with resource_manager() as r: ..."; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Lifetime(Scope)"; + } else if (tgt == "go") { + t.targetCode = "defer resource.Close()"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Lifetime(deferred)"; + } else if (tgt == "java") { + t.targetCode = "try (var r = new Resource()) { ... }"; + t.safetyChange = SafetyDelta::Neutral; + t.annotation = "@Lifetime(try-with-resources)"; + } else { + t.targetCode = "acquire(); ... release(); // manual scope"; + t.safetyChange = SafetyDelta::Lost; + t.annotation = "@Lifetime(Manual), @Risk(medium)"; + } + t.sourceModel = OwnershipModel::RAII; + t.targetModel = defaultOwnership(tgt); + return t; + } +}; diff --git a/editor/tests/step453_test.cpp b/editor/tests/step453_test.cpp new file mode 100644 index 0000000..b5a43cc --- /dev/null +++ b/editor/tests/step453_test.cpp @@ -0,0 +1,142 @@ +// Step 453: Memory Model Translation Tests (12 tests) + +#include "MemoryModelTranslator.h" + +#include + +static int passed = 0, failed = 0; +#define TEST(name) { std::cout << " " << #name << "... "; } +#define PASS() { std::cout << "PASS\n"; ++passed; } +#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } +#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} + +void test_manual_c_to_rust_gains_safety() { + TEST(manual_c_to_rust_gains_safety); + auto r = MemoryModelTranslator::analyze("p = malloc(8); free(p);", "c", "rust"); + CHECK(r.hasPattern("@Owner(Manual)"), "expected manual pattern"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Gained, "manual->rust should gain safety"); + CHECK(r.translations[0].annotation.find("@Risk(lowered)") != std::string::npos, + "expected lowered risk annotation"); + PASS(); +} + +void test_manual_c_to_java_gains_gc_safety() { + TEST(manual_c_to_java_gains_gc_safety); + auto r = MemoryModelTranslator::analyze("p = malloc(8); free(p);", "c", "java"); + CHECK(r.hasPattern("@Owner(Manual)"), "expected manual pattern"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Gained, "manual->java should gain safety"); + CHECK(r.translations[0].annotation.find("@Owner(GC)") != std::string::npos, + "expected GC owner annotation"); + PASS(); +} + +void test_unique_cpp_to_c_loses_safety() { + TEST(unique_cpp_to_c_loses_safety); + auto r = MemoryModelTranslator::analyze("std::unique_ptr p;", "cpp", "c"); + CHECK(r.hasPattern("@Owner(Unique)"), "expected unique ownership pattern"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Lost, "unique->C should lose safety"); + CHECK(r.translations[0].annotation.find("@Risk(high)") != std::string::npos, + "expected high risk annotation"); + PASS(); +} + +void test_shared_cpp_to_rust_arc_mapping() { + TEST(shared_cpp_to_rust_arc_mapping); + auto r = MemoryModelTranslator::analyze("std::shared_ptr p;", "cpp", "rust"); + CHECK(r.hasPattern("@Owner(Shared_ARC)"), "expected shared ownership pattern"); + CHECK(r.translations[0].targetCode.find("Arc::new") != std::string::npos, + "expected Arc mapping"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Neutral, "shared->shared should be neutral"); + PASS(); +} + +void test_shared_rust_to_c_loses_safety() { + TEST(shared_rust_to_c_loses_safety); + auto r = MemoryModelTranslator::analyze("let p: Arc = Arc::new(v);", "rust", "c"); + CHECK(r.hasPattern("@Owner(Shared_ARC)"), "expected shared ownership pattern"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Lost, "shared->C should lose safety"); + CHECK(r.translations[0].annotation.find("@Risk(high)") != std::string::npos, + "expected high risk annotation"); + PASS(); +} + +void test_scope_lifetime_to_go_defer() { + TEST(scope_lifetime_to_go_defer); + auto r = MemoryModelTranslator::analyze("with open(path) as f: data = f.read()", "python", "go"); + CHECK(r.hasPattern("@Lifetime(Scope)"), "expected scope lifetime pattern"); + CHECK(r.translations[0].annotation.find("@Lifetime(deferred)") != std::string::npos, + "expected deferred lifetime annotation"); + PASS(); +} + +void test_scope_to_c_loses_scope_guarantee() { + TEST(scope_to_c_loses_scope_guarantee); + auto r = MemoryModelTranslator::analyze("with resource_manager() as r: use(r)", "python", "c"); + CHECK(r.hasPattern("@Lifetime(Scope)"), "expected scope pattern"); + CHECK(r.translations[0].safetyChange == SafetyDelta::Lost, "scope->C should lose guarantee"); + CHECK(r.translations[0].annotation.find("@Risk(medium)") != std::string::npos, + "expected medium risk annotation"); + PASS(); +} + +void test_detect_source_model_rust_borrowchecked() { + TEST(detect_source_model_rust_borrowchecked); + auto r = MemoryModelTranslator::analyze("let x = Box::new(1);", "rust", "cpp"); + CHECK(r.sourceModel == OwnershipModel::BorrowChecked, "rust source should be borrow-checked"); + PASS(); +} + +void test_detect_target_model_java_gc() { + TEST(detect_target_model_java_gc); + auto r = MemoryModelTranslator::analyze("int* p = malloc(8);", "c", "java"); + CHECK(r.targetModel == OwnershipModel::GC, "java target should use GC model"); + PASS(); +} + +void test_no_pattern_on_plain_code() { + TEST(no_pattern_on_plain_code); + auto r = MemoryModelTranslator::analyze("x = a + b;", "python", "rust"); + CHECK(r.translations.empty(), "plain code should not emit memory translations"); + PASS(); +} + +void test_safety_gain_count_for_manual_migration() { + TEST(safety_gain_count_for_manual_migration); + auto r = MemoryModelTranslator::analyze("p = malloc(8); free(p);", "c", "rust"); + CHECK(r.countSafetyGained() >= 1, "expected at least one safety gain"); + CHECK(r.countSafetyLost() == 0, "manual->rust should not lose safety"); + PASS(); +} + +void test_mixed_patterns_report_gain_and_loss() { + TEST(mixed_patterns_report_gain_and_loss); + auto r = MemoryModelTranslator::analyze( + "std::unique_ptr p; q = malloc(8); free(q);", + "cpp", + "c"); + CHECK(r.translations.size() >= 2, "expected unique + manual translations"); + CHECK(r.countSafetyLost() >= 1, "expected at least one safety loss"); + CHECK(r.countSafetyGained() == 0, "cpp->c should not gain safety here"); + PASS(); +} + +int main() { + std::cout << "Step 453: Memory Model Translation Tests\n"; + + test_manual_c_to_rust_gains_safety(); // 1 + test_manual_c_to_java_gains_gc_safety(); // 2 + test_unique_cpp_to_c_loses_safety(); // 3 + test_shared_cpp_to_rust_arc_mapping(); // 4 + test_shared_rust_to_c_loses_safety(); // 5 + test_scope_lifetime_to_go_defer(); // 6 + test_scope_to_c_loses_scope_guarantee(); // 7 + test_detect_source_model_rust_borrowchecked(); // 8 + test_detect_target_model_java_gc(); // 9 + test_no_pattern_on_plain_code(); // 10 + test_safety_gain_count_for_manual_migration(); // 11 + test_mixed_patterns_report_gain_and_loss(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index ba9c2e9..f16458d 100644 --- a/progress.md +++ b/progress.md @@ -5793,3 +5793,32 @@ for async/thread/channel/mutex constructs across language runtime models. **Architecture gate check:** - `editor/src/ConcurrencyTranslator.h` within header-size limit (`249` <= `600`) - `editor/tests/step452_test.cpp` within test-file size guidance (`146` lines) + +### Step 453: Memory Model Translation +**Status:** PASS (12/12 tests) + +Completes ownership/lifetime translation across manual, RAII, GC, ARC, and +borrow-checked models with explicit safety-delta tracking. + +**Files added:** +- `editor/src/MemoryModelTranslator.h` — memory model translation engine: + - ownership model detection (`Manual`, `RAII`, `GC`, `BorrowChecked`, `ARC`) + - translation patterns for `@Owner(Unique)`, `@Owner(Shared_ARC)`, + `@Owner(Manual)`, `@Lifetime(Scope)` + - per-translation safety delta (`Gained`, `Lost`, `Neutral`) + - migration annotations (e.g., `@Risk(high)`, `@Risk(lowered)`) +- `editor/tests/step453_test.cpp` — 12 tests covering: + - manual-memory safety gains in Rust/GC targets + - unique/shared ownership degradation when targeting C + - scope-lifetime mapping (`defer`, try-with-resources, manual fallback) + - source/target model inference and mixed-pattern safety counters +- `editor/CMakeLists.txt` — `step453_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step453_test` — PASS +- `./editor/build-native/step453_test` — PASS (12/12) +- `./editor/build-native/step452_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/MemoryModelTranslator.h` within header-size limit (`249` <= `600`) +- `editor/tests/step453_test.cpp` within test-file size guidance (`142` lines)