Index: pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart |
diff --git a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart |
index 1fa7dd80fbc534085ebc48ef3453523d81fadbc0..205db064e47f64e17aa2d61affb4f1eef3d8050c 100644 |
--- a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart |
+++ b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart |
@@ -6,46 +6,74 @@ library fasta.redirecting_factory_body; |
import 'package:kernel/ast.dart' |
show |
+ DartType, |
Expression, |
ExpressionStatement, |
FunctionNode, |
+ InterfaceType, |
InvalidExpression, |
+ InvalidType, |
Let, |
Member, |
Procedure, |
StaticGet, |
StringLiteral, |
+ TypeParameterType, |
VariableDeclaration; |
+import 'package:kernel/type_algebra.dart' show Substitution; |
+ |
const String letName = "#redirecting_factory"; |
class RedirectingFactoryBody extends ExpressionStatement { |
- RedirectingFactoryBody.internal(Expression value) |
- : super(new Let(new VariableDeclaration(letName, initializer: value), |
+ Member cachedRedirectionTarget; |
+ |
+ DartType cachedReturnType; |
+ |
+ RedirectingFactoryBody.internal(Expression value, DartType type) |
+ : super(new Let( |
+ new VariableDeclaration(letName, initializer: value, type: type), |
new InvalidExpression())); |
- RedirectingFactoryBody(Member target) : this.internal(new StaticGet(target)); |
+ RedirectingFactoryBody(Member intermediateTarget, InterfaceType type) |
+ : this.internal(new StaticGet(intermediateTarget), type); |
RedirectingFactoryBody.unresolved(String name) |
- : this.internal(new StringLiteral(name)); |
+ : this.internal(new StringLiteral(name), new InvalidType()); |
- Member get target { |
- var value = getValue(expression); |
+ Member get intermediateTarget { |
+ var value = getVariable(expression)?.initializer; |
return value is StaticGet ? value.target : null; |
} |
+ DartType get intermediateReturnType => getVariable(expression)?.type; |
+ |
String get unresolvedName { |
- var value = getValue(expression); |
+ var value = getVariable(expression)?.initializer; |
return value is StringLiteral ? value.value : null; |
} |
bool get isUnresolved => unresolvedName != null; |
- static getValue(Expression expression) { |
+ Member get target { |
+ if (cachedReturnType == null) { |
+ computeRedirectionTarget(this.parent.parent, this); |
+ } |
+ return cachedRedirectionTarget; |
+ } |
+ |
+ DartType get returnType { |
+ if (cachedReturnType == null) { |
+ computeRedirectionTarget(this.parent.parent, this); |
+ } |
+ return cachedReturnType; |
+ } |
+ |
+ static VariableDeclaration getVariable(Expression expression) { |
if (expression is Let) { |
VariableDeclaration variable = expression.variable; |
if (variable.name == letName) { |
- return variable.initializer; |
+ return variable; |
} |
} |
return null; |
@@ -57,32 +85,73 @@ class RedirectingFactoryBody extends ExpressionStatement { |
// [kernel_class_builder.dart](kernel_class_builder.dart). |
FunctionNode function = factory.function; |
ExpressionStatement statement = function.body; |
+ VariableDeclaration variable = getVariable(statement.expression); |
function.body = |
- new RedirectingFactoryBody.internal(getValue(statement.expression)) |
+ new RedirectingFactoryBody.internal(variable.initializer, variable.type) |
..parent = function; |
} |
-} |
-RedirectingFactoryBody getRedirectingFactoryBody(Member member) { |
- return member is Procedure && member.function.body is RedirectingFactoryBody |
- ? member.function.body |
- : null; |
-} |
+ static RedirectingFactoryBody getRedirectingFactoryBody(Member member) { |
+ return member is Procedure && member.function.body is RedirectingFactoryBody |
+ ? member.function.body |
+ : null; |
+ } |
+ |
+ /// Iterate redirections from [factoryMethod]. |
+ static void iterateRedirections( |
+ Member factoryMethod, |
+ bool f(Member member, RedirectingFactoryBody body), |
+ void onCycle(Member member, RedirectingFactoryBody body)) { |
+ // We use the [tortoise and hare algorithm] |
+ // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to |
+ // handle cycles. |
-Member getRedirectionTarget(Procedure member) { |
- // We use the [tortoise and hare algorithm] |
- // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to |
- // handle cycles. |
- Member tortoise = member; |
- RedirectingFactoryBody tortoiseBody = getRedirectingFactoryBody(tortoise); |
- Member hare = tortoiseBody?.target; |
- RedirectingFactoryBody hareBody = getRedirectingFactoryBody(hare); |
- while (tortoise != hare) { |
- if (tortoiseBody?.isUnresolved ?? true) return tortoise; |
- tortoise = tortoiseBody.target; |
- tortoiseBody = getRedirectingFactoryBody(tortoise); |
- hare = getRedirectingFactoryBody(hareBody?.target)?.target; |
- hareBody = getRedirectingFactoryBody(hare); |
+ Member tortoise = factoryMethod; |
+ RedirectingFactoryBody tortoiseBody = getRedirectingFactoryBody(tortoise); |
+ Member hare = tortoiseBody?.intermediateTarget; |
+ RedirectingFactoryBody hareBody = getRedirectingFactoryBody(hare); |
+ while (tortoise != hare) { |
+ if (tortoiseBody == null) return; |
+ if (!f(tortoise, tortoiseBody)) return; |
+ tortoise = tortoiseBody.intermediateTarget; |
+ tortoiseBody = getRedirectingFactoryBody(tortoise); |
+ hare = getRedirectingFactoryBody(hareBody?.intermediateTarget) |
+ ?.intermediateTarget; |
+ hareBody = getRedirectingFactoryBody(hare); |
+ } |
+ onCycle(tortoise, tortoiseBody); |
+ } |
+ |
+ static void computeRedirectionTarget( |
+ Member factoryMethod, RedirectingFactoryBody body) { |
+ DartType returnType = new InterfaceType( |
+ factoryMethod.enclosingClass, |
+ factoryMethod.function.typeParameters |
+ .map((t) => new TypeParameterType(t)) |
+ .toList()); |
+ Member target; |
+ iterateRedirections(factoryMethod, |
+ (Member member, RedirectingFactoryBody body) { |
+ if (body.isUnresolved) { |
+ target = member; |
+ returnType = const InvalidType(); |
+ return false; // Stop iterating. |
+ } |
+ target = body.intermediateTarget; |
+ if (returnType is InterfaceType) { |
+ InterfaceType interfaceType = returnType; |
+ returnType = Substitution |
+ .fromPairs( |
+ member.function.typeParameters, interfaceType.typeArguments) |
+ .substituteType(body.intermediateReturnType); |
+ } |
+ return true; // Continue iterating. |
+ }, (Member member, RedirectingFactoryBody body) { |
+ returnType = const InvalidType(); |
+ target = null; |
+ }); |
+ body |
+ ..cachedRedirectionTarget = target |
+ ..cachedReturnType = returnType; |
} |
- return null; |
} |