Version 2.10.0-127.0.dev
Merge commit '809febe06a357dd6225b5620342d86b8304fbb89' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart b/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
index 7e5e457..5dd6961 100644
--- a/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/element/replace_top_bottom_visitor.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/replacement_visitor.dart';
+import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:meta/meta.dart';
@@ -54,6 +55,16 @@
}
@override
+ DartType visitTypeParameterType(TypeParameterType type) {
+ if (!_isCovariant && _typeSystem.isNonNullableByDefault) {
+ if (_typeSystem.isSubtypeOf2(type, NeverTypeImpl.instance)) {
+ return _typeSystem.objectQuestion;
+ }
+ }
+ return null;
+ }
+
+ @override
DartType visitVoidType(VoidType type) {
return _isCovariant ? _bottomType : null;
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 311d391..3c2ce69 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -16,7 +16,6 @@
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
-import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/error/assignment_verifier.dart';
import 'package:analyzer/src/error/codes.dart';
@@ -203,7 +202,7 @@
nameErrorEntity: operator,
);
node.staticElement = result.getter;
- if (_shouldReportInvalidMember(leftType, result)) {
+ if (result.needsGetterError) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_OPERATOR,
operator,
@@ -438,20 +437,6 @@
break;
}
}
-
- /// Return `true` if we should report an error for the lookup [result] on
- /// the [type].
- // TODO(scheglov) this is duplicate
- bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
- if (result.isNone && type != null && !type.isDynamic) {
- if (_typeSystem.isNonNullableByDefault &&
- _typeSystem.isPotentiallyNullable(type)) {
- return false;
- }
- return true;
- }
- return false;
- }
}
class AssignmentExpressionShared {
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index 8addd0d..489aa70 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -344,7 +344,7 @@
node.staticElement = result.getter;
node.staticInvokeType = result.getter?.type;
- if (_shouldReportInvalidMember(leftType, result)) {
+ if (result.needsGetterError) {
if (leftOperand is SuperExpression) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR,
@@ -380,18 +380,4 @@
}
_inferenceHelper.recordStaticType(node, staticType);
}
-
- /// Return `true` if we should report an error for the lookup [result] on
- /// the [type].
- ///
- /// TODO(scheglov) this is duplicate
- bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
- if (result.isNone && type != null && !type.isDynamic) {
- if (_isNonNullableByDefault && _typeSystem.isPotentiallyNullable(type)) {
- return false;
- }
- return true;
- }
- return false;
- }
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index c462243..77e033b 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -641,11 +641,6 @@
nameErrorEntity: nameNode,
);
- if (result.isAmbiguous) {
- _setDynamicResolution(node);
- return;
- }
-
var target = result.getter;
if (target != null) {
nameNode.staticElement = target;
@@ -666,6 +661,10 @@
_setDynamicResolution(node);
+ if (!result.needsGetterError) {
+ return;
+ }
+
String receiverClassName = '<unknown>';
if (receiverType is InterfaceType) {
receiverClassName = receiverType.element.name;
diff --git a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
index 230f66b4..bcebac1 100644
--- a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
@@ -14,7 +14,6 @@
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
-import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
@@ -161,7 +160,7 @@
nameErrorEntity: operand,
);
node.staticElement = result.getter;
- if (_shouldReportInvalidMember(receiverType, result)) {
+ if (result.needsGetterError) {
if (operand is SuperExpression) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR,
@@ -236,18 +235,4 @@
_flowAnalysis?.flow?.nonNullAssert_end(operand);
}
-
- /// Return `true` if we should report an error for the lookup [result] on
- /// the [type].
- ///
- /// TODO(scheglov) this is duplicate
- bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
- if (result.isNone && type != null && !type.isDynamic) {
- if (_isNonNullableByDefault && _typeSystem.isPotentiallyNullable(type)) {
- return false;
- }
- return true;
- }
- return false;
- }
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
index a723362..76cde43 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
@@ -14,7 +14,6 @@
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
-import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
@@ -195,7 +194,7 @@
nameErrorEntity: operand,
);
node.staticElement = result.getter;
- if (_shouldReportInvalidMember(readType, result)) {
+ if (result.needsGetterError) {
if (operand is SuperExpression) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR,
@@ -255,19 +254,4 @@
_flowAnalysis?.flow?.logicalNot_end(node, operand);
}
-
- /// Return `true` if we should report an error for the lookup [result] on
- /// the [type].
- ///
- /// TODO(scheglov) this is duplicate
- bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
- if (result.isNone && type != null && !type.isDynamic) {
- if (_typeSystem.isNonNullableByDefault &&
- _typeSystem.isPotentiallyNullable(type)) {
- return false;
- }
- return true;
- }
- return false;
- }
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index d4ecd63..fd6587a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -190,7 +190,7 @@
targetType.isDartCoreFunction &&
propertyName.name == FunctionElement.CALL_METHOD_NAME) {
// Referencing `.call` on a `Function` type is OK.
- } else {
+ } else if (result.needsGetterError) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_GETTER,
propertyName,
@@ -199,7 +199,7 @@
}
}
- if (hasWrite && result.setter == null && !result.isAmbiguous) {
+ if (hasWrite && result.needsSetterError) {
AssignmentVerifier(_definingLibrary, _errorReporter).verify(
node: propertyName,
requested: null,
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
index f87810c..3a19d28 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_result.dart
@@ -20,38 +20,48 @@
/// Return the element that is invoked for reading.
final ExecutableElement getter;
+ /// If `true`, then the [getter] is `null`, and this is an error that has
+ /// not yet been reported, and the client should report it.
+ ///
+ /// If `false`, then the [getter] is valid. Usually this means that the
+ /// correct target has been found. But the [getter] still might be `null`,
+ /// when there was an error, and it has already been reported (e.g. when
+ /// ambiguous extension); or when `null` is the only possible result (e.g.
+ /// when `dynamicTarget.foo`, or `functionTyped.call`).
+ final bool needsGetterError;
+
/// Return the element that is invoked for writing.
final ExecutableElement setter;
+ /// If `true`, then the [setter] is `null`, and this is an error that has
+ /// not yet been reported, and the client should report it.
+ ///
+ /// If `false`, then the [setter] is valid. Usually this means that the
+ /// correct target has been found. But the [setter] still might be `null`,
+ /// when there was an error, and it has already been reported (e.g. when
+ /// ambiguous extension); or when `null` is the only possible result (e.g.
+ /// when `dynamicTarget.foo`).
+ final bool needsSetterError;
+
/// Initialize a newly created result to represent resolving a single
/// reading and / or writing result.
- ResolutionResult({this.getter, this.setter})
- : assert(getter != null || setter != null),
- state = _ResolutionResultState.single;
+ ResolutionResult({
+ this.getter,
+ this.needsGetterError,
+ this.setter,
+ this.needsSetterError,
+ }) : state = _ResolutionResultState.single;
/// Initialize a newly created result with no elements and the given [state].
const ResolutionResult._(this.state)
: getter = null,
- setter = null;
+ needsGetterError = true,
+ setter = null,
+ needsSetterError = true;
/// Return `true` if this result represents the case where multiple ambiguous
/// elements were found.
bool get isAmbiguous => state == _ResolutionResultState.ambiguous;
-
- /// Return `true` if this result represents the case where no element was
- /// found.
- bool get isNone => state == _ResolutionResultState.none;
-
- /// Return `true` if this result represents the case where a single element
- /// was found.
- bool get isSingle => state == _ResolutionResultState.single;
-
- /// If this is a property, return `true` is the property is static.
- /// If this is a function, return `true` is the function is static.
- /// Otherwise return `false`.
- bool get isStatic {
- return getter?.isStatic ?? setter?.isStatic ?? false;
- }
}
/// The state of a [ResolutionResult].
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index 49df1af..989ea2d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
@@ -28,7 +29,15 @@
SyntacticEntity _nameErrorEntity;
String _name;
- ResolutionResult _result = ResolutionResult.none;
+ bool _needsGetterError;
+ bool _reportedGetterError;
+ ExecutableElement _getterRequested;
+ ExecutableElement _getterRecovery;
+
+ bool _needsSetterError;
+ bool _reportedSetterError;
+ ExecutableElement _setterRequested;
+ ExecutableElement _setterRecovery;
TypePropertyResolver(this._resolver)
: _definingLibrary = _resolver.definingLibrary,
@@ -37,6 +46,10 @@
_typeProvider = _resolver.typeProvider,
_extensionResolver = _resolver.extensionResolver;
+ bool get _hasGetterOrSetter {
+ return _getterRequested != null || _setterRequested != null;
+ }
+
/// Look up the property with the given [name] in the [receiverType].
///
/// The [receiver] might be `null`, used to identify `super`.
@@ -56,91 +69,133 @@
_receiver = receiver;
_name = name;
_nameErrorEntity = nameErrorEntity;
- _result = ResolutionResult.none;
+ _resetResult();
receiverType = _resolveTypeParameter(receiverType);
+ if (receiverType is DynamicTypeImpl) {
+ _lookupInterfaceType(_typeProvider.objectType);
+ _needsGetterError = false;
+ _needsSetterError = false;
+ return _toResult();
+ }
+
if (_isNonNullableByDefault &&
_typeSystem.isPotentiallyNullable(receiverType)) {
+ _lookupInterfaceType(_typeProvider.objectType);
+ if (_hasGetterOrSetter) {
+ return _toResult();
+ }
+
_lookupExtension(receiverType);
-
- if (_result.isNone) {
- _lookupInterfaceType(_typeProvider.objectType);
+ if (_hasGetterOrSetter) {
+ return _toResult();
}
- if (_result.isNone && !receiverType.isDynamic) {
- _resolver.nullableDereferenceVerifier.report(
- receiverErrorNode,
- receiverType,
- );
- // Recovery, get some resolution.
- _lookupType(receiverType);
+ _resolver.nullableDereferenceVerifier.report(
+ receiverErrorNode,
+ receiverType,
+ );
+ _reportedGetterError = true;
+ _reportedSetterError = true;
+
+ // Recovery, get some resolution.
+ if (receiverType is InterfaceType) {
+ _lookupInterfaceType(receiverType);
}
- _toLegacy();
- return _result;
+ return _toResult();
} else {
- _lookupType(receiverType);
-
- if (_result.isNone) {
- _lookupExtension(receiverType);
+ if (receiverType is InterfaceType) {
+ _lookupInterfaceType(receiverType);
+ if (_hasGetterOrSetter) {
+ return _toResult();
+ }
}
- if (_result.isNone) {
- _lookupInterfaceType(_typeProvider.objectType);
+ _lookupExtension(receiverType);
+ if (_hasGetterOrSetter) {
+ return _toResult();
}
- _toLegacy();
- return _result;
+ _lookupInterfaceType(_typeProvider.objectType);
+
+ return _toResult();
}
}
void _lookupExtension(DartType type) {
- _result = _extensionResolver.findExtension(type, _nameErrorEntity, _name);
+ var result =
+ _extensionResolver.findExtension(type, _nameErrorEntity, _name);
+ _reportedGetterError = result.isAmbiguous;
+ _reportedSetterError = result.isAmbiguous;
+
+ if (result.getter != null) {
+ _needsGetterError = false;
+ _getterRequested = result.getter;
+ }
+
+ if (result.setter != null) {
+ _needsSetterError = false;
+ _setterRequested = result.setter;
+ }
}
void _lookupInterfaceType(InterfaceType type) {
var isSuper = _receiver is SuperExpression;
- ExecutableElement typeGetter;
- ExecutableElement typeSetter;
-
if (_name == '[]') {
- typeGetter = type.lookUpMethod2(
+ _getterRequested = type.lookUpMethod2(
'[]',
_definingLibrary,
concrete: isSuper,
inherited: isSuper,
);
+ _needsGetterError = _getterRequested == null;
- typeSetter = type.lookUpMethod2(
+ _setterRequested = type.lookUpMethod2(
'[]=',
_definingLibrary,
concrete: isSuper,
inherited: isSuper,
);
+ _needsSetterError = _setterRequested == null;
} else {
var classElement = type.element as AbstractClassElementImpl;
- var getterName = Name(_definingLibrary.source.uri, _name);
- var setterName = Name(_definingLibrary.source.uri, '$_name=');
- typeGetter = _resolver.inheritance
- .getMember(type, getterName, forSuper: isSuper) ??
- classElement.lookupStaticGetter(_name, _definingLibrary) ??
- classElement.lookupStaticMethod(_name, _definingLibrary);
- typeSetter = _resolver.inheritance
- .getMember(type, setterName, forSuper: isSuper) ??
- classElement.lookupStaticSetter(_name, _definingLibrary);
- }
- if (typeGetter != null || typeSetter != null) {
- _result = ResolutionResult(getter: typeGetter, setter: typeSetter);
+ var getterName = Name(_definingLibrary.source.uri, _name);
+ _getterRequested =
+ _resolver.inheritance.getMember(type, getterName, forSuper: isSuper);
+ _needsGetterError = _getterRequested == null;
+ if (_getterRequested == null) {
+ _getterRecovery ??=
+ classElement.lookupStaticGetter(_name, _definingLibrary) ??
+ classElement.lookupStaticMethod(_name, _definingLibrary);
+ _needsGetterError = _getterRecovery == null;
+ }
+
+ var setterName = Name(_definingLibrary.source.uri, '$_name=');
+ _setterRequested =
+ _resolver.inheritance.getMember(type, setterName, forSuper: isSuper);
+ _needsSetterError = _setterRequested == null;
+ if (_setterRequested == null) {
+ _setterRecovery ??=
+ classElement.lookupStaticSetter(_name, _definingLibrary);
+ _needsSetterError = _setterRecovery == null;
+ }
}
}
- void _lookupType(DartType type) {
- if (type is InterfaceType) {
- _lookupInterfaceType(type);
- }
+ void _resetResult() {
+ _needsGetterError = false;
+ _reportedGetterError = false;
+ _getterRequested = null;
+ _getterRecovery = null;
+
+ _needsSetterError = false;
+ _reportedSetterError = false;
+ _setterRequested = null;
+ _setterRecovery = null;
}
/// If the given [type] is a type parameter, replace it with its bound.
@@ -149,12 +204,21 @@
return type?.resolveToBound(_typeProvider.objectType);
}
- void _toLegacy() {
- if (_result.isSingle) {
- _result = ResolutionResult(
- getter: _resolver.toLegacyElement(_result.getter),
- setter: _resolver.toLegacyElement(_result.setter),
- );
- }
+ ResolutionResult _toResult() {
+ _getterRequested = _resolver.toLegacyElement(_getterRequested);
+ _getterRecovery = _resolver.toLegacyElement(_getterRecovery);
+
+ _setterRequested = _resolver.toLegacyElement(_setterRequested);
+ _setterRecovery = _resolver.toLegacyElement(_setterRecovery);
+
+ var getter = _getterRequested ?? _getterRecovery;
+ var setter = _setterRequested ?? _setterRecovery;
+
+ return ResolutionResult(
+ getter: getter,
+ needsGetterError: _needsGetterError && !_reportedGetterError,
+ setter: setter,
+ needsSetterError: _needsSetterError && !_reportedSetterError,
+ );
}
}
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index e7adf8a..5faf315 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -438,12 +438,12 @@
}
if (isInGetterContext) {
- _checkForUndefinedIndexOperator(
- node, target, getterMethodName, result, result.getter, targetType);
+ _checkForUndefinedIndexOperator(node, target, getterMethodName, result,
+ result.getter, result.needsGetterError, targetType);
}
if (isInSetterContext) {
- _checkForUndefinedIndexOperator(
- node, target, setterMethodName, result, result.setter, targetType);
+ _checkForUndefinedIndexOperator(node, target, setterMethodName, result,
+ result.setter, result.needsSetterError, targetType);
}
}
@@ -879,6 +879,7 @@
String methodName,
ResolutionResult result,
ExecutableElement element,
+ bool needsError,
DartType staticType) {
if (result.isAmbiguous) {
return;
@@ -921,7 +922,7 @@
HintCode.RECEIVER_OF_TYPE_NEVER,
target,
);
- } else {
+ } else if (needsError) {
_errorReporter.reportErrorForOffset(
CompileTimeErrorCode.UNDEFINED_OPERATOR,
offset,
diff --git a/pkg/analyzer/test/src/dart/element/replace_top_bottom_test.dart b/pkg/analyzer/test/src/dart/element/replace_top_bottom_test.dart
index e3a64cf..4851abd 100644
--- a/pkg/analyzer/test/src/dart/element/replace_top_bottom_test.dart
+++ b/pkg/analyzer/test/src/dart/element/replace_top_bottom_test.dart
@@ -79,11 +79,22 @@
// Not contravariant.
_check(neverNone, 'Never');
- _check(
- functionTypeNone(returnType: intNone, parameters: [
- requiredParameter(type: neverNone),
- ]),
- 'int Function(Object?)',
+ void checkContravariant(DartType type, String expectedStr) {
+ _check(
+ functionTypeNone(returnType: intNone, parameters: [
+ requiredParameter(type: type),
+ ]),
+ 'int Function($expectedStr)',
+ );
+ }
+
+ checkContravariant(neverNone, 'Object?');
+
+ checkContravariant(
+ typeParameterTypeNone(
+ typeParameter('T', bound: neverNone),
+ ),
+ 'Object?',
);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index e9936dd..80975a0 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -921,8 +921,9 @@
}
''', [
if (typeToStringWithNullability)
- error(CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE, 11, 4),
- error(CompileTimeErrorCode.UNDEFINED_METHOD, 16, 3),
+ error(CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE, 11, 4)
+ else
+ error(CompileTimeErrorCode.UNDEFINED_METHOD, 16, 3),
]);
_assertUnresolvedMethodInvocation('foo();');
}
@@ -2452,7 +2453,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 27, 1),
- error(CompileTimeErrorCode.UNDEFINED_METHOD, 29, 3),
]);
assertMethodInvocation2(
@@ -2477,7 +2477,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 65, 1),
- error(CompileTimeErrorCode.UNDEFINED_METHOD, 67, 3),
]);
assertMethodInvocation2(
diff --git a/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart b/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
index 207edcb..d9d9872 100644
--- a/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/receiver_of_type_never_test.dart
@@ -190,7 +190,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 24, 1),
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 25, 3),
]);
assertIndexExpression(
@@ -208,8 +207,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 24, 1),
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 25, 3),
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 25, 3),
]);
assertIndexExpression(
@@ -229,7 +226,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 24, 1),
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 25, 3),
]);
assertIndexExpression(
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
index 35eb115..baf399c 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
@@ -235,7 +235,6 @@
''',
expectedErrorsByNullability(nullable: [
error(CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE, 22, 5),
- error(CompileTimeErrorCode.UNDEFINED_GETTER, 28, 3),
], legacy: [
error(CompileTimeErrorCode.UNDEFINED_GETTER, 28, 3),
]));
diff --git a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
index e59a967..a022829 100644
--- a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
@@ -624,7 +624,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 58, 1),
- error(CompileTimeErrorCode.UNDEFINED_GETTER, 60, 3),
]);
}
@@ -876,7 +875,6 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 54, 1),
- error(CompileTimeErrorCode.UNDEFINED_METHOD, 56, 3),
]);
}
diff --git a/pkg/front_end/test/incremental_compiler_leak_test.dart b/pkg/front_end/test/incremental_compiler_leak_test.dart
index 326c2e2..e8927d1 100644
--- a/pkg/front_end/test/incremental_compiler_leak_test.dart
+++ b/pkg/front_end/test/incremental_compiler_leak_test.dart
@@ -1,11 +1,8 @@
import 'dart:async';
import 'dart:io';
-import "package:vm_service/vm_service.dart" as vmService;
-import "package:vm_service/vm_service_io.dart" as vmService;
-
-import "vm_service_heap_helper.dart" as helper;
import "simple_stats.dart";
+import "vm_service_helper.dart" as vmService;
const int limit = 10;
@@ -22,7 +19,7 @@
]);
}
-class LeakFinder extends helper.LaunchingVMServiceHeapHelper {
+class LeakFinder extends vmService.LaunchingVMServiceHelper {
@override
Future<void> run() async {
vmService.VM vm = await serviceClient.getVM();
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index f858baf..e985d0b 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -129,6 +129,8 @@
corners
costly
cov
+coverage
+coverages
cp
csi
ctrl
@@ -282,6 +284,7 @@
heuristics
hi
hints
+hits
home
hoo
hosted
@@ -403,6 +406,7 @@
mismatched
misnamed
miss
+misses
misspelled
mistake
mistakes
@@ -500,6 +504,7 @@
response
result1
result2
+resuming
retaining
retainingpath
retains
diff --git a/pkg/front_end/test/vm_service_coverage.dart b/pkg/front_end/test/vm_service_coverage.dart
new file mode 100644
index 0000000..b144e3d
--- /dev/null
+++ b/pkg/front_end/test/vm_service_coverage.dart
@@ -0,0 +1,142 @@
+import 'dart:async';
+
+import 'vm_service_helper.dart' as vmService;
+
+main(List<String> args) async {
+ CoverageHelper coverageHelper = new CoverageHelper();
+
+ List<String> allArgs = new List<String>();
+ allArgs.addAll([
+ "--disable-dart-dev",
+ "--enable-asserts",
+ "--pause_isolates_on_exit",
+ ]);
+ allArgs.addAll(args);
+
+ coverageHelper.start(allArgs);
+}
+
+class CoverageHelper extends vmService.LaunchingVMServiceHelper {
+ final bool forceCompilation;
+ final bool printHits;
+
+ CoverageHelper({this.forceCompilation: false, this.printHits: true});
+
+ @override
+ Future<void> run() async {
+ vmService.VM vm = await serviceClient.getVM();
+ if (vm.isolates.length != 1) {
+ throw "Expected 1 isolate, got ${vm.isolates.length}";
+ }
+ vmService.IsolateRef isolateRef = vm.isolates.single;
+ await waitUntilIsolateIsRunnable(isolateRef.id);
+ await serviceClient.resume(isolateRef.id);
+ Completer<String> cTimeout = new Completer();
+ Timer timer = new Timer(new Duration(minutes: 20), () {
+ cTimeout.complete("Timeout");
+ killProcess();
+ });
+
+ Completer<String> cRunDone = new Completer();
+ // ignore: unawaited_futures
+ waitUntilPaused(isolateRef.id).then((value) => cRunDone.complete("Done"));
+
+ await Future.any([cRunDone.future, cTimeout.future, cProcessExited.future]);
+
+ timer.cancel();
+
+ if (!await isPausedAtExit(isolateRef.id)) {
+ killProcess();
+ throw "Expected to be paused at exit, but is just paused!";
+ }
+
+ // Get and process coverage information.
+ Stopwatch stopwatch = new Stopwatch()..start();
+ vmService.SourceReport sourceReport = await serviceClient.getSourceReport(
+ isolateRef.id, [vmService.SourceReportKind.kCoverage],
+ forceCompile: forceCompilation);
+ print("Got source report from VM in ${stopwatch.elapsedMilliseconds} ms");
+ stopwatch.reset();
+ Map<Uri, Coverage> coverages = {};
+ for (vmService.SourceReportRange range in sourceReport.ranges) {
+ vmService.ScriptRef script = sourceReport.scripts[range.scriptIndex];
+ Uri scriptUri = Uri.parse(script.uri);
+ if (!includeCoverageFor(scriptUri)) continue;
+ Coverage coverage = coverages[scriptUri] ??= new Coverage();
+
+ vmService.SourceReportCoverage sourceReportCoverage = range.coverage;
+ if (sourceReportCoverage == null) {
+ // Range not compiled. Record the range if provided.
+ assert(!range.compiled);
+ if (range.startPos >= 0 || range.endPos >= 0) {
+ coverage.notCompiled
+ .add(new StartEndPair(range.startPos, range.endPos));
+ }
+ continue;
+ }
+ coverage.hits.addAll(sourceReportCoverage.hits);
+ coverage.misses.addAll(sourceReportCoverage.misses);
+ }
+ print("Processed source report from VM in "
+ "${stopwatch.elapsedMilliseconds} ms");
+ stopwatch.reset();
+
+ // It's paused at exit, so resuming should allow us to exit.
+ await serviceClient.resume(isolateRef.id);
+
+ for (MapEntry<Uri, Coverage> entry in coverages.entries) {
+ assert(entry.value.hits.intersection(entry.value.misses).isEmpty);
+ if (entry.value.hits.isEmpty &&
+ entry.value.misses.isEmpty &&
+ entry.value.notCompiled.isEmpty) {
+ continue;
+ }
+ print(entry.key);
+ if (printHits) {
+ print("Hits: ${entry.value.hits.toList()..sort()}");
+ }
+ print("Misses: ${entry.value.misses.toList()..sort()}");
+ print("Not compiled: ${entry.value.notCompiled.toList()..sort()}");
+ print("");
+ }
+ }
+
+ Completer<String> cProcessExited = new Completer();
+ void processExited(int exitCode) {
+ cProcessExited.complete("Exit");
+ }
+
+ bool includeCoverageFor(Uri uri) {
+ if (uri.scheme == "dart") {
+ return false;
+ }
+ if (uri.scheme == "package") {
+ return uri.pathSegments.first == "front_end" ||
+ uri.pathSegments.first == "_fe_analyzer_shared" ||
+ uri.pathSegments.first == "kernel";
+ }
+ return true;
+ }
+}
+
+class Coverage {
+ final Set<int> hits = {};
+ final Set<int> misses = {};
+ final Set<StartEndPair> notCompiled = {};
+}
+
+class StartEndPair implements Comparable {
+ final int startPos;
+ final int endPos;
+
+ StartEndPair(this.startPos, this.endPos);
+
+ String toString() => "[$startPos - $endPos]";
+
+ @override
+ int compareTo(Object other) {
+ if (other is! StartEndPair) return -1;
+ StartEndPair o = other;
+ return startPos - o.startPos;
+ }
+}
diff --git a/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart b/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
new file mode 100644
index 0000000..4cab31b
--- /dev/null
+++ b/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
@@ -0,0 +1,27 @@
+import 'vm_service_coverage.dart' as helper;
+
+main(List<String> args) async {
+ CoverageHelper coverageHelper = new CoverageHelper();
+
+ List<String> allArgs = new List<String>();
+ allArgs.addAll([
+ "--disable-dart-dev",
+ "--enable-asserts",
+ "--pause_isolates_on_exit",
+ ]);
+ allArgs.addAll(args);
+
+ coverageHelper.start(allArgs);
+}
+
+class CoverageHelper extends helper.CoverageHelper {
+ CoverageHelper() : super(printHits: false);
+
+ bool includeCoverageFor(Uri uri) {
+ if (uri.scheme != "package") return false;
+ if (uri.path.startsWith("front_end/src/fasta/kernel/constant_")) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/pkg/front_end/test/vm_service_heap_finder.dart b/pkg/front_end/test/vm_service_heap_finder.dart
index ea3bcea..2e85184 100644
--- a/pkg/front_end/test/vm_service_heap_finder.dart
+++ b/pkg/front_end/test/vm_service_heap_finder.dart
@@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
-import "vm_service_heap_helper.dart";
+
+import "vm_service_helper.dart" as vmService;
class Foo {
final String x;
@@ -31,7 +32,7 @@
foos.add(new Foo("!", 44));
if (connectTo == null) connectTo = ask("Connect to");
- VMServiceHeapHelperBase vm = VMServiceHeapHelperBase();
+ VMServiceHeapHelperPrinter vm = VMServiceHeapHelperPrinter();
await vm.connect(Uri.parse(connectTo.trim()));
String isolateId = await vm.getIsolateId();
if (classToFind == null) classToFind = ask("Find what class");
@@ -61,3 +62,97 @@
stdout.write("$question: ");
return stdin.readLineSync();
}
+
+class VMServiceHeapHelperPrinter extends vmService.VMServiceHelper {
+ Future<void> printAllocationProfile(String isolateId, {String filter}) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (filter != null) {
+ if (member.classRef.name != filter) continue;
+ } else {
+ if (member.classRef.name == "") continue;
+ if (member.instancesCurrent == 0) continue;
+ }
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ if (c.location?.script?.uri == null) continue;
+ print("${member.classRef.name}: ${member.instancesCurrent}");
+ }
+ }
+
+ Future<void> filterAndPrintInstances(String isolateId, String filter,
+ String fieldName, Set<String> fieldValues) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (member.classRef.name != filter) continue;
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ if (c.location?.script?.uri == null) continue;
+ print("${member.classRef.name}: ${member.instancesCurrent}");
+ print(c.location.script.uri);
+
+ vmService.InstanceSet instances = await serviceClient.getInstances(
+ isolateId, member.classRef.id, 10000);
+ int instanceNum = 0;
+ for (vmService.ObjRef instance in instances.instances) {
+ instanceNum++;
+ vmService.Obj receivedObject =
+ await serviceClient.getObject(isolateId, instance.id);
+ if (receivedObject is! vmService.Instance) continue;
+ vmService.Instance object = receivedObject;
+ for (vmService.BoundField field in object.fields) {
+ if (field.decl.name == fieldName) {
+ if (field.value is vmService.Sentinel) continue;
+ vmService.Obj receivedValue =
+ await serviceClient.getObject(isolateId, field.value.id);
+ if (receivedValue is! vmService.Instance) continue;
+ String value = (receivedValue as vmService.Instance).valueAsString;
+ if (!fieldValues.contains(value)) continue;
+ print("${instanceNum}: ${field.decl.name}: "
+ "${value} --- ${instance.id}");
+ }
+ }
+ }
+ }
+ print("Done!");
+ }
+
+ Future<void> printRetainingPaths(String isolateId, String filter) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (member.classRef.name != filter) continue;
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ print("Found ${c.name} (location: ${c.location})");
+ print("${member.classRef.name}: "
+ "(instancesCurrent: ${member.instancesCurrent})");
+ print("");
+
+ vmService.InstanceSet instances = await serviceClient.getInstances(
+ isolateId, member.classRef.id, 10000);
+ print(" => Got ${instances.instances.length} instances");
+ print("");
+
+ for (vmService.ObjRef instance in instances.instances) {
+ vmService.Obj receivedObject =
+ await serviceClient.getObject(isolateId, instance.id);
+ print("Instance: $receivedObject");
+ vmService.RetainingPath retainingPath =
+ await serviceClient.getRetainingPath(isolateId, instance.id, 1000);
+ print("Retaining path: (length ${retainingPath.length}");
+ for (int i = 0; i < retainingPath.elements.length; i++) {
+ print(" [$i] = ${retainingPath.elements[i]}");
+ }
+
+ print("");
+ }
+ }
+ print("Done!");
+ }
+}
diff --git a/pkg/front_end/test/vm_service_heap_helper.dart b/pkg/front_end/test/vm_service_heap_helper.dart
index d801f3d..497c0b3 100644
--- a/pkg/front_end/test/vm_service_heap_helper.dart
+++ b/pkg/front_end/test/vm_service_heap_helper.dart
@@ -2,288 +2,11 @@
// 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.
-import "dart:convert";
-import "dart:io";
-
-import "package:vm_service/vm_service.dart" as vmService;
-import "package:vm_service/vm_service_io.dart" as vmService;
-
import "dijkstras_sssp_algorithm.dart";
-
-class VMServiceHeapHelperBase {
- vmService.VmService _serviceClient;
- vmService.VmService get serviceClient => _serviceClient;
-
- VMServiceHeapHelperBase();
-
- Future connect(Uri observatoryUri) async {
- String path = observatoryUri.path;
- if (!path.endsWith("/")) path += "/";
- String wsUriString = 'ws://${observatoryUri.authority}${path}ws';
- _serviceClient = await vmService.vmServiceConnectUri(wsUriString,
- log: const StdOutLog());
- }
-
- Future disconnect() async {
- await _serviceClient.dispose();
- }
-
- Future<bool> waitUntilPaused(String isolateId) async {
- int nulls = 0;
- while (true) {
- bool result = await _isPaused(isolateId);
- if (result == null) {
- nulls++;
- if (nulls > 5) {
- // We've now asked for the isolate 5 times and in all cases gotten
- // `Sentinel`. Most likely things aren't working for whatever reason.
- return false;
- }
- } else if (result) {
- return true;
- } else {
- await Future.delayed(const Duration(milliseconds: 100));
- }
- }
- }
-
- Future<bool> _isPaused(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- if (isolate.pauseEvent.kind != "Resume") return true;
- return false;
- }
- return null;
- }
-
- Future<bool> _isPausedAtStart(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- return isolate.pauseEvent.kind == "PauseStart";
- }
- return false;
- }
-
- Future<vmService.AllocationProfile> forceGC(String isolateId) async {
- await waitUntilIsolateIsRunnable(isolateId);
- int expectGcAfter = new DateTime.now().millisecondsSinceEpoch;
- while (true) {
- vmService.AllocationProfile allocationProfile;
- try {
- allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId, gc: true);
- } catch (e) {
- print(e.runtimeType);
- rethrow;
- }
- if (allocationProfile.dateLastServiceGC != null &&
- allocationProfile.dateLastServiceGC >= expectGcAfter) {
- return allocationProfile;
- }
- }
- }
-
- Future<bool> isIsolateRunnable(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- return isolate.runnable;
- }
- return null;
- }
-
- Future<void> waitUntilIsolateIsRunnable(String isolateId) async {
- int nulls = 0;
- while (true) {
- bool result = await isIsolateRunnable(isolateId);
- if (result == null) {
- nulls++;
- if (nulls > 5) {
- // We've now asked for the isolate 5 times and in all cases gotten
- // `Sentinel`. Most likely things aren't working for whatever reason.
- return;
- }
- } else if (result) {
- return;
- } else {
- await Future.delayed(const Duration(milliseconds: 100));
- }
- }
- }
-
- Future<void> printAllocationProfile(String isolateId, {String filter}) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (filter != null) {
- if (member.classRef.name != filter) continue;
- } else {
- if (member.classRef.name == "") continue;
- if (member.instancesCurrent == 0) continue;
- }
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
- }
- }
-
- Future<void> filterAndPrintInstances(String isolateId, String filter,
- String fieldName, Set<String> fieldValues) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
- print(c.location.script.uri);
-
- vmService.InstanceSet instances = await _serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
- int instanceNum = 0;
- for (vmService.ObjRef instance in instances.instances) {
- instanceNum++;
- var receivedObject =
- await _serviceClient.getObject(isolateId, instance.id);
- if (receivedObject is! vmService.Instance) continue;
- vmService.Instance object = receivedObject;
- for (vmService.BoundField field in object.fields) {
- if (field.decl.name == fieldName) {
- if (field.value is vmService.Sentinel) continue;
- var receivedValue =
- await _serviceClient.getObject(isolateId, field.value.id);
- if (receivedValue is! vmService.Instance) continue;
- String value = (receivedValue as vmService.Instance).valueAsString;
- if (!fieldValues.contains(value)) continue;
- print("${instanceNum}: ${field.decl.name}: "
- "${value} --- ${instance.id}");
- }
- }
- }
- }
- print("Done!");
- }
-
- Future<void> printRetainingPaths(String isolateId, String filter) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- print("Found ${c.name} (location: ${c.location})");
- print("${member.classRef.name}: "
- "(instancesCurrent: ${member.instancesCurrent})");
- print("");
-
- vmService.InstanceSet instances = await _serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
- print(" => Got ${instances.instances.length} instances");
- print("");
-
- for (vmService.ObjRef instance in instances.instances) {
- var receivedObject =
- await _serviceClient.getObject(isolateId, instance.id);
- print("Instance: $receivedObject");
- vmService.RetainingPath retainingPath =
- await _serviceClient.getRetainingPath(isolateId, instance.id, 1000);
- print("Retaining path: (length ${retainingPath.length}");
- for (int i = 0; i < retainingPath.elements.length; i++) {
- print(" [$i] = ${retainingPath.elements[i]}");
- }
-
- print("");
- }
- }
- print("Done!");
- }
-
- Future<String> getIsolateId() async {
- vmService.VM vm = await _serviceClient.getVM();
- if (vm.isolates.length != 1) {
- throw "Expected 1 isolate, got ${vm.isolates.length}";
- }
- vmService.IsolateRef isolateRef = vm.isolates.single;
- return isolateRef.id;
- }
-}
-
-abstract class LaunchingVMServiceHeapHelper extends VMServiceHeapHelperBase {
- Process _process;
- Process get process => _process;
-
- bool _started = false;
-
- void start(List<String> scriptAndArgs,
- {void stdinReceiver(String line),
- void stderrReceiver(String line)}) async {
- if (_started) throw "Already started";
- _started = true;
- _process = await Process.start(
- Platform.resolvedExecutable,
- ["--pause_isolates_on_start", "--enable-vm-service=0"]
- ..addAll(scriptAndArgs));
- _process.stdout
- .transform(utf8.decoder)
- .transform(new LineSplitter())
- .listen((line) {
- const kObservatoryListening = 'Observatory listening on ';
- if (line.startsWith(kObservatoryListening)) {
- Uri observatoryUri =
- Uri.parse(line.substring(kObservatoryListening.length));
- _setupAndRun(observatoryUri).catchError((e, st) {
- // Manually kill the process or it will leak,
- // see http://dartbug.com/42918
- killProcess();
- // This seems to rethrow.
- throw e;
- });
- }
- if (stdinReceiver != null) {
- stdinReceiver(line);
- } else {
- stdout.writeln("> $line");
- }
- });
- _process.stderr
- .transform(utf8.decoder)
- .transform(new LineSplitter())
- .listen((line) {
- if (stderrReceiver != null) {
- stderrReceiver(line);
- } else {
- stderr.writeln("> $line");
- }
- });
- // ignore: unawaited_futures
- _process.exitCode.then((value) {
- processExited(value);
- });
- }
-
- void processExited(int exitCode) {}
-
- void killProcess() {
- _process.kill();
- }
-
- Future _setupAndRun(Uri observatoryUri) async {
- await connect(observatoryUri);
- await run();
- }
-
- Future<void> run();
-}
+import "vm_service_helper.dart" as vmService;
class VMServiceHeapHelperSpecificExactLeakFinder
- extends LaunchingVMServiceHeapHelper {
+ extends vmService.LaunchingVMServiceHelper {
final Map<Uri, Map<String, List<String>>> _interests =
new Map<Uri, Map<String, List<String>>>();
final Map<Uri, Map<String, List<String>>> _prettyPrints =
@@ -326,7 +49,7 @@
}
void pause() async {
- await _serviceClient.pause(_isolateRef.id);
+ await serviceClient.pause(_isolateRef.id);
}
vmService.VM _vm;
@@ -336,7 +59,7 @@
/// Best effort check if the isolate is idle.
Future<bool> isIdle() async {
- dynamic tmp = await _serviceClient.getIsolate(_isolateRef.id);
+ dynamic tmp = await serviceClient.getIsolate(_isolateRef.id);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
return isolate.pauseEvent.topFrame == null;
@@ -346,15 +69,15 @@
@override
Future<void> run() async {
- _vm = await _serviceClient.getVM();
+ _vm = await serviceClient.getVM();
if (_vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${_vm.isolates.length}";
}
_isolateRef = _vm.isolates.single;
await forceGC(_isolateRef.id);
- assert(await _isPausedAtStart(_isolateRef.id));
- await _serviceClient.resume(_isolateRef.id);
+ assert(await isPausedAtStart(_isolateRef.id));
+ await serviceClient.resume(_isolateRef.id);
_iterationNumber = 1;
while (true) {
@@ -364,7 +87,7 @@
vmService.HeapSnapshotGraph heapSnapshotGraph =
await vmService.HeapSnapshotGraph.getSnapshot(
- _serviceClient, _isolateRef);
+ serviceClient, _isolateRef);
Set<String> duplicatePrints = {};
Map<String, List<vmService.HeapSnapshotObject>> groupedByToString = {};
@@ -396,7 +119,7 @@
}
}
- await _serviceClient.resume(_isolateRef.id);
+ await serviceClient.resume(_isolateRef.id);
_iterationNumber++;
}
}
@@ -695,20 +418,6 @@
Interest(this.uri, this.className, this.fieldNames);
}
-class StdOutLog implements vmService.Log {
- const StdOutLog();
-
- @override
- void severe(String message) {
- print("> SEVERE: $message");
- }
-
- @override
- void warning(String message) {
- print("> WARNING: $message");
- }
-}
-
HeapGraph convertHeapGraph(vmService.HeapSnapshotGraph graph) {
HeapGraphClassSentinel classSentinel = new HeapGraphClassSentinel();
List<HeapGraphClassActual> classes =
diff --git a/pkg/front_end/test/vm_service_helper.dart b/pkg/front_end/test/vm_service_helper.dart
new file mode 100644
index 0000000..a80eb5a
--- /dev/null
+++ b/pkg/front_end/test/vm_service_helper.dart
@@ -0,0 +1,215 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+import "dart:convert";
+import "dart:io";
+
+import "package:vm_service/vm_service.dart" as vmService;
+import "package:vm_service/vm_service_io.dart" as vmService;
+
+export "package:vm_service/vm_service.dart";
+export "package:vm_service/vm_service_io.dart";
+
+class VMServiceHelper {
+ vmService.VmService _serviceClient;
+ vmService.VmService get serviceClient => _serviceClient;
+
+ VMServiceHelper();
+
+ Future connect(Uri observatoryUri) async {
+ String path = observatoryUri.path;
+ if (!path.endsWith("/")) path += "/";
+ String wsUriString = 'ws://${observatoryUri.authority}${path}ws';
+ _serviceClient = await vmService.vmServiceConnectUri(wsUriString,
+ log: const StdOutLog());
+ }
+
+ Future disconnect() async {
+ await _serviceClient.dispose();
+ }
+
+ Future<bool> waitUntilPaused(String isolateId) async {
+ int nulls = 0;
+ while (true) {
+ bool result = await isPaused(isolateId);
+ if (result == null) {
+ nulls++;
+ if (nulls > 5) {
+ // We've now asked for the isolate 5 times and in all cases gotten
+ // `Sentinel`. Most likely things aren't working for whatever reason.
+ return false;
+ }
+ } else if (result) {
+ return true;
+ } else {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ }
+ }
+
+ Future<bool> isPaused(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ if (isolate.pauseEvent.kind != "Resume") return true;
+ return false;
+ }
+ return null;
+ }
+
+ Future<bool> isPausedAtStart(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.pauseEvent.kind == "PauseStart";
+ }
+ return false;
+ }
+
+ Future<bool> isPausedAtExit(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.pauseEvent.kind == "PauseExit";
+ }
+ return false;
+ }
+
+ Future<vmService.AllocationProfile> forceGC(String isolateId) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ int expectGcAfter = new DateTime.now().millisecondsSinceEpoch;
+ while (true) {
+ vmService.AllocationProfile allocationProfile;
+ try {
+ allocationProfile =
+ await _serviceClient.getAllocationProfile(isolateId, gc: true);
+ } catch (e) {
+ print(e.runtimeType);
+ rethrow;
+ }
+ if (allocationProfile.dateLastServiceGC != null &&
+ allocationProfile.dateLastServiceGC >= expectGcAfter) {
+ return allocationProfile;
+ }
+ }
+ }
+
+ Future<bool> isIsolateRunnable(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.runnable;
+ }
+ return null;
+ }
+
+ Future<void> waitUntilIsolateIsRunnable(String isolateId) async {
+ int nulls = 0;
+ while (true) {
+ bool result = await isIsolateRunnable(isolateId);
+ if (result == null) {
+ nulls++;
+ if (nulls > 5) {
+ // We've now asked for the isolate 5 times and in all cases gotten
+ // `Sentinel`. Most likely things aren't working for whatever reason.
+ return;
+ }
+ } else if (result) {
+ return;
+ } else {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ }
+ }
+
+ Future<String> getIsolateId() async {
+ vmService.VM vm = await _serviceClient.getVM();
+ if (vm.isolates.length != 1) {
+ throw "Expected 1 isolate, got ${vm.isolates.length}";
+ }
+ vmService.IsolateRef isolateRef = vm.isolates.single;
+ return isolateRef.id;
+ }
+}
+
+class StdOutLog implements vmService.Log {
+ const StdOutLog();
+
+ @override
+ void severe(String message) {
+ print("> SEVERE: $message");
+ }
+
+ @override
+ void warning(String message) {
+ print("> WARNING: $message");
+ }
+}
+
+abstract class LaunchingVMServiceHelper extends VMServiceHelper {
+ Process _process;
+ Process get process => _process;
+
+ bool _started = false;
+
+ void start(List<String> scriptAndArgs,
+ {void stdinReceiver(String line),
+ void stderrReceiver(String line)}) async {
+ if (_started) throw "Already started";
+ _started = true;
+ _process = await Process.start(
+ Platform.resolvedExecutable,
+ ["--pause_isolates_on_start", "--enable-vm-service=0"]
+ ..addAll(scriptAndArgs));
+ _process.stdout
+ .transform(utf8.decoder)
+ .transform(new LineSplitter())
+ .listen((line) {
+ const kObservatoryListening = 'Observatory listening on ';
+ if (line.startsWith(kObservatoryListening)) {
+ Uri observatoryUri =
+ Uri.parse(line.substring(kObservatoryListening.length));
+ _setupAndRun(observatoryUri).catchError((e, st) {
+ // Manually kill the process or it will leak,
+ // see http://dartbug.com/42918
+ killProcess();
+ // This seems to rethrow.
+ throw e;
+ });
+ }
+ if (stdinReceiver != null) {
+ stdinReceiver(line);
+ } else {
+ stdout.writeln("> $line");
+ }
+ });
+ _process.stderr
+ .transform(utf8.decoder)
+ .transform(new LineSplitter())
+ .listen((line) {
+ if (stderrReceiver != null) {
+ stderrReceiver(line);
+ } else {
+ stderr.writeln("> $line");
+ }
+ });
+ // ignore: unawaited_futures
+ _process.exitCode.then((value) {
+ processExited(value);
+ });
+ }
+
+ void processExited(int exitCode) {}
+
+ void killProcess() {
+ _process.kill();
+ }
+
+ Future _setupAndRun(Uri observatoryUri) async {
+ await connect(observatoryUri);
+ await run();
+ }
+
+ Future<void> run();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 84e9a2e..a242d7e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 126
+PRERELEASE 127
PRERELEASE_PATCH 0
\ No newline at end of file