Skip to content

Commit

Permalink
[dart2wasm] Move is/as checker generation from [Types] into their own…
Browse files Browse the repository at this point in the history
… [IsCheckers]/[AsCheckers] classes

This is analogous how we do it for [PolymorphicDispatchers]

Change-Id: I3b3187ebe11f50659cd89ed01d9fb4b29ab207c5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/382580
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
  • Loading branch information
mkustermann authored and Commit Queue committed Aug 29, 2024
1 parent ed53041 commit 8bbccbe
Showing 1 changed file with 50 additions and 26 deletions.
76 changes: 50 additions & 26 deletions pkg/dart2wasm/lib/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class Types {
/// parameter index range of their corresponding function type.
Map<StructuralParameter, int> functionTypeParameterIndex = Map.identity();

late final asCheckers = AsCheckers(translator, rtt);
late final isCheckers = IsCheckers(translator, rtt);

Types(this.translator);

w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
Expand Down Expand Up @@ -390,14 +393,14 @@ class Types {
final checkOnlyNullAssignability =
_requiresOnlyNullAssignabilityCheck(operandType, testedAgainstType);
final (typeToCheck, :checkArguments) =
_canUseTypeCheckHelper(testedAgainstType, operandType);
isCheckers.canUseTypeCheckHelper(testedAgainstType, operandType);
if (!checkOnlyNullAssignability && typeToCheck != null) {
if (checkArguments) {
for (final typeArgument in typeToCheck.typeArguments) {
makeType(codeGen, typeArgument);
}
}
b.invoke(_generateIsChecker(
b.invoke(isCheckers.generateIsChecker(
typeToCheck, checkArguments, operandType.isPotentiallyNullable));
} else {
if (testedAgainstType is InterfaceType &&
Expand Down Expand Up @@ -465,14 +468,14 @@ class Types {
_requiresOnlyNullAssignabilityCheck(operandType, testedAgainstType);

final (typeToCheck, :checkArguments) =
_canUseTypeCheckHelper(testedAgainstType, operandType);
asCheckers.canUseTypeCheckHelper(testedAgainstType, operandType);
if (!checkOnlyNullAssignability && typeToCheck != null) {
if (checkArguments) {
for (final typeArgument in typeToCheck.typeArguments) {
makeType(codeGen, typeArgument);
}
}
b.invoke(_generateAsChecker(
b.invoke(asCheckers.generateAsChecker(
typeToCheck, checkArguments, operandType.isPotentiallyNullable));
return translator.translateType(testedAgainstType);
}
Expand Down Expand Up @@ -512,9 +515,27 @@ class Types {
return operand.type;
}

bool _requiresOnlyNullAssignabilityCheck(
DartType operandType, DartType testedAgainstType) {
// We may only need to check that either of these hold:
// * value is non-null
// * value is null and the type is nullable.
return translator.typeEnvironment.isSubtypeOf(
operandType,
testedAgainstType.withDeclaredNullability(Nullability.nullable),
type_env.SubtypeCheckMode.withNullabilities);
}
}

abstract class _TypeCheckers {
final RuntimeTypeInformation rtt;
final type_env.TypeEnvironment typeEnvironment;

_TypeCheckers(this.typeEnvironment, this.rtt);

// If a type check helper can be used, returns the type the caller has to
// check and whether arguments of the type have to be checked or not.
(InterfaceType?, {bool checkArguments}) _canUseTypeCheckHelper(
(InterfaceType?, {bool checkArguments}) canUseTypeCheckHelper(
DartType testedAgainstType, DartType operandType) {
// The is/as check helpers are for cid-range checks of interface types.
if (testedAgainstType is! InterfaceType) {
Expand All @@ -540,17 +561,6 @@ class Types {
return (null, checkArguments: false);
}

bool _requiresOnlyNullAssignabilityCheck(
DartType operandType, DartType testedAgainstType) {
// We may only need to check that either of these hold:
// * value is non-null
// * value is null and the type is nullable.
return translator.typeEnvironment.isSubtypeOf(
operandType,
testedAgainstType.withDeclaredNullability(Nullability.nullable),
type_env.SubtypeCheckMode.withNullabilities);
}

bool _staticTypesEnsureTypeArgumentsMatch(
InterfaceType testedAgainstType, InterfaceType operandType) {
assert(testedAgainstType.typeArguments.isNotEmpty);
Expand All @@ -559,12 +569,11 @@ class Types {
// to tell us anything about the type arguments of testedAgainstType.
if (operandType.typeArguments.isEmpty) return false;

final sufficiency = translator.typeEnvironment
.computeTypeShapeCheckSufficiency(
expressionStaticType: operandType,
checkTargetType:
testedAgainstType.withDeclaredNullability(Nullability.nullable),
subtypeCheckMode: type_env.SubtypeCheckMode.withNullabilities);
final sufficiency = typeEnvironment.computeTypeShapeCheckSufficiency(
expressionStaticType: operandType,
checkTargetType:
testedAgainstType.withDeclaredNullability(Nullability.nullable),
subtypeCheckMode: type_env.SubtypeCheckMode.withNullabilities);

// If `true` the caller only needs to check nullabillity and the actual
// concrete class, no need to check [testedAgainstType] arguments.
Expand Down Expand Up @@ -592,6 +601,13 @@ class Types {
];
return InterfaceType(type.classNode, type.nullability, args);
}
}

class IsCheckers extends _TypeCheckers {
final Translator translator;

IsCheckers(this.translator, RuntimeTypeInformation rtt)
: super(translator.typeEnvironment, rtt);

final Map<DartType, IsCheckerCallTarget> _nullableIsCheckers = {};
final Map<DartType, IsCheckerCallTarget> _isCheckers = {};
Expand All @@ -601,7 +617,7 @@ class Types {

// Currently the is-checker helper functions only check nullability and the
// concrete class (the arguments do not have to be checked).
CallTarget _generateIsChecker(InterfaceType testedAgainstType,
CallTarget generateIsChecker(InterfaceType testedAgainstType,
bool checkArguments, bool operandIsNullable) {
assert(_hasOnlyDefaultTypeArguments(testedAgainstType) || checkArguments);

Expand Down Expand Up @@ -631,14 +647,21 @@ class Types {
operandIsNullable, checkArguments, argumentCount);
});
}
}

class AsCheckers extends _TypeCheckers {
final Translator translator;

AsCheckers(this.translator, RuntimeTypeInformation rtt)
: super(translator.typeEnvironment, rtt);

final Map<DartType, AsCheckerCallTarget> _nullableAsCheckers = {};
final Map<DartType, AsCheckerCallTarget> _asCheckers = {};
final Map<DartType, AsCheckerCallTarget> _asCheckersWithArgumentsCheck = {};
final Map<DartType, AsCheckerCallTarget>
_nullableAsCheckersWithArgumentsCheck = {};

CallTarget _generateAsChecker(InterfaceType testedAgainstType,
CallTarget generateAsChecker(InterfaceType testedAgainstType,
bool checkArguments, bool operandIsNullable) {
assert(_hasOnlyDefaultTypeArguments(testedAgainstType) || checkArguments);

Expand Down Expand Up @@ -775,7 +798,8 @@ class IsCheckerCodeGenerator implements CodeGenerator {
// performance.
const bool forceInline = true;
b.invoke(
translator.types._generateIsChecker(testedAgainstType, false, false),
translator.types.isCheckers
.generateIsChecker(testedAgainstType, false, false),
forceInline: forceInline);
b.local_set(boolTemp);

Expand Down Expand Up @@ -925,7 +949,7 @@ class AsCheckerCodeGenerator implements CodeGenerator {
// performance.
const bool forceInline = true;
b.invoke(
translator.types._generateIsChecker(
translator.types.isCheckers.generateIsChecker(
testedAgainstType, checkArguments, operandIsNullable),
forceInline: forceInline);
b.br_if(asCheckBlock);
Expand Down

0 comments on commit 8bbccbe

Please sign in to comment.