diff --git a/src/main/java/cod/interpreter/InterpreterVisitor.java b/src/main/java/cod/interpreter/InterpreterVisitor.java index 0ec1f48..10a8080 100644 --- a/src/main/java/cod/interpreter/InterpreterVisitor.java +++ b/src/main/java/cod/interpreter/InterpreterVisitor.java @@ -240,7 +240,7 @@ public Object visit(Field n) { throw new ProgramError("Cannot redeclare field: " + n.name); } } - ctx.objectInstance.fields.put(n.name, val); + ctx.setObjectField(n.name, val); return val; } diff --git a/src/main/java/cod/interpreter/context/ExecutionContext.java b/src/main/java/cod/interpreter/context/ExecutionContext.java index 0fd38cc..688cd9f 100644 --- a/src/main/java/cod/interpreter/context/ExecutionContext.java +++ b/src/main/java/cod/interpreter/context/ExecutionContext.java @@ -44,6 +44,7 @@ protected Integer initialValue() { private boolean inOptimizedLoop = false; private List pendingOutputs = new ArrayList(); private boolean unsafeExecutionContext = false; + private IdentityHashMap> activeBorrowsByContainer; // ========== TYPE HANDLER ========== private final TypeHandler typeHandler; @@ -155,6 +156,7 @@ public ExecutionContext(ObjectInstance obj, Map locals, this.currentMethodName = null; this.currentLambdaClosure = null; this.typeHandler = typeHandler; + this.activeBorrowsByContainer = new IdentityHashMap>(); // Initialize locals this.localsStack = new ArrayList>(); @@ -176,6 +178,7 @@ public ExecutionContext(ObjectInstance obj, Map locals, // Build optimized slot access structures optimizeSlotAccess(); + registerInitialBorrowState(initialLocals); } /** @@ -236,7 +239,8 @@ public void setSlotValue(String slotName, Object value) { } // Update both representations - slotValues.put(slotName, value); + Object previous = slotValues.put(slotName, value); + replaceTrackedValue(previous, value); if (slotsOptimized && slotIndexMap.containsKey(slotName)) { int index = slotIndexMap.get(slotName); @@ -326,7 +330,10 @@ public void pushScope() { public void popScope() { if (localsStack.size() > 1) { - localsStack.remove(localsStack.size() - 1); + Map removedScope = localsStack.remove(localsStack.size() - 1); + for (Object value : removedScope.values()) { + unregisterBorrowsFromValue(value); + } localTypesStack.remove(localTypesStack.size() - 1); } } @@ -373,14 +380,16 @@ public void setVariable(String name, Object value) { for (int i = localsStack.size() - 1; i >= 0; i--) { Map scope = localsStack.get(i); if (scope.containsKey(name)) { - scope.put(name, value); + Object previous = scope.put(name, value); + replaceTrackedValue(previous, value); return; } } // Create in current scope Map currentScope = localsStack.get(localsStack.size() - 1); - currentScope.put(name, value); + Object previous = currentScope.put(name, value); + replaceTrackedValue(previous, value); } public String getVariableType(String name) { @@ -423,7 +432,9 @@ public Object removeVariable(String name) { for (int i = localsStack.size() - 1; i >= 0; i--) { Map scope = localsStack.get(i); if (scope.containsKey(name)) { - return scope.remove(name); + Object removed = scope.remove(name); + unregisterBorrowsFromValue(removed); + return removed; } } return null; @@ -440,7 +451,8 @@ public boolean removeVariableFromAllScopes(String name) { for (int i = localsStack.size() - 1; i >= 0; i--) { Map scope = localsStack.get(i); if (scope.containsKey(name)) { - scope.remove(name); + Object removed = scope.remove(name); + unregisterBorrowsFromValue(removed); found = true; } } @@ -461,6 +473,29 @@ public ExecutionContext copyWithVariable(String name, Object value, String type) return new ExecutionContext(objectInstance, newLocals, slotValues, slotTypes, typeHandler); } + + public void setObjectField(String fieldName, Object value) { + if (fieldName == null) { + throw new InternalError("setObjectField called with null fieldName"); + } + if (objectInstance == null || objectInstance.fields == null) { + throw new InternalError("setObjectField called outside object context"); + } + Object previous = objectInstance.fields.put(fieldName, value); + replaceTrackedValue(previous, value); + } + + public boolean hasActiveBorrow(Object container, long index) { + if (container == null) return false; + Map countsByIndex = activeBorrowsByContainer.get(container); + if (countsByIndex == null) return false; + Integer count = countsByIndex.get(index); + return count != null && count > 0; + } + + public void trackValueReplacement(Object oldValue, Object newValue) { + replaceTrackedValue(oldValue, newValue); + } // Clear slot optimization (if slots change) public void rebuildSlotOptimization() { @@ -471,4 +506,76 @@ public void rebuildSlotOptimization() { this.slotTypesList = null; optimizeSlotAccess(); } + + private void registerInitialBorrowState(Map initialLocals) { + if (initialLocals != null) { + for (Object value : initialLocals.values()) { + registerBorrowsFromValue(value); + } + } + if (slotValues != null) { + for (Object value : slotValues.values()) { + registerBorrowsFromValue(value); + } + } + if (objectInstance != null && objectInstance.fields != null) { + for (Object value : objectInstance.fields.values()) { + registerBorrowsFromValue(value); + } + } + } + + private void replaceTrackedValue(Object oldValue, Object newValue) { + unregisterBorrowsFromValue(oldValue); + registerBorrowsFromValue(newValue); + } + + private void registerBorrowsFromValue(Object value) { + collectBorrowsRecursive(value, 1); + } + + private void unregisterBorrowsFromValue(Object value) { + collectBorrowsRecursive(value, -1); + } + + @SuppressWarnings("unchecked") + private void collectBorrowsRecursive(Object value, int delta) { + if (value == null) return; + Object unwrapped = typeHandler.unwrap(value); + if (unwrapped == null) return; + + if (unwrapped instanceof TypeHandler.PointerValue) { + TypeHandler.PointerValue pointer = (TypeHandler.PointerValue) unwrapped; + updateBorrowCount(pointer.container, pointer.index, delta); + return; + } + + if (unwrapped instanceof List) { + for (Object element : (List) unwrapped) { + collectBorrowsRecursive(element, delta); + } + } + } + + private void updateBorrowCount(Object container, long index, int delta) { + if (container == null || delta == 0) return; + + Map countsByIndex = activeBorrowsByContainer.get(container); + if (countsByIndex == null) { + if (delta < 0) return; + countsByIndex = new HashMap(); + activeBorrowsByContainer.put(container, countsByIndex); + } + + Integer current = countsByIndex.get(index); + int next = (current != null ? current : 0) + delta; + if (next > 0) { + countsByIndex.put(index, next); + } else { + countsByIndex.remove(index); + if (countsByIndex.isEmpty()) { + activeBorrowsByContainer.remove(container); + } + } + } } diff --git a/src/main/java/cod/interpreter/handler/AssignmentHandler.java b/src/main/java/cod/interpreter/handler/AssignmentHandler.java index 169e94a..4a326bb 100644 --- a/src/main/java/cod/interpreter/handler/AssignmentHandler.java +++ b/src/main/java/cod/interpreter/handler/AssignmentHandler.java @@ -189,7 +189,9 @@ private Object handleIndexAssignment(IndexAccess indexAccess, Object newValue, E NaturalArray natural = (NaturalArray) arrayObj; long index = expressionHandler.toLongIndex(indexObj); ensureNoActiveBorrow(arrayObj, index, ctx); + Object previous = natural.get(index); natural.set(index, newValue); + ctx.trackValueReplacement(previous, newValue); return newValue; } @@ -197,7 +199,8 @@ private Object handleIndexAssignment(IndexAccess indexAccess, Object newValue, E int intIndex = expressionHandler.toIntIndex(indexObj); ensureNoActiveBorrow(arrayObj, intIndex, ctx); List list = (List) arrayObj; - list.set(intIndex, newValue); + Object previous = list.set(intIndex, newValue); + ctx.trackValueReplacement(previous, newValue); return newValue; } @@ -338,15 +341,7 @@ private void ensureNoActiveBorrow(Object container, long index, ExecutionContext if (!isBorrowCheckerActive(ctx)) { return; } - if (hasBorrowInLocals(container, index, ctx.getLocalsStack())) { - throwBorrowMutationViolation(index); - } - if (ctx.objectInstance != null - && ctx.objectInstance.fields != null - && hasBorrowInMap(container, index, ctx.objectInstance.fields)) { - throwBorrowMutationViolation(index); - } - if (ctx.getSlotValues() != null && hasBorrowInMap(container, index, ctx.getSlotValues())) { + if (ctx.hasActiveBorrow(container, index)) { throwBorrowMutationViolation(index); } } @@ -359,44 +354,6 @@ private boolean isBorrowCheckerActive(ExecutionContext ctx) { return ctx != null && (ctx.isUnsafeExecutionContext() || (ctx.currentClass != null && ctx.currentClass.isUnsafe)); } - private boolean hasBorrowInLocals(Object container, long index, List> scopes) { - if (scopes == null) return false; - for (Map scope : scopes) { - if (hasBorrowInMap(container, index, scope)) { - return true; - } - } - return false; - } - - private boolean hasBorrowInMap(Object container, long index, Map values) { - if (values == null || values.isEmpty()) return false; - for (Object value : values.values()) { - if (containsBorrowForLocation(typeSystem.unwrap(value), container, index)) { - return true; - } - } - return false; - } - - @SuppressWarnings("unchecked") - private boolean containsBorrowForLocation(Object value, Object container, long index) { - if (value == null) return false; - if (value instanceof TypeHandler.PointerValue) { - TypeHandler.PointerValue pointer = (TypeHandler.PointerValue) value; - return pointer.container == container && pointer.index == index; - } - if (value instanceof List) { - List list = (List) value; - for (Object element : list) { - if (containsBorrowForLocation(typeSystem.unwrap(element), container, index)) { - return true; - } - } - } - return false; - } - private Object handleVariableAssignment(Expr target, Object newValue, ExecutionContext ctx) { try { if (target instanceof PropertyAccess) { @@ -459,7 +416,7 @@ public Object assignToVariableScoped(String varName, Object newValue, ExecutionC if (NamingValidator.isAllCaps(varName)) { throw new ProgramError("Cannot reassign constant field '" + varName + "'"); } - ctx.objectInstance.fields.put(varName, newValue); + ctx.setObjectField(varName, newValue); return newValue; } } @@ -484,7 +441,7 @@ private Object updateVariableInScope(String varName, Object newValue, newValue = typeSystem.wrapUnionType(newValue, declaredType); } - scope.put(varName, newValue); + ctx.setVariable(varName, newValue); return newValue; } catch (ProgramError e) { throw e; @@ -501,7 +458,7 @@ private Object assignToField(String fieldName, Object newValue, ExecutionContext if (NamingValidator.isAllCaps(fieldName)) { throw new ProgramError("Cannot reassign constant field '" + fieldName + "'"); } - ctx.objectInstance.fields.put(fieldName, newValue); + ctx.setObjectField(fieldName, newValue); return newValue; } else { throw new ProgramError("Cannot assign to undefined field via this: " + fieldName); @@ -535,7 +492,7 @@ private Object assignToSuperField(String fieldName, Object newValue, ExecutionCo if (NamingValidator.isAllCaps(fieldName)) { throw new ProgramError("Cannot reassign constant field '" + fieldName + "'"); } - ctx.objectInstance.fields.put(fieldName, newValue); + ctx.setObjectField(fieldName, newValue); return newValue; } throw new ProgramError("Cannot assign to undefined field via super: " + fieldName);