visitAssignmentExpression method

  1. @override
Object? visitAssignmentExpression(
  1. SAssignmentExpression node
)
override

Implementation

@override
Object? visitAssignmentExpression(SAssignmentExpression node) {
  final lhs = node.leftHandSide;
  // Evaluate RHS once, used by multiple branches below
  Object? rhsValue = node.rightHandSide!.accept<Object?>(this);

  // Handle suspension on the right-hand side
  if (rhsValue is AsyncSuspensionRequest) {
    Logger.debug(
      "[visitAssignmentExpression] RHS suspended. Propagating suspension.",
    );
    // The state machine (_determineNextNodeAfterAwait) will handle resumption.
    // It needs to know this SAssignmentExpression was the context.
    return rhsValue;
  }
  // END NEW

  final operatorType = node.operator;

  // Case 1: Simple variable assignment (lexical or implicit this)
  if (lhs is SSimpleIdentifier) {
    final variableName = lhs.name;

    Environment? definingEnv = environment.findDefiningEnvironment(
      variableName,
    );

    if (definingEnv != null) {
      // Check if the variable is a LateVariable
      final variableValue = definingEnv.get(variableName);
      if (variableValue is LateVariable) {
        if (operatorType == '=') {
          // Simple assignment to late variable
          variableValue.assign(rhsValue);
          return rhsValue;
        } else {
          // Compound assignment to late variable
          final currentValue =
              variableValue.value; // May throw if not initialized
          Object? newValue = computeCompoundValue(
            currentValue,
            rhsValue,
            operatorType,
          );
          variableValue.assign(newValue);
          return newValue;
        }
      } else {
        // Regular variable handling
        if (operatorType == '=') {
          final assigned = environment.assign(
            variableName,
            rhsValue,
          ); // Use original assign for lexical
          _propagateStaticFieldWrite(definingEnv, variableName, rhsValue);
          return assigned;
        } else {
          // Handle compound assignments on lexical variables
          final currentValue = environment.get(
            variableName,
          ); // Get from lexical scope
          Object? newValue = computeCompoundValue(
            currentValue,
            rhsValue,
            operatorType,
          );
          final assigned = environment.assign(
            variableName,
            newValue,
          ); // Assign back to lexical scope
          _propagateStaticFieldWrite(definingEnv, variableName, newValue);
          return assigned;
        }
      }
    } else {
      try {
        final thisInstance = environment.get('this');
        if (thisInstance is InterpretedInstance) {
          if (operatorType == '=') {
            Logger.debug(
              "[Assignment - implicit this] Checking for direct setter '$variableName' on ${thisInstance.runtimeType}",
            );
            final setter = thisInstance.klass.findInstanceSetter(
              variableName,
            );
            if (setter != null) {
              Logger.debug(
                "[Assignment - implicit this] Found direct setter. Calling...",
              );
              // Call setter on 'this'
              setter.bind(thisInstance).call(this, [rhsValue], {});
              return rhsValue; // Assignment expression returns RHS value
            } else {
              Logger.debug(
                "[Assignment - implicit this] No direct setter found. Trying extension setter for '$variableName' on ${thisInstance.runtimeType}",
              );
              final extensionSetter = environment.findExtensionMember(
                thisInstance,
                variableName,
              );
              if (extensionSetter is ExtensionMemberCallable &&
                  extensionSetter.isSetter) {
                Logger.debug(
                  "[Assignment - implicit this] Found extension setter '$variableName'. Calling...",
                );
                final extensionPositionalArgs = [
                  thisInstance, // Target is 'this'
                  rhsValue, // Value to assign
                ];
                try {
                  extensionSetter.call(this, extensionPositionalArgs, {});
                  Logger.debug(
                    "[Assignment - implicit this] Extension setter call finished.",
                  );
                  return rhsValue; // Simple assignment returns RHS
                } catch (e) {
                  throw RuntimeD4rtException(
                    "Error executing extension setter '$variableName' via implicit 'this': $e",
                  );
                }
              } else {
                Logger.debug(
                  "[Assignment - implicit this] No extension setter found for '$variableName'. Falling back to direct field set.",
                );
                // Assign directly to field on 'this' (original fallback)
                // WARNING: This might be incorrect if the intent was purely extension based.
                // Dart would typically throw if no setter (direct or extension) exists and no field exists.
                // Consider throwing here if direct field assignment isn't desired.
                try {
                  thisInstance.set(variableName, rhsValue, this);
                  Logger.debug(
                    "[Assignment - implicit this] Direct field set successful (?).",
                  );
                  return rhsValue; // Assignment expression returns RHS value
                } on RuntimeD4rtException catch (fieldSetError) {
                  Logger.debug(
                    "[Assignment - implicit this] Direct field set failed: ${fieldSetError.message}",
                  );
                  // If both direct setter, extension setter, and direct field set fail, THEN throw.
                  throw RuntimeD4rtException(
                    "Cannot assign to '$variableName' on implicit 'this': No setter (direct or extension) or assignable field found.",
                  );
                }
              }
            }
          } else {
            // 1. Get current value from 'this' (field or getter)
            final currentValue = thisInstance.get(
              variableName,
            ); // May throw if undefined on instance

            // 2. Calculate new value
            Object? newValue = computeCompoundValue(
              currentValue,
              rhsValue,
              operatorType,
            );

            // 3. Set new value on 'this' (field or setter)
            final setter = thisInstance.klass.findInstanceSetter(
              variableName,
            );
            if (setter != null) {
              setter.bind(thisInstance).call(this, [newValue], {});
            } else {
              thisInstance.set(variableName, newValue, this);
            }
            // Compound assignment returns the NEW value
            return newValue;
          }
        } else if (toBridgedInstance(thisInstance).$2) {
          if (rhsValue is BridgedEnumValue) {
            rhsValue = rhsValue.nativeValue;
          } else if (rhsValue is BridgedInstance) {
            // GEN-079: Unwrap BridgedInstance for native setter calls
            rhsValue = rhsValue.nativeObject;
          }
          final bridgedInstance = toBridgedInstance(thisInstance).$1!;
          final bridgedClass = bridgedInstance.bridgedClass;
          final setterAdapter = bridgedClass.findInstanceSetterAdapter(
            variableName,
          );

          if (setterAdapter != null) {
            if (operatorType == '=') {
              // Simple assignment: this.bridgedProp = value
              Logger.debug(
                "[Assignment] Assigning to bridged 'this'.$variableName via setter adapter.",
              );
              D4.withActiveVisitor<void>(
                this,
                () => setterAdapter(
                  this,
                  thisInstance.nativeObject,
                  rhsValue,
                ),
              );
              return rhsValue; // Simple assignment returns RHS value
            } else {
              // Compound assignment: this.bridgedProp op= value
              // 1. Get current value (requires a getter adapter)
              final getterAdapter = bridgedClass.findInstanceGetterAdapter(
                variableName,
              );
              if (getterAdapter == null) {
                throw RuntimeD4rtException(
                  "Cannot perform compound assignment on '${bridgedClass.name}.$variableName' via implicit 'this': No getter found.",
                );
              }
              final currentValue = getterAdapter(
                this,
                thisInstance.nativeObject,
              );
              // 2. Calculate new value
              Object? newValue = computeCompoundValue(
                currentValue,
                rhsValue,
                operatorType,
              );
              // 3. Set new value via setter adapter
              Logger.debug(
                "[Assignment] Compound assigning to bridged 'this'.$variableName via setter adapter.",
              );
              D4.withActiveVisitor<void>(
                this,
                () => setterAdapter(
                  this,
                  thisInstance.nativeObject,
                  newValue,
                ),
              );
              return newValue; // Compound assignment returns new value
            }
          } else {
            // No setter adapter found
            throw RuntimeD4rtException(
              "Cannot assign to property '$variableName' on bridged instance of '${bridgedClass.name}' accessed via implicit 'this': No setter found.",
            );
          }
        } else {
          // 'this' exists but is not an InterpretedInstance or BridgedInstance
          throw RuntimeD4rtException(
            "Assigning to undefined variable '$variableName'.",
          );
        }
      } on RuntimeD4rtException catch (e) {
        // If 'this' doesn't exist or getting/setting on 'this' failed
        // Use the original error if it came from get/set, otherwise standard undefined.
        if (e.message.contains("Undefined property '$variableName'") ||
            e.message.contains("Undefined static member")) {
          rethrow; // Propagate specific error from get/set
        }
        throw RuntimeD4rtException(
          "Assigning to undefined variable '$variableName'.",
        );
      }
    }
  }
  // Case 2: SPropertyAccess assignment (target.property op= value)
  else if (lhs is SPropertyAccess) {
    final targetExpression = lhs.target; // Keep expression for check below
    final targetValue = targetExpression?.accept<Object?>(this);
    final propertyName = lhs.propertyName!.name;
    // rhsValue and operatorType already available from the top

    if (targetValue is BoundSuper) {
      // This handles cases like: super.value = expression; or super.value += expression;
      final instance = targetValue.instance;
      final startClass = targetValue.startLookupClass;
      InterpretedClass? currentClass = startClass;
      InterpretedFunction? superSetter;
      InterpretedFunction? superGetter;

      // Look for the setter in the superclass hierarchy starting from startClass
      BridgedClass? bridgedSetter;
      while (currentClass != null) {
        final setter = currentClass.findInstanceSetter(propertyName);
        if (setter != null) {
          superSetter = setter;
          break;
        }
        // Check bridged superclass
        if (currentClass.bridgedSuperclass != null) {
          final bridged = currentClass.bridgedSuperclass!;
          if (bridged.setters.containsKey(propertyName)) {
            bridgedSetter = bridged;
            break;
          }
        }
        currentClass = currentClass.superclass;
      }

      // For compound operators, we also need to get the current value
      Object? currentValue;
      BridgedClass? bridgedGetter;
      if (operatorType != '=') {
        // First try to find a getter
        currentClass = startClass;
        while (currentClass != null) {
          final getter = currentClass.findInstanceGetter(propertyName);
          if (getter != null) {
            superGetter = getter;
            break;
          }
          // Check bridged superclass
          if (currentClass.bridgedSuperclass != null) {
            final bridged = currentClass.bridgedSuperclass!;
            if (bridged.getters.containsKey(propertyName)) {
              bridgedGetter = bridged;
              break;
            }
          }
          currentClass = currentClass.superclass;
        }

        // Get the current value using getter or bridged getter
        if (superGetter != null) {
          currentValue = superGetter.bind(instance).call(this, [], {});
        } else if (bridgedGetter != null) {
          // RC-6: Use nativeProxy as fallback for abstract class adapters
          final bridgedTarget = instance.bridgedSuperObject ?? instance.nativeProxy;
          if (bridgedTarget == null) {
            throw RuntimeD4rtException(
              "Cannot access bridged property '$propertyName': bridgedSuperObject is null",
            );
          }
          currentValue = bridgedGetter.getters[propertyName]!(
            this,
            bridgedTarget,
          );
        } else {
          // Try to get field value directly
          try {
            currentValue = instance.get(propertyName);
          } catch (e) {
            throw RuntimeD4rtException(
              "Cannot read '$propertyName' from superclass chain of '${instance.klass.name}' for compound 'super' assignment: $e",
            );
          }
        }
      }

      if (operatorType == '=') {
        // Simple assignment: super.value = rhsValue
        if (superSetter != null) {
          superSetter.bind(instance).call(this, [rhsValue], {});
          return rhsValue;
        } else if (bridgedSetter != null) {
          // RC-6: Use nativeProxy as fallback for abstract class adapters
          final bridgedTarget = instance.bridgedSuperObject ?? instance.nativeProxy;
          if (bridgedTarget == null) {
            throw RuntimeD4rtException(
              "Cannot set bridged property '$propertyName': bridgedSuperObject is null",
            );
          }
          bridgedSetter.setters[propertyName]!(this, bridgedTarget, rhsValue);
          return rhsValue;
        } else {
          // Try direct field assignment
          try {
            instance.set(propertyName, rhsValue);
            return rhsValue;
          } catch (e) {
            throw RuntimeD4rtException(
              "Setter for '$propertyName' not found in superclass chain of '${instance.klass.name}' for 'super' assignment: $e",
            );
          }
        }
      } else {
        // Compound assignment: super.value += rhsValue, etc.
        // Compute new value
        final newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );

        // Set new value
        if (superSetter != null) {
          superSetter.bind(instance).call(this, [newValue], {});
        } else if (bridgedSetter != null) {
          // RC-6: Use nativeProxy as fallback for abstract class adapters
          final bridgedTarget = instance.bridgedSuperObject ?? instance.nativeProxy;
          if (bridgedTarget == null) {
            throw RuntimeD4rtException(
              "Cannot set bridged property '$propertyName': bridgedSuperObject is null",
            );
          }
          bridgedSetter.setters[propertyName]!(this, bridgedTarget, newValue);
        } else {
          // Try direct field assignment
          try {
            instance.set(propertyName, newValue);
          } catch (e) {
            throw RuntimeD4rtException(
              "Cannot set '$propertyName' in superclass chain of '${instance.klass.name}' for compound 'super' assignment: $e",
            );
          }
        }

        return newValue;
      }
    } else if (targetValue is InterpretedInstance) {
      // This code block was accidentally removed or modified, restore it.
      if (operatorType == '=') {
        // Simple assignment: target.property = rhsValue
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [rhsValue], {});
          Logger.debug(
            "[Assignment] Assigned via direct setter for '$propertyName'",
          );
          return rhsValue;
        }
        // No direct setter, try extension setter
        final extensionSetter = environment.findExtensionMember(
          targetValue,
          propertyName,
        );
        if (extensionSetter is ExtensionMemberCallable &&
            extensionSetter.isSetter) {
          Logger.debug(
            "[Assignment] Assigning via extension setter for '$propertyName'",
          );
          final extensionPositionalArgs = [
            targetValue,
            rhsValue,
          ]; // Target + value
          try {
            extensionSetter.call(this, extensionPositionalArgs, {});
            return rhsValue;
          } catch (e) {
            throw RuntimeD4rtException(
              "Error executing extension setter '$propertyName': $e",
            );
          }
        }
        // No direct or extension setter, assign to field
        Logger.debug(
          "[Assignment] No direct or extension setter found for '$propertyName', assigning to field.",
        );
        targetValue.set(propertyName, rhsValue, this);
        return rhsValue; // Simple Assignment returns RHS value
      } else {
        // Compound assignment: target.property op= rhsValue
        // 1. Get current value
        final currentValue = targetValue.get(
          propertyName,
        ); // Use instance.get (handles field/getter)
        // 2. Calculate new value
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
        // 3. Set new value (via setter or direct field access)
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [newValue], {});
        } else {
          // Assign directly to field if no setter (using the instance's set method)
          targetValue.set(propertyName, newValue, this);
        }
        return newValue; // Compound returns new value
      }
    }
    // Handle assignment to static property (targetValue is InterpretedClass)
    else if (targetValue is InterpretedClass) {
      if (operatorType == '=') {
        // Simple assignment: Class.property = rhsValue
        final staticSetter = targetValue.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [rhsValue], {});
        } else {
          // Assign directly to static field if no setter
          targetValue.setStaticField(propertyName, rhsValue);
        }
        return rhsValue; // Simple Assignment returns RHS value
      } else {
        // Compound assignment: Class.property op= rhsValue
        // 1. Get current value (static field or getter)
        Object? currentValue;
        final staticGetter = targetValue.findStaticGetter(propertyName);
        if (staticGetter != null) {
          currentValue = staticGetter.call(this, [], {});
        } else {
          // If no getter, try getting the field directly
          try {
            currentValue = targetValue.getStaticField(propertyName);
          } catch (_) {
            throw RuntimeD4rtException(
              "Cannot get value for compound assignment on static member '$propertyName'. No getter or field found.",
            );
          }
        }

        // 2. Calculate new value
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );

        // 3. Set new value (static setter or direct field access)
        final staticSetter = targetValue.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [newValue], {});
        } else {
          targetValue.setStaticField(propertyName, newValue);
        }
        return newValue; // Compound returns new value
      }
    } else if (toBridgedInstance(targetValue).$2) {
      if (rhsValue is BridgedEnumValue) {
        rhsValue = rhsValue.nativeValue;
      } else if (rhsValue is BridgedInstance) {
        // GEN-079: Unwrap BridgedInstance for native setter calls
        rhsValue = rhsValue.nativeObject;
      }
      final bridgedInstance = toBridgedInstance(targetValue).$1!;
      final setterAdapter = bridgedInstance.bridgedClass
          .findInstanceSetterAdapter(propertyName);

      if (setterAdapter != null) {
        if (operatorType == '=') {
          // Simple assignment: bridgedInstance.property = value
          Logger.debug(
            "[Assignment] Assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.",
          );
          // Wrap with withActiveVisitor so that D4 helper methods (e.g.
          // extractBridgedArg) can access the visitor for interface proxy
          // creation when the setter coerces InterpretedInstance arguments.
          D4.withActiveVisitor<void>(
            this,
            () =>
                setterAdapter(this, bridgedInstance.nativeObject, rhsValue),
          );
          return rhsValue; // Simple assignment returns RHS value
        } else {
          // Compound assignment: bridgedInstance.property op= value
          // 1. Get current value (requires a getter adapter)
          final getterAdapter = bridgedInstance.bridgedClass
              .findInstanceGetterAdapter(propertyName);
          if (getterAdapter == null) {
            throw RuntimeD4rtException(
              "Cannot perform compound assignment on '${bridgedInstance.bridgedClass.name}.$propertyName': No getter adapter found.",
            );
          }
          final currentValue = getterAdapter(
            this,
            bridgedInstance.nativeObject,
          );
          // 2. Calculate new value
          Object? newValue = computeCompoundValue(
            currentValue,
            rhsValue,
            operatorType,
          );
          // 3. Set new value via setter adapter
          Logger.debug(
            "[Assignment] Compound assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.",
          );
          // Wrap with withActiveVisitor so that D4 helper methods (e.g.
          // extractBridgedArg) can access the visitor for interface proxy
          // creation when the setter coerces InterpretedInstance arguments.
          D4.withActiveVisitor<void>(
            this,
            () =>
                setterAdapter(this, bridgedInstance.nativeObject, newValue),
          );
          return newValue; // Compound assignment returns new value
        }
      } else {
        // 1401-TODO #7 (F9): no setter adapter on the bridge. Before
        // throwing, check the native↔interpreted reverse map: if this
        // native object is the bridged super of an InterpretedInstance
        // that declares the property (script-defined field/setter),
        // route the assignment to the InterpretedInstance side.
        //
        // Triggers for scripts that subclass a concrete bridged class
        // (e.g. `_RenderMeasureBox extends RenderProxyBox`) and add
        // their own fields/setters. The framework hands back the
        // native bridged super to script callbacks (e.g.
        // `updateRenderObject`), and the regular assignment path
        // couldn't reach the script-side onLayout setter without this
        // fallback. The native↔interpreted map is populated by
        // [D4.extractBridgedArg] when it returns
        // `arg.bridgedSuperObject`.
        final interpretedObj =
            D4.interpretedForNative(bridgedInstance.nativeObject);
        if (interpretedObj is InterpretedInstance) {
          final scriptSetter =
              interpretedObj.klass.findInstanceSetter(propertyName);
          final hasField = interpretedObj.klass
              .getInstanceFieldNames()
              .contains(propertyName);
          if (scriptSetter != null || hasField) {
            if (operatorType == '=') {
              if (scriptSetter != null) {
                scriptSetter
                    .bind(interpretedObj)
                    .call(this, [rhsValue], {});
              } else {
                interpretedObj.set(propertyName, rhsValue, this);
              }
              return rhsValue;
            } else {
              final getter = interpretedObj.klass
                  .findInstanceGetter(propertyName);
              final Object? currentValue = getter != null
                  ? getter.bind(interpretedObj).call(this, [], {})
                  : interpretedObj.get(propertyName);
              final Object? newValue = computeCompoundValue(
                currentValue,
                rhsValue,
                operatorType,
              );
              if (scriptSetter != null) {
                scriptSetter
                    .bind(interpretedObj)
                    .call(this, [newValue], {});
              } else {
                interpretedObj.set(propertyName, newValue, this);
              }
              return newValue;
            }
          }
        }
        // No setter adapter found and no script-defined fallback.
        throw RuntimeD4rtException(
          "Cannot assign to property '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}': No setter adapter found.",
        );
      }
    } else if (targetValue is BoundBridgedSuper) {
      if (rhsValue is BridgedEnumValue) {
        rhsValue = rhsValue.nativeValue;
      } else if (rhsValue is BridgedInstance) {
        // GEN-079: Unwrap BridgedInstance for native setter calls
        rhsValue = rhsValue.nativeObject;
      }
      // This handles: super.property = rhsValue; or super.property += rhsValue;
      final instance = targetValue.instance; // Instance 'this'
      final bridgedSuper = targetValue.startLookupClass;
      // RC-6: Use nativeProxy as fallback for abstract class adapters
      final nativeSuperObject = instance.bridgedSuperObject ?? instance.nativeProxy;

      if (nativeSuperObject == null) {
        throw RuntimeD4rtException(
          "Internal error: Cannot assign to super property '$propertyName' on bridged superclass '${bridgedSuper.name}' because the native super object is missing.",
        );
      }

      // Find the bridged setter adapter
      final setterAdapter = bridgedSuper.findInstanceSetterAdapter(
        propertyName,
      );

      if (operatorType == '=') {
        // Simple assignment
        if (setterAdapter != null) {
          try {
            // Call the setter adapter with the native object and the new value
            D4.withActiveVisitor<void>(
              this,
              () => setterAdapter(this, nativeSuperObject, rhsValue),
            );
            return rhsValue; // Assignment returns the right value
          } catch (e, s) {
            Logger.error(
              "Native exception during super assignment to bridged setter '${bridgedSuper.name}.$propertyName': $e\\n$s",
            );
            throw RuntimeD4rtException(
              "Native error during super assignment to bridged setter '$propertyName': $e",
              originalException: e,
            );
          }
        } else {
          // No setter found
          throw RuntimeD4rtException(
            "Setter for '$propertyName' not found in bridged superclass '${bridgedSuper.name}' for 'super' assignment.",
          );
        }
      } else {
        // Compound assignment: super.property += rhsValue, etc.
        // Need both getter and setter
        final getterAdapter = bridgedSuper.findInstanceGetterAdapter(
          propertyName,
        );
        if (getterAdapter == null) {
          throw RuntimeD4rtException(
            "Cannot perform compound assignment on bridged super property '${bridgedSuper.name}.$propertyName': No getter adapter found.",
          );
        }
        if (setterAdapter == null) {
          throw RuntimeD4rtException(
            "Cannot perform compound assignment on bridged super property '${bridgedSuper.name}.$propertyName': No setter adapter found.",
          );
        }

        try {
          // Get current value
          final currentValue = getterAdapter(this, nativeSuperObject);

          // Compute new value
          final newValue = computeCompoundValue(
            currentValue,
            rhsValue,
            operatorType,
          );

          // Set new value
          D4.withActiveVisitor<void>(
            this,
            () => setterAdapter(this, nativeSuperObject, newValue),
          );

          return newValue;
        } catch (e, s) {
          Logger.error(
            "Native exception during compound super assignment to bridged property '${bridgedSuper.name}.$propertyName': $e\\n$s",
          );
          throw RuntimeD4rtException(
            "Native error during compound super assignment to bridged property '$propertyName': $e",
            originalException: e,
          );
        }
      }
    } else {
      throw RuntimeD4rtException(
        "Assignment target must be an instance, class, or super property, got ${targetValue?.runtimeType}.",
      );
    }
  }
  // Case 3: SPrefixedIdentifier assignment (prefix.identifier op= value)
  else if (lhs is SPrefixedIdentifier) {
    final target = lhs.prefix!.accept<Object?>(this);
    final propertyName = lhs.identifier!.name;
    // rhsValue and operatorType already available from the top

    if (target is InterpretedInstance) {
      if (operatorType == '=') {
        // Simple assignment: target.property = rhsValue
        final setter = target.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(target).call(this, [rhsValue], {});
          Logger.debug(
            "[Assignment] Assigned via direct setter for SPrefixedIdentifier '$propertyName'",
          );
          return rhsValue;
        }

        final extensionSetter = environment.findExtensionMember(
          target,
          propertyName,
        );
        if (extensionSetter is ExtensionMemberCallable &&
            extensionSetter.isSetter) {
          Logger.debug(
            "[Assignment] Assigning via extension setter for SPrefixedIdentifier '$propertyName'",
          );
          final extensionPositionalArgs = [
            target,
            rhsValue,
          ]; // Target + value
          try {
            extensionSetter.call(this, extensionPositionalArgs, {});
            return rhsValue;
          } catch (e) {
            throw RuntimeD4rtException(
              "Error executing extension setter '$propertyName': $e",
            );
          }
        }

        Logger.debug(
          "[Assignment] No direct or extension setter found for SPrefixedIdentifier '$propertyName', assigning to field.",
        );
        target.set(propertyName, rhsValue, this);
        return rhsValue; // Simple Assignment returns RHS value
      } else {
        // Compound assignment: target.property op= rhsValue
        final currentValue = target.get(propertyName);
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
        final setter = target.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(target).call(this, [newValue], {});
        } else {
          target.set(propertyName, newValue, this);
        }
        return newValue; // Compound returns new value
      }
    } else if (target is InterpretedClass) {
      if (operatorType == '=') {
        // Simple assignment: Class.property = rhsValue
        final staticSetter = target.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [rhsValue], {});
        } else {
          // Assign directly to static field if no setter
          target.setStaticField(propertyName, rhsValue);
        }
        return rhsValue; // Simple Assignment returns RHS value
      } else {
        // Compound assignment: Class.property op= rhsValue
        Object? currentValue;
        final staticGetter = target.findStaticGetter(propertyName);
        if (staticGetter != null) {
          currentValue = staticGetter.call(this, [], {});
        } else {
          try {
            currentValue = target.getStaticField(propertyName);
          } catch (_) {
            throw RuntimeD4rtException(
              "Cannot get value for compound assignment on static member '$propertyName'. No getter or field found.",
            );
          }
        }
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
        final staticSetter = target.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [newValue], {});
        } else {
          target.setStaticField(propertyName, newValue);
        }
        return newValue; // Compound returns new value
      }
    } else if (target is BridgedClass) {
      final bridgedClass = target;
      if (operatorType == '=') {
        // Simple assignment: BridgedClass.property = rhsValue
        final staticSetter = bridgedClass.findStaticSetterAdapter(
          propertyName,
        );
        if (staticSetter == null) {
          throw RuntimeD4rtException(
            "Bridged class '${bridgedClass.name}' has no static setter named '$propertyName'.",
          );
        }
        Logger.debug(
          "[Assignment] Assigning to static bridged property '${bridgedClass.name}.$propertyName' via setter adapter.",
        );
        staticSetter(this, rhsValue);
        return rhsValue; // Simple Assignment returns RHS value
      } else {
        // Compound assignment: BridgedClass.property op= rhsValue
        // 1. Get current static value
        final staticGetter = bridgedClass.findStaticGetterAdapter(
          propertyName,
        );
        if (staticGetter == null) {
          throw RuntimeD4rtException(
            "Cannot perform compound assignment on static '${bridgedClass.name}.$propertyName': No static getter found.",
          );
        }
        final currentValue = staticGetter(this);
        // 2. Calculate new value
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
        // 3. Set new static value
        final staticSetter = bridgedClass.findStaticSetterAdapter(
          propertyName,
        );
        if (staticSetter == null) {
          // Should have been caught by getter check, but defensive programming
          throw RuntimeD4rtException(
            "Cannot perform compound assignment on static '${bridgedClass.name}.$propertyName': No static setter found after getter.",
          );
        }
        Logger.debug(
          "[Assignment] Compound assigning to static bridged property '${bridgedClass.name}.$propertyName' via setter adapter.",
        );
        staticSetter(this, newValue);
        return newValue; // Compound returns new value
      }
    } else if (target is InterpretedExtension) {
      final extension = target;
      if (operatorType == '=') {
        // Simple assignment: Extension.staticField = rhsValue
        final staticSetter = extension.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [rhsValue], {});
          Logger.debug(
            "[Assignment] Assigned to static extension property '${extension.name ?? '<unnamed>'}.$propertyName' via setter.",
          );
        } else if (extension.staticFields.containsKey(propertyName)) {
          extension.setStaticField(propertyName, rhsValue);
          Logger.debug(
            "[Assignment] Assigned to static extension field '${extension.name ?? '<unnamed>'}.$propertyName'.",
          );
        } else {
          throw RuntimeD4rtException(
            "Extension '${extension.name ?? '<unnamed>'}' has no static setter or field named '$propertyName'.",
          );
        }
        return rhsValue;
      } else {
        // Compound assignment: Extension.property op= rhsValue
        // 1. Get current value
        Object? currentValue;
        final staticGetter = extension.findStaticGetter(propertyName);
        if (staticGetter != null) {
          currentValue = staticGetter.call(this, [], {});
        } else if (extension.staticFields.containsKey(propertyName)) {
          currentValue = extension.getStaticField(propertyName);
        } else {
          throw RuntimeD4rtException(
            "Cannot get value for compound assignment on static extension member '$propertyName'. No getter or field found.",
          );
        }
        // 2. Calculate new value
        Object? newValue = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
        // 3. Set new value
        final staticSetter = extension.findStaticSetter(propertyName);
        if (staticSetter != null) {
          staticSetter.call(this, [newValue], {});
        } else if (extension.staticFields.containsKey(propertyName)) {
          extension.setStaticField(propertyName, newValue);
        } else {
          throw RuntimeD4rtException(
            "Cannot set value for compound assignment on static extension member '$propertyName'. No setter or field found.",
          );
        }
        return newValue;
      }
    } else if (toBridgedInstance(target).$2) {
      if (rhsValue is BridgedEnumValue) {
        rhsValue = rhsValue.nativeValue;
      } else if (rhsValue is BridgedInstance) {
        // GEN-079: Unwrap BridgedInstance for native setter calls
        rhsValue = rhsValue.nativeObject;
      }
      final bridgedInstance = toBridgedInstance(target).$1!;

      final setterAdapter = bridgedInstance.bridgedClass
          .findInstanceSetterAdapter(propertyName);

      if (setterAdapter != null) {
        if (operatorType == '=') {
          // Simple assignment: bridgedInstance.property = value
          Logger.debug(
            "[Assignment - SPropertyAccess] Assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.",
          );
          // Wrap with withActiveVisitor so D4 helpers can resolve the
          // visitor for interface proxy creation when coercing args.
          D4.withActiveVisitor<void>(
            this,
            () =>
                setterAdapter(this, bridgedInstance.nativeObject, rhsValue),
          );
          return rhsValue; // Simple assignment returns RHS value
        } else {
          // Compound assignment: bridgedInstance.property op= value
          // 1. Get current value (requires a getter adapter)
          final getterAdapter = bridgedInstance.bridgedClass
              .findInstanceGetterAdapter(propertyName);
          if (getterAdapter == null) {
            throw RuntimeD4rtException(
              "Cannot perform compound assignment on '${bridgedInstance.bridgedClass.name}.$propertyName': No getter adapter found.",
            );
          }
          final currentValue = getterAdapter(
            this,
            bridgedInstance.nativeObject,
          );
          // 2. Calculate new value
          Object? newValue = computeCompoundValue(
            currentValue,
            rhsValue,
            operatorType,
          );
          // 3. Set new value via setter adapter
          Logger.debug(
            "[Assignment - SPropertyAccess] Compound assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.",
          );
          // Wrap with withActiveVisitor so D4 helpers can resolve the
          // visitor for interface proxy creation when coercing args.
          D4.withActiveVisitor<void>(
            this,
            () =>
                setterAdapter(this, bridgedInstance.nativeObject, newValue),
          );
          return newValue; // Compound assignment returns new value
        }
      } else {
        // 1401-TODO #7 (F9): SPrefixedIdentifier variant — same shape
        // as the SPropertyAccess branch above. The script's
        // `renderObject.onLayout = …` may be parsed as a prefixed
        // identifier; if the bridge has no `onLayout` setter, check
        // the native↔interpreted reverse map for a script-defined
        // setter/field on the wrapping InterpretedInstance.
        final interpretedObj =
            D4.interpretedForNative(bridgedInstance.nativeObject);
        if (interpretedObj is InterpretedInstance) {
          final scriptSetter =
              interpretedObj.klass.findInstanceSetter(propertyName);
          final hasField = interpretedObj.klass
              .getInstanceFieldNames()
              .contains(propertyName);
          if (scriptSetter != null || hasField) {
            if (operatorType == '=') {
              if (scriptSetter != null) {
                scriptSetter
                    .bind(interpretedObj)
                    .call(this, [rhsValue], {});
              } else {
                interpretedObj.set(propertyName, rhsValue, this);
              }
              return rhsValue;
            } else {
              final getter = interpretedObj.klass
                  .findInstanceGetter(propertyName);
              final Object? currentValue = getter != null
                  ? getter.bind(interpretedObj).call(this, [], {})
                  : interpretedObj.get(propertyName);
              final Object? newValue = computeCompoundValue(
                currentValue,
                rhsValue,
                operatorType,
              );
              if (scriptSetter != null) {
                scriptSetter
                    .bind(interpretedObj)
                    .call(this, [newValue], {});
              } else {
                interpretedObj.set(propertyName, newValue, this);
              }
              return newValue;
            }
          }
        }
        // No setter adapter found and no script-defined fallback.
        throw RuntimeD4rtException(
          "Cannot assign to property '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}': No setter adapter found.",
        );
      }
    } else {
      throw RuntimeD4rtException(
        "Assignment target must be an instance or class for SPrefixedIdentifier, got ${target?.runtimeType}.",
      );
    }
  } else {
    if (lhs is SIndexExpression) {
      final targetValue = lhs.target?.accept<Object?>(this);
      final rawIndexValue = lhs.index!.accept<Object?>(this);
      // Unwrap BridgedEnumValue for Map key operations
      final indexValue = rawIndexValue is BridgedEnumValue
          ? rawIndexValue.nativeValue
          : rawIndexValue;

      // Determine the value to actually assign
      Object? finalValueToAssign;
      if (operatorType == '=') {
        finalValueToAssign = rhsValue; // Simple assignment
      } else {
        // Compound assignment (e.g., list[i] += 10)
        // 1. Get current value using index operator []
        Object? currentValue;
        if (targetValue is Map) {
          currentValue = targetValue[indexValue];
        } else if (targetValue is List && indexValue is int) {
          if (indexValue < 0 || indexValue >= targetValue.length) {
            throw RuntimeD4rtException(
              'Index out of range for compound assignment read: $indexValue',
            );
          }
          currentValue = targetValue[indexValue];
        } else if (targetValue is InterpretedInstance) {
          // Check for class operator [] method for reading current value
          final operatorMethod = targetValue.findOperator('[]');
          if (operatorMethod != null) {
            try {
              currentValue = operatorMethod.bind(targetValue).call(this, [
                indexValue,
              ], {});
            } on ReturnException catch (e) {
              currentValue = e.value;
            } catch (e) {
              throw RuntimeD4rtException(
                "Error executing class operator '[]' for compound read: $e",
              );
            }
          } else {
            // No class operator found, try extensions
            try {
              final extensionGetter = environment.findExtensionMember(
                targetValue,
                '[]',
              );
              if (extensionGetter is ExtensionMemberCallable &&
                  extensionGetter.isOperator) {
                final extensionPositionalArgs = [targetValue, indexValue];
                try {
                  currentValue = extensionGetter.call(
                    this,
                    extensionPositionalArgs,
                    {},
                  );
                } on ReturnException catch (e) {
                  currentValue = e.value;
                } catch (e) {
                  throw RuntimeD4rtException(
                    "Error executing extension operator '[]' for compound read: $e",
                  );
                }
              } else {
                throw RuntimeD4rtException(
                  'Cannot read current value for compound index assignment on ${targetValue.klass.name}: No operator [] found (class or extension).',
                );
              }
            } on RuntimeD4rtException catch (e) {
              throw RuntimeD4rtException(
                'Cannot read current value for compound index assignment on ${targetValue.klass.name}: ${e.message}',
              );
            }
          }
        } else if (toBridgedInstance(targetValue).$2) {
          // Handle BridgedInstance for reading current value via [] operator
          final bridgedInstance = toBridgedInstance(targetValue).$1!;
          final bridgedClass = bridgedInstance.bridgedClass;
          final operatorName = '[]';

          final methodAdapter = bridgedClass.findInstanceMethodAdapter(
            operatorName,
          );
          if (methodAdapter != null) {
            Logger.debug(
              "[visitAssignmentExpression-Index] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter for compound read...",
            );
            try {
              currentValue = methodAdapter(
                this,
                bridgedInstance.nativeObject,
                [indexValue],
                {},
                null,
              );
            } catch (e, s) {
              Logger.error(
                "[visitAssignmentExpression-Index] Native exception during bridged operator '$operatorName' read on ${bridgedClass.name}: $e\\n$s",
              );
              throw RuntimeD4rtException(
                "Native error during bridged operator '$operatorName' read on ${bridgedClass.name}: $e",
                originalException: e,
              );
            }
          } else {
            throw RuntimeD4rtException(
              'Cannot read current value for compound index assignment on ${bridgedClass.name}: No bridged operator [] found.',
            );
          }
        } else {
          try {
            final extensionGetter = environment.findExtensionMember(
              targetValue,
              '[]',
            );
            if (extensionGetter is ExtensionMemberCallable &&
                extensionGetter.isOperator) {
              final extensionPositionalArgs = [targetValue, indexValue];
              try {
                currentValue = extensionGetter.call(
                  this,
                  extensionPositionalArgs,
                  {},
                );
              } on ReturnException catch (e) {
                currentValue = e.value;
              } // Handle potential returns
              catch (e) {
                throw RuntimeD4rtException(
                  "Error executing extension operator '[]' for compound read: $e",
                );
              }
            } else {
              throw RuntimeD4rtException(
                'Cannot read current value for compound index assignment on type ${targetValue?.runtimeType}: No standard or extension operator [] found.',
              );
            }
          } on RuntimeD4rtException catch (e) {
            throw RuntimeD4rtException(
              'Cannot read current value for compound index assignment on type ${targetValue?.runtimeType}: ${e.message}',
            );
          }
        }

        // 2. Calculate the new value
        finalValueToAssign = computeCompoundValue(
          currentValue,
          rhsValue,
          operatorType,
        );
      }

      // Now, perform the assignment with finalValueToAssign
      if (targetValue is Map) {
        targetValue[indexValue] = finalValueToAssign;
        return finalValueToAssign;
      } else if (targetValue is List && indexValue is int) {
        if (indexValue < 0 || indexValue >= targetValue.length) {
          throw RuntimeD4rtException(
            'Index out of range for assignment: $indexValue',
          );
        }
        targetValue[indexValue] = finalValueToAssign;
        return finalValueToAssign;
      } else if (targetValue is InterpretedInstance) {
        // Check for class operator []= method
        final operatorMethod = targetValue.findOperator('[]=');
        if (operatorMethod != null) {
          Logger.debug(
            "[visitAssignmentExpression-Index] Found class operator '[]=' on ${targetValue.klass.name}. Calling...",
          );
          try {
            operatorMethod.bind(targetValue).call(this, [
              indexValue,
              finalValueToAssign,
            ], {});
            return finalValueToAssign;
          } on ReturnException catch (_) {
            return finalValueToAssign; // []= should not return a value, but assignment expression returns assigned value
          } catch (e) {
            throw RuntimeD4rtException(
              "Error executing class operator '[]=': $e",
            );
          }
        } else {
          // No class operator found, try extensions
          const operatorName = '[]=';
          try {
            final extensionSetter = environment.findExtensionMember(
              targetValue,
              operatorName,
            );
            if (extensionSetter is ExtensionMemberCallable &&
                extensionSetter.isOperator) {
              Logger.debug(
                "[Assignment] Found extension operator '[]=' for ${targetValue.klass.name}. Calling...",
              );
              // Args: receiver (targetValue), index (indexValue), value (finalValueToAssign)
              final extensionPositionalArgs = [
                targetValue,
                indexValue,
                finalValueToAssign,
              ];
              try {
                extensionSetter.call(this, extensionPositionalArgs, {});
                // '[]=' operator should not return a meaningful value, but the assignment expression returns the assigned value
                return finalValueToAssign;
              } catch (e) {
                throw RuntimeD4rtException(
                  "Error executing extension operator '[]=': $e",
                );
              }
            } else {
              throw RuntimeD4rtException(
                'Cannot assign to index on ${targetValue.klass.name}: No operator []= found (class or extension).',
              );
            }
          } on RuntimeD4rtException catch (findError) {
            throw RuntimeD4rtException(
              'Cannot assign to index on ${targetValue.klass.name}: ${findError.message}',
            );
          }
        }
      } else if (toBridgedInstance(targetValue).$2) {
        final bridgedInstance = toBridgedInstance(targetValue).$1!;
        final bridgedClass = bridgedInstance.bridgedClass;
        final operatorName = '[]=';

        final methodAdapter = bridgedClass.findInstanceMethodAdapter(
          operatorName,
        );
        if (methodAdapter != null) {
          Logger.debug(
            "[visitAssignmentExpression-Index] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter...",
          );
          try {
            methodAdapter(
              this,
              bridgedInstance.nativeObject,
              [indexValue, finalValueToAssign],
              {},
              null,
            );
            return finalValueToAssign;
          } catch (e, s) {
            Logger.error(
              "[visitAssignmentExpression-Index] Native exception during bridged operator '$operatorName' on ${bridgedClass.name}: $e\\n$s",
            );
            throw RuntimeD4rtException(
              "Native error during bridged operator '$operatorName' on ${bridgedClass.name}: $e",
              originalException: e,
            );
          }
        }
        throw RuntimeD4rtException(
          "[Bridged operator '$operatorName' not found directly for ${bridgedClass.name}. Trying extensions.",
        );
      } else {
        const operatorName = '[]=';
        try {
          final extensionSetter = environment.findExtensionMember(
            targetValue,
            operatorName,
          );
          if (extensionSetter is ExtensionMemberCallable &&
              extensionSetter.isOperator) {
            Logger.debug(
              "[Assignment] Found extension operator '[]=' for type ${targetValue?.runtimeType}. Calling...",
            );
            // Args: receiver (targetValue), index (indexValue), value (finalValueToAssign)
            final extensionPositionalArgs = [
              targetValue,
              indexValue,
              finalValueToAssign,
            ];
            try {
              extensionSetter.call(this, extensionPositionalArgs, {});
              // '[]=' operator should not return a meaningful value, but the assignment expression returns the assigned value
              return finalValueToAssign;
            } catch (e) {
              throw RuntimeD4rtException(
                "Error executing extension operator '[]=': $e",
              );
            }
          } // else: No suitable extension operator found, fall through
          Logger.debug(
            "[Assignment] No suitable extension operator '[]=' found for type ${targetValue?.runtimeType}.",
          );
        } on RuntimeD4rtException catch (findError) {
          Logger.debug(
            "[Assignment] No extension member '[]=' found for type ${targetValue?.runtimeType}. Error: ${findError.message}",
          );
          // Fall through to the final error
        }

        // If neither standard nor extension assignment worked
        throw RuntimeD4rtException(
          'Unsupported target for index assignment: ${targetValue?.runtimeType}',
        );
      }
    } else {
      throw UnimplementedD4rtException(
        'Assignation à une cible non gérée: ${lhs.runtimeType}',
      );
    }
  }
}