Complete step453 memory model translation with tests
This commit is contained in:
@@ -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)
|
||||
|
||||
249
editor/src/MemoryModelTranslator.h
Normal file
249
editor/src/MemoryModelTranslator.h
Normal file
@@ -0,0 +1,249 @@
|
||||
#pragma once
|
||||
|
||||
// Step 453: Memory Model Translation — translates ownership/lifecycle across
|
||||
// memory models with safety tracking annotations.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<MemoryTranslation> 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<T>(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<T>(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>(...);";
|
||||
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;
|
||||
}
|
||||
};
|
||||
142
editor/tests/step453_test.cpp
Normal file
142
editor/tests/step453_test.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// Step 453: Memory Model Translation Tests (12 tests)
|
||||
|
||||
#include "MemoryModelTranslator.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<Foo> 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<Foo> 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<Foo> = 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<Foo> 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;
|
||||
}
|
||||
29
progress.md
29
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)
|
||||
|
||||
Reference in New Issue
Block a user