visitMixinDeclaration method

  1. @override
Object? visitMixinDeclaration(
  1. MixinDeclaration node
)
override

Implementation

@override
Object? visitMixinDeclaration(MixinDeclaration node) {
  final mixinName = node.name.lexeme;
  Logger.debug(
      "[Visitor.visitMixinDeclaration] START for '$mixinName' in env: ${environment.hashCode}");

  // Retrieve the placeholder mixin object created in Pass 1
  Object? placeholder = environment.get(mixinName);
  if (placeholder == null ||
      placeholder is! InterpretedClass ||
      !placeholder.isMixin) {
    throw StateD4rtException(
        "Placeholder for mixin '$mixinName' not found or invalid during Pass 2.");
  }
  final mixinClass = placeholder;
  Logger.debug(
      "[Visitor.visitMixinDeclaration] Retrieved placeholder for mixin '$mixinName' (hash: ${mixinClass.hashCode})");

  // Resolve 'on' clause constraints ON THE EXISTING mixinClass object
  if (node.onClause != null) {
    mixinClass.onClauseTypes.clear(); // Clear existing before populating
    for (final typeNode in node.onClause!.superclassConstraints) {
      final typeName = typeNode.name.lexeme;
      try {
        final potentialType = environment.get(typeName);
        if (potentialType is InterpretedClass ||
            potentialType is BridgedClass) {
          // RC-7: Accept both InterpretedClass and BridgedClass for
          // mixin 'on' clause constraints. Bridged classes (e.g., State)
          // are valid superclass constraints.
          mixinClass.onClauseTypes.add(potentialType as RuntimeType);
          Logger.debug(
              "[Visitor.visitMixinDeclaration] Added 'on' constraint '$typeName' for '$mixinName'");
        } else {
          throw RuntimeD4rtException(
              "Type '$typeName' in 'on' clause of mixin '$mixinName' is not a class (${potentialType?.runtimeType}).");
        }
      } on RuntimeD4rtException {
        throw RuntimeD4rtException(
            "Type '$typeName' in 'on' clause of mixin '$mixinName' not found. Ensure it's defined.");
      }
    }
  }

  // Cluster-18: Resolve 'implements' clause for mixins. Without this, an
  // interpreted mixin like
  //   mixin _TickerProviderShim on State<T> implements TickerProvider { ... }
  // never records TickerProvider as a bridged interface, so callers using
  // `vsync: this` (where `this` is a State that mixes in this shim) cannot
  // satisfy a TickerProvider parameter through interface-proxy resolution.
  if (node.implementsClause != null) {
    Logger.debug(
        "[Visitor.visitMixinDeclaration] Processing 'implements' clause for '$mixinName' in env: ${environment.hashCode}");
    for (final interfaceType in node.implementsClause!.interfaces) {
      final interfaceName = interfaceType.name.lexeme;
      try {
        final potentialInterface = environment.get(interfaceName);
        if (potentialInterface is InterpretedClass) {
          if (potentialInterface.isBase) {
            throw RuntimeD4rtException(
                "Mixin '$mixinName' cannot implement base class '$interfaceName' outside of its library.");
          }
          mixinClass.interfaces.add(potentialInterface);
          Logger.debug(
              "[Visitor.visitMixinDeclaration] Added interface '$interfaceName' for '$mixinName'");
        } else if (potentialInterface is BridgedClass) {
          mixinClass.bridgedInterfaces.add(potentialInterface);
          Logger.debug(
              "[Visitor.visitMixinDeclaration] Added bridged interface '$interfaceName' for '$mixinName'");
        } else {
          throw RuntimeD4rtException(
              "Mixin '$mixinName' cannot implement non-class '$interfaceName' (${potentialInterface?.runtimeType}).");
        }
      } on RuntimeD4rtException {
        throw RuntimeD4rtException(
            "Interface '$interfaceName' not found for mixin '$mixinName'. Ensure it's defined.");
      }
    }
  }

  // Populate members ON THE EXISTING mixinClass object
  final declarationEnv =
      environment; // Members use the mixin's declaration env
  final originalVisitorEnv = environment;

  Logger.debug(
      "[Visitor.visitMixinDeclaration] Processing members for mixin '$mixinName' (hash: ${mixinClass.hashCode})");

  try {
    environment = declarationEnv;
    for (final member in node.members) {
      if (member is MethodDeclaration) {
        final methodName = member.name.lexeme;
        // Methods capture the GLOBAL environment via the mixinClass
        final function =
            InterpretedFunction.method(member, globalEnvironment, mixinClass);
        if (member.isStatic) {
          // Static members belong to the mixin definition itself
          if (member.isGetter) {
            mixinClass.staticGetters[methodName] = function;
          } else if (member.isSetter) {
            mixinClass.staticSetters[methodName] = function;
          } else {
            mixinClass.staticMethods[methodName] = function;
          }
        } else {
          if (member.isGetter) {
            mixinClass.getters[methodName] = function;
          } else if (member.isSetter) {
            mixinClass.setters[methodName] = function;
          } else if (member.isOperator) {
            // Add operator methods to the operators map for mixins
            mixinClass.operators[methodName] = function;
          } else {
            mixinClass.methods[methodName] = function;
          }
        }
      } else if (member is FieldDeclaration) {
        if (member.isStatic) {
          for (final variable in member.fields.variables) {
            mixinClass.staticFields[variable.name.lexeme] =
                variable.initializer?.accept<Object?>(this);
          }
        } else {
          mixinClass.fieldDeclarations.add(member);
        }
      } else if (member is ConstructorDeclaration) {
        throw RuntimeD4rtException(
            "Mixins cannot declare constructors ('$mixinName').");
      }
    }
  } finally {
    environment = originalVisitorEnv;
  }

  // Perf T6: compact the mixin's function tables now that wiring is done.
  mixinClass.freezeMemberTables();

  Logger.debug("[Visitor.visitMixinDeclaration] END for '$mixinName'");
  return null; // Mixin declaration doesn't return a value
}