visitMixinDeclaration method

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

Implementation

@override
Object? visitMixinDeclaration(SMixinDeclaration node) {
  final mixinName = node.name!.name;
  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!.name;
      try {
        final potentialType = environment.get(typeName);
        if (potentialType is InterpretedClass ||
            potentialType is BridgedClass) {
          // RC-7b: Accept both InterpretedClass and BridgedClass as valid
          // on-clause constraints. Bridged types like State, ChangeNotifier
          // are BridgedClass instances resolved from the environment.
          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!.name;
      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 SMethodDeclaration) {
        final methodName = member.name!.name;
        // 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 SFieldDeclaration) {
        if (member.isStatic) {
          for (final variable in member.fields!.variables) {
            mixinClass.staticFields[variable.name!.name] = variable
                .initializer
                ?.accept<Object?>(this);
          }
        } else {
          mixinClass.fieldDeclarations.add(member);
        }
      } else if (member is SConstructorDeclaration) {
        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
}