Skip to content

Commit

Permalink
Version 3.4.0-70.0.dev
Browse files Browse the repository at this point in the history
Merge f04e42b into dev
  • Loading branch information
Dart CI committed Jan 25, 2024
2 parents 6211fed + f04e42b commit beb0030
Show file tree
Hide file tree
Showing 44 changed files with 779 additions and 351 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,9 @@ mixin TypeAnalyzer<
/// Stack effect: pushes (Expression, Pattern).
///
/// Returns a [PatternForInResult] containing information on reported errors.
///
/// Note, however, that the caller is responsible for reporting an error if
/// the static type of [expression] is potentially nullable.
PatternForInResult<Type, Error> analyzePatternForIn({
required Node node,
required bool hasAwait,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,23 @@ abstract interface class TypeAnalyzerOperations<Variable extends Object,
TypeSchema mapTypeSchema(
{required TypeSchema keyTypeSchema, required TypeSchema valueTypeSchema});

/// If [type] is a subtype of the type `Iterable<T>` for some `T`, returns
/// If [type] is a subtype of the type `Iterable<T>?` for some `T`, returns
/// the type `T`. Otherwise returns `null`.
Type? matchIterableType(Type type);

/// If [typeSchema] is the type schema `Iterable<T>` (or a subtype thereof),
/// If [typeSchema] is the type schema `Iterable<T>?` (or a subtype thereof),
/// for some `T`, returns the type `T`. Otherwise returns `null`.
TypeSchema? matchIterableTypeSchema(TypeSchema typeSchema);

/// If [type] is a subtype of the type `List<T>` for some `T`, returns the
/// If [type] is a subtype of the type `List<T>?` for some `T`, returns the
/// type `T`. Otherwise returns `null`.
Type? matchListType(Type type);

/// If [type] is a subtype of the type `Map<K, V>` for some `K` and `V`,
/// If [type] is a subtype of the type `Map<K, V>?` for some `K` and `V`,
/// returns these `K` and `V`. Otherwise returns `null`.
MapPatternTypeArguments<Type>? matchMapType(Type type);

/// If [type] is a subtype of the type `Stream<T>` for some `T`, returns
/// If [type] is a subtype of the type `Stream<T>?` for some `T`, returns
/// the type `T`. Otherwise returns `null`.
Type? matchStreamType(Type type);

Expand Down
36 changes: 13 additions & 23 deletions pkg/_js_interop_checks/lib/js_interop_checks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -915,46 +915,36 @@ class JsInteropChecks extends RecursiveVisitor {
return false;
}

/// Return whether [type] can be used on a `dart:js_interop` external member
/// or in the signature of a function that is converted via `toJS`.
bool _isAllowedExternalType(DartType type) {
// TODO(joshualitt): We allow only JS types on external JS interop APIs with
// two exceptions: `void` and `Null`. Both of these exceptions exist largely
// to support passing Dart functions to JS as callbacks. Furthermore, both
// of these types mean no actual values needs to be returned to JS. That
// said, for completeness, we may restrict these two types someday, and
// provide JS types equivalents, but likely only if we have implicit
// conversions between Dart types and JS types.

if (type is VoidType || type is NullType) return true;
if (type is TypeParameterType || type is StructuralParameterType) {
final bound = type.nonTypeVariableBound;
final isStaticInteropBound =
bound is InterfaceType && hasStaticInteropAnnotation(bound.classNode);
final isInteropExtensionTypeBound = bound is ExtensionType &&
_extensionIndex
.isInteropExtensionType(bound.extensionTypeDeclaration);
// If it can be used as a representation type of an interop extension
// type, it is okay to be used as a bound.
// TODO(srujzs): We may want to support type parameters with primitive
// bounds that are themselves allowed e.g. `num`. If so, we should handle
// that change in dart2wasm.
if (isStaticInteropBound || isInteropExtensionTypeBound) {
return true;
}
if (_extensionIndex.isAllowedRepresentationType(bound)) return true;
}
// If it can be used as a representation type of an interop extension type,
// it is okay to be used on an external member.
if (_extensionIndex.isAllowedRepresentationType(type)) return true;
if (type is InterfaceType) {
final cls = type.classNode;
// Primitive types are okay.
if (cls == _coreTypes.boolClass ||
cls == _coreTypes.numClass ||
cls == _coreTypes.doubleClass ||
cls == _coreTypes.intClass ||
cls == _coreTypes.stringClass) {
return true;
}
if (hasStaticInteropAnnotation(cls)) return true;
}
if (type is ExtensionType) {
if (_extensionIndex
.isInteropExtensionType(type.extensionTypeDeclaration)) {
return true;
}
} else if (type is ExtensionType) {
// Extension types that wrap other allowed types are also okay. Interop
// extension types are handled above, so this is essentially for extension
// types on primtives.
return _isAllowedExternalType(type.extensionTypeErasure);
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,18 @@ class JsUtilOptimizer extends Transformer {
final Procedure _setPropertyUncheckedTarget;

/// Dynamic members in js_util that interop allowed.
static final Iterable<String> _allowedInteropJsUtilMembers = <String>[
static const List<String> _allowedInteropJsUtilMembers = [
'callConstructor',
'callMethod',
'getProperty',
'jsify',
'newObject',
'setProperty'
];

final Procedure _allowInteropTarget;
final Iterable<Procedure> _allowedInteropJsUtilTargets;
final Procedure _jsTarget;
final Procedure _allowInteropTarget;
final Procedure _listEmptyFactory;

final CoreTypes _coreTypes;
Expand Down Expand Up @@ -690,7 +691,7 @@ class JsUtilOptimizer extends Transformer {
/// member in question if needed. We only process JS interop extension types and
/// extensions on either JS interop or @Native classes.
class ExtensionIndex {
final CoreTypes _coreTypes;
final Map<Reference, Reference?> _coreInteropTypeIndex = {};
final Map<Reference, Annotatable> _extensionAnnotatableIndex = {};
final Map<Reference, Extension> _extensionIndex = {};
final Map<Reference, ExtensionMemberDescriptor> _extensionMemberIndex = {};
Expand All @@ -699,13 +700,15 @@ class ExtensionIndex {
final Map<Reference, ExtensionTypeMemberDescriptor>
_extensionTypeMemberIndex = {};
final Map<Reference, Reference> _extensionTypeTearOffIndex = {};
final Map<Reference, bool> _interopExtensionTypeIndex = {};
final Class? _javaScriptObject;
final Set<Library> _processedExtensionLibraries = {};
final Set<Library> _processedExtensionTypeLibraries = {};
final Set<Reference> _shouldTrustType = {};
final TypeEnvironment _typeEnvironment;

ExtensionIndex(this._coreTypes, this._typeEnvironment);
ExtensionIndex(CoreTypes coreTypes, this._typeEnvironment)
: _javaScriptObject = coreTypes.index
.tryGetClass('dart:_interceptors', 'JavaScriptObject');

/// If unprocessed, for all extension members in [library] whose on-type is a
/// JS interop or `@Native` class, does the following:
Expand Down Expand Up @@ -786,50 +789,71 @@ class ExtensionIndex {
return _extensionTearOffIndex[member.reference];
}

/// Caches and returns whether the ultimate representation type that
/// corresponds to [extensionType]'s representation type is an interop type
/// that can be statically interoperable.
/// Returns whether [extensionType] is an "interop extension type".
///
/// This currently allows the interface type to be:
/// - all package:js classes
/// - dart:js_interop types
/// - @Native types that implement JavaScriptObject
/// - extension types that wrap any of the above
/// Interop extension types have either another interop extension type or a
/// "core" interop type (see below) as their representation type. Extension
/// types can only declare external JS interop members if they are interop
/// extension types.
bool isInteropExtensionType(ExtensionTypeDeclaration extensionType) {
final reference = extensionType.reference;
if (_interopExtensionTypeIndex.containsKey(reference)) {
return _interopExtensionTypeIndex[reference]!;
}
// Check if this is an dart:js_interop JS type or recursively an extension
// type on one.
DartType repType = ExtensionType(extensionType, Nullability.nonNullable);
while (repType is ExtensionType) {
final declaration = repType.extensionTypeDeclaration;
return getCoreInteropType(
// Nullability is irrelevant for this purpose.
ExtensionType(extensionType, Nullability.undetermined)) != null;
}

/// Returns the "core" interop type of [type], unwrapping extension types as
/// needed and caching along the way.
///
/// A type is a "core" interop type if it is:
/// - a `dart:js_interop` extension type
/// - a `@staticInterop` type
/// - an `@Native` type that <: `JavaScriptObject`. Note that this excludes
/// `dart:typed_data`, as typed list factories return a type that is
/// <: `JavaScriptObject`, but the typed lists themselves are not such a
/// type. This is expected and intended since unlike `dart:html`,
/// `dart:typed_data` can be used in dart2wasm, and since we do not want
/// typed lists to be considered interoperable there, it makes sense to
/// exclude them here.
///
/// If [type] is allowed and is an extension type, it is an interop extension
/// type as well.
///
/// Returns `null` if there is no [type] that neither wraps nor is a "core"
/// interop type.
Reference? getCoreInteropType(DartType type) {
if (type is ExtensionType) {
final declaration = type.extensionTypeDeclaration;
final reference = declaration.reference;
if (_coreInteropTypeIndex.containsKey(reference)) {
return _coreInteropTypeIndex[reference];
}
if (declaration.enclosingLibrary.importUri.toString() ==
'dart:js_interop') {
return true;
return _coreInteropTypeIndex[reference] = reference;
}
repType = declaration.declaredRepresentationType;
}
if (repType is InterfaceType) {
final cls = repType.classNode;
final javaScriptObject = _coreTypes.index
.tryGetClass('dart:_interceptors', 'JavaScriptObject');
// Note that we recurse instead of using the erasure, as JS types are
// extension types.
return _coreInteropTypeIndex[reference] =
getCoreInteropType(declaration.declaredRepresentationType);
} else if (type is InterfaceType) {
final cls = type.classNode;
final reference = cls.reference;
if (hasStaticInteropAnnotation(cls) ||
(javaScriptObject != null &&
(_javaScriptObject != null &&
hasNativeAnnotation(cls) &&
_typeEnvironment.isSubtypeOf(
repType,
InterfaceType(javaScriptObject, Nullability.nullable),
type,
InterfaceType(_javaScriptObject!, Nullability.nullable),
SubtypeCheckMode.withNullabilities))) {
_interopExtensionTypeIndex[reference] = true;
return true;
return _coreInteropTypeIndex[reference] = reference;
}
}
_interopExtensionTypeIndex[reference] = false;
return false;
return null;
}

bool isAllowedRepresentationType(DartType type) =>
getCoreInteropType(type) != null;

/// If unprocessed, for all extension type members in [library] whose
/// extension type is static interop, does the following:
///
Expand Down
31 changes: 18 additions & 13 deletions pkg/analyzer/lib/src/analysis_options/apply_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,27 @@ extension on AnalysisOptionsImpl {
}

void applyOptionalChecks(YamlNode? config) {
if (config is YamlMap) {
config.nodes.forEach((k, v) {
if (k is YamlScalar && v is YamlScalar) {
var feature = k.value?.toString();
var boolValue = v.boolValue;
if (boolValue != null) {
if (feature == AnalyzerOptions.chromeOsManifestChecks) {
chromeOsManifestChecks = boolValue;
switch (config) {
case YamlMap():
for (var MapEntry(:key, :value) in config.nodes.entries) {
if (key is YamlScalar && value is YamlScalar) {
if (value.boolValue case var boolValue?) {
switch ('${key.value}') {
case AnalyzerOptions.chromeOsManifestChecks:
chromeOsManifestChecks = boolValue;
case AnalyzerOptions.propagateLinterExceptions:
propagateLinterExceptions = boolValue;
}
}
}
}
});
} else if (config is YamlScalar) {
if (config.value?.toString() == AnalyzerOptions.chromeOsManifestChecks) {
chromeOsManifestChecks = true;
}
case YamlScalar():
switch ('${config.value}') {
case AnalyzerOptions.chromeOsManifestChecks:
chromeOsManifestChecks = true;
case AnalyzerOptions.propagateLinterExceptions:
propagateLinterExceptions = true;
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ class ForResolver {
dispatchBody: dispatchBody,
);
_resolver.popRewrite();
_resolver.nullableDereferenceVerifier.expression(
CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
forLoopParts.iterable,
);
}

/// Given an iterable expression from a foreach loop, attempt to infer
Expand Down
Loading

0 comments on commit beb0030

Please sign in to comment.