[dart2js] migrate runtime_types.dart

Change-Id: Ide7ac30296612be3b4208267dc9135f3361b5cf2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264344
Reviewed-by: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Sigmund Cherem 2022-10-25 16:42:16 +00:00 committed by Commit Queue
parent 77a480ca49
commit 04239b784f
2 changed files with 44 additions and 71 deletions

View file

@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a // for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file. // BSD-style license that can be found in the LICENSE file.
// @dart = 2.10
library js_backend.runtime_types; library js_backend.runtime_types;
import '../common.dart'; import '../common.dart';
@ -96,8 +94,7 @@ class TrivialRuntimeTypesChecksBuilder implements RuntimeTypesChecksBuilder {
..functionType = _computeFunctionType(_elementEnvironment, cls); ..functionType = _computeFunctionType(_elementEnvironment, cls);
classUseMap[cls] = classUse; classUseMap[cls] = classUse;
} }
TypeChecks typeChecks = _substitutions._requiredChecks = TypeChecks typeChecks = _substitutions._computeChecks(classUseMap);
_substitutions._computeChecks(classUseMap);
return TrivialTypesChecks(typeChecks); return TrivialTypesChecks(typeChecks);
} }
@ -118,7 +115,6 @@ class TrivialRuntimeTypesChecksBuilder implements RuntimeTypesChecksBuilder {
abstract class RuntimeTypesSubstitutionsMixin abstract class RuntimeTypesSubstitutionsMixin
implements RuntimeTypesSubstitutions { implements RuntimeTypesSubstitutions {
JClosedWorld get _closedWorld; JClosedWorld get _closedWorld;
TypeChecks get _requiredChecks;
JElementEnvironment get _elementEnvironment => JElementEnvironment get _elementEnvironment =>
_closedWorld.elementEnvironment; _closedWorld.elementEnvironment;
@ -148,9 +144,9 @@ abstract class RuntimeTypesSubstitutionsMixin
result[cls] = checks; result[cls] = checks;
// Find the superclass from which [cls] inherits checks. // Find the superclass from which [cls] inherits checks.
ClassEntity superClass = _elementEnvironment.getSuperClass(cls, ClassEntity? superClass = _elementEnvironment.getSuperClass(cls,
skipUnnamedMixinApplications: true); skipUnnamedMixinApplications: true);
ClassChecks superChecks; ClassChecks? superChecks;
bool extendsSuperClassTrivially = false; bool extendsSuperClassTrivially = false;
if (superClass != null) { if (superClass != null) {
// Compute the checks inherited from [superClass]. // Compute the checks inherited from [superClass].
@ -173,7 +169,7 @@ abstract class RuntimeTypesSubstitutionsMixin
if (classUse.typeArgument || if (classUse.typeArgument ||
classUse.typeLiteral || classUse.typeLiteral ||
(isNativeClass && classUse.checkedInstance)) { (isNativeClass && classUse.checkedInstance)) {
Substitution substitution = computeSubstitution(cls, cls); Substitution? substitution = computeSubstitution(cls, cls);
// We need [cls] at runtime - even if [cls] is not instantiated. Either // We need [cls] at runtime - even if [cls] is not instantiated. Either
// as a type argument, for a type literal or for an is-test if [cls] is // as a type argument, for a type literal or for an is-test if [cls] is
// native. // native.
@ -185,7 +181,7 @@ abstract class RuntimeTypesSubstitutionsMixin
// This set reflects the emitted class hierarchy and therefore uses // This set reflects the emitted class hierarchy and therefore uses
// `getEffectiveMixinClass` to find the inherited mixins. // `getEffectiveMixinClass` to find the inherited mixins.
Set<ClassEntity> inheritedClasses = {}; Set<ClassEntity> inheritedClasses = {};
ClassEntity other = cls; ClassEntity? other = cls;
while (other != null) { while (other != null) {
inheritedClasses.add(other); inheritedClasses.add(other);
if (classUse.instance && if (classUse.instance &&
@ -193,7 +189,7 @@ abstract class RuntimeTypesSubstitutionsMixin
// We don't mixin [other] if [cls] isn't instantiated, directly or // We don't mixin [other] if [cls] isn't instantiated, directly or
// indirectly. // indirectly.
inheritedClasses inheritedClasses
.add(_elementEnvironment.getEffectiveMixinClass(other)); .add(_elementEnvironment.getEffectiveMixinClass(other)!);
} }
other = _elementEnvironment.getSuperClass(other); other = _elementEnvironment.getSuperClass(other);
} }
@ -263,7 +259,7 @@ abstract class RuntimeTypesSubstitutionsMixin
_elementEnvironment.isGenericClass(checkedClass); _elementEnvironment.isGenericClass(checkedClass);
// The checks for [checkedClass] inherited for [superClass]. // The checks for [checkedClass] inherited for [superClass].
TypeCheck checkFromSuperClass = TypeCheck? checkFromSuperClass =
superChecks != null ? superChecks[checkedClass] : null; superChecks != null ? superChecks[checkedClass] : null;
// Whether [cls] need an explicit $isX property for [checkedClass]. // Whether [cls] need an explicit $isX property for [checkedClass].
@ -294,11 +290,10 @@ abstract class RuntimeTypesSubstitutionsMixin
// We need a non-trivial substitution function for // We need a non-trivial substitution function for
// [checkedClass]. // [checkedClass].
Substitution substitution = Substitution substitution =
computeSubstitution(cls, checkedClass); computeSubstitution(cls, checkedClass)!;
checks checks
.add(TypeCheck(checkedClass, substitution, needsIs: false)); .add(TypeCheck(checkedClass, substitution, needsIs: false));
assert(substitution != null);
for (DartType argument in substitution.arguments) { for (DartType argument in substitution.arguments) {
argument = argument.withoutNullability; argument = argument.withoutNullability;
if (argument is InterfaceType) { if (argument is InterfaceType) {
@ -318,11 +313,10 @@ abstract class RuntimeTypesSubstitutionsMixin
// We need a non-trivial substitution function for // We need a non-trivial substitution function for
// [checkedClass]. // [checkedClass].
Substitution substitution = Substitution substitution =
computeSubstitution(cls, checkedClass); computeSubstitution(cls, checkedClass)!;
checks checks
.add(TypeCheck(checkedClass, substitution, needsIs: needsIs)); .add(TypeCheck(checkedClass, substitution, needsIs: needsIs));
assert(substitution != null);
for (DartType argument in substitution.arguments) { for (DartType argument in substitution.arguments) {
argument = argument.withoutNullability; argument = argument.withoutNullability;
if (argument is InterfaceType) { if (argument is InterfaceType) {
@ -361,7 +355,7 @@ abstract class RuntimeTypesSubstitutionsMixin
for (ClassEntity target in checks.classes) { for (ClassEntity target in checks.classes) {
ClassChecks classChecks = checks[target]; ClassChecks classChecks = checks[target];
for (TypeCheck check in classChecks.checks) { for (TypeCheck check in classChecks.checks) {
Substitution substitution = check.substitution; Substitution? substitution = check.substitution;
if (substitution != null) { if (substitution != null) {
collector.collectAll(substitution.arguments); collector.collectAll(substitution.arguments);
} }
@ -398,7 +392,7 @@ abstract class RuntimeTypesSubstitutionsMixin
} }
InterfaceType originalType = _elementEnvironment.getThisType(cls); InterfaceType originalType = _elementEnvironment.getThisType(cls);
InterfaceType type = _types.asInstanceOf(originalType, check); InterfaceType? type = _types.asInstanceOf(originalType, check);
// [type] is not a subtype of [check]. we do not generate a check and do not // [type] is not a subtype of [check]. we do not generate a check and do not
// need a substitution. // need a substitution.
if (type == null) return true; if (type == null) return true;
@ -419,20 +413,12 @@ abstract class RuntimeTypesSubstitutionsMixin
return true; return true;
} }
@override // TODO(sigmund): consider using the existing required checks returned by
Substitution getSubstitution(ClassEntity cls, ClassEntity other) { // `_substitutions.computeChecks` to cache existing substitutions. Before the
// Look for a precomputed check. // null-safety migration this library had logic to retrieve substitutions from
for (TypeCheck check in _requiredChecks[cls].checks) { // such a cache, but the algorithm never used it. Because of that we deleted
if (check.cls == other) { // the caching mechanism altogether.
return check.substitution; Substitution? computeSubstitution(ClassEntity cls, ClassEntity check,
}
}
// There is no precomputed check for this pair (because the check is not
// done on type arguments only. Compute a new substitution.
return computeSubstitution(cls, other);
}
Substitution computeSubstitution(ClassEntity cls, ClassEntity check,
{bool alwaysGenerateFunction = false}) { {bool alwaysGenerateFunction = false}) {
if (isTrivialSubstitution(cls, check)) return null; if (isTrivialSubstitution(cls, check)) return null;
@ -440,7 +426,7 @@ abstract class RuntimeTypesSubstitutionsMixin
// are never instantiated and their checks are overwritten by the class that // are never instantiated and their checks are overwritten by the class that
// they are mixed into. // they are mixed into.
InterfaceType type = _elementEnvironment.getThisType(cls); InterfaceType type = _elementEnvironment.getThisType(cls);
InterfaceType target = _types.asInstanceOf(type, check); InterfaceType target = _types.asInstanceOf(type, check)!;
List<DartType> typeVariables = type.typeArguments; List<DartType> typeVariables = type.typeArguments;
if (_closedWorld.nativeData.isJsInteropClass(cls)) { if (_closedWorld.nativeData.isJsInteropClass(cls)) {
int typeArguments = target.typeArguments.length; int typeArguments = target.typeArguments.length;
@ -458,8 +444,6 @@ abstract class RuntimeTypesSubstitutionsMixin
class TrivialRuntimeTypesSubstitutions extends RuntimeTypesSubstitutionsMixin { class TrivialRuntimeTypesSubstitutions extends RuntimeTypesSubstitutionsMixin {
@override @override
final JClosedWorld _closedWorld; final JClosedWorld _closedWorld;
@override
TypeChecks _requiredChecks;
TrivialRuntimeTypesSubstitutions(this._closedWorld); TrivialRuntimeTypesSubstitutions(this._closedWorld);
} }
@ -495,8 +479,6 @@ class RuntimeTypesImpl
// The set of tested type variable bounds. // The set of tested type variable bounds.
final Set<DartType> checkedBounds = {}; final Set<DartType> checkedBounds = {};
TypeChecks cachedRequiredChecks;
@override @override
bool rtiChecksBuilderClosed = false; bool rtiChecksBuilderClosed = false;
@ -509,10 +491,7 @@ class RuntimeTypesImpl
@override @override
RuntimeTypesNeed get _rtiNeed => _closedWorld.rtiNeed; RuntimeTypesNeed get _rtiNeed => _closedWorld.rtiNeed;
@override Map<ClassEntity, ClassUse>? classUseMapForTesting;
TypeChecks get _requiredChecks => cachedRequiredChecks;
Map<ClassEntity, ClassUse> classUseMapForTesting;
final Set<GenericInstantiation> _genericInstantiations = {}; final Set<GenericInstantiation> _genericInstantiations = {};
@ -547,14 +526,14 @@ class RuntimeTypesImpl
Set<ClassEntity> typeArguments = {}; Set<ClassEntity> typeArguments = {};
Iterable<DartType> instantiateTypeVariable(TypeVariableEntity variable) { Iterable<DartType> instantiateTypeVariable(TypeVariableEntity variable) {
Entity declaration = variable.typeDeclaration; Entity? declaration = variable.typeDeclaration;
int index = variable.index; int index = variable.index;
if (declaration is ClassEntity) { if (declaration is ClassEntity) {
return typeVariableTests return typeVariableTests
.classInstantiationsOf(declaration) .classInstantiationsOf(declaration)
.map((InterfaceType interface) => interface.typeArguments[index]); .map((InterfaceType interface) => interface.typeArguments[index]);
} else { } else {
return typeVariableTests.instantiationsOf(declaration).map( return typeVariableTests.instantiationsOf(declaration!).map(
(GenericInstantiation instantiation) => (GenericInstantiation instantiation) =>
instantiation.typeArguments[index]); instantiation.typeArguments[index]);
} }
@ -573,8 +552,8 @@ class RuntimeTypesImpl
// new A<B Function(C)>(); // new A<B Function(C)>();
// //
// makes A and B live but C tested. // makes A and B live but C tested.
TypeVisitor liveTypeVisitor = TypeVisitor( _TypeVisitor liveTypeVisitor = _TypeVisitor(
onClass: (ClassEntity cls, {TypeVisitorState state}) { onClass: (ClassEntity cls, {required TypeVisitorState state}) {
ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse()); ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
switch (state) { switch (state) {
case TypeVisitorState.covariantTypeArgument: case TypeVisitorState.covariantTypeArgument:
@ -608,8 +587,8 @@ class RuntimeTypesImpl
// o is A<B Function(C)>; // o is A<B Function(C)>;
// //
// makes A and B tested but C live. // makes A and B tested but C live.
TypeVisitor testedTypeVisitor = TypeVisitor( _TypeVisitor testedTypeVisitor = _TypeVisitor(
onClass: (ClassEntity cls, {TypeVisitorState state}) { onClass: (ClassEntity cls, {required TypeVisitorState state}) {
ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse()); ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
switch (state) { switch (state) {
case TypeVisitorState.covariantTypeArgument: case TypeVisitorState.covariantTypeArgument:
@ -641,7 +620,7 @@ class RuntimeTypesImpl
ClassUse classUse = ClassUse classUse =
classUseMap.putIfAbsent(type.element, () => ClassUse()); classUseMap.putIfAbsent(type.element, () => ClassUse());
classUse.directInstance = true; classUse.directInstance = true;
FunctionType callType = _types.getCallType(type); FunctionType? callType = _types.getCallType(type);
if (callType != null) { if (callType != null) {
liveTypeVisitor.visitType(callType, TypeVisitorState.direct); liveTypeVisitor.visitType(callType, TypeVisitorState.direct);
} }
@ -717,7 +696,7 @@ class RuntimeTypesImpl
// closures have a signature method iff they need it and should have a // closures have a signature method iff they need it and should have a
// function type iff they have a signature, we process all classes. // function type iff they have a signature, we process all classes.
void processClass(ClassEntity cls) { void processClass(ClassEntity cls) {
ClassFunctionType functionType = ClassFunctionType? functionType =
_computeFunctionType(_elementEnvironment, cls); _computeFunctionType(_elementEnvironment, cls);
if (functionType != null) { if (functionType != null) {
ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse()); ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
@ -752,10 +731,9 @@ class RuntimeTypesImpl
} }
}); });
cachedRequiredChecks = _computeChecks(classUseMap); final checks = _computeChecks(classUseMap);
rtiChecksBuilderClosed = true; rtiChecksBuilderClosed = true;
return _RuntimeTypesChecks( return _RuntimeTypesChecks(this, checks, typeArguments, typeLiterals);
this, cachedRequiredChecks, typeArguments, typeLiterals);
} }
} }
@ -763,13 +741,13 @@ class RuntimeTypesImpl
/// ///
/// In Dart 1, any class with a `call` method has a function type, in Dart 2 /// In Dart 1, any class with a `call` method has a function type, in Dart 2
/// only closure classes have a function type. /// only closure classes have a function type.
ClassFunctionType _computeFunctionType( ClassFunctionType? _computeFunctionType(
ElementEnvironment elementEnvironment, ClassEntity cls) { ElementEnvironment elementEnvironment, ClassEntity cls) {
FunctionEntity signatureFunction; FunctionEntity? signatureFunction;
if (cls.isClosure) { if (cls.isClosure) {
// Use signature function if available. // Use signature function if available.
signatureFunction = signatureFunction = elementEnvironment.lookupLocalClassMember(
elementEnvironment.lookupLocalClassMember(cls, Names.signature); cls, Names.signature) as FunctionEntity?;
if (signatureFunction == null) { if (signatureFunction == null) {
// In Dart 2, a closure only needs its function type if it has a // In Dart 2, a closure only needs its function type if it has a
// signature function. // signature function.
@ -779,10 +757,10 @@ ClassFunctionType _computeFunctionType(
// Only closures have function type in Dart 2. // Only closures have function type in Dart 2.
return null; return null;
} }
MemberEntity call = MemberEntity? call =
elementEnvironment.lookupLocalClassMember(cls, Names.call); elementEnvironment.lookupLocalClassMember(cls, Names.call);
if (call != null && call.isFunction) { if (call != null && call.isFunction) {
FunctionEntity callFunction = call; FunctionEntity callFunction = call as FunctionEntity;
FunctionType callType = elementEnvironment.getFunctionType(callFunction); FunctionType callType = elementEnvironment.getFunctionType(callFunction);
return ClassFunctionType(callFunction, callType, signatureFunction); return ClassFunctionType(callFunction, callType, signatureFunction);
} }
@ -794,7 +772,7 @@ class TypeCheckMapping implements TypeChecks {
@override @override
ClassChecks operator [](ClassEntity element) { ClassChecks operator [](ClassEntity element) {
ClassChecks result = map[element]; ClassChecks? result = map[element];
return result ?? const ClassChecks.empty(); return result ?? const ClassChecks.empty();
} }
@ -895,15 +873,16 @@ enum TypeVisitorState {
typeLiteral, typeLiteral,
} }
class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> { class _TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
final Set<TypeVariableType> _visitedTypeVariables = {}; final Set<TypeVariableType> _visitedTypeVariables = {};
final Set<FunctionTypeVariable> _visitedFunctionTypeVariables = {}; final Set<FunctionTypeVariable> _visitedFunctionTypeVariables = {};
final void Function(ClassEntity entity, {TypeVisitorState state}) onClass; final void Function(ClassEntity entity, {required TypeVisitorState state})
onClass;
final Iterable<DartType> Function(TypeVariableEntity entity) final Iterable<DartType> Function(TypeVariableEntity entity)
instantiateTypeVariable; instantiateTypeVariable;
TypeVisitor({this.onClass, this.instantiateTypeVariable}); _TypeVisitor({required this.onClass, required this.instantiateTypeVariable});
void visitType(DartType type, TypeVisitorState state) => void visitType(DartType type, TypeVisitorState state) =>
type.accept(this, state); type.accept(this, state);
@ -919,7 +898,6 @@ class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
case TypeVisitorState.typeLiteral: case TypeVisitorState.typeLiteral:
return TypeVisitorState.typeLiteral; return TypeVisitorState.typeLiteral;
} }
throw UnsupportedError("Unexpected TypeVisitorState $state");
} }
TypeVisitorState contravariantArgument(TypeVisitorState state) { TypeVisitorState contravariantArgument(TypeVisitorState state) {
@ -933,7 +911,6 @@ class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
case TypeVisitorState.typeLiteral: case TypeVisitorState.typeLiteral:
return TypeVisitorState.typeLiteral; return TypeVisitorState.typeLiteral;
} }
throw UnsupportedError("Unexpected TypeVisitorState $state");
} }
void visitTypes(List<DartType> types, TypeVisitorState state) { void visitTypes(List<DartType> types, TypeVisitorState state) {
@ -958,7 +935,7 @@ class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
@override @override
void visitTypeVariableType(TypeVariableType type, TypeVisitorState state) { void visitTypeVariableType(TypeVariableType type, TypeVisitorState state) {
if (_visitedTypeVariables.add(type) && instantiateTypeVariable != null) { if (_visitedTypeVariables.add(type)) {
for (DartType instantiation in instantiateTypeVariable(type.element)) { for (DartType instantiation in instantiateTypeVariable(type.element)) {
visitType(instantiation, state); visitType(instantiation, state);
} }
@ -985,9 +962,7 @@ class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
@override @override
void visitInterfaceType(InterfaceType type, TypeVisitorState state) { void visitInterfaceType(InterfaceType type, TypeVisitorState state) {
if (onClass != null) {
onClass(type.element, state: state); onClass(type.element, state: state);
}
visitTypes(type.typeArguments, covariantArgument(state)); visitTypes(type.typeArguments, covariantArgument(state));
} }
@ -1076,7 +1051,7 @@ class ClassUse {
/// ///
/// Furthermore optimization might also omit function type that are known not /// Furthermore optimization might also omit function type that are known not
/// to be valid in any subtype test. /// to be valid in any subtype test.
ClassFunctionType functionType; ClassFunctionType? functionType;
/// `true` if the class is 'live' either through instantiation or use in /// `true` if the class is 'live' either through instantiation or use in
/// type arguments. /// type arguments.

View file

@ -29,7 +29,7 @@ class ClassFunctionType {
class TypeCheck { class TypeCheck {
final ClassEntity cls; final ClassEntity cls;
final bool needsIs; final bool needsIs;
final Substitution substitution; final Substitution? substitution;
@override @override
final int hashCode = _nextHash = (_nextHash + 100003).toUnsigned(30); final int hashCode = _nextHash = (_nextHash + 100003).toUnsigned(30);
static int _nextHash = 0; static int _nextHash = 0;
@ -130,8 +130,6 @@ class Substitution {
abstract class RuntimeTypesSubstitutions { abstract class RuntimeTypesSubstitutions {
bool isTrivialSubstitution(ClassEntity cls, ClassEntity check); bool isTrivialSubstitution(ClassEntity cls, ClassEntity check);
Substitution getSubstitution(ClassEntity cls, ClassEntity other);
Set<ClassEntity> getClassesUsedInSubstitutions(TypeChecks checks); Set<ClassEntity> getClassesUsedInSubstitutions(TypeChecks checks);
static bool hasTypeArguments(DartTypes dartTypes, DartType type) { static bool hasTypeArguments(DartTypes dartTypes, DartType type) {