Version 2.14.0-78.0.dev
Merge commit 'df8024189956eb428876b47f8837072617dce08b' into 'dev'
diff --git a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
index 65eaa13..67323f9 100644
--- a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
@@ -2,6 +2,8 @@
// 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 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
@@ -108,6 +110,50 @@
expect(contents[mainFilePath], equals(expectedContent));
}
+ Future<void> test_errorMessage_invalidIntegers() async {
+ // A VS Code code coverage extension has been seen to use Number.MAX_VALUE
+ // for the character position and resulted in:
+ //
+ // type 'double' is not a subtype of type 'int'
+ //
+ // This test ensures the error message for these invalid params is clearer,
+ // indicating this is not a valid (Dart) int.
+ // https://github.com/dart-lang/sdk/issues/42786
+
+ newFile(mainFilePath);
+ await initialize();
+
+ final request = makeRequest(
+ Method.textDocument_codeAction,
+ _RawParams('''
+ {
+ "textDocument": {
+ "uri": "${mainFileUri.toString()}"
+ },
+ "range": {
+ "start": {
+ "line": 3,
+ "character": 2
+ },
+ "end": {
+ "line": 3,
+ "character": 1.7976931348623157e+308
+ }
+ }
+ }
+ '''),
+ );
+ final resp = await sendRequestToServer(request);
+ final error = resp.error!;
+ expect(error.code, equals(ErrorCodes.InvalidParams));
+ expect(
+ error.message,
+ allOf([
+ contains('Invalid params for textDocument/codeAction'),
+ contains('params.range.end.character must be of type int'),
+ ]));
+ }
+
Future<void> test_nonDartFile() async {
newFile(pubspecFilePath, content: simplePubspecContent);
await initialize(
@@ -288,3 +334,12 @@
.toList(),
);
}
+
+class _RawParams extends ToJsonable {
+ final String _json;
+
+ _RawParams(this._json);
+
+ @override
+ Object toJson() => jsonDecode(_json);
+}
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index f7b74c7..d80784a 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.dart
@@ -159,6 +159,7 @@
TreeNode visitInvalidExpression(InvalidExpression expr) => nullary(expr);
TreeNode visitSuperPropertyGet(SuperPropertyGet expr) => nullary(expr);
TreeNode visitStaticGet(StaticGet expr) => nullary(expr);
+ TreeNode visitStaticTearOff(StaticTearOff expr) => nullary(expr);
TreeNode visitRethrow(Rethrow expr) => nullary(expr);
// Getting a final or const variable is not an effect so it can be evaluated
@@ -211,15 +212,32 @@
});
}
+ @override
TreeNode visitVariableSet(VariableSet expr) => unary(expr);
+ @override
TreeNode visitPropertyGet(PropertyGet expr) => unary(expr);
+ @override
+ TreeNode visitInstanceGet(InstanceGet expr) => unary(expr);
+ @override
+ TreeNode visitDynamicGet(DynamicGet expr) => unary(expr);
+ @override
+ TreeNode visitInstanceTearOff(InstanceTearOff expr) => unary(expr);
+ @override
+ TreeNode visitFunctionTearOff(FunctionTearOff expr) => unary(expr);
+ @override
TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr);
+ @override
TreeNode visitStaticSet(StaticSet expr) => unary(expr);
+ @override
TreeNode visitNot(Not expr) => unary(expr);
+ @override
TreeNode visitIsExpression(IsExpression expr) => unary(expr);
+ @override
TreeNode visitAsExpression(AsExpression expr) => unary(expr);
+ @override
TreeNode visitThrow(Throw expr) => unary(expr);
+ @override
TreeNode visitPropertySet(PropertySet expr) {
return transformTreeNode(expr, () {
expr.value = transform(expr.value)..parent = expr;
@@ -227,6 +245,22 @@
});
}
+ @override
+ TreeNode visitInstanceSet(InstanceSet expr) {
+ return transformTreeNode(expr, () {
+ expr.value = transform(expr.value)..parent = expr;
+ expr.receiver = transform(expr.receiver)..parent = expr;
+ });
+ }
+
+ @override
+ TreeNode visitDynamicSet(DynamicSet expr) {
+ return transformTreeNode(expr, () {
+ expr.value = transform(expr.value)..parent = expr;
+ expr.receiver = transform(expr.receiver)..parent = expr;
+ });
+ }
+
TreeNode visitArguments(Arguments args) {
for (var named in args.named.reversed) {
named.value = transform(named.value)..parent = named;
@@ -240,6 +274,7 @@
return args;
}
+ @override
TreeNode visitMethodInvocation(MethodInvocation expr) {
return transformTreeNode(expr, () {
visitArguments(expr.arguments);
@@ -247,6 +282,48 @@
});
}
+ @override
+ TreeNode visitInstanceInvocation(InstanceInvocation expr) {
+ return transformTreeNode(expr, () {
+ visitArguments(expr.arguments);
+ expr.receiver = transform(expr.receiver)..parent = expr;
+ });
+ }
+
+ @override
+ TreeNode visitLocalFunctionInvocation(LocalFunctionInvocation expr) {
+ return transformTreeNode(expr, () {
+ visitArguments(expr.arguments);
+ });
+ }
+
+ @override
+ TreeNode visitDynamicInvocation(DynamicInvocation expr) {
+ return transformTreeNode(expr, () {
+ visitArguments(expr.arguments);
+ expr.receiver = transform(expr.receiver)..parent = expr;
+ });
+ }
+
+ @override
+ TreeNode visitFunctionInvocation(FunctionInvocation expr) {
+ return transformTreeNode(expr, () {
+ visitArguments(expr.arguments);
+ expr.receiver = transform(expr.receiver)..parent = expr;
+ });
+ }
+
+ @override
+ TreeNode visitEqualsNull(EqualsNull expr) => unary(expr);
+
+ @override
+ TreeNode visitEqualsCall(EqualsCall expr) {
+ return transformTreeNode(expr, () {
+ expr.right = transform(expr.right)..parent = expr;
+ expr.left = transform(expr.left)..parent = expr;
+ });
+ }
+
TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
return transformTreeNode(expr, () {
visitArguments(expr.arguments);
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index e3722a1..c843506 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -55,6 +55,11 @@
receiverType: receiver.getStaticType(_staticTypeContext!));
}
+ void annotateWithFunctionType(TreeNode node, FunctionType type) {
+ _metadata.mapping[node] =
+ new CallSiteAttributesMetadata(receiverType: type);
+ }
+
@override
visitPropertySet(PropertySet node) {
super.visitPropertySet(node);
@@ -65,6 +70,15 @@
}
@override
+ visitInstanceSet(InstanceSet node) {
+ super.visitInstanceSet(node);
+
+ if (hasGenericCovariantParameters(node.interfaceTarget)) {
+ annotateWithType(node, node.receiver);
+ }
+ }
+
+ @override
visitMethodInvocation(MethodInvocation node) {
super.visitMethodInvocation(node);
@@ -76,6 +90,46 @@
}
}
+ @override
+ visitInstanceInvocation(InstanceInvocation node) {
+ super.visitInstanceInvocation(node);
+
+ // TODO(34162): We don't need to save the type here for calls, just whether
+ // or not it's a statically-checked call.
+ if (hasGenericCovariantParameters(node.interfaceTarget)) {
+ annotateWithType(node, node.receiver);
+ }
+ }
+
+ @override
+ visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+ super.visitLocalFunctionInvocation(node);
+
+ // TODO(34162): We don't need to save the type here for calls, just whether
+ // or not it's a statically-checked call.
+ annotateWithFunctionType(node, node.functionType);
+ }
+
+ @override
+ visitFunctionInvocation(FunctionInvocation node) {
+ super.visitFunctionInvocation(node);
+
+ // TODO(34162): We don't need to save the type here for calls, just whether
+ // or not it's a statically-checked call.
+ annotateWithType(node, node.receiver);
+ }
+
+ @override
+ visitEqualsCall(EqualsCall node) {
+ super.visitEqualsCall(node);
+
+ // TODO(34162): We don't need to save the type here for calls, just whether
+ // or not it's a statically-checked call.
+ if (hasGenericCovariantParameters(node.interfaceTarget)) {
+ annotateWithType(node, node.left);
+ }
+ }
+
/// Return [true] if the given list of [VariableDeclaration] contains
/// any annotated with generic-covariant-impl.
static bool containsGenericCovariantImpl(List<VariableDeclaration> decls) =>
diff --git a/pkg/vm/lib/transformations/devirtualization.dart b/pkg/vm/lib/transformations/devirtualization.dart
index a81d089..16189aa 100644
--- a/pkg/vm/lib/transformations/devirtualization.dart
+++ b/pkg/vm/lib/transformations/devirtualization.dart
@@ -101,11 +101,8 @@
super.visitLibrary(node);
}
- @override
- visitMethodInvocation(MethodInvocation node) {
- super.visitMethodInvocation(node);
-
- final Member target = node.interfaceTarget;
+ void _handleMethodInvocation(
+ TreeNode node, Member target, Arguments arguments) {
if (target != null && !isMethod(target)) {
return;
}
@@ -116,17 +113,42 @@
// check into an assertion once front-end implements all override checks.
if ((directCall != null) &&
isMethod(directCall.target) &&
- isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
+ isLegalTargetForMethodInvocation(directCall.target, arguments) &&
!hasExtraTargetForNull(directCall)) {
makeDirectCall(node, target, directCall);
}
}
@override
- visitPropertyGet(PropertyGet node) {
- super.visitPropertyGet(node);
+ visitMethodInvocation(MethodInvocation node) {
+ super.visitMethodInvocation(node);
+ _handleMethodInvocation(node, node.interfaceTarget, node.arguments);
+ }
- final Member target = node.interfaceTarget;
+ @override
+ visitInstanceInvocation(InstanceInvocation node) {
+ super.visitInstanceInvocation(node);
+ _handleMethodInvocation(node, node.interfaceTarget, node.arguments);
+ }
+
+ @override
+ visitDynamicInvocation(DynamicInvocation node) {
+ super.visitDynamicInvocation(node);
+ _handleMethodInvocation(node, null, node.arguments);
+ }
+
+ @override
+ visitEqualsCall(EqualsCall node) {
+ super.visitEqualsCall(node);
+
+ final target = node.interfaceTarget;
+ final DirectCallMetadata directCall = getDirectCall(node, target);
+ if (directCall != null && !directCall.checkReceiverForNull) {
+ makeDirectCall(node, target, directCall);
+ }
+ }
+
+ void _handlePropertyGet(TreeNode node, Member target) {
if (target != null && !isFieldOrGetter(target)) {
return;
}
@@ -141,16 +163,48 @@
}
@override
- visitPropertySet(PropertySet node) {
- super.visitPropertySet(node);
+ visitPropertyGet(PropertyGet node) {
+ super.visitPropertyGet(node);
+ _handlePropertyGet(node, node.interfaceTarget);
+ }
- final Member target = node.interfaceTarget;
+ @override
+ visitInstanceGet(InstanceGet node) {
+ super.visitInstanceGet(node);
+ _handlePropertyGet(node, node.interfaceTarget);
+ }
+
+ @override
+ visitDynamicGet(DynamicGet node) {
+ super.visitDynamicGet(node);
+ _handlePropertyGet(node, null);
+ }
+
+ void _handlePropertySet(TreeNode node, Member target) {
final DirectCallMetadata directCall =
getDirectCall(node, target, setter: true);
if (directCall != null) {
makeDirectCall(node, target, directCall);
}
}
+
+ @override
+ visitPropertySet(PropertySet node) {
+ super.visitPropertySet(node);
+ _handlePropertySet(node, node.interfaceTarget);
+ }
+
+ @override
+ visitInstanceSet(InstanceSet node) {
+ super.visitInstanceSet(node);
+ _handlePropertySet(node, node.interfaceTarget);
+ }
+
+ @override
+ visitDynamicSet(DynamicSet node) {
+ super.visitDynamicSet(node);
+ _handlePropertySet(node, null);
+ }
}
/// Devirtualization based on the closed-world class hierarchy analysis.
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index a094f53..153a07a 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -704,6 +704,44 @@
return node;
}
+ @override
+ visitInstanceInvocation(InstanceInvocation node) {
+ super.visitInstanceInvocation(node);
+
+ final Member target = node.interfaceTarget;
+ try {
+ if (target == elementAtMethod) {
+ final DartType pointerType =
+ node.receiver.getStaticType(_staticTypeContext);
+ final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
+
+ _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
+
+ Expression inlineSizeOf = _inlineSizeOf(nativeType);
+ if (inlineSizeOf != null) {
+ // Generates `receiver.offsetBy(inlineSizeOfExpression)`.
+ return MethodInvocation(
+ node.receiver,
+ offsetByMethod.name,
+ Arguments([
+ MethodInvocation(
+ node.arguments.positional.single,
+ numMultiplication.name,
+ Arguments([inlineSizeOf]),
+ numMultiplication)
+ ]),
+ offsetByMethod);
+ }
+ }
+ } on _FfiStaticTypeError {
+ // It's OK to swallow the exception because the diagnostics issued will
+ // cause compilation to fail. By continuing, we can report more
+ // diagnostics before compilation ends.
+ }
+
+ return node;
+ }
+
DartType _pointerTypeGetTypeArg(DartType pointerType) {
return pointerType is InterfaceType ? pointerType.typeArguments[0] : null;
}
diff --git a/pkg/vm/lib/transformations/mixin_deduplication.dart b/pkg/vm/lib/transformations/mixin_deduplication.dart
index a328961..8cfc503 100644
--- a/pkg/vm/lib/transformations/mixin_deduplication.dart
+++ b/pkg/vm/lib/transformations/mixin_deduplication.dart
@@ -172,18 +172,51 @@
}
@override
+ visitInstanceGet(InstanceGet node) {
+ node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget)!;
+ super.visitInstanceGet(node);
+ }
+
+ @override
+ visitInstanceTearOff(InstanceTearOff node) {
+ node.interfaceTarget =
+ _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+ super.visitInstanceTearOff(node);
+ }
+
+ @override
visitPropertySet(PropertySet node) {
node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
super.visitPropertySet(node);
}
@override
+ visitInstanceSet(InstanceSet node) {
+ node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget)!;
+ super.visitInstanceSet(node);
+ }
+
+ @override
visitMethodInvocation(MethodInvocation node) {
node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
super.visitMethodInvocation(node);
}
@override
+ visitInstanceInvocation(InstanceInvocation node) {
+ node.interfaceTarget =
+ _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+ super.visitInstanceInvocation(node);
+ }
+
+ @override
+ visitEqualsCall(EqualsCall node) {
+ node.interfaceTarget =
+ _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+ super.visitEqualsCall(node);
+ }
+
+ @override
visitSuperPropertyGet(SuperPropertyGet node) {
node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
super.visitSuperPropertyGet(node);
diff --git a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
index 9b7e27c..0f9284b 100644
--- a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
+++ b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
@@ -190,6 +190,28 @@
}
@override
+ visitInstanceInvocation(InstanceInvocation node) {
+ super.visitInstanceInvocation(node);
+ if (node.receiver is! ThisExpression) {
+ nonThisSelectors.add(new Selector.doInvoke(node.name));
+ }
+ }
+
+ @override
+ visitDynamicInvocation(DynamicInvocation node) {
+ super.visitDynamicInvocation(node);
+ dynamicSelectors.add(new Selector.doInvoke(node.name));
+ }
+
+ @override
+ visitEqualsCall(EqualsCall node) {
+ super.visitEqualsCall(node);
+ if (node.left is! ThisExpression) {
+ nonThisSelectors.add(new Selector.doInvoke(Name('==')));
+ }
+ }
+
+ @override
visitPropertyGet(PropertyGet node) {
super.visitPropertyGet(node);
@@ -209,6 +231,29 @@
}
@override
+ visitInstanceGet(InstanceGet node) {
+ super.visitInstanceGet(node);
+ if (node.receiver is! ThisExpression) {
+ nonThisSelectors.add(new Selector.doGet(node.name));
+ }
+ }
+
+ @override
+ visitDynamicGet(DynamicGet node) {
+ super.visitDynamicGet(node);
+ dynamicSelectors.add(new Selector.doGet(node.name));
+ }
+
+ @override
+ visitInstanceTearOff(InstanceTearOff node) {
+ super.visitInstanceTearOff(node);
+ if (node.receiver is! ThisExpression) {
+ nonThisSelectors.add(new Selector.doGet(node.name));
+ }
+ tearOffSelectors.add(new Selector.doInvoke(node.name));
+ }
+
+ @override
visitPropertySet(PropertySet node) {
super.visitPropertySet(node);
@@ -221,4 +266,18 @@
}
}
}
+
+ @override
+ visitInstanceSet(InstanceSet node) {
+ super.visitInstanceSet(node);
+ if (node.receiver is! ThisExpression) {
+ nonThisSelectors.add(new Selector.doSet(node.name));
+ }
+ }
+
+ @override
+ visitDynamicSet(DynamicSet node) {
+ super.visitDynamicSet(node);
+ dynamicSelectors.add(new Selector.doSet(node.name));
+ }
}
diff --git a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
index 7643e45..865f56d 100644
--- a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
+++ b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
@@ -143,7 +143,8 @@
Statistics.protobufMetadataFieldsPruned += cls.numberOfFieldsPruned;
}
- bool _isUnusedMetadata(_MessageClass cls, MethodInvocation node) {
+ bool _isUnusedMetadataMethodInvocation(
+ _MessageClass cls, MethodInvocation node) {
if (node.interfaceTarget != null &&
node.interfaceTarget.enclosingClass == _builderInfoClass &&
fieldAddingMethods.contains(node.name.text)) {
@@ -152,6 +153,15 @@
}
return false;
}
+
+ bool _isUnusedMetadata(_MessageClass cls, InstanceInvocation node) {
+ if (node.interfaceTarget.enclosingClass == _builderInfoClass &&
+ fieldAddingMethods.contains(node.name.text)) {
+ final tagNumber = (node.arguments.positional[0] as IntLiteral).value;
+ return !cls._usedTags.contains(tagNumber);
+ }
+ return false;
+ }
}
class _MessageClass {
@@ -170,7 +180,7 @@
@override
TreeNode visitMethodInvocation(MethodInvocation node) {
- if (!ph._isUnusedMetadata(cls, node)) {
+ if (!ph._isUnusedMetadataMethodInvocation(cls, node)) {
super.visitMethodInvocation(node);
return node;
}
@@ -197,4 +207,34 @@
ph._builderInfoAddMethod)
..fileOffset = node.fileOffset;
}
+
+ @override
+ TreeNode visitInstanceInvocation(InstanceInvocation node) {
+ if (!ph._isUnusedMetadata(cls, node)) {
+ super.visitInstanceInvocation(node);
+ return node;
+ }
+ // Replace the field metadata method with a dummy call to
+ // `BuilderInfo.add`. This is to preserve the index calculations when
+ // removing a field.
+ // Change the tag-number to 0. Otherwise the decoder will get confused.
+ ++numberOfFieldsPruned;
+ return MethodInvocation(
+ node.receiver,
+ ph._builderInfoAddMethod.name,
+ Arguments(
+ <Expression>[
+ IntLiteral(0), // tagNumber
+ NullLiteral(), // name
+ NullLiteral(), // fieldType
+ NullLiteral(), // defaultOrMaker
+ NullLiteral(), // subBuilder
+ NullLiteral(), // valueOf
+ NullLiteral(), // enumValues
+ ],
+ types: <DartType>[const NullType()],
+ ),
+ ph._builderInfoAddMethod)
+ ..fileOffset = node.fileOffset;
+ }
}
diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
index c6c7608..709604c 100644
--- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
+++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
@@ -233,7 +233,8 @@
shaker.typeFlowAnalysis.nativeCodeOracle
.isMemberReferencedFromNativeCode(member) ||
shaker.typeFlowAnalysis.nativeCodeOracle.isRecognized(member) ||
- getExternalName(member) != null) {
+ getExternalName(member) != null ||
+ member.name.text == '==') {
info.eligible = false;
}
}
@@ -302,6 +303,12 @@
}
@override
+ void visitInstanceInvocation(InstanceInvocation node) {
+ collectCall(node.interfaceTarget, node.arguments);
+ super.visitInstanceInvocation(node);
+ }
+
+ @override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
collectCall(node.interfaceTarget, node.arguments);
super.visitSuperMethodInvocation(node);
@@ -639,6 +646,12 @@
}
@override
+ void visitInstanceInvocation(InstanceInvocation node) {
+ super.visitInstanceInvocation(node);
+ transformCall(node.interfaceTarget, node, node.receiver, node.arguments);
+ }
+
+ @override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
super.visitSuperMethodInvocation(node);
transformCall(node.interfaceTarget, node, null, node.arguments);
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 34c375b..a20c840 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -1131,7 +1131,8 @@
TypeExpr _makeNarrowNotNull(TreeNode node, TypeExpr arg) {
assert(node is NullCheck ||
- node is MethodInvocation && isComparisonWithNull(node));
+ node is MethodInvocation && isComparisonWithNull(node) ||
+ node is EqualsNull);
if (arg is NarrowNotNull) {
nullTests[node] = arg;
return arg;
@@ -1388,6 +1389,40 @@
_variableValues = null;
return;
}
+ } else if (node is EqualsCall && node.left is VariableGet) {
+ final lhs = node.left as VariableGet;
+ final rhs = node.right;
+ if ((rhs is IntLiteral &&
+ _isSubtype(lhs.variable.type,
+ _environment.coreTypes.intLegacyRawType)) ||
+ (rhs is StringLiteral &&
+ _isSubtype(lhs.variable.type,
+ _environment.coreTypes.stringLegacyRawType)) ||
+ (rhs is ConstantExpression &&
+ !_hasOverriddenEquals(lhs.variable.type))) {
+ // 'x == c', where x is a variable and c is a constant.
+ _addUse(_visit(node));
+ final int varIndex = _variablesInfo.varIndex[lhs.variable];
+ if (_variableCells[varIndex] == null) {
+ trueState[varIndex] = _visit(rhs);
+ }
+ _variableValues = null;
+ return;
+ }
+ } else if (node is EqualsNull && node.expression is VariableGet) {
+ final lhs = node.expression as VariableGet;
+ // 'x == null', where x is a variable.
+ final expr = _visit(lhs);
+ _makeCall(node, DirectSelector(_environment.coreTypes.objectEquals),
+ Args<TypeExpr>([expr, _nullType]));
+ final narrowedNotNull = _makeNarrowNotNull(node, expr);
+ final int varIndex = _variablesInfo.varIndex[lhs.variable];
+ if (_variableCells[varIndex] == null) {
+ trueState[varIndex] = _nullType;
+ falseState[varIndex] = narrowedNotNull;
+ }
+ _variableValues = null;
+ return;
} else if (node is IsExpression && node.operand is VariableGet) {
// Handle 'x is T', where x is a variable.
final operand = node.operand as VariableGet;
@@ -1630,6 +1665,30 @@
return result;
}
+ @override
+ TypeExpr visitInstanceInvocation(InstanceInvocation node) {
+ final receiverNode = node.receiver;
+ final receiver = _visit(receiverNode);
+ final args = _visitArguments(receiver, node.arguments);
+ final target = node.interfaceTarget;
+ if (receiverNode is ConstantExpression && node.name.text == '[]') {
+ Constant constant = receiverNode.constant;
+ if (constant is ListConstant) {
+ return _handleIndexingIntoListConstant(constant);
+ }
+ }
+ assert(target is Procedure && !target.isGetter);
+ // TODO(alexmarkov): overloaded arithmetic operators
+ final result = _makeCall(
+ node,
+ (node.receiver is ThisExpression)
+ ? new VirtualSelector(target)
+ : new InterfaceSelector(target),
+ args);
+ _updateReceiverAfterCall(receiverNode, receiver, node.name);
+ return result;
+ }
+
TypeExpr _handleIndexingIntoListConstant(ListConstant list) {
final elementTypes = new Set<Type>();
for (var element in list.entries) {
@@ -1649,27 +1708,103 @@
}
@override
- TypeExpr visitPropertyGet(PropertyGet node) {
- var receiver = _visit(node.receiver);
- var args = new Args<TypeExpr>([receiver]);
+ TypeExpr visitDynamicInvocation(DynamicInvocation node) {
+ final receiverNode = node.receiver;
+ final receiver = _visit(receiverNode);
+ final args = _visitArguments(receiver, node.arguments);
+ final result =
+ _makeCall(node, new DynamicSelector(CallKind.Method, node.name), args);
+ _updateReceiverAfterCall(receiverNode, receiver, node.name);
+ return result;
+ }
+
+ @override
+ TypeExpr visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+ _visitArguments(null, node.arguments);
+ return _staticType(node);
+ }
+
+ @override
+ TypeExpr visitFunctionInvocation(FunctionInvocation node) {
+ final receiverNode = node.receiver;
+ final receiver = _visit(receiverNode);
+ _visitArguments(receiver, node.arguments);
+ final result = _staticType(node);
+ _updateReceiverAfterCall(receiverNode, receiver, Name('call'));
+ return result;
+ }
+
+ @override
+ TypeExpr visitEqualsCall(EqualsCall node) {
+ final left = _visit(node.left);
+ final right = _visit(node.right);
final target = node.interfaceTarget;
+ return _makeCall(
+ node,
+ (node.left is ThisExpression)
+ ? new VirtualSelector(target)
+ : new InterfaceSelector(target),
+ Args<TypeExpr>([left, right]));
+ }
+
+ @override
+ TypeExpr visitEqualsNull(EqualsNull node) {
+ final arg = _visit(node.expression);
+ _makeNarrowNotNull(node, arg);
+ _makeCall(node, DirectSelector(_environment.coreTypes.objectEquals),
+ Args<TypeExpr>([arg, _nullType]));
+ return _boolType;
+ }
+
+ TypeExpr _handlePropertyGet(
+ TreeNode node, Expression receiverNode, Member target, Name selector) {
+ var receiver = _visit(receiverNode);
+ var args = new Args<TypeExpr>([receiver]);
TypeExpr result;
if (target == null) {
result = _makeCall(
- node, new DynamicSelector(CallKind.PropertyGet, node.name), args);
+ node, new DynamicSelector(CallKind.PropertyGet, selector), args);
} else {
result = _makeCall(
node,
- (node.receiver is ThisExpression)
+ (receiverNode is ThisExpression)
? new VirtualSelector(target, callKind: CallKind.PropertyGet)
: new InterfaceSelector(target, callKind: CallKind.PropertyGet),
args);
}
- _updateReceiverAfterCall(node.receiver, receiver, node.name);
+ _updateReceiverAfterCall(receiverNode, receiver, selector);
return result;
}
@override
+ TypeExpr visitPropertyGet(PropertyGet node) {
+ return _handlePropertyGet(
+ node, node.receiver, node.interfaceTarget, node.name);
+ }
+
+ @override
+ TypeExpr visitInstanceGet(InstanceGet node) {
+ return _handlePropertyGet(
+ node, node.receiver, node.interfaceTarget, node.name);
+ }
+
+ @override
+ TypeExpr visitInstanceTearOff(InstanceTearOff node) {
+ return _handlePropertyGet(
+ node, node.receiver, node.interfaceTarget, node.name);
+ }
+
+ @override
+ TypeExpr visitFunctionTearOff(FunctionTearOff node) {
+ return _handlePropertyGet(node, node.receiver, null, Name('call'));
+ }
+
+ @override
+ TypeExpr visitDynamicGet(DynamicGet node) {
+ return _handlePropertyGet(node, node.receiver, null, node.name);
+ }
+
+ @override
TypeExpr visitPropertySet(PropertySet node) {
var receiver = _visit(node.receiver);
var value = _visit(node.value);
@@ -1693,6 +1828,35 @@
}
@override
+ TypeExpr visitInstanceSet(InstanceSet node) {
+ var receiver = _visit(node.receiver);
+ var value = _visit(node.value);
+ var args = new Args<TypeExpr>([receiver, value]);
+ final target = node.interfaceTarget;
+ assert((target is Field) || ((target is Procedure) && target.isSetter));
+ _makeCall(
+ node,
+ (node.receiver is ThisExpression)
+ ? new VirtualSelector(target, callKind: CallKind.PropertySet)
+ : new InterfaceSelector(target, callKind: CallKind.PropertySet),
+ args);
+ _updateReceiverAfterCall(node.receiver, receiver, node.name,
+ isSetter: true);
+ return value;
+ }
+
+ @override
+ TypeExpr visitDynamicSet(DynamicSet node) {
+ var receiver = _visit(node.receiver);
+ var value = _visit(node.value);
+ var args = new Args<TypeExpr>([receiver, value]);
+ _makeCall(node, new DynamicSelector(CallKind.PropertySet, node.name), args);
+ _updateReceiverAfterCall(node.receiver, receiver, node.name,
+ isSetter: true);
+ return value;
+ }
+
+ @override
TypeExpr visitSuperMethodInvocation(SuperMethodInvocation node) {
assert(kPartialMixinResolution);
assert(_receiver != null, "Should have receiver. Node: $node");
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 427af73..e7ac652 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -256,7 +256,13 @@
}
final bool markSkipCheck = !callSite.useCheckedEntry &&
- (node is MethodInvocation || node is PropertySet);
+ (node is MethodInvocation ||
+ node is InstanceInvocation ||
+ node is DynamicInvocation ||
+ node is EqualsCall ||
+ node is PropertySet ||
+ node is InstanceSet ||
+ node is DynamicSet);
bool markReceiverNotInt = false;
@@ -264,7 +270,8 @@
// No information is needed for static calls.
if (node is! StaticInvocation &&
node is! StaticSet &&
- node is! StaticGet) {
+ node is! StaticGet &&
+ node is! StaticTearOff) {
// The compiler uses another heuristic in addition to the call-site
// annotation: if the call-site is non-dynamic and the interface target does
// not exist in the parent chain of _Smi (int is used as an approxmiation
@@ -298,11 +305,17 @@
// Tell the table selector assigner about the callsite.
final Selector selector = callSite.selector;
if (selector is InterfaceSelector && !_callSiteUsesDirectCall(node)) {
- if (node is PropertyGet) {
+ if (node is PropertyGet ||
+ node is InstanceGet ||
+ node is InstanceTearOff) {
_tableSelectorAssigner.registerGetterCall(
selector.member, callSite.isNullableReceiver);
} else {
- assert(node is MethodInvocation || node is PropertySet);
+ assert(node is MethodInvocation ||
+ node is InstanceInvocation ||
+ node is EqualsCall ||
+ node is PropertySet ||
+ node is InstanceSet);
_tableSelectorAssigner.registerMethodOrSetterCall(
selector.member, callSite.isNullableReceiver);
}
@@ -428,18 +441,84 @@
}
@override
+ visitInstanceInvocation(InstanceInvocation node) {
+ _annotateCallSite(node, node.interfaceTarget);
+ super.visitInstanceInvocation(node);
+ }
+
+ @override
+ visitDynamicInvocation(DynamicInvocation node) {
+ _annotateCallSite(node, null);
+ super.visitDynamicInvocation(node);
+ }
+
+ @override
+ visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+ _annotateCallSite(node, null);
+ super.visitLocalFunctionInvocation(node);
+ }
+
+ @override
+ visitFunctionInvocation(FunctionInvocation node) {
+ _annotateCallSite(node, null);
+ super.visitFunctionInvocation(node);
+ }
+
+ @override
+ visitEqualsCall(EqualsCall node) {
+ _annotateCallSite(node, null);
+ super.visitEqualsCall(node);
+ }
+
+ @override
visitPropertyGet(PropertyGet node) {
_annotateCallSite(node, node.interfaceTarget);
super.visitPropertyGet(node);
}
@override
+ visitInstanceGet(InstanceGet node) {
+ _annotateCallSite(node, node.interfaceTarget);
+ super.visitInstanceGet(node);
+ }
+
+ @override
+ visitInstanceTearOff(InstanceTearOff node) {
+ _annotateCallSite(node, node.interfaceTarget);
+ super.visitInstanceTearOff(node);
+ }
+
+ @override
+ visitFunctionTearOff(FunctionTearOff node) {
+ _annotateCallSite(node, null);
+ super.visitFunctionTearOff(node);
+ }
+
+ @override
+ visitDynamicGet(DynamicGet node) {
+ _annotateCallSite(node, null);
+ super.visitDynamicGet(node);
+ }
+
+ @override
visitPropertySet(PropertySet node) {
_annotateCallSite(node, node.interfaceTarget);
super.visitPropertySet(node);
}
@override
+ visitInstanceSet(InstanceSet node) {
+ _annotateCallSite(node, node.interfaceTarget);
+ super.visitInstanceSet(node);
+ }
+
+ @override
+ visitDynamicSet(DynamicSet node) {
+ _annotateCallSite(node, null);
+ super.visitDynamicSet(node);
+ }
+
+ @override
visitSuperMethodInvocation(SuperMethodInvocation node) {
_annotateCallSite(node, node.interfaceTarget);
super.visitSuperMethodInvocation(node);
@@ -999,6 +1078,78 @@
}
@override
+ TreeNode visitInstanceInvocation(
+ InstanceInvocation node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall(
+ _flattenArguments(node.arguments, receiver: node.receiver));
+ }
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+ shaker.addUsedMember(node.interfaceTarget);
+ return node;
+ }
+
+ @override
+ TreeNode visitDynamicInvocation(
+ DynamicInvocation node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall(
+ _flattenArguments(node.arguments, receiver: node.receiver));
+ }
+ return node;
+ }
+
+ @override
+ TreeNode visitLocalFunctionInvocation(
+ LocalFunctionInvocation node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall(_flattenArguments(node.arguments));
+ }
+ return node;
+ }
+
+ @override
+ TreeNode visitFunctionInvocation(
+ FunctionInvocation node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall(
+ _flattenArguments(node.arguments, receiver: node.receiver));
+ }
+ return node;
+ }
+
+ @override
+ TreeNode visitEqualsCall(EqualsCall node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.left, node.right]);
+ }
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+ shaker.addUsedMember(node.interfaceTarget);
+ return node;
+ }
+
+ @override
+ TreeNode visitEqualsNull(EqualsNull node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.expression]);
+ }
+ final nullTest = _getNullTest(node);
+ if (nullTest.isAlwaysNull || nullTest.isAlwaysNotNull) {
+ return _evaluateArguments(
+ [node.expression], BoolLiteral(nullTest.isAlwaysNull));
+ }
+ return node;
+ }
+
+ @override
TreeNode visitPropertyGet(PropertyGet node, TreeNode removalSentinel) {
node.transformOrRemoveChildren(this);
if (_isUnreachable(node)) {
@@ -1014,6 +1165,54 @@
}
@override
+ TreeNode visitInstanceGet(InstanceGet node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver]);
+ } else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+ shaker.addUsedMember(node.interfaceTarget);
+ return node;
+ }
+ }
+
+ @override
+ TreeNode visitInstanceTearOff(
+ InstanceTearOff node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver]);
+ } else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+ shaker.addUsedMember(node.interfaceTarget);
+ return node;
+ }
+ }
+
+ @override
+ TreeNode visitDynamicGet(DynamicGet node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver]);
+ } else {
+ return node;
+ }
+ }
+
+ @override
+ TreeNode visitFunctionTearOff(
+ FunctionTearOff node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver]);
+ } else {
+ return node;
+ }
+ }
+
+ @override
TreeNode visitPropertySet(PropertySet node, TreeNode removalSentinel) {
node.transformOrRemoveChildren(this);
if (_isUnreachable(node)) {
@@ -1029,6 +1228,29 @@
}
@override
+ TreeNode visitInstanceSet(InstanceSet node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver, node.value]);
+ } else {
+ node.interfaceTarget = fieldMorpher
+ .adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
+ shaker.addUsedMember(node.interfaceTarget);
+ return node;
+ }
+ }
+
+ @override
+ TreeNode visitDynamicSet(DynamicSet node, TreeNode removalSentinel) {
+ node.transformOrRemoveChildren(this);
+ if (_isUnreachable(node)) {
+ return _makeUnreachableCall([node.receiver, node.value]);
+ } else {
+ return node;
+ }
+ }
+
+ @override
TreeNode visitSuperMethodInvocation(
SuperMethodInvocation node, TreeNode removalSentinel) {
node.transformOrRemoveChildren(this);
diff --git a/runtime/tests/vm/dart/regress_45898_test.dart b/runtime/tests/vm/dart/regress_45898_test.dart
new file mode 100644
index 0000000..fef076b
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45898_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2021, 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:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart' show withTempDir, run;
+
+final buildDir = path.dirname(Platform.executable);
+final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
+final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
+final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
+final genSnapshot =
+ File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
+
+const classCount = 10000;
+const subclassCount = 5000;
+
+// Generates an example that causes generation of a TypeTestingStub checking for
+// 10000 classes - thereby making the TTS larger than 32 KB.
+//
+// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
+// of I0 do not have consecutive class ids.
+String generateExample() {
+ final sb = StringBuffer()..writeln('''
+class I0 {}
+class I1 {}
+ ''');
+ for (int i = 0; i < classCount; ++i) {
+ sb.writeln('class S$i extends I${i % 2} {}');
+ }
+ sb.writeln('final all = <Object>[');
+ for (int i = 0; i < classCount; ++i) {
+ sb.writeln(' S$i(),');
+ }
+ sb.writeln('];');
+ sb.writeln('''
+main() {
+ int succeeded = 0;
+ int failed = 0;
+ for (dynamic obj in all) {
+ try {
+ obj as I0;
+ succeeded++;
+ } on TypeError catch (e, s) {
+ failed++;
+ }
+ }
+ if (succeeded != $subclassCount ||
+ failed != $subclassCount) {
+ throw 'Error: succeeded: \$succeeded, failed: \$failed';
+ }
+}
+ ''');
+ return sb.toString();
+}
+
+void main(List<String> args) async {
+ if (!Platform.isLinux) {
+ // We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
+ // As written it wouldn't run on Windows / Android due to testing setup.
+ return;
+ }
+
+ final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
+
+ await withTempDir('tts', (String temp) async {
+ final script = path.join(temp, 'script.dart');
+ await File(script).writeAsString(generateExample());
+
+ // We always compile to .dill file because simarm/simarm64 runs really slow
+ // from source (and this dart2kernel compilation happens with checked-in
+ // binaries).
+ final scriptDill = path.join(temp, 'script.dart.dill');
+ await run('pkg/vm/tool/gen_kernel', <String>[
+ isAot ? '--aot' : '--no-aot',
+ '--platform=$platformDill',
+ '-o',
+ scriptDill,
+ script,
+ ]);
+
+ String mainFile = scriptDill;
+ if (isAot) {
+ final elfFile = path.join(temp, 'script.dart.dill.elf');
+ await run(genSnapshot, <String>[
+ '--snapshot-kind=app-aot-elf',
+ '--elf=$elfFile',
+ scriptDill,
+ ]);
+ mainFile = elfFile;
+ }
+ await run(Platform.executable, [mainFile]);
+ });
+}
diff --git a/runtime/tests/vm/dart_2/regress_45898_test.dart b/runtime/tests/vm/dart_2/regress_45898_test.dart
new file mode 100644
index 0000000..fef076b
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45898_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2021, 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:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart' show withTempDir, run;
+
+final buildDir = path.dirname(Platform.executable);
+final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
+final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
+final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
+final genSnapshot =
+ File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
+
+const classCount = 10000;
+const subclassCount = 5000;
+
+// Generates an example that causes generation of a TypeTestingStub checking for
+// 10000 classes - thereby making the TTS larger than 32 KB.
+//
+// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
+// of I0 do not have consecutive class ids.
+String generateExample() {
+ final sb = StringBuffer()..writeln('''
+class I0 {}
+class I1 {}
+ ''');
+ for (int i = 0; i < classCount; ++i) {
+ sb.writeln('class S$i extends I${i % 2} {}');
+ }
+ sb.writeln('final all = <Object>[');
+ for (int i = 0; i < classCount; ++i) {
+ sb.writeln(' S$i(),');
+ }
+ sb.writeln('];');
+ sb.writeln('''
+main() {
+ int succeeded = 0;
+ int failed = 0;
+ for (dynamic obj in all) {
+ try {
+ obj as I0;
+ succeeded++;
+ } on TypeError catch (e, s) {
+ failed++;
+ }
+ }
+ if (succeeded != $subclassCount ||
+ failed != $subclassCount) {
+ throw 'Error: succeeded: \$succeeded, failed: \$failed';
+ }
+}
+ ''');
+ return sb.toString();
+}
+
+void main(List<String> args) async {
+ if (!Platform.isLinux) {
+ // We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
+ // As written it wouldn't run on Windows / Android due to testing setup.
+ return;
+ }
+
+ final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
+
+ await withTempDir('tts', (String temp) async {
+ final script = path.join(temp, 'script.dart');
+ await File(script).writeAsString(generateExample());
+
+ // We always compile to .dill file because simarm/simarm64 runs really slow
+ // from source (and this dart2kernel compilation happens with checked-in
+ // binaries).
+ final scriptDill = path.join(temp, 'script.dart.dill');
+ await run('pkg/vm/tool/gen_kernel', <String>[
+ isAot ? '--aot' : '--no-aot',
+ '--platform=$platformDill',
+ '-o',
+ scriptDill,
+ script,
+ ]);
+
+ String mainFile = scriptDill;
+ if (isAot) {
+ final elfFile = path.join(temp, 'script.dart.dill.elf');
+ await run(genSnapshot, <String>[
+ '--snapshot-kind=app-aot-elf',
+ '--elf=$elfFile',
+ scriptDill,
+ ]);
+ mainFile = elfFile;
+ }
+ await run(Platform.executable, [mainFile]);
+ });
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 436bad9..6003995 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -284,7 +284,9 @@
[ $compiler == dartkp && ($arch == simarm || $arch == simarm64 || $arch == simarm64c) ]
dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
+dart/regress_45898_test: Pass, Slow
dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
+dart_2/regress_45898_test: Pass, Slow
[ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index f0dee56..fa584f9 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -305,16 +305,7 @@
ObjectPtr new_obj = ScavengeObject(raw_obj);
// Update the reference.
- if (!new_obj->IsNewObject()) {
- // Setting the mark bit above must not be ordered after a publishing store
- // of this object. Note this could be a publishing store even if the
- // object was promoted by an early invocation of ScavengePointer. Compare
- // Object::Allocate.
- reinterpret_cast<std::atomic<ObjectPtr>*>(p)->store(
- new_obj, std::memory_order_release);
- } else {
- *p = new_obj;
- }
+ *p = new_obj;
// Update the store buffer as needed.
if (visiting_old_object_ != nullptr) {
@@ -412,7 +403,9 @@
// push it to the mark stack after forwarding its slots.
tags = UntaggedObject::OldAndNotMarkedBit::update(
!thread_->is_marking(), tags);
- new_obj->untag()->tags_ = tags;
+ // release: Setting the mark bit above must not be ordered after a
+ // publishing store of this object. Compare Object::Allocate.
+ new_obj->untag()->tags_.store(tags, std::memory_order_release);
}
intptr_t cid = UntaggedObject::ClassIdTag::decode(header);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index d7c6b01..384b853 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2648,11 +2648,9 @@
// concurrent marker on ARM and ARM64 (the marker may observe a
// publishing store of this object before the stores that initialize its
// slots), and helps the collection to finish sooner.
- raw_obj->untag()->SetMarkBitUnsynchronized();
- // Setting the mark bit must not be ordered after a publishing store of
- // this object. Adding a barrier here is cheaper than making every store
- // into the heap a store-release. Compare Scavenger::ScavengePointer.
- std::atomic_thread_fence(std::memory_order_release);
+ // release: Setting the mark bit must not be ordered after a publishing
+ // store of this object. Compare Scavenger::ScavengePointer.
+ raw_obj->untag()->SetMarkBitRelease();
heap->old_space()->AllocateBlack(size);
}
#ifndef PRODUCT
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 50f35b8..7c62065 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -219,13 +219,13 @@
Tags() : tags_(0) {}
operator T() const { return tags_.load(std::memory_order_relaxed); }
-
T operator=(T tags) {
tags_.store(tags, std::memory_order_relaxed);
return tags;
}
T load(std::memory_order order) const { return tags_.load(order); }
+ void store(T value, std::memory_order order) { tags_.store(value, order); }
bool compare_exchange_weak(T old_tags,
T new_tags,
@@ -243,12 +243,13 @@
return TagBitField::decode(*reinterpret_cast<const T*>(&tags_));
}
- template <class TagBitField>
+ template <class TagBitField,
+ std::memory_order order = std::memory_order_relaxed>
void UpdateBool(bool value) {
if (value) {
- tags_.fetch_or(TagBitField::encode(true), std::memory_order_relaxed);
+ tags_.fetch_or(TagBitField::encode(true), order);
} else {
- tags_.fetch_and(~TagBitField::encode(true), std::memory_order_relaxed);
+ tags_.fetch_and(~TagBitField::encode(true), order);
}
}
@@ -320,6 +321,11 @@
ASSERT(!IsMarked());
tags_.UpdateUnsynchronized<OldAndNotMarkedBit>(false);
}
+ void SetMarkBitRelease() {
+ ASSERT(IsOldObject());
+ ASSERT(!IsMarked());
+ tags_.UpdateBool<OldAndNotMarkedBit, std::memory_order_release>(false);
+ }
void ClearMarkBit() {
ASSERT(IsOldObject());
ASSERT(IsMarked());
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index d0ff2d8..260517b 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -2,11 +2,14 @@
// 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.
-#include "vm/type_testing_stubs.h"
+#include <functional>
+
#include "vm/compiler/assembler/disassembler.h"
+#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/stub_code.h"
#include "vm/timeline.h"
+#include "vm/type_testing_stubs.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/flow_graph_compiler.h"
@@ -195,6 +198,37 @@
#if !defined(TARGET_ARCH_IA32)
#if !defined(DART_PRECOMPILED_RUNTIME)
+#if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64)
+#define ONLY_ON_ARM(...) __VA_ARGS__
+#else
+#define ONLY_ON_ARM(...)
+#endif
+
+static CodePtr RetryCompilationWithFarBranches(
+ Thread* thread,
+ std::function<CodePtr(compiler::Assembler&)> fun) {
+ bool use_far_branches = false;
+ while (true) {
+ LongJumpScope jump;
+ if (setjmp(*jump.Set()) == 0) {
+ // To use the already-defined __ Macro !
+ compiler::Assembler assembler(nullptr ONLY_ON_ARM(, use_far_branches));
+ return fun(assembler);
+ } else {
+ // We bailed out or we encountered an error.
+ const Error& error = Error::Handle(thread->StealStickyError());
+ if (error.ptr() == Object::branch_offset_error().ptr()) {
+ ASSERT(!use_far_branches);
+ use_far_branches = true;
+ } else {
+ UNREACHABLE();
+ }
+ }
+ }
+}
+
+#undef ONLY_ON_ARM
+
CodePtr TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
auto thread = Thread::Current();
auto zone = thread->zone();
@@ -214,55 +248,64 @@
slow_tts_stub = thread->isolate_group()->object_store()->slow_tts_stub();
}
- // To use the already-defined __ Macro !
- compiler::Assembler assembler(nullptr);
- compiler::UnresolvedPcRelativeCalls unresolved_calls;
- BuildOptimizedTypeTestStub(&assembler, &unresolved_calls, slow_tts_stub, hi,
- type, type_class);
+ const Code& code = Code::Handle(
+ thread->zone(),
+ RetryCompilationWithFarBranches(
+ thread, [&](compiler::Assembler& assembler) {
+ compiler::UnresolvedPcRelativeCalls unresolved_calls;
+ BuildOptimizedTypeTestStub(&assembler, &unresolved_calls,
+ slow_tts_stub, hi, type, type_class);
- const auto& static_calls_table =
- Array::Handle(zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
- zone, &unresolved_calls));
+ const auto& static_calls_table = Array::Handle(
+ zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
+ zone, &unresolved_calls));
- const char* name = namer_.StubNameForType(type);
- const auto pool_attachment = FLAG_use_bare_instructions
- ? Code::PoolAttachment::kNotAttachPool
- : Code::PoolAttachment::kAttachPool;
+ const char* name = namer_.StubNameForType(type);
+ const auto pool_attachment =
+ FLAG_use_bare_instructions
+ ? Code::PoolAttachment::kNotAttachPool
+ : Code::PoolAttachment::kAttachPool;
- Code& code = Code::Handle(thread->zone());
- auto install_code_fun = [&]() {
- code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
- /*optimized=*/false, /*stats=*/nullptr);
- if (!static_calls_table.IsNull()) {
- code.set_static_calls_target_table(static_calls_table);
- }
- };
+ Code& code = Code::Handle(thread->zone());
+ auto install_code_fun = [&]() {
+ code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
+ /*optimized=*/false, /*stats=*/nullptr);
+ if (!static_calls_table.IsNull()) {
+ code.set_static_calls_target_table(static_calls_table);
+ }
+ };
- // We have to ensure no mutators are running, because:
- //
- // a) We allocate an instructions object, which might cause us to
- // temporarily flip page protections from (RX -> RW -> RX).
- //
- SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
- thread->isolate_group()->RunWithStoppedMutators(install_code_fun,
- /*use_force_growth=*/true);
+ // We have to ensure no mutators are running, because:
+ //
+ // a) We allocate an instructions object, which might cause us to
+ // temporarily flip page protections from (RX -> RW -> RX).
+ //
+ SafepointWriteRwLocker ml(thread,
+ thread->isolate_group()->program_lock());
+ thread->isolate_group()->RunWithStoppedMutators(
+ install_code_fun,
+ /*use_force_growth=*/true);
- Code::NotifyCodeObservers(name, code, /*optimized=*/false);
+ Code::NotifyCodeObservers(name, code, /*optimized=*/false);
- code.set_owner(type);
+ code.set_owner(type);
#ifndef PRODUCT
- if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
- LogBlock lb;
- THR_Print("Code for stub '%s' (type = %s): {\n", name, type.ToCString());
- DisassembleToStdout formatter;
- code.Disassemble(&formatter);
- THR_Print("}\n");
- const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
- if (!object_pool.IsNull()) {
- object_pool.DebugPrint();
- }
- }
+ if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
+ LogBlock lb;
+ THR_Print("Code for stub '%s' (type = %s): {\n", name,
+ type.ToCString());
+ DisassembleToStdout formatter;
+ code.Disassemble(&formatter);
+ THR_Print("}\n");
+ const ObjectPool& object_pool =
+ ObjectPool::Handle(code.object_pool());
+ if (!object_pool.IsNull()) {
+ object_pool.DebugPrint();
+ }
+ }
#endif // !PRODUCT
+ return code.ptr();
+ }));
return code.ptr();
}
diff --git a/tools/VERSION b/tools/VERSION
index 547d936c..0fb429d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 77
+PRERELEASE 78
PRERELEASE_PATCH 0
\ No newline at end of file