mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:04:53 +00:00
[widget_cache] lookup framework Class on each run, expose isExtended method
Invalidations below the framework layer could lead to NPEs, since the framework class types will get recreated. To ensure this doesn't happen, look them up each time. Instead of checking the component for subtypes, use an isExtended API and the existing mixed in API to get the same results. This should allow this check to work correctly with the new invalidation strategy. Bug: https://github.com/dart-lang/sdk/issues/43760 Change-Id: I439c085540403e29083c74b2add642656b3fa0a3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167441 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
16f7ce8e57
commit
1f3c76ccdd
|
@ -14,7 +14,7 @@ class WidgetCache {
|
|||
WidgetCache(Component fullComponent) {
|
||||
Library frameworkLibrary;
|
||||
for (Library library in fullComponent.libraries) {
|
||||
if (library?.importUri?.path == 'flutter/src/widgets/framework.dart') {
|
||||
if (library?.importUri?.path == _frameworkLibrary) {
|
||||
frameworkLibrary = library;
|
||||
break;
|
||||
}
|
||||
|
@ -22,15 +22,7 @@ class WidgetCache {
|
|||
if (frameworkLibrary == null) {
|
||||
return;
|
||||
}
|
||||
for (Class classDeclaration in frameworkLibrary.classes) {
|
||||
if (classDeclaration.name == _statelessWidgetClassName) {
|
||||
_statelessWidget = classDeclaration;
|
||||
} else if (classDeclaration.name == _statefulWidgetClassName) {
|
||||
_statefulWidget = classDeclaration;
|
||||
} else if (classDeclaration.name == _stateClassName) {
|
||||
_state = classDeclaration;
|
||||
}
|
||||
}
|
||||
_locatedClassDeclarations(frameworkLibrary);
|
||||
_frameworkTypesLocated =
|
||||
_statefulWidget != null && _state != null && _statelessWidget != null;
|
||||
}
|
||||
|
@ -44,6 +36,8 @@ class WidgetCache {
|
|||
Class _statefulWidget;
|
||||
bool _frameworkTypesLocated = false;
|
||||
|
||||
static const String _frameworkLibrary = 'flutter/src/widgets/framework.dart';
|
||||
|
||||
/// Mark [uri] as invalidated.
|
||||
void invalidate(Uri uri) {
|
||||
_invalidatedLibraries.add(uri);
|
||||
|
@ -126,9 +120,19 @@ class WidgetCache {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Update the class references to stateless, stateful, and state classes.
|
||||
if (classHierarchy is ClosedWorldClassHierarchy) {
|
||||
for (Library library in classHierarchy.knownLibraries) {
|
||||
if (library?.importUri?.path == _frameworkLibrary) {
|
||||
_locatedClassDeclarations(library);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (classHierarchy.isSubclassOf(newClass, _statelessWidget) ||
|
||||
classHierarchy.isSubclassOf(newClass, _statefulWidget)) {
|
||||
if (_hasSubClasses(newClass, partialComponent, classHierarchy)) {
|
||||
if (classHierarchy.isExtended(newClass) ||
|
||||
classHierarchy.isUsedAsMixin(newClass)) {
|
||||
return null;
|
||||
}
|
||||
return newClass.name;
|
||||
|
@ -150,8 +154,8 @@ class WidgetCache {
|
|||
if (statefulWidgetType.name == _statefulWidgetClassName) {
|
||||
return null;
|
||||
}
|
||||
if (_hasSubClasses(
|
||||
statefulWidgetType, partialComponent, classHierarchy)) {
|
||||
if (classHierarchy.isExtended(statefulWidgetType) ||
|
||||
classHierarchy.isUsedAsMixin(statefulWidgetType)) {
|
||||
return null;
|
||||
}
|
||||
return statefulWidgetType.name;
|
||||
|
@ -161,22 +165,6 @@ class WidgetCache {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Checks whether the class [node] has any subclasses.
|
||||
bool _hasSubClasses(
|
||||
Class node, Component component, ClassHierarchy classHierarchy) {
|
||||
for (Library library in component.libraries) {
|
||||
for (Class otherClass in library.classes) {
|
||||
if (identical(otherClass, node)) {
|
||||
continue;
|
||||
}
|
||||
if (classHierarchy.isSubclassOf(otherClass, node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locate the that fully contains the edit range, or null.
|
||||
Class _locateContainingClass(
|
||||
Library library, int startOffset, int endOffset) {
|
||||
|
@ -188,4 +176,16 @@ class WidgetCache {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _locatedClassDeclarations(Library library) {
|
||||
for (Class classDeclaration in library.classes) {
|
||||
if (classDeclaration.name == _statelessWidgetClassName) {
|
||||
_statelessWidget = classDeclaration;
|
||||
} else if (classDeclaration.name == _statefulWidgetClassName) {
|
||||
_statefulWidget = classDeclaration;
|
||||
} else if (classDeclaration.name == _stateClassName) {
|
||||
_state = classDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,6 +164,9 @@ abstract class ClassHierarchy implements ClassHierarchyBase {
|
|||
/// mixin application (i.e. [Class.mixedInType]).
|
||||
bool isUsedAsMixin(Class class_);
|
||||
|
||||
/// True if the given class is extended by another class using `extends`.
|
||||
bool isExtended(Class class_);
|
||||
|
||||
/// Invokes [callback] for every member declared in or inherited by [class_]
|
||||
/// that overrides or implements a member in a supertype of [class_]
|
||||
/// (or in rare cases, overrides a member declared in [class_]).
|
||||
|
@ -547,6 +550,11 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
|
|||
return infoFor(class_).directMixers.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isExtended(Class class_) {
|
||||
return infoFor(class_).directExtenders.isNotEmpty;
|
||||
}
|
||||
|
||||
List<_ClassInfo> _getRankedSuperclassInfos(_ClassInfo info) {
|
||||
if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos;
|
||||
var heap = new _LubHeap()..add(info);
|
||||
|
|
Loading…
Reference in a new issue