visitMixinDeclaration method
Object?
visitMixinDeclaration(
- 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
}